diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json index 020c9ce92449..30efc842b00f 100644 --- a/.codeqlmanifest.json +++ b/.codeqlmanifest.json @@ -1,5 +1,6 @@ { "provide": [ "*/ql/src/qlpack.yml", "*/ql/test/qlpack.yml", + "*/ql/examples/qlpack.yml", "*/upgrades/qlpack.yml", "misc/legacy-support/*/qlpack.yml", "misc/suite-helpers/qlpack.yml" ] } diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index eef634fae2a3..8f9ffdde4de4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,6 +4,6 @@ "slevesque.vscode-zipexplorer" ], "settings": { - "codeQL.experimentalBqrsParsing": true + "codeQL.runningQueries.memory": 2048 } } diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 000000000000..cbd1f33a7f66 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,11 @@ +name: "CodeQL config" + +queries: + - uses: security-and-quality + +paths-ignore: + - '/cpp/' + - '/java/' + - '/python/' + - '/javascript/ql/test' + - '/javascript/extractor/tests' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000000..f1844da86cff --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,52 @@ +name: "Code scanning - action" + +on: + push: + pull_request: + schedule: + - cron: '0 9 * * 1' + +jobs: + CodeQL-Build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + # Override language selection by uncommenting this and choosing your languages + with: + languages: csharp + config-file: ./.github/codeql/codeql-config.yml + + # 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@v1 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000000..3606bd75cc55 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,11 @@ +name: "Pull Request Labeler" +on: +- pull_request_target + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v2 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/query-list.yml b/.github/workflows/query-list.yml new file mode 100644 index 000000000000..a9f24b8c9c7c --- /dev/null +++ b/.github/workflows/query-list.yml @@ -0,0 +1,49 @@ +name: Build code scanning query list + +on: + push: + branches: + - main + - 'rc/**' + pull_request: + paths: + - '.github/workflows/query-list.yml' + - 'misc/scripts/generate-code-scanning-query-list.py' + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Clone self (github/codeql) + uses: actions/checkout@v2 + with: + path: codeql + - name: Clone github/codeql-go + uses: actions/checkout@v2 + with: + repository: 'github/codeql-go' + path: codeql-go + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c + with: + repo: "github/codeql-cli-binaries" + version: "latest" + file: "codeql-linux64.zip" + token: ${{ secrets.GITHUB_TOKEN }} + - name: Unzip CodeQL CLI + run: unzip -d codeql-cli codeql-linux64.zip + - name: Build code scanning query list + run: | + PATH="$PATH:codeql-cli/codeql" python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv + - name: Upload code scanning query list + uses: actions/upload-artifact@v2 + with: + name: code-scanning-query-list + path: code-scanning-query-list.csv + diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 8465a7c2f866..8eec4887a1e0 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,8 +3,8 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp // List of extensions which should be recommended for users of this workspace. "recommendations": [ - "github.vscode-codeql" + "GitHub.vscode-codeql" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000000..b467b469f224 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "omnisharp.autoStart": false +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f23df3b4e8bc..fa88395e5d09 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,14 +53,6 @@ After the experimental query is merged, we welcome pull requests to improve it. ## Using your personal data -If you contribute to this project, we will record your name and email -address (as provided by you with your contributions) as part of the code -repositories, which are public. We might also use this information -to contact you in relation to your contributions, as well as in the -normal course of software development. We also store records of your -CLA agreements. Under GDPR legislation, we do this -on the basis of our legitimate interest in creating the CodeQL product. - -Please do get in touch (privacy@github.com) if you have any questions about -this or our data protection policies. +If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product. +Please do get in touch (privacy@github.com) if you have any questions about this or our data protection policies. diff --git a/README.md b/README.md index 08e572a02463..9012e83f10da 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ You can use the [interactive query console](https://lgtm.com/help/lgtm/using-que ## Contributing -We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/master/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query. +We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/main/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query. ## License diff --git a/change-notes/1.25/analysis-cpp.md b/change-notes/1.25/analysis-cpp.md index 7ab98ffe859a..47997f070527 100644 --- a/change-notes/1.25/analysis-cpp.md +++ b/change-notes/1.25/analysis-cpp.md @@ -13,6 +13,8 @@ The following changes in version 1.25 affect C/C++ analysis in all applications. | **Query** | **Expected impact** | **Change** | |----------------------------|------------------------|------------------------------------------------------------------| +| Uncontrolled format string (`cpp/tainted-format-string`) | | This query is now displayed by default on LGTM. | +| Uncontrolled format string (through global variable) (`cpp/tainted-format-string-through-global`) | | This query is now displayed by default on LGTM. | ## Changes to libraries diff --git a/change-notes/1.25/analysis-csharp.md b/change-notes/1.25/analysis-csharp.md index de4761c92489..10684aca4809 100644 --- a/change-notes/1.25/analysis-csharp.md +++ b/change-notes/1.25/analysis-csharp.md @@ -28,27 +28,51 @@ The following changes in version 1.25 affect C# analysis in all applications. such as `A.B`, no longer are considered unbound generics. (Such nested types do, however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.) * The data-flow library has been improved, which affects most security queries by potentially - adding more results. Flow through methods now takes nested field reads/writes into account. - For example, the library is able to track flow from `"taint"` to `Sink()` via the method - `GetF2F1()` in - ```csharp - class C1 - { - string F1; - } - - class C2 - { - C1 F2; - - string GetF2F1() => F2.F1; // Nested field read - - void M() - { - F2 = new C1() { F1 = "taint" }; - Sink(GetF2F1()); // NEW: "taint" reaches here - } - } - ``` + adding more results: + - Flow through methods now takes nested field reads/writes into account. + For example, the library is able to track flow from `"taint"` to `Sink()` via the method + `GetF2F1()` in + ```csharp + class C1 + { + string F1; + } + + class C2 + { + C1 F2; + + string GetF2F1() => F2.F1; // Nested field read + + void M() + { + F2 = new C1() { F1 = "taint" }; + Sink(GetF2F1()); // NEW: "taint" reaches here + } + } + ``` + - Flow through collections is now modeled precisely. For example, instead of modeling an array + store `a[i] = x` as a taint-step from `x` to `a`, we now model it as a data-flow step that + stores `x` into `a`. To get the value back out, a matching read step must be taken. + + For source-code based data-flow analysis, the following constructs are modeled as stores into + collections: + - Direct array assignments, `a[i] = x`. + - Array initializers, `new [] { x }`. + - C# 6-style array initializers, `new C() { Array = { [i] = x } }`. + - Call arguments that match a `params` parameter, where the C# compiler creates an array under-the-hood. + - `yield return` statements. + + The following source-code constructs read from a collection: + - Direct array reads, `a[i]`. + - `foreach` statements. + + For calls out to library code, existing flow summaries have been refined to precisely + capture how they interact with collection contents. For example, a call to + `System.Collections.Generic.List.Add(T)` stores the value of the argument into the + qualifier, and a call to `System.Collections.Generic.List.get_Item(int)` (that is, an + indexer call) reads contents out of the qualifier. Moreover, the effect of + collection-clearing methods such as `System.Collections.Generic.List.Clear()` is now + also modeled. ## Changes to autobuilder diff --git a/change-notes/1.25/analysis-java.md b/change-notes/1.25/analysis-java.md index 7cdd9e491a2b..ab11e5aaaf1e 100644 --- a/change-notes/1.25/analysis-java.md +++ b/change-notes/1.25/analysis-java.md @@ -4,20 +4,26 @@ The following changes in version 1.25 affect Java analysis in all applications. ## General improvements -## New queries - -| **Query** | **Tags** | **Purpose** | -|-----------------------------|-----------|--------------------------------------------------------------------| - +The Java autobuilder has been improved to detect more Gradle Java versions. ## Changes to existing queries | **Query** | **Expected impact** | **Change** | |------------------------------|------------------------|-----------------------------------| - +| Hard-coded credential in API call (`java/hardcoded-credential-api-call`) | More results | The query now recognizes the `BasicAWSCredentials` class of the Amazon client SDK library with hardcoded access key/secret key. | +| Deserialization of user-controlled data (`java/unsafe-deserialization`) | Fewer false positive results | The query no longer reports results using `org.apache.commons.io.serialization.ValidatingObjectInputStream`. | +| Use of a broken or risky cryptographic algorithm (`java/weak-cryptographic-algorithm`) | More results | The query now recognizes the `MessageDigest.getInstance` method. | +| Use of a potentially broken or risky cryptographic algorithm (`java/potentially-weak-cryptographic-algorithm`) | More results | The query now recognizes the `MessageDigest.getInstance` method. | +| Reading from a world writable file (`java/world-writable-file-read`) | More results | The query now recognizes more JDK file operations. | ## Changes to libraries +* The data-flow library has been improved with more taint flow modeling for the + Collections framework and other classes of the JDK. This affects all security + queries using data flow and can yield additional results. +* The data-flow library has been improved with more taint flow modeling for the + Spring framework. This affects all security queries using data flow and can + yield additional results on project that rely on the Spring framework. * The data-flow library has been improved, which affects most security queries by potentially adding more results. Flow through methods now takes nested field reads/writes into account. For example, the library is able to track flow from `"taint"` to `sink()` via the method @@ -39,3 +45,5 @@ The following changes in version 1.25 affect Java analysis in all applications. } } ``` +* The library has been extended with more support for Java 14 features + (`switch` expressions and pattern-matching for `instanceof`). diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 2aada0cbd86a..9491ecb657fc 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -6,22 +6,33 @@ - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) - [bluebird](http://bluebirdjs.com/) - [express](https://www.npmjs.com/package/express) + - [execa](https://www.npmjs.com/package/execa) + - [fancy-log](https://www.npmjs.com/package/fancy-log) - [fastify](https://www.npmjs.com/package/fastify) + - [foreground-child](https://www.npmjs.com/package/foreground-child) - [fstream](https://www.npmjs.com/package/fstream) - [jGrowl](https://github.com/stanlemon/jGrowl) - [jQuery](https://jquery.com/) - [marsdb](https://www.npmjs.com/package/marsdb) + - [micro](https://www.npmjs.com/package/micro/) - [minimongo](https://www.npmjs.com/package/minimongo/) - [mssql](https://www.npmjs.com/package/mssql) - [mysql](https://www.npmjs.com/package/mysql) + - [npmlog](https://www.npmjs.com/package/npmlog) + - [opener](https://www.npmjs.com/package/opener) - [pg](https://www.npmjs.com/package/pg) - [sequelize](https://www.npmjs.com/package/sequelize) - [spanner](https://www.npmjs.com/package/spanner) - [sqlite](https://www.npmjs.com/package/sqlite) - [ssh2-streams](https://www.npmjs.com/package/ssh2-streams) - [ssh2](https://www.npmjs.com/package/ssh2) + - [vue](https://www.npmjs.com/package/vue) + - [yargs](https://www.npmjs.com/package/yargs) + - [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server) -* TypeScript 3.9 is now supported. +* TypeScript 4.0 is now supported. + +* TypeScript code embedded in HTML and Vue files is now extracted and analyzed. * The analysis of sanitizers has improved, leading to more accurate results from the security queries. @@ -30,10 +41,17 @@ | **Query** | **Tags** | **Purpose** | |---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Cross-site scripting through DOM (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are not shown on LGTM by default. | +| DOM text reinterpreted as HTML (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are shown on LGTM by default. | | Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. | | Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. | | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. | +| Download of sensitive file through insecure connection (`js/insecure-download`) | security, external/cwe/cwe-829 | Highlights downloads of sensitive files through an unencrypted protocol. Results are shown on LGTM by default. | +| Exposure of private files (`js/exposure-of-private-files`) | security, external/cwe/cwe-200 | Highlights servers that serve private files. Results are shown on LGTM by default. | +| Creating biased random numbers from a cryptographically secure source (`js/biased-cryptographic-random`) | security, external/cwe/cwe-327 | Highlights mathematical operations on cryptographically secure numbers that can create biased results. Results are shown on LGTM by default. | +| Storage of sensitive information in build artifact (`js/build-artifact-leak`) | security, external/cwe/cwe-312 | Highlights storage of sensitive information in build artifacts. Results are shown on LGTM by default. | +| Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. | +| Disabling certificate validation (`js/disabling-certificate-validation`) | security, external/cwe-295 | Highlights locations where SSL certificate validation is disabled. Results are shown on LGTM by default. | +| Incomplete multi-character sanitization (`js/incomplete-multi-character-sanitization`) | correctness, security, external/cwe/cwe-20, external/cwe/cwe-116 | Highlights sanitizers that fail to remove dangerous substrings completely. Results are shown on LGTM by default. | ## Changes to existing queries @@ -42,14 +60,19 @@ | Client-side cross-site scripting (`js/xss`) | Fewer results | This query now recognizes additional safe patterns of constructing HTML. | | Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Fewer results | This query now recognizes additional safe patterns of doing URL redirects. | | Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving NoSQL code operators are now recognized. | +| Exception text reinterpreted as HTML (`js/exception-xss`) | Rephrased and changed visibility | Rephrased name and alert message. Severity lowered from error to warning. Results are now shown on LGTM by default. | | Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. | | Hard-coded credentials (`js/hardcoded-credentials`) | More results | This query now recognizes hard-coded credentials sent via HTTP authorization headers. | | Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. | +| Insecure randomness (`js/insecure-randomness`) | Fewer results | This query now recognizes when an insecure random value is used as a fallback when secure random values are unsupported. | | Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. | +| Non-linear pattern (`js/non-linear-pattern`) | Fewer duplicates and message changed | This query now generates fewer duplicate alerts and has a clearer explanation in case of type annotations used in a pattern. | | Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. | | Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. | | Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. | +| Uncontrolled data used in path expression (`js/path-injection`) | Fewer results | This query no longer flags paths that have been checked to be part of a collection. | | Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. | +| Unneeded defensive code (`js/unneeded-defensive-code`) | Fewer false-positive results | This query now recognizes checks meant to handle the `document.all` object. | | Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. | | Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. | @@ -85,3 +108,4 @@ The following low-precision queries are no longer run by default on LGTM (their - `ParameterNode.asExpr()` and `.getAstNode()` now gets the parameter's AST node, whereas previously it had no result. - `Expr.flow()` now has a more meaningful result for destructuring patterns. Previously this node was disconnected from the data flow graph. Now it represents the values being destructured by the pattern. * The global data-flow and taint-tracking libraries now model indirect parameter accesses through the `arguments` object in some cases, which may lead to additional results from some of the security queries, particularly "Prototype pollution in utility function". +* The predicates `Type.getProperty()` and variants of `Type.getMethod()` have been deprecated due to lack of use-cases. Looking up a named property of a static type is no longer supported, favoring faster extraction times instead. diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md index 5d0fc69ec808..ed3496bc7349 100644 --- a/change-notes/1.25/analysis-python.md +++ b/change-notes/1.25/analysis-python.md @@ -1,22 +1,9 @@ # Improvements to Python analysis -The following changes in version 1.25 affect Python analysis in all applications. - -## General improvements - - -## New queries - -| **Query** | **Tags** | **Purpose** | -|-----------------------------|-----------|--------------------------------------------------------------------| - - -## Changes to existing queries - -| **Query** | **Expected impact** | **Change** | -|----------------------------|------------------------|------------------------------------------------------------------| - - -## Changes to libraries - * Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`). +* Added model of taint sources for HTTP servers using `http.server`. +* Added taint modeling of routed parameters in Flask. +* Improved modeling of built-in methods on strings for taint tracking. +* Improved classification of test files. +* New class `BoundMethodValue` represents a bound method during runtime. +* The query `py/command-line-injection` now recognizes command execution with the `fabric` and `invoke` Python libraries. diff --git a/change-notes/1.26/analysis-cpp.md b/change-notes/1.26/analysis-cpp.md new file mode 100644 index 000000000000..6b31cf76050f --- /dev/null +++ b/change-notes/1.26/analysis-cpp.md @@ -0,0 +1,30 @@ +# Improvements to C/C++ analysis + +The following changes in version 1.26 affect C/C++ analysis in all applications. + +## General improvements + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| +| Declaration hides parameter (`cpp/declaration-hides-parameter`) | Fewer false positive results | False positives involving template functions have been fixed. | +| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. | +| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | | The precision of this query has been decreased from "high" to "medium". As a result, the query is still run but results are no longer displayed on LGTM by default. | +| Comparison result is always the same (`cpp/constant-comparison`) | More correct results | Bounds on expressions involving multiplication can now be determined in more cases. | + +## Changes to libraries + +* The QL class `Block`, denoting the `{ ... }` statement, is renamed to `BlockStmt`. +* The models library now models many taint flows through `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`. +* The models library now models many more taint flows through `std::string`. +* The models library now models many taint flows through `std::istream` and `std::ostream`. +* The models library now models some taint flows through `std::shared_ptr`, `std::unique_ptr`, `std::make_shared` and `std::make_unique`. +* The models library now models many taint flows through `std::pair`, `std::map`, `std::unordered_map`, `std::set` and `std::unordered_set`. +* The `SimpleRangeAnalysis` library now supports multiplications of the form + `e1 * e2` and `x *= e2` when `e1` and `e2` are unsigned or constant. diff --git a/change-notes/1.26/analysis-csharp.md b/change-notes/1.26/analysis-csharp.md new file mode 100644 index 000000000000..3d17e00ab704 --- /dev/null +++ b/change-notes/1.26/analysis-csharp.md @@ -0,0 +1,35 @@ +# Improvements to C# analysis + +The following changes in version 1.26 affect C# analysis in all applications. + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| + + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|------------------------------|------------------------|-----------------------------------| +| Weak encryption: Insufficient key size (`cs/insufficient-key-size`) | More results | The required key size has been increased from 1024 to 2048. | + +## Removal of old queries + +## Changes to code extraction + +* Partial method bodies are extracted. Previously, partial method bodies were skipped completely. +* Inferring the lengths of implicitely sized arrays is fixed. Previously, multidimensional arrays were always extracted with the same length for +each dimension. With the fix, the array sizes `2` and `1` are extracted for `new int[,]{{1},{2}}`. Previously `2` and `2` were extracted. +* The extractor is now assembly-insensitive by default. This means that two entities with the same + fully-qualified name are now mapped to the same entity in the resulting database, regardless of + whether they belong to different assemblies. Assembly sensitivity can be reenabled by passing + `--assemblysensitivetrap` to the extractor. + +## Changes to libraries + +## Changes to autobuilder + +## Changes to tooling support + +* The Abstract Syntax Tree of C# files can be printed in Visual Studio Code. diff --git a/change-notes/1.26/analysis-java.md b/change-notes/1.26/analysis-java.md new file mode 100644 index 000000000000..cf87be2d8b85 --- /dev/null +++ b/change-notes/1.26/analysis-java.md @@ -0,0 +1,21 @@ +# Improvements to Java analysis + +The following changes in version 1.26 affect Java analysis in all applications. + +## General improvements + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| + + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|------------------------------|------------------------|-----------------------------------| + + +## Changes to libraries + +* The QL class `Block`, denoting the `{ ... }` statement, is renamed to `BlockStmt`. diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md new file mode 100644 index 000000000000..01d35a12c2dc --- /dev/null +++ b/change-notes/1.26/analysis-javascript.md @@ -0,0 +1,49 @@ +# Improvements to JavaScript analysis + +## General improvements + +* Support for the following frameworks and libraries has been improved: + - [AWS Serverless](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) + - [Alibaba Serverless](https://www.alibabacloud.com/help/doc-detail/156876.htm) + - [bluebird](https://www.npmjs.com/package/bluebird) + - [express](https://www.npmjs.com/package/express) + - [fast-json-stable-stringify](https://www.npmjs.com/package/fast-json-stable-stringify) + - [fast-safe-stringify](https://www.npmjs.com/package/fast-safe-stringify) + - [http](https://nodejs.org/api/http.html) + - [javascript-stringify](https://www.npmjs.com/package/javascript-stringify) + - [js-stringify](https://www.npmjs.com/package/js-stringify) + - [json-stable-stringify](https://www.npmjs.com/package/json-stable-stringify) + - [json-stringify-safe](https://www.npmjs.com/package/json-stringify-safe) + - [json3](https://www.npmjs.com/package/json3) + - [lodash](https://www.npmjs.com/package/lodash) + - [needle](https://www.npmjs.com/package/needle) + - [object-inspect](https://www.npmjs.com/package/object-inspect) + - [pretty-format](https://www.npmjs.com/package/pretty-format) + - [stringify-object](https://www.npmjs.com/package/stringify-object) + - [underscore](https://www.npmjs.com/package/underscore) + +* Analyzing files with the ".cjs" extension is now supported. +* ES2021 features are now supported. + +## New queries + +| **Query** | **Tags** | **Purpose** | +|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|--------------------------------|------------------------------|---------------------------------------------------------------------------| +| Potentially unsafe external link (`js/unsafe-external-link`) | Fewer results | This query no longer flags URLs constructed using a template system where only the hash or query part of the URL is dynamic. | +| Incomplete URL substring sanitization (`js/incomplete-url-substring-sanitization`) | More results | This query now recognizes additional URLs when the substring check is an inclusion check. | +| Ambiguous HTML id attribute (`js/duplicate-html-id`) | Results no longer shown | Precision tag reduced to "low". The query is no longer run by default. | +| Unused loop iteration variable (`js/unused-loop-variable`) | Fewer results | This query no longer flags variables in a destructuring array assignment that are not the last variable in the destructed array. | +| Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | More results | This query now recognizes more commands where colon, dash, and underscore are used. | +| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | More results | This query now detects more unsafe uses of nested option properties. | +| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | More results | This query now recognizes some unsafe uses of `importScripts()` inside WebWorkers. | +| Missing CSRF middleware (`js/missing-token-validation`) | More results | This query now recognizes writes to cookie and session variables as potentially vulnerable to CSRF attacks. | + + +## Changes to libraries +* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction. diff --git a/change-notes/1.26/analysis-python.md b/change-notes/1.26/analysis-python.md new file mode 100644 index 000000000000..f60eb6b4354d --- /dev/null +++ b/change-notes/1.26/analysis-python.md @@ -0,0 +1,22 @@ +# Improvements to Python analysis + +The following changes in version 1.26 affect Python analysis in all applications. + +## General improvements + + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| + + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| + + +## Changes to libraries + +* Added taint tracking support for string formatting through f-strings. diff --git a/config/identical-files.json b/config/identical-files.json index 1a1324687a0d..9be5c0d0dd2d 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -1,5 +1,5 @@ { - "DataFlow Java/C++/C#": [ + "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", @@ -18,15 +18,18 @@ "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" + "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll", + "python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll", + "python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll" ], - "DataFlow Java/C++/C# Common": [ + "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" + "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll", + "python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll" ], - "TaintTracking::Configuration Java/C++/C#": [ + "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", @@ -37,13 +40,35 @@ "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" + "java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll", + "python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll" ], - "DataFlow Java/C++/C# Consistency checks": [ + "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" + "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll", + "python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.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" + ], + "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" + ], + "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" + ], + "Bound Java/C#": [ + "java/ql/src/semmle/code/java/dataflow/Bound.qll", + "csharp/ql/src/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" ], "C++ SubBasicBlocks": [ "cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll", @@ -53,114 +78,122 @@ "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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll" + "csharp/ql/src/experimental/ir/implementation/IRType.qll" ], "IR IRConfiguration": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll" + "csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll" ], "IR UseSoundEscapeAnalysis": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll", - "csharp/ql/src/semmle/code/csharp/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", + "csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll" ], "IR Operand Tag": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll" + "csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll" ], - "IR TIRVariable":[ + "IR TInstruction": [ + "cpp/ql/src/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", - "csharp/ql/src/semmle/code/csharp/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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRConsistency.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll" + "csharp/ql/src/experimental/ir/internal/IntegerConstant.qll" ], "IR IntegerInteval": [ "cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll", - "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll" + "csharp/ql/src/experimental/ir/internal/IntegerInterval.qll" ], "IR IntegerPartial": [ "cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll", - "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll" + "csharp/ql/src/experimental/ir/internal/IntegerPartial.qll" ], "IR Overlap": [ "cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll", - "csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll" + "csharp/ql/src/experimental/ir/internal/Overlap.qll" ], "IR EdgeKind": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll" + "csharp/ql/src/experimental/ir/implementation/EdgeKind.qll" ], "IR MemoryAccessKind": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll" + "csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll" ], "IR TempVariableTag": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll" + "csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll" ], "IR Opcode": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll", - "csharp/ql/src/semmle/code/csharp/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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", @@ -177,6 +210,11 @@ "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" ], + "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" + ], "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", @@ -199,7 +237,7 @@ "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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll" + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll" ], "C++ SSA AliasAnalysisImports": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll", @@ -212,42 +250,42 @@ ], "IR SSA SimpleSSA": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll", - "csharp/ql/src/semmle/code/csharp/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", - "csharp/ql/src/semmle/code/csharp/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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/PrintValueNumbering.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_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", @@ -276,32 +314,40 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll" ], "C# IR InstructionImports": [ - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll" + "csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll", + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll" ], "C# IR IRImports": [ - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll" + "csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll", + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll" ], "C# IR IRBlockImports": [ - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll" + "csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll", + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll" + ], + "C# IR IRFunctionImports": [ + "csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll", + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll" ], "C# IR IRVariableImports": [ - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll" + "csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll", + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll" ], "C# IR OperandImports": [ - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll" + "csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll", + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll" ], "C# IR PrintIRImports": [ - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll" + "csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll", + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll" ], "C# IR ValueNumberingImports": [ - "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll" + "csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll", + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll" + ], + "Inline Test Expectations": [ + "cpp/ql/test/TestUtilities/InlineExpectationsTest.qll", + "python/ql/test/TestUtilities/InlineExpectationsTest.qll" ], "XML": [ "cpp/ql/src/semmle/code/cpp/XML.qll", @@ -309,5 +355,50 @@ "java/ql/src/semmle/code/xml/XML.qll", "javascript/ql/src/semmle/javascript/XML.qll", "python/ql/src/semmle/python/xml/XML.qll" + ], + "DuplicationProblems.qhelp": [ + "cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp", + "csharp/ql/src/Metrics/Files/DuplicationProblems.qhelp", + "javascript/ql/src/Metrics/DuplicationProblems.qhelp", + "python/ql/src/Metrics/DuplicationProblems.qhelp" + ], + "CommentedOutCodeQuery.qhelp": [ + "cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp", + "python/ql/src/Lexical/CommentedOutCodeQuery.qhelp", + "csharp/ql/src/Bad Practices/Comments/CommentedOutCodeQuery.qhelp", + "java/ql/src/Violations of Best Practice/Comments/CommentedOutCodeQuery.qhelp", + "javascript/ql/src/Comments/CommentedOutCodeQuery.qhelp" + ], + "FLinesOfCodeReferences.qhelp": [ + "java/ql/src/Metrics/Files/FLinesOfCodeReferences.qhelp", + "javascript/ql/src/Metrics/FLinesOfCodeReferences.qhelp" + ], + "FCommentRatioCommon.qhelp": [ + "java/ql/src/Metrics/Files/FCommentRatioCommon.qhelp", + "javascript/ql/src/Metrics/FCommentRatioCommon.qhelp" + ], + "FLinesOfCodeOverview.qhelp": [ + "java/ql/src/Metrics/Files/FLinesOfCodeOverview.qhelp", + "javascript/ql/src/Metrics/FLinesOfCodeOverview.qhelp" + ], + "CommentedOutCodeMetricOverview.qhelp": [ + "cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp", + "csharp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp", + "java/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp", + "javascript/ql/src/Comments/CommentedOutCodeMetricOverview.qhelp", + "python/ql/src/Lexical/CommentedOutCodeMetricOverview.qhelp" + ], + "FLinesOfDuplicatedCodeCommon.qhelp": [ + "cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp", + "java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp", + "javascript/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.qhelp", + "python/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.qhelp" + ], + "CommentedOutCodeReferences.qhelp": [ + "cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp", + "csharp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp", + "java/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp", + "javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp", + "python/ql/src/Lexical/CommentedOutCodeReferences.qhelp" ] -} +} \ No newline at end of file diff --git a/config/opcode-qldoc.py b/config/opcode-qldoc.py new file mode 100644 index 000000000000..e379e6a3ea96 --- /dev/null +++ b/config/opcode-qldoc.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +import os +import re +path = os.path + +needs_an_re = re.compile(r'^(?!Unary)[AEIOU]') # Name requiring "an" instead of "a". +start_qldoc_re = re.compile(r'^\s*/\*\*') # Start of a QLDoc comment +end_qldoc_re = re.compile(r'\*/\s*$') # End of a QLDoc comment +blank_qldoc_line_re = re.compile(r'^\s*\*\s*$') # A line in a QLDoc comment with only the '*' +instruction_class_re = re.compile(r'^class (?P[A-aa-z0-9]+)Instruction\s') # Declaration of an `Instruction` class +opcode_base_class_re = re.compile(r'^abstract class (?P[A-aa-z0-9]+)Opcode\s') # Declaration of an `Opcode` base class +opcode_class_re = re.compile(r'^ class (?P[A-aa-z0-9]+)\s') # Declaration of an `Opcode` class + +script_dir = path.realpath(path.dirname(__file__)) +instruction_path = path.realpath(path.join(script_dir, '../cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll')) +opcode_path = path.realpath(path.join(script_dir, '../cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll')) + +# Scan `Instruction.qll`, keeping track of the QLDoc comment attached to each declaration of a class +# whose name ends with `Instruction`. +instruction_comments = {} +in_qldoc = False +saw_blank_line_in_qldoc = False +qldoc_lines = [] +with open(instruction_path, 'r', encoding='utf-8') as instr: + for line in instr: + if in_qldoc: + if end_qldoc_re.search(line): + qldoc_lines.append(line) + in_qldoc = False + elif blank_qldoc_line_re.search(line): + # We're going to skip any lines after the first blank line, to avoid duplicating all + # of the verbose description. + saw_blank_line_in_qldoc = True + elif not saw_blank_line_in_qldoc: + qldoc_lines.append(line) + else: + if start_qldoc_re.search(line): + # Starting a new QLDoc comment. + saw_blank_line_in_qldoc = False + qldoc_lines.append(line) + if not end_qldoc_re.search(line): + in_qldoc = True + else: + instruction_match = instruction_class_re.search(line) + if instruction_match: + # Found the declaration of an `Instruction` class. Record the QLDoc comments. + instruction_comments[instruction_match.group('name')] = qldoc_lines + qldoc_lines = [] + +# Scan `Opcode.qll`. Whenever we see the declaration of an `Opcode` class for which we have a +# corresponding `Instruction` class, we'll attach a copy of the `Instruction`'s QLDoc comment. +in_qldoc = False +qldoc_lines = [] +output_lines = [] +with open(opcode_path, 'r', encoding='utf-8') as opcode: + for line in opcode: + if in_qldoc: + qldoc_lines.append(line) + if end_qldoc_re.search(line): + in_qldoc = False + else: + if start_qldoc_re.search(line): + qldoc_lines.append(line) + if not end_qldoc_re.search(line): + in_qldoc = True + else: + name_without_suffix = None + name = None + indent = '' + opcode_base_match = opcode_base_class_re.search(line) + if opcode_base_match: + name_without_suffix = opcode_base_match.group('name') + name = name_without_suffix + 'Opcode' + else: + opcode_match = opcode_class_re.search(line) + if opcode_match: + name_without_suffix = opcode_match.group('name') + name = name_without_suffix + # Indent by two additional spaces, since opcodes are declared in the + # `Opcode` module. + indent = ' ' + + if name_without_suffix: + # Found an `Opcode` that matches a known `Instruction`. Replace the QLDoc with + # a copy of the one from the `Instruction`. + if instruction_comments.get(name_without_suffix): + article = 'an' if needs_an_re.search(name_without_suffix) else 'a' + qldoc_lines = [ + indent + '/**\n', + indent + ' * The `Opcode` for ' + article + ' `' + name_without_suffix + 'Instruction`.\n', + indent + ' *\n', + indent + ' * See the `' + name_without_suffix + 'Instruction` documentation for more details.\n', + indent + ' */\n' + ] + output_lines.extend(qldoc_lines) + qldoc_lines = [] + output_lines.append(line) + +# Write out the updated `Opcode.qll` +with open(opcode_path, 'w', encoding='utf-8') as opcode: + opcode.writelines(output_lines) diff --git a/constraintsolving/generation/data.py b/constraintsolving/generation/data.py index b39bb99381e0..4cbf0d59ce81 100644 --- a/constraintsolving/generation/data.py +++ b/constraintsolving/generation/data.py @@ -112,7 +112,11 @@ def _get_tsm_query_file(self, query_type: str, filename: str) -> str: return os.path.join(global_config.sources_root, "javascript", "ql", "src", "TSM", query_type, filename) def _get_tsm_bqrs_file_for_entity(self, queried_entity: str, query_type: str) -> str: - return self._get_tsm_bqrs_file(f"{queried_entity}-{query_type}.bqrs") + # Add query_type as prefix folder since database analyze + # reads the ql file from query_type/file.ql and generates the same route + # this will change if we replace database analyze with database query + return self._get_tsm_bqrs_file( f"{query_type}/{queried_entity}-{query_type}.bqrs") + def _get_tsm_bqrs_file(self, filename: str) -> str: return os.path.join(constaintssolving_dir, self.project_dir, "results", "codeql-javascript", "TSM", filename) diff --git a/constraintsolving/main.py b/constraintsolving/main.py index 6a959590ddfe..eaa95d491900 100644 --- a/constraintsolving/main.py +++ b/constraintsolving/main.py @@ -34,10 +34,10 @@ def create_project_list(projectListFile): parser.add_argument("--project-dir", dest="project_dir", required=True, type=str, help="Directory of the CodeQL database") -parser.add_argument("--query-type", dest="query_type", required=True, type=str, choices=["Xss", "NoSql", "Sql", "Sel"], +parser.add_argument("--query-type", dest="query_type", required=True, type=str, choices=["Xss", "NoSql", "Sql", "Sel", "Path"], help="Type of the query to solve") parser.add_argument("--query-name", dest="query_name", required=True, type=str, - choices=["NosqlInjectionWorse", "SqlInjectionWorse", "DomBasedXssWorse", "SeldonWorse"], + choices=["NosqlInjectionWorse", "SqlInjectionWorse", "DomBasedXssWorse", "SeldonWorse", "TaintedPathWorse"], help="Name of the query to solve") parser.add_argument("--project-list", dest="projectListFile", required=False, type=str, help="Run all steps on the project passed on this list") diff --git a/constraintsolving/misc/combinescores.py b/constraintsolving/misc/combinescores.py index fc4313cd5a67..a75393f17894 100644 --- a/constraintsolving/misc/combinescores.py +++ b/constraintsolving/misc/combinescores.py @@ -74,7 +74,7 @@ def combine_scores(query, \ parser.add_argument("--project-dir", dest="project_dir", required=True, type=str, help="Directory of the results score") parser.add_argument("--query-name", dest="query_name", required=True, type=str, - choices=["NosqlInjectionWorse", "SqlInjectionWorse", "DomBasedXssWorse"], + choices=["NosqlInjectionWorse", "SqlInjectionWorse", "DomBasedXssWorse","TaintedPathWorse"], help="Name of the query to solve") parsed_arguments = parser.parse_args() diff --git a/constraintsolving/taintedpath_projects.txt b/constraintsolving/taintedpath_projects.txt new file mode 100644 index 000000000000..81a1950c3d87 --- /dev/null +++ b/constraintsolving/taintedpath_projects.txt @@ -0,0 +1,49 @@ +ably/ably-js +adaptlearning/adapt_authoring +alecperkins/missive +alphagov/public-sector-contract-prototype +cdnjs/new-website +cliss/camel +#killed deltaepsilon/quiver-cms +Espacorede/WikiStats +FurryBotCo/FurryBot +gitseo/vm +gpac/node-gpac-dash +hexparrot/mineos-node +#hobbyquaker/ccu.io +huangtao/cloud-test +ibm-cloud-security/appid-serversdk-nodejs +#idettman/idettman.github.io +infor-design/enterprise +JayK0720/Front-End +juan-carlos-correa/mini-spotify +mapbox/mapbox-studio-classic +mgh3326/JSWebCrawler +moeiscool/Shinobi +muaz-khan/Chrome-Extensions +nodecg/nodecg +nodulusteam/-nodulus-zipem +onap/ccsdk-distribution +opencomponents/oc +openhybrid/object +OstlerDev/PopcornTV +paypal/react-engine +rap1ds/dippa +rddill-IBM/ZeroToBlockchain +ShinobiCCTV/Shinobi +shy2850/node-server +SkillsFundingAgency/das-frontend +starikan/kartofan +substance/io +suculent/thinx-device-api +Sv443/JokeAPI +TheEvilCorp/OverReact +thinx-cloud/thinx +tocttou/choreograph-parent +TrapTeamCCNZ/TTMS-DEPLOY +#wdshare-group/wdshare-site +#xesf/agrippa +YiFeng0755/cloud-test +zhufengnodejs/201602node +zohooo/jekyde +ztaom/liveomg \ No newline at end of file diff --git a/cpp/autobuilder/.gitignore b/cpp/autobuilder/.gitignore new file mode 100644 index 000000000000..f81ecc73dffa --- /dev/null +++ b/cpp/autobuilder/.gitignore @@ -0,0 +1,13 @@ +obj/ +TestResults/ +*.manifest +*.pdb +*.suo +*.mdb +*.vsmdi +csharp.log +**/bin/Debug +**/bin/Release +*.tlog +.vs +*.user \ No newline at end of file diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs new file mode 100644 index 000000000000..87390b7bf8fe --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs @@ -0,0 +1,296 @@ +īģŋusing Xunit; +using Semmle.Autobuild.Shared; +using System.Collections.Generic; +using System; +using System.Linq; +using Microsoft.Build.Construction; +using System.Xml; + +namespace Semmle.Autobuild.Cpp.Tests +{ + /// + /// Test class to script Autobuilder scenarios. + /// For most methods, it uses two fields: + /// - an IList to capture the the arguments passed to it + /// - an IDictionary of possible return values. + /// + class TestActions : IBuildActions + { + /// + /// List of strings passed to FileDelete. + /// + public IList FileDeleteIn = new List(); + + void IBuildActions.FileDelete(string file) + { + FileDeleteIn.Add(file); + } + + public IList FileExistsIn = new List(); + public IDictionary FileExists = new Dictionary(); + + bool IBuildActions.FileExists(string file) + { + FileExistsIn.Add(file); + if (FileExists.TryGetValue(file, out var ret)) + return ret; + if (FileExists.TryGetValue(System.IO.Path.GetFileName(file), out ret)) + return ret; + throw new ArgumentException("Missing FileExists " + file); + } + + public IList RunProcessIn = new List(); + public IDictionary RunProcess = new Dictionary(); + public IDictionary RunProcessOut = new Dictionary(); + public IDictionary RunProcessWorkingDirectory = new Dictionary(); + + int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env, out IList stdOut) + { + var pattern = cmd + " " + args; + RunProcessIn.Add(pattern); + if (RunProcessOut.TryGetValue(pattern, out var str)) + stdOut = str.Split("\n"); + else + throw new ArgumentException("Missing RunProcessOut " + pattern); + RunProcessWorkingDirectory.TryGetValue(pattern, out var wd); + if (wd != workingDirectory) + throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern); + if (RunProcess.TryGetValue(pattern, out var ret)) + return ret; + throw new ArgumentException("Missing RunProcess " + pattern); + } + + int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env) + { + var pattern = cmd + " " + args; + RunProcessIn.Add(pattern); + RunProcessWorkingDirectory.TryGetValue(pattern, out var wd); + if (wd != workingDirectory) + throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern); + if (RunProcess.TryGetValue(pattern, out var ret)) + return ret; + throw new ArgumentException("Missing RunProcess " + pattern); + } + + public IList DirectoryDeleteIn = new List(); + + void IBuildActions.DirectoryDelete(string dir, bool recursive) + { + DirectoryDeleteIn.Add(dir); + } + + public IDictionary DirectoryExists = new Dictionary(); + public IList DirectoryExistsIn = new List(); + + bool IBuildActions.DirectoryExists(string dir) + { + DirectoryExistsIn.Add(dir); + if (DirectoryExists.TryGetValue(dir, out var ret)) + return ret; + throw new ArgumentException("Missing DirectoryExists " + dir); + } + + public IDictionary GetEnvironmentVariable = new Dictionary(); + + string? IBuildActions.GetEnvironmentVariable(string name) + { + if (GetEnvironmentVariable.TryGetValue(name, out var ret)) + return ret; + throw new ArgumentException("Missing GetEnvironmentVariable " + name); + } + + public string GetCurrentDirectory = ""; + + string IBuildActions.GetCurrentDirectory() + { + return GetCurrentDirectory; + } + + public IDictionary EnumerateFiles = new Dictionary(); + + IEnumerable IBuildActions.EnumerateFiles(string dir) + { + if (EnumerateFiles.TryGetValue(dir, out var str)) + return str.Split("\n"); + throw new ArgumentException("Missing EnumerateFiles " + dir); + } + + public IDictionary EnumerateDirectories = new Dictionary(); + + IEnumerable IBuildActions.EnumerateDirectories(string dir) + { + if (EnumerateDirectories.TryGetValue(dir, out var str)) + return string.IsNullOrEmpty(str) ? Enumerable.Empty() : str.Split("\n"); + throw new ArgumentException("Missing EnumerateDirectories " + dir); + } + + public bool IsWindows; + + bool IBuildActions.IsWindows() => IsWindows; + + string IBuildActions.PathCombine(params string[] parts) + { + return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p))); + } + + string IBuildActions.GetFullPath(string path) => path; + + void IBuildActions.WriteAllText(string filename, string contents) + { + } + + public IDictionary LoadXml = new Dictionary(); + XmlDocument IBuildActions.LoadXml(string filename) + { + if (LoadXml.TryGetValue(filename, out var xml)) + return xml; + throw new ArgumentException("Missing LoadXml " + filename); + } + + public string EnvironmentExpandEnvironmentVariables(string s) + { + foreach (var kvp in GetEnvironmentVariable) + s = s.Replace($"%{kvp.Key}%", kvp.Value); + return s; + } + } + + /// + /// A fake solution to build. + /// + class TestSolution : ISolution + { + public IEnumerable Configurations => throw new NotImplementedException(); + + public string DefaultConfigurationName => "Release"; + + public string DefaultPlatformName => "x86"; + + public string FullPath { get; set; } + + public Version ToolsVersion => new Version("14.0"); + + public IEnumerable IncludedProjects => throw new NotImplementedException(); + + public TestSolution(string path) + { + FullPath = path; + } + } + + public class BuildScriptTests + { + TestActions Actions = new TestActions(); + + // Records the arguments passed to StartCallback. + IList StartCallbackIn = new List(); + + void StartCallback(string s, bool silent) + { + StartCallbackIn.Add(s); + } + + // Records the arguments passed to EndCallback + IList EndCallbackIn = new List(); + IList EndCallbackReturn = new List(); + + void EndCallback(int ret, string s, bool silent) + { + EndCallbackReturn.Add(ret); + EndCallbackIn.Add(s); + } + + CppAutobuilder CreateAutoBuilder(bool isWindows, + string? buildless = null, string? solution = null, string? buildCommand = null, string? ignoreErrors = null, + string? msBuildArguments = null, string? msBuildPlatform = null, string? msBuildConfiguration = null, string? msBuildTarget = null, + string? dotnetArguments = null, string? dotnetVersion = null, string? vsToolsVersion = null, + string? nugetRestore = null, string? allSolutions = null, + string cwd = @"C:\Project") + { + string codeqlUpperLanguage = Language.Cpp.UpperCaseName; + Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false"; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = ""; + Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}"; + Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java"; + Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa"; + Actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java"; + Actions.GetEnvironmentVariable["SEMMLE_PLATFORM_TOOLS"] = @"C:\odasa\tools"; + Actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion; + Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_ARGUMENTS"] = msBuildArguments; + Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_PLATFORM"] = msBuildPlatform; + Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_CONFIGURATION"] = msBuildConfiguration; + Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_TARGET"] = msBuildTarget; + Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_ARGUMENTS"] = dotnetArguments; + Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_VERSION"] = dotnetVersion; + Actions.GetEnvironmentVariable["LGTM_INDEX_BUILD_COMMAND"] = buildCommand; + Actions.GetEnvironmentVariable["LGTM_INDEX_SOLUTION"] = solution; + Actions.GetEnvironmentVariable["LGTM_INDEX_IGNORE_ERRORS"] = ignoreErrors; + Actions.GetEnvironmentVariable["LGTM_INDEX_BUILDLESS"] = buildless; + Actions.GetEnvironmentVariable["LGTM_INDEX_ALL_SOLUTIONS"] = allSolutions; + Actions.GetEnvironmentVariable["LGTM_INDEX_NUGET_RESTORE"] = nugetRestore; + Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = isWindows ? @"C:\Program Files (x86)" : null; + Actions.GetCurrentDirectory = cwd; + Actions.IsWindows = isWindows; + + var options = new AutobuildOptions(Actions, Language.Cpp); + return new CppAutobuilder(Actions, options); + } + + void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun) + { + Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(Actions, StartCallback, EndCallback)); + + // Check expected commands actually ran + Assert.Equal(commandsRun, StartCallbackIn.Count); + Assert.Equal(commandsRun, EndCallbackIn.Count); + Assert.Equal(commandsRun, EndCallbackReturn.Count); + + var action = Actions.RunProcess.GetEnumerator(); + for (int cmd = 0; cmd < commandsRun; ++cmd) + { + Assert.True(action.MoveNext()); + + Assert.Equal(action.Current.Key, StartCallbackIn[cmd]); + Assert.Equal(action.Current.Value, EndCallbackReturn[cmd]); + } + } + + + [Fact] + public void TestDefaultCppAutobuilder() + { + Actions.EnumerateFiles[@"C:\Project"] = ""; + Actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(true); + var script = autobuilder.GetBuildScript(); + + // Fails due to no solutions present. + Assert.NotEqual(0, script.Run(Actions, StartCallback, EndCallback)); + } + + [Fact] + public void TestCppAutobuilderSuccess() + { + Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test.sln"] = 1; + Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /p:UseSharedCompilation=false /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0; + Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = ""; + Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1; + Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0; + Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = ""; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = true; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = true; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; + Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true; + Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.slx"; + Actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(true); + var solution = new TestSolution(@"C:\Project\test.sln"); + autobuilder.ProjectsOrSolutionsToBuild.Add(solution); + TestAutobuilderScript(autobuilder, 0, 2); + } + } +} diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj new file mode 100644 index 000000000000..7de677b56100 --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj @@ -0,0 +1,25 @@ + + + + Exe + netcoreapp3.1 + false + win-x64;linux-x64;osx-x64 + enable + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs new file mode 100644 index 000000000000..44c34656a2a8 --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs @@ -0,0 +1,23 @@ +īģŋusing Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.Cpp +{ + public class CppAutobuilder : Autobuilder + { + public CppAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { } + + public override BuildScript GetBuildScript() + { + if (Options.BuildCommand != null) + return new BuildCommandRule((_, f) => f(null)).Analyse(this, false); + + return + // First try MSBuild + new MsBuildRule().Analyse(this, true) | + // Then look for a script that might be a build script + (() => new BuildCommandAutoRule((_, f) => f(null)).Analyse(this, true)) | + // All attempts failed: print message + AutobuildFailure(); + } + } +} diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs new file mode 100644 index 000000000000..3f4627c53d54 --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs @@ -0,0 +1,33 @@ +īģŋusing System; +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.Cpp +{ + class Program + { + static int Main() + { + + try + { + var actions = SystemBuildActions.Instance; + var options = new AutobuildOptions(actions, Language.Cpp); + try + { + Console.WriteLine("CodeQL C++ autobuilder"); + var builder = new CppAutobuilder(actions, options); + return builder.AttemptBuild(); + } + catch(InvalidEnvironmentException ex) + { + Console.WriteLine("The environment is invalid: {0}", ex.Message); + } + } + catch (ArgumentOutOfRangeException ex) + { + Console.WriteLine("The value \"{0}\" for parameter \"{1}\" is invalid", ex.ActualValue, ex.ParamName); + } + return 1; + } + } +} diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..2d14b0e909d6 --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +īģŋusing System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Semmle.Autobuild.Cpp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("GitHub")] +[assembly: AssemblyProduct("CodeQL autobuilder for C++")] +[assembly: AssemblyCopyright("Copyright Š GitHub 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj new file mode 100644 index 000000000000..aadcc07568df --- /dev/null +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp3.1 + Semmle.Autobuild.Cpp + Semmle.Autobuild.Cpp + + Exe + + false + win-x64;linux-x64;osx-x64 + enable + + + + + + + + + + + + + + + + diff --git a/cpp/change-notes/2020-09-29-range-analysis-rollup.md b/cpp/change-notes/2020-09-29-range-analysis-rollup.md new file mode 100644 index 000000000000..d5b2c729bcbc --- /dev/null +++ b/cpp/change-notes/2020-09-29-range-analysis-rollup.md @@ -0,0 +1,14 @@ +lgtm,codescanning +* The `SimpleRangeAnalysis` library has gained support for several language + constructs it did not support previously. These improvements primarily affect + the queries `cpp/constant-comparison`, `cpp/comparison-with-wider-type`, and + `cpp/integer-multiplication-cast-to-long`. The newly supported language + features are: + * Multiplication of unsigned numbers. + * Multiplication by a constant. + * Reference-typed function parameters. + * Comparing a variable not equal to an endpoint of its range, thus narrowing the range by one. + * Using `if (x)` or `if (!x)` or similar to test for equality to zero. +* The `SimpleRangeAnalysis` library can now be extended with custom rules. See + examples in + `cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/`. diff --git a/cpp/ql/examples/qlpack.yml b/cpp/ql/examples/qlpack.yml new file mode 100644 index 000000000000..67289f8e360c --- /dev/null +++ b/cpp/ql/examples/qlpack.yml @@ -0,0 +1,3 @@ +name: codeql-cpp-examples +version: 0.0.0 +libraryPathDependencies: codeql-cpp diff --git a/cpp/ql/examples/snippets/emptyblock.ql b/cpp/ql/examples/snippets/emptyblock.ql index 2f1e198fc595..46ee35a68644 100644 --- a/cpp/ql/examples/snippets/emptyblock.ql +++ b/cpp/ql/examples/snippets/emptyblock.ql @@ -9,6 +9,6 @@ import cpp -from Block blk +from BlockStmt blk where blk.getNumStmt() = 0 select blk diff --git a/cpp/ql/examples/snippets/emptythen.ql b/cpp/ql/examples/snippets/emptythen.ql index 0ae060838d26..7e31ac7e4e18 100644 --- a/cpp/ql/examples/snippets/emptythen.ql +++ b/cpp/ql/examples/snippets/emptythen.ql @@ -13,5 +13,5 @@ import cpp from IfStmt i -where i.getThen().(Block).getNumStmt() = 0 +where i.getThen().(BlockStmt).getNumStmt() = 0 select i diff --git a/cpp/ql/examples/snippets/singletonblock.ql b/cpp/ql/examples/snippets/singletonblock.ql index a265d84a0da3..265bc85927c8 100644 --- a/cpp/ql/examples/snippets/singletonblock.ql +++ b/cpp/ql/examples/snippets/singletonblock.ql @@ -8,6 +8,6 @@ import cpp -from Block b +from BlockStmt b where b.getNumStmt() = 1 select b diff --git a/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql b/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql index cfa197109347..97481bc8b038 100644 --- a/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql +++ b/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql @@ -14,7 +14,7 @@ import cpp class ComplexStmt extends Stmt { ComplexStmt() { - exists(Block body | + exists(BlockStmt body | body = this.(Loop).getStmt() or body = this.(SwitchStmt).getStmt() | @@ -24,7 +24,7 @@ class ComplexStmt extends Stmt { } } -from Block b, int n, ComplexStmt complexStmt +from BlockStmt b, int n, ComplexStmt complexStmt where n = strictcount(ComplexStmt s | s = b.getAStmt()) and n > 3 and diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql index 91317c53af85..64e5760ed2b4 100644 --- a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql @@ -11,6 +11,17 @@ import cpp +/** + * Gets the template that a function `f` is constructed from, or just `f` if it + * is not from a template instantiation. + */ +Function getConstructedFrom(Function f) { + f.isConstructedFrom(result) + or + not f.isConstructedFrom(_) and + result = f +} + /** * Gets the parameter of `f` with name `name`, which has to come from the * _definition_ of `f` and not a prototype declaration. @@ -18,13 +29,17 @@ import cpp * This should not happen in a single application but since we * have a system wide view it is likely to happen for instance for * the main function. + * + * Note: we use `getConstructedFrom` to ensure that we look at template + * functions rather than their instantiations. We get better results this way + * as the instantiation is artificial and may have inherited parameter names + * from the declaration rather than the definition. */ ParameterDeclarationEntry functionParameterNames(Function f, string name) { exists(FunctionDeclarationEntry fe | result.getFunctionDeclarationEntry() = fe and - fe.getFunction() = f and + getConstructedFrom(f).getDefinition() = fe and fe.getLocation() = f.getDefinitionLocation() and - result.getFile() = fe.getFile() and // Work around CPP-331 strictcount(f.getDefinitionLocation()) = 1 and result.getName() = name ) diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql index 675fd7178d8a..d10d346c5135 100644 --- a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql @@ -17,7 +17,7 @@ where shadowing(lv1, lv2) and not lv1.isCompilerGenerated() and not lv2.isCompilerGenerated() and - not lv1.getParentScope().(Block).isInMacroExpansion() and - not lv2.getParentScope().(Block).isInMacroExpansion() + not lv1.getParentScope().(BlockStmt).isInMacroExpansion() and + not lv2.getParentScope().(BlockStmt).isInMacroExpansion() select lv1, "Variable " + lv1.getName() + " hides another variable of the same name (on $@).", lv2, "line " + lv2.getLocation().getStartLine().toString() diff --git a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql index 9880a3ae2a0a..9fa8c4e5e3fb 100644 --- a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql +++ b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql @@ -14,7 +14,7 @@ import cpp -predicate emptyBlock(ControlStructure s, Block b) { +predicate emptyBlock(ControlStructure s, BlockStmt b) { b = s.getAChild() and not exists(b.getAChild()) and not b.isInMacroExpansion() and @@ -23,7 +23,7 @@ predicate emptyBlock(ControlStructure s, Block b) { class AffectedFile extends File { AffectedFile() { - exists(Block b | + exists(BlockStmt b | emptyBlock(_, b) and this = b.getFile() ) @@ -37,7 +37,7 @@ class AffectedFile extends File { class BlockOrNonChild extends Element { BlockOrNonChild() { ( - this instanceof Block + this instanceof BlockStmt or this instanceof Comment or @@ -78,7 +78,7 @@ class BlockOrNonChild extends Element { /** * A block that contains a non-child element. */ -predicate emptyBlockContainsNonchild(Block b) { +predicate emptyBlockContainsNonchild(BlockStmt b) { emptyBlock(_, b) and exists(BlockOrNonChild c, AffectedFile file | c.(BlockOrNonChild).getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and @@ -91,7 +91,7 @@ predicate emptyBlockContainsNonchild(Block b) { * A block that is entirely on one line, which also contains a comment. Chances * are the comment is intended to refer to the block. */ -predicate lineComment(Block b) { +predicate lineComment(BlockStmt b) { emptyBlock(_, b) and exists(Location bLocation, File f, int line | bLocation = b.getLocation() and @@ -106,7 +106,7 @@ predicate lineComment(Block b) { ) } -from ControlStructure s, Block eb +from ControlStructure s, BlockStmt eb where emptyBlock(s, eb) and not emptyBlockContainsNonchild(eb) and diff --git a/cpp/ql/src/Critical/DeadCodeGoto.ql b/cpp/ql/src/Critical/DeadCodeGoto.ql index 4e16325f943d..a2f8db771b07 100644 --- a/cpp/ql/src/Critical/DeadCodeGoto.ql +++ b/cpp/ql/src/Critical/DeadCodeGoto.ql @@ -12,7 +12,7 @@ import cpp import semmle.code.cpp.commons.Exclusions -Stmt getNextRealStmt(Block b, int i) { +Stmt getNextRealStmt(BlockStmt b, int i) { result = b.getStmt(i + 1) and not result instanceof EmptyStmt or @@ -20,7 +20,7 @@ Stmt getNextRealStmt(Block b, int i) { result = getNextRealStmt(b, i + 1) } -from JumpStmt js, Block b, int i, Stmt s +from JumpStmt js, BlockStmt b, int i, Stmt s where b.getStmt(i) = js and s = getNextRealStmt(b, i) and diff --git a/cpp/ql/src/Critical/OverflowDestination.ql b/cpp/ql/src/Critical/OverflowDestination.ql index ad925daed628..bff3cac9326c 100644 --- a/cpp/ql/src/Critical/OverflowDestination.ql +++ b/cpp/ql/src/Critical/OverflowDestination.ql @@ -23,10 +23,7 @@ import semmle.code.cpp.security.TaintTracking * ``` */ predicate sourceSized(FunctionCall fc, Expr src) { - exists(string name | - (name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and - fc.getTarget().hasGlobalOrStdName(name) - ) and + fc.getTarget().hasGlobalOrStdName(["strncpy", "strncat", "memcpy", "memmove"]) and exists(Expr dest, Expr size, Variable v | fc.getArgument(0) = dest and fc.getArgument(1) = src and diff --git a/cpp/ql/src/Critical/SizeCheck2.ql b/cpp/ql/src/Critical/SizeCheck2.ql index 1b716d79d49e..1b7e1347c308 100644 --- a/cpp/ql/src/Critical/SizeCheck2.ql +++ b/cpp/ql/src/Critical/SizeCheck2.ql @@ -15,12 +15,7 @@ import cpp class Allocation extends FunctionCall { - Allocation() { - exists(string name | - this.getTarget().hasGlobalOrStdName(name) and - (name = "malloc" or name = "calloc" or name = "realloc") - ) - } + Allocation() { this.getTarget().hasGlobalOrStdName(["malloc", "calloc", "realloc"]) } private string getName() { this.getTarget().hasGlobalOrStdName(result) } diff --git a/cpp/ql/src/Critical/aliasAnalysisWarning.qhelp b/cpp/ql/src/Critical/aliasAnalysisWarning.qhelp new file mode 100644 index 000000000000..77d395c7da53 --- /dev/null +++ b/cpp/ql/src/Critical/aliasAnalysisWarning.qhelp @@ -0,0 +1,11 @@ + + + + +This check is an approximation, so some results may not be actual defects in the program. +It is not possible in general to compute the exact value of the variable without running the program with all possible input data. + + + diff --git a/cpp/ql/src/Critical/callGraphWarning.qhelp b/cpp/ql/src/Critical/callGraphWarning.qhelp new file mode 100644 index 000000000000..1e408db15270 --- /dev/null +++ b/cpp/ql/src/Critical/callGraphWarning.qhelp @@ -0,0 +1,12 @@ + + + + +This check is an approximation, so some results may not be actual defects in the program. +It is not possible in general to compute which function is actually called in a virtual call, +or a call through a pointer, without running the program with all possible input data. + + + diff --git a/cpp/ql/src/Critical/dataFlowWarning.qhelp b/cpp/ql/src/Critical/dataFlowWarning.qhelp new file mode 100644 index 000000000000..f96c82226c21 --- /dev/null +++ b/cpp/ql/src/Critical/dataFlowWarning.qhelp @@ -0,0 +1,13 @@ + + + + +This check is an approximation, so some results may not be actual defects in the program. +It is not possible in general to compute the actual branch taken in conditional statements such +as "if" without running the program with all possible input data. This means that it is not possible +to determine if a particular statement is going to be executed. + + + diff --git a/cpp/ql/src/Critical/pointsToWarning.qhelp b/cpp/ql/src/Critical/pointsToWarning.qhelp new file mode 100644 index 000000000000..2fa777ebfe97 --- /dev/null +++ b/cpp/ql/src/Critical/pointsToWarning.qhelp @@ -0,0 +1,11 @@ + + + + +This check is an approximation, so some results may not be actual defects in the program. It is not possible +in general to compute the values of pointers without running the program with all input data. + + + diff --git a/cpp/ql/src/Documentation/CommentedOutCode.qhelp b/cpp/ql/src/Documentation/CommentedOutCode.qhelp index 4ce0ee029b62..ba056ab73f77 100644 --- a/cpp/ql/src/Documentation/CommentedOutCode.qhelp +++ b/cpp/ql/src/Documentation/CommentedOutCode.qhelp @@ -3,5 +3,5 @@ "qhelp.dtd"> - + diff --git a/cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp b/cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp new file mode 100644 index 000000000000..eb40ecdb7088 --- /dev/null +++ b/cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp @@ -0,0 +1,25 @@ + + + + +

+Commented-out code is distracting and confusing for developers who read the surrounding code, +and its significance is often unclear. It will not get compiled or tested when the code around +it changes, so it's likely to break over time. For these reasons, commented-out code should be +avoided. +

+ +
+ + + +

+Remove or reinstate the commented-out code. If you want to include a snippet of example code +in a comment, consider enclosing it in quotes or marking it up as appropriate for the source +language. +

+ +
+
diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql index 7468b6e71748..0623036a5924 100644 --- a/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql @@ -13,14 +13,7 @@ import cpp class ForbiddenFunction extends Function { - ForbiddenFunction() { - exists(string name | name = this.getName() | - name = "setjmp" or - name = "longjmp" or - name = "sigsetjmp" or - name = "siglongjmp" - ) - } + ForbiddenFunction() { this.getName() = ["setjmp", "longjmp", "sigsetjmp", "siglongjmp"] } } from FunctionCall call diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql index 602be971763b..9ed5675e7955 100644 --- a/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql @@ -12,7 +12,7 @@ import cpp int lineInBlock(File f) { - exists(Block block, Location blockLocation | + exists(BlockStmt block, Location blockLocation | block.getFile() = f and blockLocation = block.getLocation() | result in [blockLocation.getStartLine() .. blockLocation.getEndLine()] diff --git a/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.ql b/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.ql index 31af5b3ce794..ca36e6d1ce2e 100644 --- a/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.ql +++ b/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.ql @@ -40,9 +40,7 @@ class DateStructModifiedFieldAccess extends LeapYearFieldAccess { */ class SafeTimeGatheringFunction extends Function { SafeTimeGatheringFunction() { - this.getQualifiedName() = "GetFileTime" or - this.getQualifiedName() = "GetSystemTime" or - this.getQualifiedName() = "NtQuerySystemTime" + this.getQualifiedName() = ["GetFileTime", "GetSystemTime", "NtQuerySystemTime"] } } @@ -51,15 +49,11 @@ class SafeTimeGatheringFunction extends Function { */ class TimeConversionFunction extends Function { TimeConversionFunction() { - this.getQualifiedName() = "FileTimeToSystemTime" or - this.getQualifiedName() = "SystemTimeToFileTime" or - this.getQualifiedName() = "SystemTimeToTzSpecificLocalTime" or - this.getQualifiedName() = "SystemTimeToTzSpecificLocalTimeEx" or - this.getQualifiedName() = "TzSpecificLocalTimeToSystemTime" or - this.getQualifiedName() = "TzSpecificLocalTimeToSystemTimeEx" or - this.getQualifiedName() = "RtlLocalTimeToSystemTime" or - this.getQualifiedName() = "RtlTimeToSecondsSince1970" or - this.getQualifiedName() = "_mkgmtime" + this.getQualifiedName() = + ["FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime", + "SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime", + "TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime", + "RtlTimeToSecondsSince1970", "_mkgmtime"] } } diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql index 4f23ed50b365..bf724de3d70c 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql @@ -27,11 +27,11 @@ predicate macroUseLocation(File f, int start, int end) { } pragma[noopt] -predicate emptyIf(IfStmt s, Block b, File f, int start, int end) { +predicate emptyIf(IfStmt s, BlockStmt b, File f, int start, int end) { s instanceof IfStmt and not exists(s.getElse()) and b = s.getThen() and - b instanceof Block and + b instanceof BlockStmt and not exists(b.getAChild()) and f = b.getFile() and exists(Location l | @@ -42,7 +42,7 @@ predicate emptyIf(IfStmt s, Block b, File f, int start, int end) { } pragma[noopt] -predicate query(IfStmt s, Block b) { +predicate query(IfStmt s, BlockStmt b) { exists(File f, int blockStart, int blockEnd | emptyIf(s, b, f, blockStart, blockEnd) and not exists(int macroStart, int macroEnd | @@ -53,7 +53,7 @@ predicate query(IfStmt s, Block b) { ) } -from IfStmt s, Block b +from IfStmt s, BlockStmt b where query(s, b) and not b.isInMacroExpansion() diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql index b53cb1d50b32..074c82bc03b1 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql @@ -15,6 +15,15 @@ import cpp import semmle.code.cpp.models.implementations.Strcpy import semmle.code.cpp.dataflow.DataFlow +/** + * A string copy function that returns a string, rather than an error code (for + * example, `strcpy` returns a string, whereas `strcpy_s` returns an error + * code). + */ +class InterestingStrcpyFunction extends StrcpyFunction { + InterestingStrcpyFunction() { getType().getUnspecifiedType() instanceof PointerType } +} + predicate isBoolean(Expr e1) { exists(Type t1 | t1 = e1.getType() and @@ -25,12 +34,12 @@ predicate isBoolean(Expr e1) { predicate isStringCopyCastedAsBoolean(FunctionCall func, Expr expr1, string msg) { DataFlow::localExprFlow(func, expr1) and isBoolean(expr1.getConversion*()) and - func.getTarget() instanceof StrcpyFunction and + func.getTarget() instanceof InterestingStrcpyFunction and msg = "Return value of " + func.getTarget().getName() + " used as a Boolean." } predicate isStringCopyUsedInLogicalOperationOrCondition(FunctionCall func, Expr expr1, string msg) { - func.getTarget() instanceof StrcpyFunction and + func.getTarget() instanceof InterestingStrcpyFunction and ( ( // it is being used in an equality or logical operation diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql b/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql index aaf52883e059..5e3af3478214 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql @@ -50,7 +50,12 @@ predicate illDefinedDecrForStmt( DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(lesserOperand)) and // `initialCondition` < `terminalCondition` ( - upperBound(initialCondition) < lowerBound(terminalCondition) + upperBound(initialCondition) < lowerBound(terminalCondition) and + ( + // exclude cases where the loop counter is `unsigned` (where wrapping behaviour can be used deliberately) + v.getUnspecifiedType().(IntegralType).isSigned() or + initialCondition.getValue().toInt() = 0 + ) or (forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue()) ) diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql index 6ba835acbdc8..65ba665dff29 100644 --- a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql +++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql @@ -23,7 +23,7 @@ import semmle.code.cpp.ir.ValueNumbering class NullInstruction extends ConstantValueInstruction { NullInstruction() { this.getValue() = "0" and - this.getResultType().getUnspecifiedType() instanceof PointerType + this.getResultIRType() instanceof IRAddressType } } @@ -44,8 +44,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) { bool = any(ConvertInstruction convert | checked = convert.getUnary() and - convert.getResultType() instanceof BoolType and - checked.getResultType() instanceof PointerType + convert.getResultIRType() instanceof IRBooleanType and + checked.getResultIRType() instanceof IRAddressType ) } diff --git a/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql index 7062911ca4df..47ddb9d1f1e6 100644 --- a/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql +++ b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name External dependencies * @description Count the number of dependencies a C/C++ source file has on external libraries. * @kind treemap diff --git a/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql b/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql index f2c110a0ac19..cf305ab0aeaf 100644 --- a/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql +++ b/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name External dependency source links * @kind source-link * @metricType externalDependency diff --git a/cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp b/cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp new file mode 100644 index 000000000000..217b6d175295 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp @@ -0,0 +1,12 @@ + + + +

+This metric counts the number of lines of commented-out code in each file. Large amounts of +commented-out code often indicate poorly maintained code. +

+ +
+
diff --git a/cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp b/cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp new file mode 100644 index 000000000000..462eb7795f91 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp @@ -0,0 +1,12 @@ + + + + +
  • Mark Needham: The danger of commenting out code.
  • +
  • Los Techies: Commented Code == Technical Debt.
  • +
  • High Integrity C++ Coding Standard: 2.3.2 Do not comment out code.
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp b/cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp new file mode 100644 index 000000000000..54397da6c994 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp @@ -0,0 +1,16 @@ + + + +

    +Duplicated code increases overall code size, making the code base +harder to maintain and harder to understand. It also becomes harder to fix bugs, +since a programmer applying a fix to one copy has to always remember to update +other copies accordingly. Finally, code duplication is generally an indication of +a poorly designed or hastily written code base, which typically suffers from other +problems as well. +

    + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql index 3a11eb3575a1..ab9317ea316e 100644 --- a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql +++ b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Duplicated lines in files * @description The number of lines in a file, including code, comment * and whitespace lines, which are duplicated in at least diff --git a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp new file mode 100644 index 000000000000..17171fb75871 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp @@ -0,0 +1,35 @@ + + + + +

    +This metric measures the number of lines in a file that are contained within a block that is duplicated elsewhere. These lines may include code, comments and whitespace, and the duplicate block may be in this file or in another file. +

    + +

    +A file that contains many lines that are duplicated within the code base is problematic +for a number of reasons. +

    + +
    + + + + +

    +Refactor files with lots of duplicated code to extract the common code into +a shared library or module. +

    + +
    + + + +
  • Wikipedia: Duplicate code.
  • +
  • M. Fowler, Refactoring. Addison-Wesley, 1999.
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FNumberOfTests.ql b/cpp/ql/src/Metrics/Files/FNumberOfTests.ql index a474b9862037..421fc2baba1f 100644 --- a/cpp/ql/src/Metrics/Files/FNumberOfTests.ql +++ b/cpp/ql/src/Metrics/Files/FNumberOfTests.ql @@ -18,6 +18,9 @@ Expr getTest() { or // boost tests; http://www.boost.org/ result.(FunctionCall).getTarget().hasQualifiedName("boost::unit_test", "make_test_case") + or + // googletest tests; https://github.com/google/googletest/ + result.(FunctionCall).getTarget().hasQualifiedName("testing::internal", "MakeAndRegisterTestInfo") } from File f, int n diff --git a/cpp/ql/src/Microsoft/SAL.qll b/cpp/ql/src/Microsoft/SAL.qll index 04ea9a9c5fa8..46fedbb5d80c 100644 --- a/cpp/ql/src/Microsoft/SAL.qll +++ b/cpp/ql/src/Microsoft/SAL.qll @@ -1,14 +1,17 @@ +/** + * Provides classes for identifying and reasoning about Microsoft source code + * annotation language (SAL) macros. + */ + import cpp +/** + * A SAL macro defined in `sal.h` or a similar header file. + */ class SALMacro extends Macro { SALMacro() { - exists(string filename | filename = this.getFile().getBaseName() | - filename = "sal.h" or - filename = "specstrings_strict.h" or - filename = "specstrings.h" or - filename = "w32p.h" or - filename = "minwindef.h" - ) and + this.getFile().getBaseName() = + ["sal.h", "specstrings_strict.h", "specstrings.h", "w32p.h", "minwindef.h"] and ( // Dialect for Windows 8 and above this.getName().matches("\\_%\\_") @@ -20,36 +23,44 @@ class SALMacro extends Macro { } pragma[noinline] -predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) } +private predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) } +/** + * An invocation of a SAL macro (excluding invocations inside other macros). + */ class SALAnnotation extends MacroInvocation { SALAnnotation() { this.getMacro() instanceof SALMacro and isTopLevelMacroAccess(this) } - /** Returns the `Declaration` annotated by `this`. */ + /** Gets the `Declaration` annotated by `this`. */ Declaration getDeclaration() { annotatesAt(this, result.getADeclarationEntry(), _, _) and not result instanceof Type // exclude typedefs } - /** Returns the `DeclarationEntry` annotated by `this`. */ + /** Gets the `DeclarationEntry` annotated by `this`. */ DeclarationEntry getDeclarationEntry() { annotatesAt(this, result, _, _) and not result instanceof TypeDeclarationEntry // exclude typedefs } } +/** + * A SAL macro indicating that the return value of a function should always be + * checked. + */ class SALCheckReturn extends SALAnnotation { SALCheckReturn() { - exists(SALMacro m | m = this.getMacro() | - m.getName() = "_Check_return_" or - m.getName() = "_Must_inspect_result_" - ) + this.getMacro().(SALMacro).getName() = ["_Check_return_", "_Must_inspect_result_"] } } +/** + * A SAL macro indicating that a pointer variable or return value should not be + * `NULL`. + */ class SALNotNull extends SALAnnotation { SALNotNull() { exists(SALMacro m | m = this.getMacro() | @@ -69,6 +80,9 @@ class SALNotNull extends SALAnnotation { } } +/** + * A SAL macro indicating that a value may be `NULL`. + */ class SALMaybeNull extends SALAnnotation { SALMaybeNull() { exists(SALMacro m | m = this.getMacro() | @@ -79,13 +93,29 @@ class SALMaybeNull extends SALAnnotation { } } +/** + * A parameter annotated by one or more SAL annotations. + */ +class SALParameter extends Parameter { + /** One of this parameter's annotations. */ + SALAnnotation a; + + SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) } + + predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") } + + predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") } + + predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") } +} + /////////////////////////////////////////////////////////////////////////////// // Implementation details /** * Holds if `a` annotates the declaration entry `d` and * its start position is the `idx`th position in `file` that holds a SAL element. */ -predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) { +private predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) { annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx) } @@ -109,22 +139,6 @@ private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File ) } -/** - * A parameter annotated by one or more SAL annotations. - */ -class SALParameter extends Parameter { - /** One of this parameter's annotations. */ - SALAnnotation a; - - SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) } - - predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") } - - predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") } - - predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") } -} - /** * A SAL element, that is, a SAL annotation or a declaration entry * that may have SAL annotations. diff --git a/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql b/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql index 2b26a8356830..701d56e6ce24 100644 --- a/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql +++ b/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql @@ -27,7 +27,7 @@ int logicalLength(FunctionDeclarationEntry f) { count(Stmt s | s.getEnclosingFunction() = f.getFunction() and s.getFile() = f.getFile() and - not s instanceof Block and + not s instanceof BlockStmt and not s instanceof EmptyStmt and not exists(ForStmt for | s = for.getInitialization()) and not s.isAffectedByMacro() diff --git a/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql b/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql index c3936e1ed89d..7c66cdd41f40 100644 --- a/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql +++ b/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql @@ -14,7 +14,7 @@ import cpp class OneLineStmt extends Stmt { OneLineStmt() { this.getLocation().getStartLine() = this.getLocation().getEndLine() and - not this instanceof Block and + not this instanceof BlockStmt and not exists(ForStmt for | this = for.getInitialization()) and ( // Either this statement is not touched by a macro at all... diff --git a/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql b/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql index 1fbbaf99b58d..3bb926b8a649 100644 --- a/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql +++ b/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql @@ -27,7 +27,7 @@ int logicalLength(FunctionDeclarationEntry f) { count(Stmt s | s.getEnclosingFunction() = f.getFunction() and s.getFile() = f.getFile() and - not s instanceof Block and + not s instanceof BlockStmt and not s instanceof EmptyStmt and not exists(ForStmt for | s = for.getInitialization()) and not s.isAffectedByMacro() diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp index d7432369f0e8..eba2ede58f52 100644 --- a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp +++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp @@ -39,7 +39,7 @@ access all the system's passwords.

  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql index 473f0f72f122..bfb3a2fbb816 100644 --- a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql +++ b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql @@ -56,7 +56,7 @@ class VarargsFunction extends Function { } string normalTerminator(int cnt) { - (result = "0" or result = "-1") and + result = ["0", "-1"] and cnt = trailingArgValueCount(result) and 2 * cnt > totalCount() and not exists(FunctionCall fc, int index | diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql index 91ccc5c4d400..b64091263e09 100644 --- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql @@ -5,7 +5,7 @@ * or data representation problems. * @kind path-problem * @problem.severity warning - * @precision medium + * @precision high * @id cpp/tainted-format-string * @tags reliability * security diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql index 96cffdb024bf..d38f3eb24c2f 100644 --- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql @@ -5,7 +5,7 @@ * or data representation problems. * @kind path-problem * @problem.severity warning - * @precision medium + * @precision high * @id cpp/tainted-format-string-through-global * @tags reliability * security diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index 54c6d1e7a966..cc2d52385c77 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -4,7 +4,7 @@ * user can result in integer overflow. * @kind path-problem * @problem.severity error - * @precision high + * @precision medium * @id cpp/uncontrolled-allocation-size * @tags reliability * security diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 3f6ff63635e0..af64a1789c3b 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -18,7 +18,7 @@ abstract class InsecureCryptoSpec extends Locatable { } Function getAnInsecureFunction() { - result.getName().regexpMatch(algorithmBlacklistRegex()) and + result.getName().regexpMatch(getInsecureAlgorithmRegex()) and exists(result.getACallToThisFunction()) } @@ -33,7 +33,7 @@ class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall { } Macro getAnInsecureMacro() { - result.getName().regexpMatch(algorithmBlacklistRegex()) and + result.getName().regexpMatch(getInsecureAlgorithmRegex()) and exists(result.getAnInvocation()) } diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql index fc47d04c2c51..32bdfbaf1007 100644 --- a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql +++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql @@ -66,10 +66,7 @@ class IFStream extends Type { */ class CinVariable extends NamespaceVariable { CinVariable() { - ( - getName() = "cin" or - getName() = "wcin" - ) and + getName() = ["cin", "wcin"] and getNamespace().getName() = "std" } } diff --git a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql index 71601ec2181c..4316fe229b26 100644 --- a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql +++ b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql @@ -14,12 +14,7 @@ import cpp predicate potentiallyDangerousFunction(Function f, string message) { exists(string name | f.hasGlobalName(name) | - ( - name = "gmtime" or - name = "localtime" or - name = "ctime" or - name = "asctime" - ) and + name = ["gmtime", "localtime", "ctime", "asctime"] and message = "Call to " + name + " is potentially dangerous" ) } diff --git a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql index 269e7e33b59e..95790298347d 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql +++ b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql @@ -19,12 +19,7 @@ predicate worldWritableCreation(FileCreationExpr fc, int mode) { } predicate setWorldWritable(FunctionCall fc, int mode) { - exists(string name | fc.getTarget().getName() = name | - name = "chmod" or - name = "fchmod" or - name = "_chmod" or - name = "_wchmod" - ) and + fc.getTarget().getName() = ["chmod", "fchmod", "_chmod", "_wchmod"] and mode = fc.getArgument(1).getValue().toInt() and sets(mode, s_iwoth()) } diff --git a/cpp/ql/src/Security/CWE/CWE-732/FilePermissions.qll b/cpp/ql/src/Security/CWE/CWE-732/FilePermissions.qll index bb9c5b12f986..d62f3e6a6daa 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/FilePermissions.qll +++ b/cpp/ql/src/Security/CWE/CWE-732/FilePermissions.qll @@ -31,11 +31,7 @@ predicate sets(int mask, int fields) { mask.bitAnd(fields) != 0 } * one of the `umask` family of functions. */ private int umask(FunctionCall fc) { - exists(string name | name = fc.getTarget().getName() | - name = "umask" or - name = "_umask" or - name = "_umask_s" - ) and + fc.getTarget().getName() = ["umask", "_umask", "_umask_s"] and result = fc.getArgument(0).getValue().toInt() } @@ -89,11 +85,7 @@ abstract class FileCreationExpr extends FunctionCall { class OpenCreationExpr extends FileCreationExpr { OpenCreationExpr() { - exists(string name | name = this.getTarget().getName() | - name = "open" or - name = "_open" or - name = "_wopen" - ) and + this.getTarget().getName() = ["open", "_open", "_wopen"] and sets(this.getArgument(1).getValue().toInt(), o_creat()) } @@ -134,14 +126,9 @@ private int fopenMode() { class FopenCreationExpr extends FileCreationExpr { FopenCreationExpr() { - exists(string name | name = this.getTarget().getName() | - name = "fopen" or - name = "_wfopen" or - name = "fsopen" or - name = "_wfsopen" - ) and + this.getTarget().getName() = ["fopen", "_wfopen", "fsopen", "_wfsopen"] and exists(string mode | - (mode = "w" or mode = "a") and + mode = ["w", "a"] and this.getArgument(1).getValue().matches(mode + "%") ) } diff --git a/cpp/ql/src/codeql-suites/cpp-code-scanning.qls b/cpp/ql/src/codeql-suites/cpp-code-scanning.qls index 27bff98ea5d0..f811010a26a7 100644 --- a/cpp/ql/src/codeql-suites/cpp-code-scanning.qls +++ b/cpp/ql/src/codeql-suites/cpp-code-scanning.qls @@ -2,3 +2,5 @@ - qlpack: codeql-cpp - apply: code-scanning-selectors.yml from: codeql-suite-helpers +- apply: codeql-suites/exclude-slow-queries.yml + from: codeql-cpp diff --git a/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls b/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls index e9fda1cdb9e8..b6775597c30a 100644 --- a/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls +++ b/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls @@ -2,18 +2,14 @@ - qlpack: codeql-cpp - apply: lgtm-selectors.yml from: codeql-suite-helpers -# These queries are infeasible to compute on large projects: -- exclude: - query path: - - Security/CWE/CWE-497/ExposedSystemData.ql - - Critical/DescriptorMayNotBeClosed.ql - - Critical/DescriptorNeverClosed.ql - - Critical/FileMayNotBeClosed.ql - - Critical/FileNeverClosed.ql - - Critical/MemoryMayNotBeFreed.ql - - Critical/MemoryNeverFreed.ql +- apply: codeql-suites/exclude-slow-queries.yml + from: codeql-cpp # These are only for IDE use. - exclude: tags contain: - ide-contextual-queries/local-definitions - ide-contextual-queries/local-references +- query: Metrics/Files/FLinesOfCode.ql +- query: Metrics/Files/FLinesOfCommentedOutCode.ql +- query: Metrics/Files/FLinesOfComments.ql +- query: Metrics/Files/FNumberOfTests.ql diff --git a/cpp/ql/src/codeql-suites/cpp-security-and-quality.qls b/cpp/ql/src/codeql-suites/cpp-security-and-quality.qls new file mode 100644 index 000000000000..297f46bc7522 --- /dev/null +++ b/cpp/ql/src/codeql-suites/cpp-security-and-quality.qls @@ -0,0 +1,6 @@ +- description: Security-and-quality queries for C and C++ +- qlpack: codeql-cpp +- apply: security-and-quality-selectors.yml + from: codeql-suite-helpers +- apply: codeql-suites/exclude-slow-queries.yml + from: codeql-cpp diff --git a/cpp/ql/src/codeql-suites/cpp-security-extended.qls b/cpp/ql/src/codeql-suites/cpp-security-extended.qls new file mode 100644 index 000000000000..fa69559add0b --- /dev/null +++ b/cpp/ql/src/codeql-suites/cpp-security-extended.qls @@ -0,0 +1,6 @@ +- description: Security-extended queries for C and C++ +- qlpack: codeql-cpp +- apply: security-extended-selectors.yml + from: codeql-suite-helpers +- apply: codeql-suites/exclude-slow-queries.yml + from: codeql-cpp diff --git a/cpp/ql/src/codeql-suites/exclude-slow-queries.yml b/cpp/ql/src/codeql-suites/exclude-slow-queries.yml new file mode 100644 index 000000000000..a1a4ced9c7db --- /dev/null +++ b/cpp/ql/src/codeql-suites/exclude-slow-queries.yml @@ -0,0 +1,11 @@ +- description: C/C++ queries which are infeasible to compute on large projects +# These queries are infeasible to compute on large projects: +- exclude: + query path: + - Security/CWE/CWE-497/ExposedSystemData.ql + - Critical/DescriptorMayNotBeClosed.ql + - Critical/DescriptorNeverClosed.ql + - Critical/FileMayNotBeClosed.ql + - Critical/FileNeverClosed.ql + - Critical/MemoryMayNotBeFreed.ql + - Critical/MemoryNeverFreed.ql diff --git a/cpp/ql/src/cpp.qll b/cpp/ql/src/cpp.qll index 78fde101a42f..a989c9a6c9d4 100644 --- a/cpp/ql/src/cpp.qll +++ b/cpp/ql/src/cpp.qll @@ -32,6 +32,7 @@ import semmle.code.cpp.Enum import semmle.code.cpp.Member import semmle.code.cpp.Field import semmle.code.cpp.Function +import semmle.code.cpp.MemberFunction import semmle.code.cpp.Parameter import semmle.code.cpp.Variable import semmle.code.cpp.Initializer diff --git a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.cpp b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.cpp new file mode 100644 index 000000000000..3765d0b14d40 --- /dev/null +++ b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.cpp @@ -0,0 +1,11 @@ +void test(char *arg1, int *arg2) { + if (arg1[0] == 'A') { + if (arg2 != NULL) { //maybe redundant + *arg2 = 42; + } + } + if (arg1[1] == 'B') + { + *arg2 = 54; //dereferenced without checking first + } +} diff --git a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.qhelp b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.qhelp new file mode 100644 index 000000000000..25cbeff355ec --- /dev/null +++ b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.qhelp @@ -0,0 +1,29 @@ + + + + +

    This rule finds comparisons of a function parameter to null that occur when in another path the parameter is dereferenced without a guard check. It's +likely either the check is not required and can be removed, or it should be added before the dereference +so that a null pointer dereference does not occur.

    +
    + + +

    A check should be added to before the dereference, in a way that prevents a null pointer value from +being dereferenced. If it's clear that the pointer cannot be null, consider removing the check instead.

    +
    + + + + + + +
  • + + Null Dereference + +
  • +
    + +
    diff --git a/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql new file mode 100644 index 000000000000..f1a3663bb960 --- /dev/null +++ b/cpp/ql/src/experimental/Likely Bugs/RedundantNullCheckParam.ql @@ -0,0 +1,56 @@ +/** + * @name Redundant null check or missing null check of parameter + * @description Checking a parameter for nullness in one path, + * and not in another is likely to be a sign that either + * the check can be removed, or added in the other case. + * @kind problem + * @id cpp/redundant-null-check-param + * @problem.severity recommendation + * @tags reliability + * security + * external/cwe/cwe-476 + */ + +import cpp + +predicate blockDominates(BlockStmt check, BlockStmt access) { + check.getLocation().getStartLine() <= access.getLocation().getStartLine() and + check.getLocation().getEndLine() >= access.getLocation().getEndLine() +} + +predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked) { + checked = any(VariableAccess va | va.getTarget() = unchecked.getTarget()) and + //Simple test if the first access in this code path is dereferenced + not dereferenced(checked) and + blockDominates(checked.getEnclosingBlock(), unchecked.getEnclosingBlock()) +} + +predicate candidateResultUnchecked(VariableAccess unchecked) { + not isCheckedInstruction(unchecked, _) +} + +predicate candidateResultChecked(VariableAccess check, EqualityOperation eqop) { + //not dereferenced to check against pointer, not its pointed value + not dereferenced(check) and + //assert macros are not taken into account + not check.isInMacroExpansion() and + // is part of a comparison against some constant NULL + eqop.getAnOperand() = check and + eqop.getAnOperand() instanceof NullValue +} + +from VariableAccess unchecked, VariableAccess check, EqualityOperation eqop, Parameter param +where + // a dereference + dereferenced(unchecked) and + // for a function parameter + unchecked.getTarget() = param and + // this function parameter is not overwritten + count(param.getAnAssignment()) = 0 and + check.getTarget() = param and + // which is once checked + candidateResultChecked(check, eqop) and + // and which has not been checked before in this code path + candidateResultUnchecked(unchecked) +select check, "This null check is redundant or there is a missing null check before $@ ", unchecked, + "where dereferencing happens" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.cpp new file mode 100644 index 000000000000..001f4da6028d --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.cpp @@ -0,0 +1,25 @@ +///// Library routines ///// + +int scanf(const char *format, ...); +int sscanf(const char *str, const char *format, ...); +int fscanf(const char *str, const char *format, ...); + +///// EXAMPLES ///// + +int main(int argc, char **argv) +{ + + // BAD, do not use scanf without specifying a length first + char buf1[10]; + scanf("%s", buf1); + + // GOOD, length is specified. The length should be one less than the size of the buffer, since the last character is the NULL terminator. + char buf2[10]; + sscanf(buf2, "%9s"); + + // BAD, do not use scanf without specifying a length first + char file[10]; + fscanf(file, "%s", buf2); + + return 0; +} diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.qhelp new file mode 100644 index 000000000000..c3561a6c17f6 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.qhelp @@ -0,0 +1,25 @@ + + + +

    It is bad practice to use any of the scanf functions without including a specified length within the format parameter, as it will be vulnerable to buffer overflows.

    + +
    + + + +

    Specify a length within the format string parameter, and make this length one less than the size of the buffer, since the last character should be reserved for the NULL terminator.

    + +
    + + +

    The following example demonstrates safe and unsafe uses of scanf type functions.

    + + +
    + + + + +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.ql b/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.ql new file mode 100644 index 000000000000..dd5c389fdafc --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.ql @@ -0,0 +1,19 @@ +/** + * @name Scanf function without a specified length + * @description Use of one of the scanf functions without a specified length. + * @kind problem + * @problem.severity warning + * @id cpp/memory-unsafe-function-scan + * @tags reliability + * security + * external/cwe/cwe-120 + */ + +import cpp +import semmle.code.cpp.commons.Scanf + +from FunctionCall call, ScanfFunction sff +where + call.getTarget() = sff and + call.getArgument(sff.getFormatParameterIndex()).getValue().regexpMatch(".*%l?s.*") +select call, "Dangerous use of one of the scanf functions" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-359/PrivateCleartextWrite.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-359/PrivateCleartextWrite.qhelp new file mode 100644 index 000000000000..5403f030b52e --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-359/PrivateCleartextWrite.qhelp @@ -0,0 +1,32 @@ + + + +

    Private data that is stored in a file or buffer unencrypted is accessible to an attacker who gains access to the +storage.

    + +
    + + +

    Ensure that private data is always encrypted before being stored. +It may be wise to encrypt information before it is put into a buffer that may be readable in memory.

    + +

    In general, decrypt private data only at the point where it is necessary for it to be used in +cleartext.

    + +
    + + + +
  • OWASP Sensitive_Data_Exposure
  • +
  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • +
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • + + + + + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql b/cpp/ql/src/experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql new file mode 100644 index 000000000000..60b13525aff4 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql @@ -0,0 +1,21 @@ +/** + * @name Exposure of private information + * @description If private information is written to an external location, it may be accessible by + * unauthorized persons. + * @kind path-problem + * @problem.severity error + * @id cpp/private-cleartext-write + * @tags security + * external/cwe/cwe-359 + */ + +import cpp +import experimental.semmle.code.cpp.security.PrivateCleartextWrite +import experimental.semmle.code.cpp.security.PrivateCleartextWrite::PrivateCleartextWrite +import DataFlow::PathGraph + +from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink +where b.hasFlowPath(source, sink) +select sink.getNode(), + "This write into the external location '" + sink + "' may contain unencrypted data from $@", + source, "this source." diff --git a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll new file mode 100644 index 000000000000..0cf570a72b43 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll @@ -0,0 +1,65 @@ +/** + * EXPERIMENTAL: The API of this module may change without notice. + * + * Provides a class for modeling `RangeSsaDefinition`s with a restricted range. + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * EXPERIMENTAL: The API of this class may change without notice. + * + * An SSA definition for which a range can be deduced. As with + * `RangeSsaDefinition` and `SsaDefinition`, instances of this class + * correspond to points in the program where one or more variables are defined + * or have their value constrained in some way. + * + * Extend this class to add functionality to the range analysis library. + */ +abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition { + /** + * Holds if this `SimpleRangeAnalysisDefinition` adds range information for + * `v`. Because a `SimpleRangeAnalysisDefinition` is just a point in the + * program, it's possible that more than one variable might be defined at + * this point. This predicate clarifies which variable(s) should get range + * information from `this`. + * + * This predicate **must be overridden** to hold for any `v` that can show + * up in the other members of `SimpleRangeAnalysisDefinition`. Conversely, + * the other members **must be accurate** for any `v` in this predicate. + */ + abstract predicate hasRangeInformationFor(StackVariable v); + + /** + * Holds if `(this, v)` depends on the range of the unconverted expression + * `e`. This information is used to inform the range analysis about cyclic + * dependencies. Without this information, range analysis might work for + * simple cases but will go into infinite loops on complex code. + * + * For example, when modelling the definition by reference in a call to an + * overloaded `operator=`, written as `v = e`, the definition of `(this, v)` + * depends on `e`. + */ + abstract predicate dependsOnExpr(StackVariable v, Expr e); + + /** + * Gets the lower bound of the variable `v` defined by this definition. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their dependencies. + */ + abstract float getLowerBounds(StackVariable v); + + /** + * Gets the upper bound of the variable `v` defined by this definition. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their dependencies. + */ + abstract float getUpperBounds(StackVariable v); +} + +import SimpleRangeAnalysisInternal diff --git a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll new file mode 100644 index 000000000000..c8c1110b3af8 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll @@ -0,0 +1,78 @@ +/** + * EXPERIMENTAL: The API of this module may change without notice. + * + * Provides a class for modeling `Expr`s with a restricted range. + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * EXPERIMENTAL: The API of this class may change without notice. + * + * An expression for which a range can be deduced. Extend this class to add + * functionality to the range analysis library. + */ +abstract class SimpleRangeAnalysisExpr extends Expr { + /** + * Gets the lower bound of the expression. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their children. + */ + abstract float getLowerBounds(); + + /** + * Gets the upper bound of the expression. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their children. + */ + abstract float getUpperBounds(); + + /** + * Holds if the range this expression depends on the definition `srcDef` for + * StackVariable `srcVar`. + * + * Because this predicate cannot be recursive, most implementations should + * override `dependsOnChild` instead. + */ + predicate dependsOnDef(RangeSsaDefinition srcDef, StackVariable srcVar) { none() } + + /** + * Holds if this expression depends on the range of its unconverted + * subexpression `child`. This information is used to inform the range + * analysis about cyclic dependencies. Without this information, range + * analysis might work for simple cases but will go into infinite loops on + * complex code. + * + * For example, when modeling a function call whose return value depends on + * all of its arguments, implement this predicate as + * `child = this.getAnArgument()`. + */ + abstract predicate dependsOnChild(Expr child); +} + +import SimpleRangeAnalysisInternal + +/** + * This class exists to prevent the QL front end from emitting compile errors + * inside `SimpleRangeAnalysis.qll` about certain conjuncts being empty + * because the overrides of `SimpleRangeAnalysisExpr` that happen to be in + * scope do not make use of every feature it offers. + */ +private class Empty extends SimpleRangeAnalysisExpr { + Empty() { + // This predicate is complicated enough that the QL type checker doesn't + // see it as empty but simple enough that the optimizer should. + this = this and none() + } + + override float getLowerBounds() { none() } + + override float getUpperBounds() { none() } + + override predicate dependsOnChild(Expr child) { none() } +} diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll index 8388511932e7..39db446e3d30 100644 --- a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll @@ -20,7 +20,7 @@ import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.ValueNumbering private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.models.interfaces.Allocation -private import semmle.code.cpp.rangeanalysis.RangeUtils +private import experimental.semmle.code.cpp.rangeanalysis.RangeUtils private newtype TLength = TZeroLength() or diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/Bound.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/Bound.qll new file mode 100644 index 000000000000..bdd0e39f9b7e --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/Bound.qll @@ -0,0 +1,82 @@ +import cpp +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.ValueNumbering + +private newtype TBound = + TBoundZero() or + TBoundValueNumber(ValueNumber vn) { + exists(Instruction i | + vn.getAnInstruction() = i and + ( + i.getResultIRType() instanceof IRIntegerType or + i.getResultIRType() instanceof IRAddressType + ) and + not vn.getAnInstruction() instanceof ConstantInstruction + | + i instanceof PhiInstruction + or + i instanceof InitializeParameterInstruction + or + i instanceof CallInstruction + or + i instanceof VariableAddressInstruction + or + i instanceof FieldAddressInstruction + or + i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction + or + i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction + or + i.getAUse() instanceof ArgumentOperand + ) + } + +/** + * A bound that may be inferred for an expression plus/minus an integer delta. + */ +abstract class Bound extends TBound { + abstract string toString(); + + /** Gets an expression that equals this bound plus `delta`. */ + abstract Instruction getInstruction(int delta); + + /** Gets an expression that equals this bound. */ + Instruction getInstruction() { result = getInstruction(0) } + + abstract Location getLocation(); +} + +/** + * The bound that corresponds to the integer 0. This is used to represent all + * integer bounds as bounds are always accompanied by an added integer delta. + */ +class ZeroBound extends Bound, TBoundZero { + override string toString() { result = "0" } + + override Instruction getInstruction(int delta) { + result.(ConstantValueInstruction).getValue().toInt() = delta + } + + override Location getLocation() { result instanceof UnknownDefaultLocation } +} + +/** + * A bound corresponding to the value of an `Instruction`. + */ +class ValueNumberBound extends Bound, TBoundValueNumber { + ValueNumber vn; + + ValueNumberBound() { this = TBoundValueNumber(vn) } + + /** Gets an `Instruction` that equals this bound. */ + override Instruction getInstruction(int delta) { + this = TBoundValueNumber(valueNumber(result)) and delta = 0 + } + + override string toString() { result = vn.getExampleInstruction().toString() } + + override Location getLocation() { result = vn.getLocation() } + + /** Gets the value number that equals this bound. */ + ValueNumber getValueNumber() { result = vn } +} diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll new file mode 100644 index 000000000000..bc63d740c321 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll @@ -0,0 +1,5 @@ +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +// +// Import each extension we want to enable +import extensions.SubtractSelf +import extensions.ConstantBitwiseAndExprRange diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll index 94227a5a8acd..a7375f66b669 100644 --- a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll @@ -13,7 +13,7 @@ import cpp private import experimental.semmle.code.cpp.rangeanalysis.ArrayLengthAnalysis -private import semmle.code.cpp.rangeanalysis.RangeAnalysis +private import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis /** * Gets the instruction that computes the address of memory that `i` accesses. diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll new file mode 100644 index 000000000000..4a5bef36b200 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll @@ -0,0 +1,624 @@ +/** + * Provides classes and predicates for range analysis. + * + * An inferred bound can either be a specific integer or a `ValueNumber` + * representing the abstract value of a set of `Instruction`s. + * + * If an inferred bound relies directly on a condition, then this condition is + * reported as the reason for the bound. + */ + +/* + * This library tackles range analysis as a flow problem. Consider e.g.: + * ``` + * len = arr.length; + * if (x < len) { ... y = x-1; ... y ... } + * ``` + * In this case we would like to infer `y <= arr.length - 2`, and this is + * accomplished by tracking the bound through a sequence of steps: + * ``` + * arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y + * ``` + * + * In its simplest form the step relation `I1 --> I2` relates two `Instruction`s + * such that `I1 <= B` implies `I2 <= B` for any `B` (with a second separate + * step relation handling lower bounds). Examples of such steps include + * assignments `I2 = I1` and conditions `x <= I1` where `I2` is a use of `x` + * guarded by the condition. + * + * In order to handle subtractions and additions with constants, and strict + * comparisons, the step relation is augmented with an integer delta. With this + * generalization `I1 --(delta)--> I2` relates two `Instruction`s and an integer + * such that `I1 <= B` implies `I2 <= B + delta` for any `B`. This corresponds + * to the predicate `boundFlowStep`. + * + * The complete range analysis is then implemented as the transitive closure of + * the step relation summing the deltas along the way. If `I1` transitively + * steps to `I2`, `delta` is the sum of deltas along the path, and `B` is an + * interesting bound equal to the value of `I1` then `I2 <= B + delta`. This + * corresponds to the predicate `boundedInstruction`. + * + * Bounds come in two forms: either they are relative to zero (and thus provide + * a constant bound), or they are relative to some program value. This value is + * represented by the `ValueNumber` class, each instance of which represents a + * set of `Instructions` that must have the same value. + * + * Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`. + * There are essentially two cases: + * - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`. + * - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`. + * The first case is for whenever a bound can be proven without taking looping + * into account. The second case is relevant when `x2` comes from a back-edge + * where we can prove that the variable has been non-increasing through the + * loop-iteration as this means that any upper bound that holds prior to the + * loop also holds for the variable during the loop. + * This generalizes to a phi node with `n` inputs, so if + * `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we + * also have `x0 <= B + delta` if we can prove either: + * - `xj <= B + d` with `d <= delta` or + * - `xj <= x0 + d` with `d <= 0` + * for each input `xj`. + * + * As all inferred bounds can be related directly to a path in the source code + * the only source of non-termination is if successive redundant (and thereby + * increasingly worse) bounds are calculated along a loop in the source code. + * We prevent this by weakening the bound to a small finite set of bounds when + * a path follows a second back-edge (we postpone weakening till the second + * back-edge as a precise bound might require traversing a loop once). + */ + +import cpp +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.controlflow.IRGuards +private import semmle.code.cpp.ir.ValueNumbering +private import RangeUtils +private import SignAnalysis +import Bound + +cached +private module RangeAnalysisCache { + cached + module RangeAnalysisPublic { + /** + * Holds if `b + delta` is a valid bound for `i` and this is the best such delta. + * - `upper = true` : `i <= b + delta` + * - `upper = false` : `i >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ + cached + predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) { + boundedInstruction(i, b, delta, upper, _, _, reason) and + bestInstructionBound(i, b, delta, upper) + } + + /** + * Holds if `b + delta` is a valid bound for `op` and this is the best such delta. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ + cached + predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) { + boundedOperandCand(op, b, delta, upper, reason) and + bestOperandBound(op, b, delta, upper) + } + } + + /** + * Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`. + */ + cached + predicate possibleReason(IRGuardCondition guard) { + guard = boundFlowCond(_, _, _, _, _) + or + guard = eqFlowCond(_, _, _, _, _) + } +} + +private import RangeAnalysisCache +import RangeAnalysisPublic + +/** + * Holds if `b + delta` is a valid bound for `e` and this is the best such delta. + * - `upper = true` : `e <= b + delta` + * - `upper = false` : `e >= b + delta` + */ +private predicate bestInstructionBound(Instruction i, Bound b, int delta, boolean upper) { + delta = min(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = true + or + delta = max(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = false +} + +/** + * Holds if `b + delta` is a valid bound for `op`. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ +private predicate boundedOperandCand(Operand op, Bound b, int delta, boolean upper, Reason reason) { + boundedNonPhiOperand(op, b, delta, upper, _, _, reason) + or + boundedPhiOperand(op, b, delta, upper, _, _, reason) +} + +/** + * Holds if `b + delta` is a valid bound for `op` and this is the best such delta. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + */ +private predicate bestOperandBound(Operand op, Bound b, int delta, boolean upper) { + delta = min(int d | boundedOperandCand(op, b, d, upper, _)) and upper = true + or + delta = max(int d | boundedOperandCand(op, b, d, upper, _)) and upper = false +} + +/** + * Gets a condition that tests whether `vn` equals `bound + delta`. + * + * If the condition evaluates to `testIsTrue`: + * - `isEq = true` : `vn == bound + delta` + * - `isEq = false` : `vn != bound + delta` + */ +private IRGuardCondition eqFlowCond( + ValueNumber vn, Operand bound, int delta, boolean isEq, boolean testIsTrue +) { + result.comparesEq(vn.getAUse(), bound, delta, isEq, testIsTrue) +} + +/** + * Holds if `op1 + delta` is a valid bound for `op2`. + * - `upper = true` : `op2 <= op1 + delta` + * - `upper = false` : `op2 >= op1 + delta` + */ +private predicate boundFlowStepSsa( + NonPhiOperand op2, Operand op1, int delta, boolean upper, Reason reason +) { + exists(IRGuardCondition guard, boolean testIsTrue | + guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and + guard.controls(op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) + or + exists(IRGuardCondition guard, boolean testIsTrue, SafeCastInstruction cast | + valueNumberOfOperand(op2) = valueNumber(cast.getUnary()) and + guard = boundFlowCond(valueNumber(cast), op1, delta, upper, testIsTrue) and + guard.controls(op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +/** + * Gets a condition that tests whether `vn` is bounded by `bound + delta`. + * + * If the condition evaluates to `testIsTrue`: + * - `upper = true` : `vn <= bound + delta` + * - `upper = false` : `vn >= bound + delta` + */ +private IRGuardCondition boundFlowCond( + ValueNumber vn, NonPhiOperand bound, int delta, boolean upper, boolean testIsTrue +) { + exists(int d | + result.comparesLt(vn.getAUse(), bound, d, upper, testIsTrue) and + // `comparesLt` provides bounds of the form `x < y + k` or `x >= y + k`, but we need + // `x <= y + k` so we strengthen here. `testIsTrue` has the same semantics in `comparesLt` as + // it does here, so we don't need to account for it. + if upper = true then delta = d - 1 else delta = d + ) + or + result = eqFlowCond(vn, bound, delta, true, testIsTrue) and + (upper = true or upper = false) +} + +private newtype TReason = + TNoReason() or + TCondReason(IRGuardCondition guard) { possibleReason(guard) } + +/** + * A reason for an inferred bound. This can either be `CondReason` if the bound + * is due to a specific condition, or `NoReason` if the bound is inferred + * without going through a bounding condition. + */ +abstract class Reason extends TReason { + abstract string toString(); +} + +class NoReason extends Reason, TNoReason { + override string toString() { result = "NoReason" } +} + +class CondReason extends Reason, TCondReason { + IRGuardCondition getCond() { this = TCondReason(result) } + + override string toString() { result = getCond().toString() } +} + +/** + * Holds if `typ` is a small integral type with the given lower and upper bounds. + */ +private predicate typeBound(IRIntegerType typ, int lowerbound, int upperbound) { + typ.isSigned() and typ.getByteSize() = 1 and lowerbound = -128 and upperbound = 127 + or + typ.isUnsigned() and typ.getByteSize() = 1 and lowerbound = 0 and upperbound = 255 + or + typ.isSigned() and typ.getByteSize() = 2 and lowerbound = -32768 and upperbound = 32767 + or + typ.isUnsigned() and typ.getByteSize() = 2 and lowerbound = 0 and upperbound = 65535 +} + +/** + * A cast to a small integral type that may overflow or underflow. + */ +private class NarrowingCastInstruction extends ConvertInstruction { + NarrowingCastInstruction() { + not this instanceof SafeCastInstruction and + typeBound(getResultIRType(), _, _) + } + + /** Gets the lower bound of the resulting type. */ + int getLowerBound() { typeBound(getResultIRType(), result, _) } + + /** Gets the upper bound of the resulting type. */ + int getUpperBound() { typeBound(getResultIRType(), _, result) } +} + +/** + * Holds if `op + delta` is a valid bound for `i`. + * - `upper = true` : `i <= op + delta` + * - `upper = false` : `i >= op + delta` + */ +private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, boolean upper) { + valueFlowStep(i, op, delta) and + (upper = true or upper = false) + or + i.(SafeCastInstruction).getAnOperand() = op and + delta = 0 and + (upper = true or upper = false) + or + exists(Operand x | + i.(AddInstruction).getAnOperand() = op and + i.(AddInstruction).getAnOperand() = x and + op != x + | + not exists(getValue(getConstantValue(op.getUse()))) and + not exists(getValue(getConstantValue(x.getUse()))) and + if strictlyPositive(x) + then upper = false and delta = 1 + else + if positive(x) + then upper = false and delta = 0 + else + if strictlyNegative(x) + then upper = true and delta = -1 + else + if negative(x) + then upper = true and delta = 0 + else none() + ) + or + exists(Operand x | + exists(SubInstruction sub | + i = sub and + sub.getLeftOperand() = op and + sub.getRightOperand() = x + ) + | + // `x` with constant value is covered by valueFlowStep + not exists(getValue(getConstantValue(x.getUse()))) and + if strictlyPositive(x) + then upper = true and delta = -1 + else + if positive(x) + then upper = true and delta = 0 + else + if strictlyNegative(x) + then upper = false and delta = 1 + else + if negative(x) + then upper = false and delta = 0 + else none() + ) + or + i.(RemInstruction).getRightOperand() = op and positive(op) and delta = -1 and upper = true + or + i.(RemInstruction).getLeftOperand() = op and positive(op) and delta = 0 and upper = true + or + i.(BitAndInstruction).getAnOperand() = op and positive(op) and delta = 0 and upper = true + or + i.(BitOrInstruction).getAnOperand() = op and + positiveInstruction(i) and + delta = 0 and + upper = false + // TODO: min, max, rand +} + +private predicate boundFlowStepMul(Instruction i1, Operand op, int factor) { + exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | + i1.(MulInstruction).hasOperands(op, c.getAUse()) and factor = k + or + exists(ShiftLeftInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRightOperand() = c.getAUse() and factor = 2.pow(k) + ) + ) +} + +private predicate boundFlowStepDiv(Instruction i1, Operand op, int factor) { + exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | + exists(DivInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = k + ) + or + exists(ShiftRightInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = 2.pow(k) + ) + ) +} + +/** + * Holds if `b` is a valid bound for `op` + */ +pragma[noinline] +private predicate boundedNonPhiOperand( + NonPhiOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(NonPhiOperand op2, int d1, int d2 | + boundFlowStepSsa(op, op2, d1, upper, reason) and + boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, _) and + delta = d1 + d2 + ) + or + boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) + or + exists(int d, Reason r1, Reason r2 | + boundedNonPhiOperand(op, b, d, upper, fromBackEdge, origdelta, r2) + | + unequalOperand(op, b, d, r1) and + ( + upper = true and delta = d - 1 + or + upper = false and delta = d + 1 + ) and + ( + reason = r1 + or + reason = r2 and not r2 instanceof NoReason + ) + ) +} + +/** + * Holds if `op1 + delta` is a valid bound for `op2`. + * - `upper = true` : `op2 <= op1 + delta` + * - `upper = false` : `op2 >= op1 + delta` + */ +private predicate boundFlowStepPhi( + PhiInputOperand op2, Operand op1, int delta, boolean upper, Reason reason +) { + op2.getDef().(CopyInstruction).getSourceValueOperand() = op1 and + (upper = true or upper = false) and + reason = TNoReason() and + delta = 0 + or + exists(IRGuardCondition guard, boolean testIsTrue | + guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and + guard.controlsEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +private predicate boundedPhiOperand( + PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(NonPhiOperand op2, int d1, int d2, Reason r1, Reason r2 | + boundFlowStepPhi(op, op2, d1, upper, r1) and + boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, r2) and + delta = d1 + d2 and + (if r1 instanceof NoReason then reason = r2 else reason = r1) + ) + or + boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) + or + exists(int d, Reason r1, Reason r2 | + boundedInstruction(op.getDef(), b, d, upper, fromBackEdge, origdelta, r2) + | + unequalOperand(op, b, d, r1) and + ( + upper = true and delta = d - 1 + or + upper = false and delta = d + 1 + ) and + ( + reason = r1 + or + reason = r2 and not r2 instanceof NoReason + ) + ) +} + +/** Holds if `op2 != op1 + delta` at `pos`. */ +private predicate unequalFlowStep(Operand op2, Operand op1, int delta, Reason reason) { + exists(IRGuardCondition guard, boolean testIsTrue | + guard = eqFlowCond(valueNumberOfOperand(op2), op1, delta, false, testIsTrue) and + guard.controls(op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +/** + * Holds if `op != b + delta` at `pos`. + */ +private predicate unequalOperand(Operand op, Bound b, int delta, Reason reason) { + exists(Operand op2, int d1, int d2 | + unequalFlowStep(op, op2, d1, reason) and + boundedNonPhiOperand(op2, b, d2, true, _, _, _) and + boundedNonPhiOperand(op2, b, d2, false, _, _, _) and + delta = d1 + d2 + ) +} + +private predicate boundedPhiCandValidForEdge( + PhiInstruction phi, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason, PhiInputOperand op +) { + boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and + ( + exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = true and d <= delta) + or + exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = false and d >= delta) + or + selfBoundedPhiInp(phi, op, upper) + ) +} + +/** Weakens a delta to lie in the range `[-1..1]`. */ +bindingset[delta, upper] +private int weakenDelta(boolean upper, int delta) { + delta in [-1 .. 1] and result = delta + or + upper = true and result = -1 and delta < -1 + or + upper = false and result = 1 and delta > 1 +} + +private predicate boundedPhiInp( + PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, + int origdelta, Reason reason +) { + phi.getAnOperand() = op and + exists(int d, boolean fromBackEdge0 | + boundedPhiOperand(op, b, d, upper, fromBackEdge0, origdelta, reason) + or + b.(ValueNumberBound).getInstruction() = op.getDef() and + d = 0 and + (upper = true or upper = false) and + fromBackEdge0 = false and + origdelta = 0 and + reason = TNoReason() + | + if backEdge(phi, op) + then + fromBackEdge = true and + ( + fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta + or + fromBackEdge0 = false and delta = d + ) + else ( + delta = d and fromBackEdge = fromBackEdge0 + ) + ) +} + +pragma[noinline] +private predicate boundedPhiInp1( + PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper +) { + boundedPhiInp(phi, op, b, delta, upper, _, _, _) +} + +private predicate selfBoundedPhiInp(PhiInstruction phi, PhiInputOperand op, boolean upper) { + exists(int d, ValueNumberBound phibound | + phibound.getInstruction() = phi and + boundedPhiInp(phi, op, phibound, d, upper, _, _, _) and + ( + upper = true and d <= 0 + or + upper = false and d >= 0 + ) + ) +} + +pragma[noinline] +private predicate boundedPhiCand( + PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(PhiInputOperand op | + boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason) + ) +} + +/** + * Holds if the value being cast has an upper (for `upper = true`) or lower + * (for `upper = false`) bound within the bounds of the resulting type. + * For `upper = true` this means that the cast will not overflow and for + * `upper = false` this means that the cast will not underflow. + */ +private predicate safeNarrowingCast(NarrowingCastInstruction cast, boolean upper) { + exists(int bound | + boundedNonPhiOperand(cast.getAnOperand(), any(ZeroBound zb), bound, upper, _, _, _) + | + upper = true and bound <= cast.getUpperBound() + or + upper = false and bound >= cast.getLowerBound() + ) +} + +pragma[noinline] +private predicate boundedCastExpr( + NarrowingCastInstruction cast, Bound b, int delta, boolean upper, boolean fromBackEdge, + int origdelta, Reason reason +) { + boundedNonPhiOperand(cast.getAnOperand(), b, delta, upper, fromBackEdge, origdelta, reason) +} + +/** + * Holds if `b + delta` is a valid bound for `i`. + * - `upper = true` : `i <= b + delta` + * - `upper = false` : `i >= b + delta` + */ +private predicate boundedInstruction( + Instruction i, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + i instanceof PhiInstruction and + forex(PhiInputOperand op | op = i.getAnOperand() | + boundedPhiCandValidForEdge(i, b, delta, upper, fromBackEdge, origdelta, reason, op) + ) + or + i = b.getInstruction(delta) and + (upper = true or upper = false) and + fromBackEdge = false and + origdelta = delta and + reason = TNoReason() + or + exists(Operand mid, int d1, int d2 | + boundFlowStep(i, mid, d1, upper) and + boundedNonPhiOperand(mid, b, d2, upper, fromBackEdge, origdelta, reason) and + delta = d1 + d2 and + not exists(getValue(getConstantValue(i))) + ) + or + exists(Operand mid, int factor, int d | + boundFlowStepMul(i, mid, factor) and + boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and + b instanceof ZeroBound and + delta = d * factor and + not exists(getValue(getConstantValue(i))) + ) + or + exists(Operand mid, int factor, int d | + boundFlowStepDiv(i, mid, factor) and + boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and + d >= 0 and + b instanceof ZeroBound and + delta = d / factor and + not exists(getValue(getConstantValue(i))) + ) + or + exists(NarrowingCastInstruction cast | + cast = i and + safeNarrowingCast(cast, upper.booleanNot()) and + boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason) + ) +} diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeUtils.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeUtils.qll new file mode 100644 index 000000000000..bffd08fbe527 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/RangeUtils.qll @@ -0,0 +1,134 @@ +import cpp +private import semmle.code.cpp.ir.IR +// TODO: move this dependency +import semmle.code.cpp.ir.internal.IntegerConstant + +// TODO: move this out of test code +language[monotonicAggregates] +IntValue getConstantValue(Instruction instr) { + result = instr.(IntegerConstantInstruction).getValue().toInt() + or + exists(BinaryInstruction binInstr, IntValue left, IntValue right | + binInstr = instr and + left = getConstantValue(binInstr.getLeft()) and + right = getConstantValue(binInstr.getRight()) and + ( + binInstr instanceof AddInstruction and result = add(left, right) + or + binInstr instanceof SubInstruction and result = sub(left, right) + or + binInstr instanceof MulInstruction and result = mul(left, right) + or + binInstr instanceof DivInstruction and result = div(left, right) + ) + ) + or + result = getConstantValue(instr.(CopyInstruction).getSourceValue()) + or + exists(PhiInstruction phi | + phi = instr and + result = + max(PhiInputOperand operand | + operand = phi.getAnOperand() + | + getConstantValue(operand.getDef()) + ) and + result = + min(PhiInputOperand operand | + operand = phi.getAnOperand() + | + getConstantValue(operand.getDef()) + ) + ) +} + +predicate valueFlowStep(Instruction i, Operand op, int delta) { + i.(CopyInstruction).getSourceValueOperand() = op and delta = 0 + or + exists(Operand x | + i.(AddInstruction).getAnOperand() = op and + i.(AddInstruction).getAnOperand() = x and + op != x + | + delta = getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(SubInstruction).getLeftOperand() = op and + i.(SubInstruction).getRightOperand() = x + | + delta = -getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(PointerAddInstruction).getAnOperand() = op and + i.(PointerAddInstruction).getAnOperand() = x and + op != x + | + delta = i.(PointerAddInstruction).getElementSize() * getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(PointerSubInstruction).getLeftOperand() = op and + i.(PointerSubInstruction).getRightOperand() = x + | + delta = i.(PointerSubInstruction).getElementSize() * -getValue(getConstantValue(x.getDef())) + ) +} + +predicate backEdge(PhiInstruction phi, PhiInputOperand op) { + phi.getAnOperand() = op and + phi.getBlock() = op.getPredecessorBlock().getBackEdgeSuccessor(_) +} + +/** + * Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of + * range analysis. + */ +pragma[inline] +private predicate safeCast(IRIntegerType fromtyp, IRIntegerType totyp) { + fromtyp.getByteSize() < totyp.getByteSize() and + ( + fromtyp.isUnsigned() + or + totyp.isSigned() + ) + or + fromtyp.getByteSize() <= totyp.getByteSize() and + ( + fromtyp.isSigned() and + totyp.isSigned() + or + fromtyp.isUnsigned() and + totyp.isUnsigned() + ) +} + +/** + * A `ConvertInstruction` which casts from one pointer type to another. + */ +class PtrToPtrCastInstruction extends ConvertInstruction { + PtrToPtrCastInstruction() { + getResultIRType() instanceof IRAddressType and + getUnary().getResultIRType() instanceof IRAddressType + } +} + +/** + * A `ConvertInstruction` which casts from one integer type to another in a way + * that cannot overflow or underflow. + */ +class SafeIntCastInstruction extends ConvertInstruction { + SafeIntCastInstruction() { safeCast(getUnary().getResultIRType(), getResultIRType()) } +} + +/** + * A `ConvertInstruction` which does not invalidate bounds determined by + * range analysis. + */ +class SafeCastInstruction extends ConvertInstruction { + SafeCastInstruction() { + this instanceof PtrToPtrCastInstruction or + this instanceof SafeIntCastInstruction + } +} diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/SignAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/SignAnalysis.qll new file mode 100644 index 000000000000..0bd73105cc5f --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/SignAnalysis.qll @@ -0,0 +1,583 @@ +/** + * Provides sign analysis to determine whether expression are always positive + * or negative. + * + * The analysis is implemented as an abstract interpretation over the + * three-valued domain `{negative, zero, positive}`. + */ + +import cpp +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.controlflow.IRGuards +private import semmle.code.cpp.ir.ValueNumbering +private import SignAnalysisCached + +private newtype TSign = + TNeg() or + TZero() or + TPos() + +private class Sign extends TSign { + string toString() { + result = "-" and this = TNeg() + or + result = "0" and this = TZero() + or + result = "+" and this = TPos() + } + + Sign inc() { + this = TNeg() and result = TNeg() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TPos() + or + this = TPos() and result = TPos() + } + + Sign dec() { result.inc() = this } + + Sign neg() { + this = TNeg() and result = TPos() + or + this = TZero() and result = TZero() + or + this = TPos() and result = TNeg() + } + + Sign bitnot() { + this = TNeg() and result = TPos() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TNeg() + or + this = TPos() and result = TNeg() + } + + Sign add(Sign s) { + this = TZero() and result = s + or + s = TZero() and result = this + or + this = s and this = result + or + this = TPos() and s = TNeg() + or + this = TNeg() and s = TPos() + } + + Sign mul(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign div(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign rem(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = this and s = TNeg() + or + result = this and s = TPos() + } + + Sign bitand(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TZero() and this = TPos() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TNeg() + or + result = TPos() and this = TPos() and s = TPos() + } + + Sign bitor(Sign s) { + result = TZero() and this = TZero() and s = TZero() + or + result = TNeg() and this = TNeg() + or + result = TNeg() and s = TNeg() + or + result = TPos() and this = TPos() and s = TZero() + or + result = TPos() and this = TZero() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + } + + Sign bitxor(Sign s) { + result = TZero() and this = s + or + result = this and s = TZero() + or + result = s and this = TZero() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign lshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + this != TZero() and s != TZero() + } + + Sign rshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result = TNeg() and this = TNeg() + or + result != TNeg() and this = TPos() and s != TZero() + } + + Sign urshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result != TZero() and this = TNeg() and s != TZero() + or + result != TNeg() and this = TPos() and s != TZero() + } +} + +private Sign certainInstructionSign(Instruction inst) { + exists(int i | inst.(IntegerConstantInstruction).getValue().toInt() = i | + i < 0 and result = TNeg() + or + i = 0 and result = TZero() + or + i > 0 and result = TPos() + ) + or + exists(float f | f = inst.(FloatConstantInstruction).getValue().toFloat() | + f < 0 and result = TNeg() + or + f = 0 and result = TZero() + or + f > 0 and result = TPos() + ) +} + +private newtype CastKind = + TWiden() or + TSame() or + TNarrow() + +private CastKind getCastKind(ConvertInstruction ci) { + exists(int fromSize, int toSize | + toSize = ci.getResultSize() and + fromSize = ci.getUnary().getResultSize() + | + fromSize < toSize and + result = TWiden() + or + fromSize = toSize and + result = TSame() + or + fromSize > toSize and + result = TNarrow() + ) +} + +private predicate bindBool(boolean bool) { + bool = true or + bool = false +} + +private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck) { + result = TZero() and + ( + bindBool(fromSigned) and + bindBool(toSigned) and + s = TZero() + or + bindBool(fromSigned) and + bindBool(toSigned) and + ck = TNarrow() + ) + or + result = TPos() and + ( + bindBool(fromSigned) and + bindBool(toSigned) and + s = TPos() + or + bindBool(fromSigned) and + bindBool(toSigned) and + s = TNeg() and + ck = TNarrow() + or + fromSigned = true and + toSigned = false and + s = TNeg() + ) + or + result = TNeg() and + ( + fromSigned = true and + toSigned = true and + s = TNeg() + or + fromSigned = false and + toSigned = true and + s = TPos() and + ck != TWiden() + ) +} + +/** Holds if the sign of `e` is too complicated to determine. */ +private predicate unknownSign(Instruction i) { + // REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than + // the ones we don't understand. Currently, if we try to compute the sign of an instruction that + // we don't understand, and it isn't on this list, we incorrectly compute the sign as "none" + // instead of "+,0,-". + // Even better, we could track the state of each instruction as a power set of {non-negative, + // non-positive, non-zero}, which would mean that the representation of the sign of an unknown + // value would be the empty set. + ( + i instanceof UninitializedInstruction + or + i instanceof InitializeParameterInstruction + or + i instanceof BuiltInOperationInstruction + or + i instanceof CallInstruction + or + i instanceof ChiInstruction + ) +} + +/** + * Holds if `lowerbound` is a lower bound for `bounded`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate lowerBound( + IRGuardCondition comp, Operand lowerbound, Operand bounded, boolean isStrict +) { + exists(int adjustment, Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + ( + isStrict = true and + adjustment = 0 + or + isStrict = false and + adjustment = 1 + ) and + comp.ensuresLt(lowerbound, compared, adjustment, bounded.getUse().getBlock(), true) + ) +} + +/** + * Holds if `upperbound` is an upper bound for `bounded` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate upperBound( + IRGuardCondition comp, Operand upperbound, Operand bounded, boolean isStrict +) { + exists(int adjustment, Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + ( + isStrict = true and + adjustment = 0 + or + isStrict = false and + adjustment = 1 + ) and + comp.ensuresLt(compared, upperbound, adjustment, bounded.getUse().getBlock(), true) + ) +} + +/** + * Holds if `eqbound` is an equality/inequality for `bounded` at `pos`. This is + * restricted to only include bounds for which we might determine a sign. The + * boolean `isEq` gives the polarity: + * - `isEq = true` : `bounded = eqbound` + * - `isEq = false` : `bounded != eqbound` + */ +private predicate eqBound(IRGuardCondition guard, Operand eqbound, Operand bounded, boolean isEq) { + exists(Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + guard.ensuresEq(compared, eqbound, 0, bounded.getUse().getBlock(), isEq) + ) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in + * order for `v` to be positive. + */ +private predicate posBound(IRGuardCondition comp, Operand bound, Operand op) { + upperBound(comp, bound, op, _) or + eqBound(comp, bound, op, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in + * order for `v` to be negative. + */ +private predicate negBound(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) or + eqBound(comp, bound, op, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` + * can be zero. + */ +private predicate zeroBound(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) or + upperBound(comp, bound, op, _) or + eqBound(comp, bound, op, _) +} + +/** Holds if `bound` allows `v` to be positive at `pos`. */ +private predicate posBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + posBound(comp, bound, op) and TPos() = operandSign(bound) +} + +/** Holds if `bound` allows `v` to be negative at `pos`. */ +private predicate negBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + negBound(comp, bound, op) and TNeg() = operandSign(bound) +} + +/** Holds if `bound` allows `v` to be zero at `pos`. */ +private predicate zeroBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) and TNeg() = operandSign(bound) + or + lowerBound(comp, bound, op, false) and TZero() = operandSign(bound) + or + upperBound(comp, bound, op, _) and TPos() = operandSign(bound) + or + upperBound(comp, bound, op, false) and TZero() = operandSign(bound) + or + eqBound(comp, bound, op, true) and TZero() = operandSign(bound) + or + eqBound(comp, bound, op, false) and TZero() != operandSign(bound) +} + +private Sign binaryOpLhsSign(BinaryInstruction i) { result = operandSign(i.getLeftOperand()) } + +private Sign binaryOpRhsSign(BinaryInstruction i) { result = operandSign(i.getRightOperand()) } + +pragma[noinline] +private predicate binaryOpSigns(BinaryInstruction i, Sign lhs, Sign rhs) { + lhs = binaryOpLhsSign(i) and + rhs = binaryOpRhsSign(i) +} + +private Sign unguardedOperandSign(Operand operand) { + result = instructionSign(operand.getDef()) and + not hasGuard(operand, result) +} + +private Sign guardedOperandSign(Operand operand) { + result = instructionSign(operand.getDef()) and + hasGuard(operand, result) +} + +private Sign guardedOperandSignOk(Operand operand) { + result = TPos() and + forex(IRGuardCondition guard, Operand bound | posBound(guard, bound, operand) | + posBoundOk(guard, bound, operand) + ) + or + result = TNeg() and + forex(IRGuardCondition guard, Operand bound | negBound(guard, bound, operand) | + negBoundOk(guard, bound, operand) + ) + or + result = TZero() and + forex(IRGuardCondition guard, Operand bound | zeroBound(guard, bound, operand) | + zeroBoundOk(guard, bound, operand) + ) +} + +/** + * Holds if there is a bound that might restrict whether `v` has the sign `s` + * at `pos`. + */ +private predicate hasGuard(Operand op, Sign s) { + s = TPos() and posBound(_, _, op) + or + s = TNeg() and negBound(_, _, op) + or + s = TZero() and zeroBound(_, _, op) +} + +cached +module SignAnalysisCached { + /** + * Gets a sign that `operand` may have at `pos`, taking guards into account. + */ + cached + Sign operandSign(Operand operand) { + result = unguardedOperandSign(operand) + or + result = guardedOperandSign(operand) and + result = guardedOperandSignOk(operand) + or + // `result` is unconstrained if the definition is inexact. Then any sign is possible. + operand.isDefinitionInexact() + } + + cached + Sign instructionSign(Instruction i) { + result = certainInstructionSign(i) + or + not exists(certainInstructionSign(i)) and + not ( + result = TNeg() and + i.getResultIRType().(IRIntegerType).isUnsigned() + ) and + ( + unknownSign(i) + or + exists(ConvertInstruction ci, Instruction prior, boolean fromSigned, boolean toSigned | + i = ci and + prior = ci.getUnary() and + ( + if ci.getResultIRType().(IRIntegerType).isSigned() + then toSigned = true + else toSigned = false + ) and + ( + if prior.getResultIRType().(IRIntegerType).isSigned() + then fromSigned = true + else fromSigned = false + ) and + result = castSign(operandSign(ci.getAnOperand()), fromSigned, toSigned, getCastKind(ci)) + ) + or + result = operandSign(i.(CopyInstruction).getSourceValueOperand()) + or + result = operandSign(i.(BitComplementInstruction).getAnOperand()).bitnot() + or + result = operandSign(i.(NegateInstruction).getAnOperand()).neg() + or + exists(Sign s1, Sign s2 | binaryOpSigns(i, s1, s2) | + i instanceof AddInstruction and result = s1.add(s2) + or + i instanceof SubInstruction and result = s1.add(s2.neg()) + or + i instanceof MulInstruction and result = s1.mul(s2) + or + i instanceof DivInstruction and result = s1.div(s2) + or + i instanceof RemInstruction and result = s1.rem(s2) + or + i instanceof BitAndInstruction and result = s1.bitand(s2) + or + i instanceof BitOrInstruction and result = s1.bitor(s2) + or + i instanceof BitXorInstruction and result = s1.bitxor(s2) + or + i instanceof ShiftLeftInstruction and result = s1.lshift(s2) + or + i instanceof ShiftRightInstruction and + i.getResultIRType().(IRIntegerType).isSigned() and + result = s1.rshift(s2) + or + i instanceof ShiftRightInstruction and + not i.getResultIRType().(IRIntegerType).isSigned() and + result = s1.urshift(s2) + ) + or + // use hasGuard here? + result = operandSign(i.(PhiInstruction).getAnOperand()) + ) + } +} + +/** Holds if `i` can be positive and cannot be negative. */ +predicate positiveInstruction(Instruction i) { + instructionSign(i) = TPos() and + not instructionSign(i) = TNeg() +} + +/** Holds if `i` at `pos` can be positive at and cannot be negative. */ +predicate positive(Operand op) { + operandSign(op) = TPos() and + not operandSign(op) = TNeg() +} + +/** Holds if `i` can be negative and cannot be positive. */ +predicate negativeInstruction(Instruction i) { + instructionSign(i) = TNeg() and + not instructionSign(i) = TPos() +} + +/** Holds if `i` at `pos` can be negative and cannot be positive. */ +predicate negative(Operand op) { + operandSign(op) = TNeg() and + not operandSign(op) = TPos() +} + +/** Holds if `i` is strictly positive. */ +predicate strictlyPositiveInstruction(Instruction i) { + instructionSign(i) = TPos() and + not instructionSign(i) = TNeg() and + not instructionSign(i) = TZero() +} + +/** Holds if `i` is strictly positive at `pos`. */ +predicate strictlyPositive(Operand op) { + operandSign(op) = TPos() and + not operandSign(op) = TNeg() and + not operandSign(op) = TZero() +} + +/** Holds if `i` is strictly negative. */ +predicate strictlyNegativeInstruction(Instruction i) { + instructionSign(i) = TNeg() and + not instructionSign(i) = TPos() and + not instructionSign(i) = TZero() +} + +/** Holds if `i` is strictly negative at `pos`. */ +predicate strictlyNegative(Operand op) { + operandSign(op) = TNeg() and + not operandSign(op) = TPos() and + not operandSign(op) = TZero() +} diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/ConstantBitwiseAndExprRange.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/ConstantBitwiseAndExprRange.qll new file mode 100644 index 000000000000..33776bd81050 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/ConstantBitwiseAndExprRange.qll @@ -0,0 +1,90 @@ +private import cpp +private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr +private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils + +/** + * Holds if `e` is a constant or if it is a variable with a constant value + */ +float evaluateConstantExpr(Expr e) { + result = e.getValue().toFloat() + or + exists(SsaDefinition defn, StackVariable sv | + defn.getAUse(sv) = e and + result = defn.getDefiningValue(sv).getValue().toFloat() + ) +} + +/** + * The current implementation for `BitwiseAndExpr` only handles cases where both operands are + * either unsigned or non-negative constants. This class not only covers these cases, but also + * adds support for `&` expressions between a signed integer with a non-negative range and a + * non-negative constant. It also adds support for `&=` for the same set of cases as `&`. + */ +private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr { + ConstantBitwiseAndExprRange() { + exists(Expr l, Expr r | + l = this.(BitwiseAndExpr).getLeftOperand() and + r = this.(BitwiseAndExpr).getRightOperand() + or + l = this.(AssignAndExpr).getLValue() and + r = this.(AssignAndExpr).getRValue() + | + // No operands can be negative constants + not (evaluateConstantExpr(l) < 0 or evaluateConstantExpr(r) < 0) and + // At least one operand must be a non-negative constant + (evaluateConstantExpr(l) >= 0 or evaluateConstantExpr(r) >= 0) + ) + } + + Expr getLeftOperand() { + result = this.(BitwiseAndExpr).getLeftOperand() or + result = this.(AssignAndExpr).getLValue() + } + + Expr getRightOperand() { + result = this.(BitwiseAndExpr).getRightOperand() or + result = this.(AssignAndExpr).getRValue() + } + + override float getLowerBounds() { + // If an operand can have negative values, the lower bound is unconstrained. + // Otherwise, the lower bound is zero. + exists(float lLower, float rLower | + lLower = getFullyConvertedLowerBounds(getLeftOperand()) and + rLower = getFullyConvertedLowerBounds(getRightOperand()) and + ( + (lLower < 0 or rLower < 0) and + result = exprMinVal(this) + or + // This technically results in two lowerBounds when an operand range is negative, but + // that's fine since `exprMinVal(x) <= 0`. We can't use an if statement here without + // non-monotonic recursion issues + result = 0 + ) + ) + } + + override float getUpperBounds() { + // If an operand can have negative values, the upper bound is unconstrained. + // Otherwise, the upper bound is the minimum of the upper bounds of the operands + exists(float lLower, float lUpper, float rLower, float rUpper | + lLower = getFullyConvertedLowerBounds(getLeftOperand()) and + lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and + rLower = getFullyConvertedLowerBounds(getRightOperand()) and + rUpper = getFullyConvertedUpperBounds(getRightOperand()) and + ( + (lLower < 0 or rLower < 0) and + result = exprMaxVal(this) + or + // This technically results in two upperBounds when an operand range is negative, but + // that's fine since `exprMaxVal(b) >= result`. We can't use an if statement here without + // non-monotonic recursion issues + result = rUpper.minimum(lUpper) + ) + ) + } + + override predicate dependsOnChild(Expr child) { + child = getLeftOperand() or child = getRightOperand() + } +} diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll new file mode 100644 index 000000000000..ff716d02d6f6 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll @@ -0,0 +1,15 @@ +import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr + +private class SelfSub extends SimpleRangeAnalysisExpr, SubExpr { + SelfSub() { + // Match `x - x` but not `myInt - (unsigned char)myInt`. + getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() = + getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget() + } + + override float getLowerBounds() { result = 0 } + + override float getUpperBounds() { result = 0 } + + override predicate dependsOnChild(Expr child) { none() } +} diff --git a/cpp/ql/src/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll b/cpp/ql/src/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll new file mode 100644 index 000000000000..b495412f5a21 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll @@ -0,0 +1,63 @@ +/** + * Provides a taint-tracking configuration for reasoning about private information flowing unencrypted to an external location. + */ + +import cpp +import semmle.code.cpp.dataflow.TaintTracking +import experimental.semmle.code.cpp.security.PrivateData +import semmle.code.cpp.security.FileWrite +import semmle.code.cpp.security.BufferWrite +import semmle.code.cpp.dataflow.TaintTracking + +module PrivateCleartextWrite { + /** + * A data flow source for private information flowing unencrypted to an external location. + */ + abstract class Source extends DataFlow::ExprNode { } + + /** + * A data flow sink for private information flowing unencrypted to an external location. + */ + abstract class Sink extends DataFlow::ExprNode { } + + /** + * A sanitizer for private information flowing unencrypted to an external location. + */ + abstract class Sanitizer extends DataFlow::ExprNode { } + + /** A call to any method whose name suggests that it encodes or encrypts the parameter. */ + class ProtectSanitizer extends Sanitizer { + ProtectSanitizer() { + exists(Function m, string s | + this.getExpr().(FunctionCall).getTarget() = m and + m.getName().regexpMatch("(?i).*" + s + ".*") + | + s = "protect" or s = "encode" or s = "encrypt" + ) + } + } + + class WriteConfig extends TaintTracking::Configuration { + WriteConfig() { this = "Write configuration" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + } + + class PrivateDataSource extends Source { + PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr } + } + + class WriteSink extends Sink { + WriteSink() { + exists(FileWrite f, BufferWrite b | + this.asExpr() = f.getASource() + or + this.asExpr() = b.getAChild() + ) + } + } +} diff --git a/cpp/ql/src/experimental/semmle/code/cpp/security/PrivateData.qll b/cpp/ql/src/experimental/semmle/code/cpp/security/PrivateData.qll new file mode 100644 index 000000000000..621e8aad707b --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/security/PrivateData.qll @@ -0,0 +1,53 @@ +/** + * 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() { + // Inspired by the list on https://cwe.mitre.org/data/definitions/359.html + // Government identifiers, such as Social Security Numbers + result = "%social%security%number%" or + // Contact information, such as home addresses and telephone numbers + result = "%postcode%" or + result = "%zipcode%" or + // result = "%telephone%" or + // Geographic location - where the user is (or was) + result = "%latitude%" or + result = "%longitude%" or + // Financial data - such as credit card numbers, salary, bank accounts, and debts + result = "%creditcard%" or + result = "%salary%" or + result = "%bankaccount%" or + // Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc. + // result = "%email%" or + // result = "%mobile%" or + result = "%employer%" or + // Health - medical conditions, insurance status, prescription records + result = "%medical%" +} + +/** 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())) + } +} diff --git a/cpp/ql/src/external/CodeDuplication.qll b/cpp/ql/src/external/CodeDuplication.qll index 4548e0be85ed..8cc56d12e190 100644 --- a/cpp/ql/src/external/CodeDuplication.qll +++ b/cpp/ql/src/external/CodeDuplication.qll @@ -117,7 +117,7 @@ private predicate blockCoversStatement(int equivClass, int first, int last, Stmt private Stmt statementInMethod(FunctionDeclarationEntry m) { result.getParent+() = m.getBlock() and not result.getLocation() instanceof UnknownStmtLocation and - not result instanceof Block + not result instanceof BlockStmt } private predicate duplicateStatement( diff --git a/cpp/ql/src/external/DuplicateBlock.ql b/cpp/ql/src/external/DuplicateBlock.ql index 90070ed6a7df..3fdef9b510d4 100644 --- a/cpp/ql/src/external/DuplicateBlock.ql +++ b/cpp/ql/src/external/DuplicateBlock.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Duplicate code * @description This block of code is duplicated elsewhere. If possible, the shared code should be refactored so there is only one occurrence left. It may not always be possible to address these issues; other duplicate code checks (such as duplicate function, duplicate class) give subsets of the results with higher confidence. * @kind problem diff --git a/cpp/ql/src/external/DuplicateFunction.ql b/cpp/ql/src/external/DuplicateFunction.ql index ca749315bbfd..1a861867fcbc 100644 --- a/cpp/ql/src/external/DuplicateFunction.ql +++ b/cpp/ql/src/external/DuplicateFunction.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Duplicate function * @description There is another identical implementation of this function. Extract the code to a common file or superclass or delegate to improve sharing. * @kind problem diff --git a/cpp/ql/src/external/MostlyDuplicateClass.ql b/cpp/ql/src/external/MostlyDuplicateClass.ql index a7d6cc605a63..20b9f39214ed 100644 --- a/cpp/ql/src/external/MostlyDuplicateClass.ql +++ b/cpp/ql/src/external/MostlyDuplicateClass.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Mostly duplicate class * @description More than 80% of the methods in this class are duplicated in another class. Create a common supertype to improve code sharing. * @kind problem diff --git a/cpp/ql/src/external/MostlyDuplicateFile.ql b/cpp/ql/src/external/MostlyDuplicateFile.ql index 13ad0707ce8e..8cb23a432d24 100644 --- a/cpp/ql/src/external/MostlyDuplicateFile.ql +++ b/cpp/ql/src/external/MostlyDuplicateFile.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Mostly duplicate file * @description There is another file that shares a lot of the code with this file. Merge the two files to improve maintainability. * @kind problem diff --git a/cpp/ql/src/external/MostlyDuplicateFunction.ql b/cpp/ql/src/external/MostlyDuplicateFunction.ql index 1ec592a65680..8a7454e4c97f 100644 --- a/cpp/ql/src/external/MostlyDuplicateFunction.ql +++ b/cpp/ql/src/external/MostlyDuplicateFunction.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Mostly duplicate function * @description There is another function that shares a lot of the code with this one. Extract the code to a common file/superclass or delegate to improve sharing. * @kind problem diff --git a/cpp/ql/src/external/MostlySimilarFile.ql b/cpp/ql/src/external/MostlySimilarFile.ql index 27830a5c9515..81a6ed02d6cc 100644 --- a/cpp/ql/src/external/MostlySimilarFile.ql +++ b/cpp/ql/src/external/MostlySimilarFile.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Mostly similar file * @description There is another file that shares a lot of the code with this file. Notice that names of variables and types may have been changed. Merge the two files to improve maintainability. * @kind problem diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp index 7513d84d5267..70c8460c8355 100644 --- a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp @@ -7,7 +7,7 @@ - +

    This query highlights calls to the standard library functions abort, exit, getenv and system. diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql index d78bc0ee2a70..dedd5658bdf2 100644 --- a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql @@ -18,6 +18,7 @@ from Include i, File f, string extension where f = i.getIncludedFile() and extension = f.getExtension().toLowerCase() and + extension != "inc" and extension != "inl" and extension != "tcc" and extension != "tpp" and diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql index db8ad52e1f3c..a01e0c16bd4e 100644 --- a/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql @@ -18,6 +18,6 @@ import cpp from File f where - (f.getExtension().toLowerCase() = "h" or f.getExtension().toLowerCase() = "hpp") and + f.getExtension().toLowerCase() = ["h", "hpp"] and f.getExtension() != "h" select f, "AV Rule 53: Header files will always have a file name extension of .h." diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql index 2a5681093c7b..63e318125dc6 100644 --- a/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql @@ -13,7 +13,7 @@ import cpp from Stmt parent, Stmt child where - not child instanceof Block and + not child instanceof BlockStmt and ( child = parent.(IfStmt).getThen() or diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql index 02220f3d4d3a..67559557c3cc 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql @@ -45,6 +45,16 @@ predicate dereferenceThis(Expr e) { or // `*this = ...` (where `=` is not overloaded, so an `AssignExpr`) dereferenceThis(e.(AssignExpr).getLValue()) + or + // `e ? ... : ... ` + exists(ConditionalExpr cond | + cond = e and + dereferenceThis(cond.getThen()) and + dereferenceThis(cond.getElse()) + ) + or + // `..., ... ` + dereferenceThis(e.(CommaExpr).getRightOperand()) } /** diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp index 61275fedefab..69cc5322ad5f 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp @@ -7,7 +7,7 @@ - +

    This query ensures that all operators with opposites (e.g. == and !=) are both defined, and diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql index dcfaf06f49ba..26a56faab5c0 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql @@ -28,7 +28,7 @@ predicate oppositeOperators(string op1, string op2) { * `!op2(_, _)`. */ predicate implementedAsNegationOf(Operator op1, Operator op2) { - exists(Block b, ReturnStmt r, NotExpr n, Expr o | + exists(BlockStmt b, ReturnStmt r, NotExpr n, Expr o | b = op1.getBlock() and b.getNumStmt() = 1 and r = b.getStmt(0) and diff --git a/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql b/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql index bc1ebf8c1d91..0a608f1dbe9c 100644 --- a/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql +++ b/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql @@ -21,8 +21,8 @@ import cpp */ class WarningLateTemplateSpecialization extends CompilerWarning { WarningLateTemplateSpecialization() { - this.getTag() = "partial_spec_after_instantiation" or - this.getTag() = "partial_spec_after_instantiation_ambiguous" + this.getTag() = + ["partial_spec_after_instantiation", "partial_spec_after_instantiation_ambiguous"] } } diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp index 7dbbb12dba31..614fe4711fd1 100644 --- a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp @@ -7,7 +7,7 @@ - +

    This query highlights return statements that return pointers to an object allocated on the stack. The lifetime @@ -18,7 +18,7 @@ memory after the function has already returned will have undefined results. - + diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp index e47368f5d664..086d1ea1fb60 100644 --- a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp @@ -12,7 +12,7 @@ calling convention for x86, it would be whatever value was in the AX/EAX registe assuming the function had a non-float return type that can fit in a machine word.

    - + diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp index 6729583ff772..d67d84c5e064 100644 --- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp @@ -7,7 +7,7 @@ - +

    This query highlights identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope. diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql index 0b3572af97e5..d3e23c9a7f6b 100644 --- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql @@ -29,7 +29,7 @@ predicate localShadowsParameter(LocalVariable lv, Parameter p) { from Variable v, Variable shadowed where - not v.getParentScope().(Block).isInMacroExpansion() and + not v.getParentScope().(BlockStmt).isInMacroExpansion() and ( v.(LocalVariableOrParameter).shadowsGlobal(shadowed.(GlobalVariable)) or localShadowsParameter(v, shadowed) or diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp index e184b9edd6f7..73e66f0b0067 100644 --- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp @@ -7,7 +7,7 @@ - +

    This query highlights variables with the register storage class specifier. Modern compilers are now capable of diff --git a/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql index 973cb0165795..1f7fe79fd77b 100644 --- a/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql +++ b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql @@ -38,7 +38,7 @@ predicate noDefUsePath(LocalVariable lv, ControlFlowNode n) { } predicate neighbouringStmts(Stmt s1, Stmt s2) { - exists(Block b, int i | + exists(BlockStmt b, int i | i in [0 .. b.getNumStmt() - 2] and s1 = b.getStmt(i) and s2 = b.getStmt(i + 1) diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp index 75c4f03e9800..1633cb2a8674 100644 --- a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp +++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp @@ -7,7 +7,7 @@ - +

    This query highlights portions of code that can expose the floating point implementation of the underlying diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp index 9c6c369cfed9..b30e828428c9 100644 --- a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp +++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp @@ -7,7 +7,7 @@ - +

    This query highlights string literals that are assigned to a non-const variable. String literals diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp index 3c502a249285..946e0873ab0d 100644 --- a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp +++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp @@ -7,7 +7,7 @@ - +

    This query finds bit fields with members that are not explicitly declared to be unsigned. diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp index 5961991fcdc1..1beb8b94f35b 100644 --- a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp @@ -7,7 +7,7 @@ - +

    This query finds unsigned values that are being negated. Behavior is undefined in such cases. diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql index 7a8919faf896..5893f3f35a39 100644 --- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql @@ -22,6 +22,6 @@ where not s instanceof ControlStructure and // Exclude blocks; if a child of the block violates the rule that will still // be picked up so there is no point in blaming the block as well - not s instanceof Block and + not s instanceof BlockStmt and s.isPure() select s, "AV Rule 187: All non-null statements shall potentially have a side-effect." diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp index e65f5b8dfb42..85db1463d56b 100644 --- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp @@ -6,7 +6,7 @@ - +

    Use of goto statements makes code more difficult to understand and maintain. Consequently, the use of goto statements is deprecated except as a mechanism for breaking out of multiple nested loops. diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql index 579a615c27fd..2e730b666549 100644 --- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql @@ -18,7 +18,7 @@ import cpp // whether t is the last statement of s, possibly peeling off blocks predicate isTerminatingStmt(Stmt s, Stmt t) { - s = t or isTerminatingStmt(s.(Block).getLastStmt(), t) + s = t or isTerminatingStmt(s.(BlockStmt).getLastStmt(), t) } from BreakStmt s diff --git a/cpp/ql/src/jsf/jsfNote.qhelp b/cpp/ql/src/jsf/jsfNote.qhelp new file mode 100644 index 000000000000..4a05d0c6e344 --- /dev/null +++ b/cpp/ql/src/jsf/jsfNote.qhelp @@ -0,0 +1,18 @@ + + + +

    +This query is part of a suite that tests code against +the Joint Strike Fighter Air Vehicle C++ Coding Standard (JSF). +Alerts reported by this query highlight code that may break the +JSF rule listed in the References section. +

    + +

    +The JSF rule this query tests is likely to be too strict for projects +that do not follow the JSF standard. +

    + + diff --git a/cpp/ql/src/printAst.ql b/cpp/ql/src/printAst.ql new file mode 100644 index 000000000000..622e5812be13 --- /dev/null +++ b/cpp/ql/src/printAst.ql @@ -0,0 +1,27 @@ +/** + * @name Print AST + * @description Outputs a representation of a file's Abstract Syntax Tree. This + * query is used by the VS Code extension. + * @id cpp/print-ast + * @kind graph + * @tags ide-contextual-queries/print-ast + */ + +import cpp +import semmle.code.cpp.PrintAST +import definitions + +/** + * The source file to generate an AST from. + */ +external string selectedSourceFile(); + +class Cfg extends PrintASTConfiguration { + /** + * Holds if the AST for `func` should be printed. + * Print All functions from the selected file. + */ + override predicate shouldPrintFunction(Function func) { + func.getFile() = getEncodedFile(selectedSourceFile()) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll index 11ebef3e5ff2..f81f00064d7d 100644 --- a/cpp/ql/src/semmle/code/cpp/Class.qll +++ b/cpp/ql/src/semmle/code/cpp/Class.qll @@ -33,7 +33,7 @@ private import semmle.code.cpp.internal.ResolveClass class Class extends UserType { Class() { isClass(underlyingElement(this)) } - override string getCanonicalQLClass() { result = "Class" } + override string getAPrimaryQlClass() { result = "Class" } /** Gets a child declaration of this class, struct or union. */ override Declaration getADeclaration() { result = this.getAMember() } @@ -768,7 +768,7 @@ class ClassDerivation extends Locatable, @derivation { */ Class getBaseClass() { result = getBaseType().getUnderlyingType() } - override string getCanonicalQLClass() { result = "ClassDerivation" } + override string getAPrimaryQlClass() { result = "ClassDerivation" } /** * Gets the type from which we are deriving, without resolving any @@ -849,9 +849,7 @@ class ClassDerivation extends Locatable, @derivation { class LocalClass extends Class { LocalClass() { isLocal() } - override string getCanonicalQLClass() { - not this instanceof LocalStruct and result = "LocalClass" - } + override string getAPrimaryQlClass() { not this instanceof LocalStruct and result = "LocalClass" } override Function getEnclosingAccessHolder() { result = this.getEnclosingFunction() } } @@ -872,7 +870,7 @@ class LocalClass extends Class { class NestedClass extends Class { NestedClass() { this.isMember() } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof NestedStruct and result = "NestedClass" } @@ -893,7 +891,7 @@ class NestedClass extends Class { class AbstractClass extends Class { AbstractClass() { exists(PureVirtualFunction f | this.getAMemberFunction() = f) } - override string getCanonicalQLClass() { result = "AbstractClass" } + override string getAPrimaryQlClass() { result = "AbstractClass" } } /** @@ -934,7 +932,7 @@ class TemplateClass extends Class { exists(result.getATemplateArgument()) } - override string getCanonicalQLClass() { result = "TemplateClass" } + override string getAPrimaryQlClass() { result = "TemplateClass" } } /** @@ -955,7 +953,7 @@ class ClassTemplateInstantiation extends Class { ClassTemplateInstantiation() { tc.getAnInstantiation() = this } - override string getCanonicalQLClass() { result = "ClassTemplateInstantiation" } + override string getAPrimaryQlClass() { result = "ClassTemplateInstantiation" } /** * Gets the class template from which this instantiation was instantiated. @@ -996,7 +994,7 @@ abstract class ClassTemplateSpecialization extends Class { count(int i | exists(result.getTemplateArgument(i))) } - override string getCanonicalQLClass() { result = "ClassTemplateSpecialization" } + override string getAPrimaryQlClass() { result = "ClassTemplateSpecialization" } } /** @@ -1025,7 +1023,7 @@ class FullClassTemplateSpecialization extends ClassTemplateSpecialization { not this instanceof ClassTemplateInstantiation } - override string getCanonicalQLClass() { result = "FullClassTemplateSpecialization" } + override string getAPrimaryQlClass() { result = "FullClassTemplateSpecialization" } } /** @@ -1064,7 +1062,7 @@ class PartialClassTemplateSpecialization extends ClassTemplateSpecialization { count(int i | exists(getTemplateArgument(i))) } - override string getCanonicalQLClass() { result = "PartialClassTemplateSpecialization" } + override string getAPrimaryQlClass() { result = "PartialClassTemplateSpecialization" } } /** @@ -1089,7 +1087,7 @@ deprecated class Interface extends Class { ) } - override string getCanonicalQLClass() { result = "Interface" } + override string getAPrimaryQlClass() { result = "Interface" } } /** @@ -1104,7 +1102,7 @@ deprecated class Interface extends Class { class VirtualClassDerivation extends ClassDerivation { VirtualClassDerivation() { hasSpecifier("virtual") } - override string getCanonicalQLClass() { result = "VirtualClassDerivation" } + override string getAPrimaryQlClass() { result = "VirtualClassDerivation" } } /** @@ -1124,7 +1122,7 @@ class VirtualClassDerivation extends ClassDerivation { class VirtualBaseClass extends Class { VirtualBaseClass() { exists(VirtualClassDerivation cd | cd.getBaseClass() = this) } - override string getCanonicalQLClass() { result = "VirtualBaseClass" } + override string getAPrimaryQlClass() { result = "VirtualBaseClass" } /** A virtual class derivation of which this class/struct is the base. */ VirtualClassDerivation getAVirtualDerivation() { result.getBaseClass() = this } @@ -1146,7 +1144,7 @@ class VirtualBaseClass extends Class { class ProxyClass extends UserType { ProxyClass() { usertypes(underlyingElement(this), _, 9) } - override string getCanonicalQLClass() { result = "ProxyClass" } + override string getAPrimaryQlClass() { result = "ProxyClass" } /** Gets the location of the proxy class. */ override Location getLocation() { result = getTemplateParameter().getDefinitionLocation() } diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll index 6245005fd410..e5d3d83326ba 100644 --- a/cpp/ql/src/semmle/code/cpp/Declaration.qll +++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll @@ -124,7 +124,7 @@ class Declaration extends Locatable, @declaration { * To test whether this declaration has a particular name in the global * namespace, use `hasGlobalName`. */ - abstract string getName(); + string getName() { none() } // overridden in subclasses /** Holds if this declaration has the given name. */ predicate hasName(string name) { name = this.getName() } @@ -140,7 +140,7 @@ class Declaration extends Locatable, @declaration { } /** Gets a specifier of this declaration. */ - abstract Specifier getASpecifier(); + 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) } @@ -156,7 +156,7 @@ class Declaration extends Locatable, @declaration { * Gets the location of a declaration entry corresponding to this * declaration. */ - abstract Location getADeclarationLocation(); + Location getADeclarationLocation() { none() } // overridden in subclasses /** * Gets the declaration entry corresponding to this declaration that is a @@ -165,7 +165,7 @@ class Declaration extends Locatable, @declaration { DeclarationEntry getDefinition() { none() } /** Gets the location of the definition, if any. */ - abstract Location getDefinitionLocation(); + Location getDefinitionLocation() { none() } // overridden in subclasses /** Holds if the declaration has a definition. */ predicate hasDefinition() { exists(this.getDefinition()) } @@ -289,6 +289,8 @@ class Declaration extends Locatable, @declaration { } } +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: @@ -304,9 +306,9 @@ class Declaration extends Locatable, @declaration { * See the comment above `Declaration` for an explanation of the relationship * between `Declaration` and `DeclarationEntry`. */ -abstract class DeclarationEntry extends Locatable { +class DeclarationEntry extends Locatable, TDeclarationEntry { /** Gets a specifier associated with this declaration entry. */ - abstract string getASpecifier(); + string getASpecifier() { none() } // overridden in subclasses /** * Gets the name associated with the corresponding definition (where @@ -329,10 +331,10 @@ abstract class DeclarationEntry extends Locatable { * `I.getADeclarationEntry()` returns `D` * but `D.getDeclaration()` only returns `C` */ - abstract Declaration getDeclaration(); + Declaration getDeclaration() { none() } // overridden in subclasses /** Gets the name associated with this declaration entry, if any. */ - abstract string getName(); + string getName() { none() } // overridden in subclasses /** * Gets the type associated with this declaration entry. @@ -341,7 +343,7 @@ abstract class DeclarationEntry extends Locatable { * For function declarations, get the return type of the function. * For type declarations, get the type being declared. */ - abstract Type getType(); + Type getType() { none() } // overridden in subclasses /** * Gets the type associated with this declaration entry after specifiers @@ -359,7 +361,7 @@ abstract class DeclarationEntry extends Locatable { predicate hasSpecifier(string specifier) { getASpecifier() = specifier } /** Holds if this declaration entry is a definition. */ - abstract predicate isDefinition(); + predicate isDefinition() { none() } // overridden in subclasses override string toString() { if isDefinition() @@ -371,6 +373,8 @@ abstract class DeclarationEntry extends Locatable { } } +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 @@ -392,7 +396,7 @@ abstract class DeclarationEntry extends Locatable { * the informal phrase "_R_ occurs in a member or friend of class C", where * `AccessHolder` corresponds to this _R_. */ -abstract class AccessHolder extends Declaration { +class AccessHolder extends Declaration, TAccessHolder { /** * Holds if `this` can access private members of class `c`. * @@ -410,7 +414,7 @@ abstract class AccessHolder extends Declaration { /** * Gets the nearest enclosing `AccessHolder`. */ - abstract AccessHolder getEnclosingAccessHolder(); + AccessHolder getEnclosingAccessHolder() { none() } // overridden in subclasses /** * Holds if a base class `base` of `derived` _is accessible at_ `this` (N4140 diff --git a/cpp/ql/src/semmle/code/cpp/Element.qll b/cpp/ql/src/semmle/code/cpp/Element.qll index 50b72037ff77..c7cc4a65b368 100644 --- a/cpp/ql/src/semmle/code/cpp/Element.qll +++ b/cpp/ql/src/semmle/code/cpp/Element.qll @@ -55,12 +55,21 @@ class ElementBase extends @element { cached string toString() { none() } + /** DEPRECATED: use `getAPrimaryQlClass` instead. */ + deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() } + /** - * Canonical QL class corresponding to this element. + * 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. * - * ElementBase is the root class for this predicate. + * This predicate always has a result. If no primary class can be + * determined, the result is `"???"`. If multiple primary classes match, + * this predicate can have multiple results. */ - string getCanonicalQLClass() { result = "???" } + string getAPrimaryQlClass() { result = "???" } } /** @@ -119,7 +128,7 @@ class Element extends ElementBase { /** * Gets the parent scope of this `Element`, if any. - * A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `Block`, a `Function`, + * A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `BlockStmt`, a `Function`, * or certain kinds of `Statement`. */ Element getParentScope() { @@ -152,7 +161,7 @@ class Element extends ElementBase { exists(EnumConstant e | this = e and result = e.getDeclaringEnum()) or // result instanceof block|function - exists(Block b | this = b and blockscope(unresolveElement(b), unresolveElement(result))) + exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result))) or exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf) or @@ -188,7 +197,8 @@ class Element extends ElementBase { 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)) + using_container(unresolveElement(result), underlyingElement(this)) or + static_asserts(unresolveElement(this), _, _, _, underlyingElement(result)) } /** Gets the closest `Element` enclosing this one. */ @@ -269,12 +279,12 @@ class StaticAssert extends Locatable, @static_assert { /** * Gets the expression which this static assertion ensures is true. */ - Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _) } + 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, _) } + string getMessage() { static_asserts(underlyingElement(this), _, result, _, _) } - override Location getLocation() { static_asserts(underlyingElement(this), _, _, result) } + override Location getLocation() { static_asserts(underlyingElement(this), _, _, result, _) } } diff --git a/cpp/ql/src/semmle/code/cpp/Enum.qll b/cpp/ql/src/semmle/code/cpp/Enum.qll index 2c51a5228d98..9cddeb78f9b1 100644 --- a/cpp/ql/src/semmle/code/cpp/Enum.qll +++ b/cpp/ql/src/semmle/code/cpp/Enum.qll @@ -38,7 +38,7 @@ class Enum extends UserType, IntegralOrEnumType { enumconstants(unresolveElement(result), underlyingElement(this), index, _, _, _) } - override string getCanonicalQLClass() { result = "Enum" } + override string getAPrimaryQlClass() { result = "Enum" } /** * Gets a descriptive string for the enum. This method is only intended to @@ -87,7 +87,7 @@ class Enum extends UserType, IntegralOrEnumType { class LocalEnum extends Enum { LocalEnum() { isLocal() } - override string getCanonicalQLClass() { result = "LocalEnum" } + override string getAPrimaryQlClass() { result = "LocalEnum" } } /** @@ -105,7 +105,7 @@ class LocalEnum extends Enum { class NestedEnum extends Enum { NestedEnum() { this.isMember() } - override string getCanonicalQLClass() { result = "NestedEnum" } + override string getAPrimaryQlClass() { result = "NestedEnum" } /** Holds if this member is private. */ predicate isPrivate() { this.hasSpecifier("private") } @@ -130,7 +130,7 @@ class NestedEnum extends Enum { class ScopedEnum extends Enum { ScopedEnum() { usertypes(underlyingElement(this), _, 13) } - override string getCanonicalQLClass() { result = "ScopedEnum" } + override string getAPrimaryQlClass() { result = "ScopedEnum" } } /** @@ -153,7 +153,7 @@ class EnumConstant extends Declaration, @enumconstant { enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _) } - override string getCanonicalQLClass() { result = "EnumConstant" } + override string getAPrimaryQlClass() { result = "EnumConstant" } override Class getDeclaringType() { result = this.getDeclaringEnum().getDeclaringType() } diff --git a/cpp/ql/src/semmle/code/cpp/Field.qll b/cpp/ql/src/semmle/code/cpp/Field.qll index 79c9b58dfea1..5ed5e8e4b4bc 100644 --- a/cpp/ql/src/semmle/code/cpp/Field.qll +++ b/cpp/ql/src/semmle/code/cpp/Field.qll @@ -23,7 +23,7 @@ import semmle.code.cpp.exprs.Access class Field extends MemberVariable { Field() { fieldoffsets(underlyingElement(this), _, _) } - override string getCanonicalQLClass() { result = "Field" } + override string getAPrimaryQlClass() { result = "Field" } /** * Gets the offset of this field in bytes from the start of its declaring @@ -90,7 +90,7 @@ class Field extends MemberVariable { class BitField extends Field { BitField() { bitfield(underlyingElement(this), _, _) } - override string getCanonicalQLClass() { result = "BitField" } + override string getAPrimaryQlClass() { result = "BitField" } /** * Gets the size of this bitfield in bits (on the machine where facts diff --git a/cpp/ql/src/semmle/code/cpp/File.qll b/cpp/ql/src/semmle/code/cpp/File.qll index 061e79c7d452..1887f43b70cb 100644 --- a/cpp/ql/src/semmle/code/cpp/File.qll +++ b/cpp/ql/src/semmle/code/cpp/File.qll @@ -178,7 +178,7 @@ class Folder extends Container, @folder { result.hasLocationInfo(_, 0, 0, 0, 0) } - override string getCanonicalQLClass() { result = "Folder" } + override string getAPrimaryQlClass() { result = "Folder" } /** * DEPRECATED: Use `getLocation` instead. @@ -246,7 +246,7 @@ class File extends Container, @file { override string toString() { result = Container.super.toString() } - override string getCanonicalQLClass() { result = "File" } + override string getAPrimaryQlClass() { result = "File" } override Location getLocation() { result.getContainer() = this and @@ -382,7 +382,7 @@ class HeaderFile extends File { exists(Include i | i.getIncludedFile() = this) } - override string getCanonicalQLClass() { result = "HeaderFile" } + override string getAPrimaryQlClass() { result = "HeaderFile" } /** * Holds if this header file does not contain any declaration entries or top level @@ -408,7 +408,7 @@ class HeaderFile extends File { class CFile extends File { CFile() { exists(string ext | ext = this.getExtension().toLowerCase() | ext = "c" or ext = "i") } - override string getCanonicalQLClass() { result = "CFile" } + override string getAPrimaryQlClass() { result = "CFile" } } /** @@ -436,7 +436,7 @@ class CppFile extends File { ) } - override string getCanonicalQLClass() { result = "CppFile" } + override string getAPrimaryQlClass() { result = "CppFile" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll index e0a6b04b1fcd..9a35f7527d52 100644 --- a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll +++ b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll @@ -27,7 +27,7 @@ class FriendDecl extends Declaration, @frienddecl { */ override Location getADeclarationLocation() { result = this.getLocation() } - override string getCanonicalQLClass() { result = "FriendDecl" } + override string getAPrimaryQlClass() { result = "FriendDecl" } /** * Implements the abstract method `Declaration.getDefinitionLocation`. A diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll index 20abef21d7b8..2c45fa184cf6 100644 --- a/cpp/ql/src/semmle/code/cpp/Function.qll +++ b/cpp/ql/src/semmle/code/cpp/Function.qll @@ -1,10 +1,8 @@ /** - * Provides classes for working with functions, including C++ constructors, destructors, - * user-defined operators, and template functions. + * Provides classes for working with functions, including template functions. */ import semmle.code.cpp.Location -import semmle.code.cpp.Member import semmle.code.cpp.Class import semmle.code.cpp.Parameter import semmle.code.cpp.exprs.Call @@ -270,7 +268,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * block, this gives the block guarded by the try statement. See * `FunctionTryStmt` for further information. */ - Block getBlock() { result.getParentScope() = this } + BlockStmt getBlock() { result.getParentScope() = this } /** Holds if this function has an entry point. */ predicate hasEntryPoint() { exists(getEntryPoint()) } @@ -278,7 +276,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { /** * Gets the first node in this function's control flow graph. * - * For most functions, this first node will be the `Block` returned by + * For most functions, this first node will be the `BlockStmt` returned by * `getBlock`. However in C++, the first node can also be a * `FunctionTryStmt`. */ @@ -515,7 +513,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { /** Gets the function which is being declared or defined. */ override Function getDeclaration() { result = getFunction() } - override string getCanonicalQLClass() { result = "FunctionDeclarationEntry" } + override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" } /** Gets the function which is being declared or defined. */ Function getFunction() { fun_decls(underlyingElement(this), unresolveElement(result), _, _, _) } @@ -566,7 +564,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { * If this is a function definition, get the block containing the * function body. */ - Block getBlock() { + BlockStmt getBlock() { this.isDefinition() and result = getFunction().getBlock() and result.getFile() = this.getFile() @@ -578,7 +576,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { */ pragma[noopt] int getNumberOfLines() { - exists(Block b, Location l, int start, int end, int diff | b = getBlock() | + exists(BlockStmt b, Location l, int start, int end, int diff | b = getBlock() | l = b.getLocation() and start = l.getStartLine() and end = l.getEndLine() and @@ -700,430 +698,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { class TopLevelFunction extends Function { TopLevelFunction() { not this.isMember() } - override string getCanonicalQLClass() { result = "TopLevelFunction" } -} - -/** - * A C++ function declared as a member of a class [N4140 9.3]. This includes - * static member functions. For example the functions `MyStaticMemberFunction` - * and `MyMemberFunction` in: - * ``` - * class MyClass { - * public: - * void MyMemberFunction() { - * DoSomething(); - * } - * - * static void MyStaticMemberFunction() { - * DoSomething(); - * } - * }; - * ``` - */ -class MemberFunction extends Function { - MemberFunction() { this.isMember() } - - override string getCanonicalQLClass() { - not this instanceof CopyAssignmentOperator and - not this instanceof MoveAssignmentOperator and - result = "MemberFunction" - } - - /** - * Gets the number of parameters of this function, including any implicit - * `this` parameter. - */ - override int getEffectiveNumberOfParameters() { - if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1 - } - - /** 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") } - - /** Holds if this function overrides that function. */ - predicate overrides(MemberFunction that) { - overrides(underlyingElement(this), unresolveElement(that)) - } - - /** Gets a directly overridden function. */ - MemberFunction getAnOverriddenFunction() { this.overrides(result) } - - /** Gets a directly overriding function. */ - MemberFunction getAnOverridingFunction() { result.overrides(this) } - - /** - * Gets the declaration entry for this member function that is within the - * class body. - */ - FunctionDeclarationEntry getClassBodyDeclarationEntry() { - if strictcount(getADeclarationEntry()) = 1 - then result = getDefinition() - else ( - result = getADeclarationEntry() and result != getDefinition() - ) - } -} - -/** - * A C++ virtual function. For example the two functions called - * `myVirtualFunction` in the following code are each a - * `VirtualFunction`: - * ``` - * class A { - * public: - * virtual void myVirtualFunction() = 0; - * }; - * - * class B: public A { - * public: - * virtual void myVirtualFunction() { - * doSomething(); - * } - * }; - * ``` - */ -class VirtualFunction extends MemberFunction { - VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) } - - override string getCanonicalQLClass() { result = "VirtualFunction" } - - /** Holds if this virtual function is pure. */ - predicate isPure() { this instanceof PureVirtualFunction } - - /** - * Holds if this function was declared with the `override` specifier - * [N4140 10.3]. - */ - predicate isOverrideExplicit() { this.hasSpecifier("override") } -} - -/** - * A C++ pure virtual function [N4140 10.4]. For example the first function - * called `myVirtualFunction` in the following code: - * ``` - * class A { - * public: - * virtual void myVirtualFunction() = 0; - * }; - * - * class B: public A { - * public: - * virtual void myVirtualFunction() { - * doSomething(); - * } - * }; - * ``` - */ -class PureVirtualFunction extends VirtualFunction { - PureVirtualFunction() { purefunctions(underlyingElement(this)) } - - override string getCanonicalQLClass() { result = "PureVirtualFunction" } -} - -/** - * A const C++ member function [N4140 9.3.1/4]. A const function has the - * `const` specifier and does not modify the state of its class. For example - * the member function `day` in the following code: - * ``` - * class MyClass { - * ... - * - * int day() const { - * return d; - * } - * - * ... - * }; - * ``` - */ -class ConstMemberFunction extends MemberFunction { - ConstMemberFunction() { this.hasSpecifier("const") } - - override string getCanonicalQLClass() { result = "ConstMemberFunction" } -} - -/** - * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the - * following code is a constructor: - * ``` - * class MyClass { - * public: - * MyClass() { - * ... - * } - * }; - * ``` - */ -class Constructor extends MemberFunction { - Constructor() { functions(underlyingElement(this), _, 2) } - - override string getCanonicalQLClass() { result = "Constructor" } - - /** - * Holds if this constructor serves as a default constructor. - * - * This holds for constructors with zero formal parameters. It also holds - * for constructors which have a non-zero number of formal parameters, - * provided that every parameter has a default value. - */ - predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) } - - /** - * Gets an entry in the constructor's initializer list, or a - * compiler-generated action which initializes a base class or member - * variable. - */ - ConstructorInit getAnInitializer() { result = getInitializer(_) } - - /** - * Gets an entry in the constructor's initializer list, or a - * compiler-generated action which initializes a base class or member - * variable. The index specifies the order in which the initializer is - * to be evaluated. - */ - ConstructorInit getInitializer(int i) { - exprparents(unresolveElement(result), i, underlyingElement(this)) - } -} - -/** - * A function that defines an implicit conversion. - */ -abstract class ImplicitConversionFunction extends MemberFunction { - /** Gets the type this `ImplicitConversionFunction` takes as input. */ - abstract Type getSourceType(); - - /** Gets the type this `ImplicitConversionFunction` converts to. */ - abstract Type getDestType(); -} - -/** - * A C++ constructor that also defines an implicit conversion. For example the - * function `MyClass` in the following code is a `ConversionConstructor`: - * ``` - * class MyClass { - * public: - * MyClass(const MyOtherClass &from) { - * ... - * } - * }; - * ``` - */ -class ConversionConstructor extends Constructor, ImplicitConversionFunction { - ConversionConstructor() { - strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and - not hasSpecifier("explicit") and - not this instanceof CopyConstructor - } - - override string getCanonicalQLClass() { - not this instanceof MoveConstructor and result = "ConversionConstructor" - } - - /** Gets the type this `ConversionConstructor` takes as input. */ - override Type getSourceType() { result = this.getParameter(0).getType() } - - /** Gets the type this `ConversionConstructor` is a constructor of. */ - override Type getDestType() { result = this.getDeclaringType() } -} - -private predicate hasCopySignature(MemberFunction f) { - f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType() -} - -private predicate hasMoveSignature(MemberFunction f) { - f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType() -} - -/** - * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in - * the following code is a `CopyConstructor`: - * ``` - * class MyClass { - * public: - * MyClass(const MyClass &from) { - * ... - * } - * }; - * ``` - * - * As per the standard, a copy constructor of class `T` is a non-template - * constructor whose first parameter has type `T&`, `const T&`, `volatile - * T&`, or `const volatile T&`, and either there are no other parameters, - * or the rest of the parameters all have default values. - * - * For template classes, it can generally not be determined until instantiation - * whether a constructor is a copy constructor. For such classes, `CopyConstructor` - * over-approximates the set of copy constructors; if an under-approximation is - * desired instead, see the member predicate - * `mayNotBeCopyConstructorInInstantiation`. - */ -class CopyConstructor extends Constructor { - CopyConstructor() { - hasCopySignature(this) and - ( - // The rest of the parameters all have default values - forall(int i | i > 0 and exists(getParameter(i)) | 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 - ) and - not exists(getATemplateArgument()) - } - - override string getCanonicalQLClass() { result = "CopyConstructor" } - - /** - * Holds if we cannot determine that this constructor will become a copy - * constructor in all instantiations. Depending on template parameters of the - * enclosing class, this may become an ordinary constructor or a copy - * constructor. - */ - predicate mayNotBeCopyConstructorInInstantiation() { - // In general, default arguments of template classes can only be - // 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 - } -} - -/** - * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in - * the following code is a `MoveConstructor`: - * ``` - * class MyClass { - * public: - * MyClass(MyClass &&from) { - * ... - * } - * }; - * ``` - * - * As per the standard, a move constructor of class `T` is a non-template - * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`, - * or `const volatile T&&`, and either there are no other parameters, or - * the rest of the parameters all have default values. - * - * For template classes, it can generally not be determined until instantiation - * whether a constructor is a move constructor. For such classes, `MoveConstructor` - * over-approximates the set of move constructors; if an under-approximation is - * desired instead, see the member predicate - * `mayNotBeMoveConstructorInInstantiation`. - */ -class MoveConstructor extends Constructor { - MoveConstructor() { - hasMoveSignature(this) and - ( - // The rest of the parameters all have default values - forall(int i | i > 0 and exists(getParameter(i)) | 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 - ) and - not exists(getATemplateArgument()) - } - - override string getCanonicalQLClass() { result = "MoveConstructor" } - - /** - * Holds if we cannot determine that this constructor will become a move - * constructor in all instantiations. Depending on template parameters of the - * enclosing class, this may become an ordinary constructor or a move - * constructor. - */ - predicate mayNotBeMoveConstructorInInstantiation() { - // In general, default arguments of template classes can only be - // 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 - } -} - -/** - * A C++ constructor that takes no arguments ('default' constructor). This - * is the constructor that is invoked when no initializer is given. For - * example the function `MyClass` in the following code is a - * `NoArgConstructor`: - * ``` - * class MyClass { - * public: - * MyClass() { - * ... - * } - * }; - * ``` - */ -class NoArgConstructor extends Constructor { - NoArgConstructor() { this.getNumberOfParameters() = 0 } -} - -/** - * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the - * following code is a destructor: - * ``` - * class MyClass { - * public: - * ~MyClass() { - * ... - * } - * }; - * ``` - */ -class Destructor extends MemberFunction { - Destructor() { functions(underlyingElement(this), _, 3) } - - override string getCanonicalQLClass() { result = "Destructor" } - - /** - * Gets a compiler-generated action which destructs a base class or member - * variable. - */ - DestructorDestruction getADestruction() { result = getDestruction(_) } - - /** - * Gets a compiler-generated action which destructs a base class or member - * variable. The index specifies the order in which the destruction should - * be evaluated. - */ - DestructorDestruction getDestruction(int i) { - exprparents(unresolveElement(result), i, underlyingElement(this)) - } -} - -/** - * A C++ conversion operator [N4140 12.3.2]. For example the function - * `operator int` in the following code is a `ConversionOperator`: - * ``` - * class MyClass { - * public: - * operator int(); - * }; - * ``` - */ -class ConversionOperator extends MemberFunction, ImplicitConversionFunction { - ConversionOperator() { functions(underlyingElement(this), _, 4) } - - override string getCanonicalQLClass() { result = "ConversionOperator" } - - override Type getSourceType() { result = this.getDeclaringType() } - - override Type getDestType() { result = this.getType() } + override string getAPrimaryQlClass() { result = "TopLevelFunction" } } /** @@ -1132,69 +707,11 @@ class ConversionOperator extends MemberFunction, ImplicitConversionFunction { class Operator extends Function { Operator() { functions(underlyingElement(this), _, 5) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof MemberFunction and result = "Operator" } } -/** - * A C++ copy assignment operator [N4140 12.8]. For example the function - * `operator=` in the following code is a `CopyAssignmentOperator`: - * ``` - * class MyClass { - * public: - * MyClass &operator=(const MyClass &other); - * }; - * ``` - * - * As per the standard, a copy assignment operator of class `T` is a - * non-template non-static member function with the name `operator=` that - * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile - * T&`, or `const volatile T&`. - */ -class CopyAssignmentOperator extends Operator { - CopyAssignmentOperator() { - hasName("operator=") and - ( - hasCopySignature(this) - or - // Unlike CopyConstructor, this member allows a non-reference - // parameter. - getParameter(0).getUnspecifiedType() = getDeclaringType() - ) and - not exists(this.getParameter(1)) and - not exists(getATemplateArgument()) - } - - override string getCanonicalQLClass() { result = "CopyAssignmentOperator" } -} - -/** - * A C++ move assignment operator [N4140 12.8]. For example the function - * `operator=` in the following code is a `MoveAssignmentOperator`: - * ``` - * class MyClass { - * public: - * MyClass &operator=(MyClass &&other); - * }; - * ``` - * - * As per the standard, a move assignment operator of class `T` is a - * non-template non-static member function with the name `operator=` that - * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`, - * or `const volatile T&&`. - */ -class MoveAssignmentOperator extends Operator { - MoveAssignmentOperator() { - hasName("operator=") and - hasMoveSignature(this) and - not exists(this.getParameter(1)) and - not exists(getATemplateArgument()) - } - - override string getCanonicalQLClass() { result = "MoveAssignmentOperator" } -} - /** * A C++ function which has a non-empty template argument list. For example * the function `myTemplateFunction` in the following code: @@ -1221,7 +738,7 @@ class TemplateFunction extends Function { is_function_template(underlyingElement(this)) and exists(getATemplateArgument()) } - override string getCanonicalQLClass() { result = "TemplateFunction" } + override string getAPrimaryQlClass() { result = "TemplateFunction" } /** * Gets a compiler-generated instantiation of this function template. @@ -1261,7 +778,7 @@ class FunctionTemplateInstantiation extends Function { FunctionTemplateInstantiation() { tf.getAnInstantiation() = this } - override string getCanonicalQLClass() { result = "FunctionTemplateInstantiation" } + override string getAPrimaryQlClass() { result = "FunctionTemplateInstantiation" } /** * Gets the function template from which this instantiation was instantiated. @@ -1306,7 +823,7 @@ class FunctionTemplateInstantiation extends Function { class FunctionTemplateSpecialization extends Function { FunctionTemplateSpecialization() { this.isSpecialization() } - override string getCanonicalQLClass() { result = "FunctionTemplateSpecialization" } + override string getAPrimaryQlClass() { result = "FunctionTemplateSpecialization" } /** * Gets the primary template for the specialization (the function template diff --git a/cpp/ql/src/semmle/code/cpp/Include.qll b/cpp/ql/src/semmle/code/cpp/Include.qll index 11702ce1bf69..f21edb2651d9 100644 --- a/cpp/ql/src/semmle/code/cpp/Include.qll +++ b/cpp/ql/src/semmle/code/cpp/Include.qll @@ -21,7 +21,7 @@ class Include extends PreprocessorDirective, @ppd_include { /** * Gets the token which occurs after `#include`, for example `"filename"` - * or `<filename>`. + * or ``. */ string getIncludeText() { result = getHead() } diff --git a/cpp/ql/src/semmle/code/cpp/Initializer.qll b/cpp/ql/src/semmle/code/cpp/Initializer.qll index 80601f659da8..643a880ddf2c 100644 --- a/cpp/ql/src/semmle/code/cpp/Initializer.qll +++ b/cpp/ql/src/semmle/code/cpp/Initializer.qll @@ -1,3 +1,7 @@ +/** + * Provides the `Initializer` class, representing C/C++ declaration initializers. + */ + import semmle.code.cpp.controlflow.ControlFlowGraph /** @@ -18,7 +22,7 @@ import semmle.code.cpp.controlflow.ControlFlowGraph class Initializer extends ControlFlowNode, @initialiser { override Location getLocation() { initialisers(underlyingElement(this), _, _, result) } - override string getCanonicalQLClass() { result = "Initializer" } + override string getAPrimaryQlClass() { result = "Initializer" } /** Holds if this initializer is explicit in the source. */ override predicate fromSource() { not this.getLocation() instanceof UnknownLocation } diff --git a/cpp/ql/src/semmle/code/cpp/Iteration.qll b/cpp/ql/src/semmle/code/cpp/Iteration.qll index fd7ba60ea194..d87306c4babe 100644 --- a/cpp/ql/src/semmle/code/cpp/Iteration.qll +++ b/cpp/ql/src/semmle/code/cpp/Iteration.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for loop iteration variables. + */ + import semmle.code.cpp.Variable /** @@ -7,14 +11,18 @@ import semmle.code.cpp.Variable class LoopCounter extends Variable { LoopCounter() { exists(ForStmt f | f.getAnIterationVariable() = this) } - // Gets an access of this variable within loop `f`. + /** + * Gets an access of this variable within loop `f`. + */ VariableAccess getVariableAccessInLoop(ForStmt f) { this.getALoop() = f and result.getEnclosingStmt().getParent*() = f and this = result.getTarget() } - // Gets a loop which uses this variable as its counter. + /** + * Gets a loop which uses this variable as its counter. + */ ForStmt getALoop() { result.getAnIterationVariable() = this } } @@ -25,14 +33,18 @@ class LoopCounter extends Variable { class LoopControlVariable extends Variable { LoopControlVariable() { this = loopControlVariable(_) } - // Gets an access of this variable within loop `f`. + /** + * Gets an access of this variable within loop `f`. + */ VariableAccess getVariableAccessInLoop(ForStmt f) { this.getALoop() = f and result.getEnclosingStmt().getParent*() = f and this = result.getTarget() } - // Gets a loop which uses this variable as its control variable. + /** + * Gets a loop which uses this variable as its control variable. + */ ForStmt getALoop() { this = loopControlVariable(result) } } diff --git a/cpp/ql/src/semmle/code/cpp/Linkage.qll b/cpp/ql/src/semmle/code/cpp/Linkage.qll index 7912c9e25e1b..54a6099eaef2 100644 --- a/cpp/ql/src/semmle/code/cpp/Linkage.qll +++ b/cpp/ql/src/semmle/code/cpp/Linkage.qll @@ -1,3 +1,7 @@ +/** + * Proivdes the `LinkTarget` class representing linker invocations during the build process. + */ + import semmle.code.cpp.Class import semmle.code.cpp.File import semmle.code.cpp.Function diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll index 129ffba0f748..dc37d4009c05 100644 --- a/cpp/ql/src/semmle/code/cpp/Location.qll +++ b/cpp/ql/src/semmle/code/cpp/Location.qll @@ -1,3 +1,7 @@ +/** + * Provides classes and predicates for locations in the source code. + */ + import semmle.code.cpp.Element import semmle.code.cpp.File @@ -11,16 +15,16 @@ class Location extends @location { /** Gets the file corresponding to this location, if any. */ File getFile() { result = this.getContainer() } - /** Gets the start line of this location. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ int getStartLine() { this.fullLocationInfo(_, result, _, _, _) } - /** Gets the start column of this location. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) } - /** Gets the end line of this location. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ int getEndLine() { this.fullLocationInfo(_, _, _, result, _) } - /** Gets the end column of this location. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) } /** @@ -101,16 +105,23 @@ class Location extends @location { } /** + * DEPRECATED: Use `Location` instead. * A location of an element. Not used for expressions or statements, which * instead use LocationExpr and LocationStmt respectively. */ -library class LocationDefault extends Location, @location_default { } +deprecated library class LocationDefault extends Location, @location_default { } -/** A location of a statement. */ -library class LocationStmt extends Location, @location_stmt { } +/** + * DEPRECATED: Use `Location` instead. + * A location of a statement. + */ +deprecated library class LocationStmt extends Location, @location_stmt { } -/** A location of an expression. */ -library class LocationExpr extends Location, @location_expr { } +/** + * 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`. diff --git a/cpp/ql/src/semmle/code/cpp/Macro.qll b/cpp/ql/src/semmle/code/cpp/Macro.qll index 389634912b7d..aa4b8d419990 100644 --- a/cpp/ql/src/semmle/code/cpp/Macro.qll +++ b/cpp/ql/src/semmle/code/cpp/Macro.qll @@ -13,7 +13,7 @@ class Macro extends PreprocessorDirective, @ppd_define { */ override string getHead() { preproctext(underlyingElement(this), result, _) } - override string getCanonicalQLClass() { result = "Macro" } + override string getAPrimaryQlClass() { result = "Macro" } /** * Gets the body of this macro. For example, `(((x)>(y))?(x):(y))` in @@ -74,7 +74,7 @@ class MacroAccess extends Locatable, @macroinvocation { */ override Location getLocation() { result = this.getOutermostMacroAccess().getActualLocation() } - override string getCanonicalQLClass() { result = "MacroAccess" } + override string getAPrimaryQlClass() { result = "MacroAccess" } /** * Gets the location of this macro access. For a nested access, where @@ -147,7 +147,7 @@ class MacroAccess extends Locatable, @macroinvocation { class MacroInvocation extends MacroAccess { MacroInvocation() { macroinvocations(underlyingElement(this), _, _, 1) } - override string getCanonicalQLClass() { result = "MacroInvocation" } + override string getAPrimaryQlClass() { result = "MacroInvocation" } /** * Gets an element that occurs in this macro invocation or a nested macro @@ -179,6 +179,11 @@ class MacroInvocation extends MacroAccess { result.(Stmt).getGeneratingMacro() = this } + /** + * Gets a function that includes an expression that is affected by this macro + * invocation. If the macro expansion includes the end of one function and + * the beginning of another, this predicate will get both. + */ Function getEnclosingFunction() { result = this.getAnAffectedElement().(Expr).getEnclosingFunction() } diff --git a/cpp/ql/src/semmle/code/cpp/Member.qll b/cpp/ql/src/semmle/code/cpp/Member.qll index 92769486ae98..f47edbddeba0 100644 --- a/cpp/ql/src/semmle/code/cpp/Member.qll +++ b/cpp/ql/src/semmle/code/cpp/Member.qll @@ -1,2 +1,6 @@ +/** + * DEPRECATED: import `semmle.code.cpp.Element` and/or `semmle.code.cpp.Type` directly as required. + */ + import semmle.code.cpp.Element import semmle.code.cpp.Type diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll new file mode 100644 index 000000000000..0a692f755ed7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -0,0 +1,499 @@ +/** + * Provides classes for working with C++ member functions, constructors, destructors, + * and user-defined operators. + */ + +import cpp + +/** + * A C++ function declared as a member of a class [N4140 9.3]. This includes + * static member functions. For example the functions `MyStaticMemberFunction` + * and `MyMemberFunction` in: + * ``` + * class MyClass { + * public: + * void MyMemberFunction() { + * DoSomething(); + * } + * + * static void MyStaticMemberFunction() { + * DoSomething(); + * } + * }; + * ``` + */ +class MemberFunction extends Function { + MemberFunction() { this.isMember() } + + override string getAPrimaryQlClass() { + not this instanceof CopyAssignmentOperator and + not this instanceof MoveAssignmentOperator and + result = "MemberFunction" + } + + /** + * Gets the number of parameters of this function, including any implicit + * `this` parameter. + */ + override int getEffectiveNumberOfParameters() { + if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1 + } + + /** 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") } + + /** Holds if this function overrides that function. */ + predicate overrides(MemberFunction that) { + overrides(underlyingElement(this), unresolveElement(that)) + } + + /** Gets a directly overridden function. */ + MemberFunction getAnOverriddenFunction() { this.overrides(result) } + + /** Gets a directly overriding function. */ + MemberFunction getAnOverridingFunction() { result.overrides(this) } + + /** + * Gets the declaration entry for this member function that is within the + * class body. + */ + FunctionDeclarationEntry getClassBodyDeclarationEntry() { + if strictcount(getADeclarationEntry()) = 1 + then result = getDefinition() + else ( + result = getADeclarationEntry() and result != getDefinition() + ) + } + + /** + * Gets the type of the `this` parameter associated with this member function, if any. The type + * may have `const` and/or `volatile` qualifiers, matching the function declaration. + */ + PointerType getTypeOfThis() { + member_function_this_type(underlyingElement(this), unresolveElement(result)) + } +} + +/** + * A C++ virtual function. For example the two functions called + * `myVirtualFunction` in the following code are each a + * `VirtualFunction`: + * ``` + * class A { + * public: + * virtual void myVirtualFunction() = 0; + * }; + * + * class B: public A { + * public: + * virtual void myVirtualFunction() { + * doSomething(); + * } + * }; + * ``` + */ +class VirtualFunction extends MemberFunction { + VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) } + + override string getAPrimaryQlClass() { result = "VirtualFunction" } + + /** Holds if this virtual function is pure. */ + predicate isPure() { this instanceof PureVirtualFunction } + + /** + * Holds if this function was declared with the `override` specifier + * [N4140 10.3]. + */ + predicate isOverrideExplicit() { this.hasSpecifier("override") } +} + +/** + * A C++ pure virtual function [N4140 10.4]. For example the first function + * called `myVirtualFunction` in the following code: + * ``` + * class A { + * public: + * virtual void myVirtualFunction() = 0; + * }; + * + * class B: public A { + * public: + * virtual void myVirtualFunction() { + * doSomething(); + * } + * }; + * ``` + */ +class PureVirtualFunction extends VirtualFunction { + PureVirtualFunction() { purefunctions(underlyingElement(this)) } + + override string getAPrimaryQlClass() { result = "PureVirtualFunction" } +} + +/** + * A const C++ member function [N4140 9.3.1/4]. A const function has the + * `const` specifier and does not modify the state of its class. For example + * the member function `day` in the following code: + * ``` + * class MyClass { + * ... + * + * int day() const { + * return d; + * } + * + * ... + * }; + * ``` + */ +class ConstMemberFunction extends MemberFunction { + ConstMemberFunction() { this.hasSpecifier("const") } + + override string getAPrimaryQlClass() { result = "ConstMemberFunction" } +} + +/** + * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the + * following code is a constructor: + * ``` + * class MyClass { + * public: + * MyClass() { + * ... + * } + * }; + * ``` + */ +class Constructor extends MemberFunction { + Constructor() { functions(underlyingElement(this), _, 2) } + + override string getAPrimaryQlClass() { result = "Constructor" } + + /** + * Holds if this constructor serves as a default constructor. + * + * This holds for constructors with zero formal parameters. It also holds + * for constructors which have a non-zero number of formal parameters, + * provided that every parameter has a default value. + */ + predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) } + + /** + * Gets an entry in the constructor's initializer list, or a + * compiler-generated action which initializes a base class or member + * variable. + */ + ConstructorInit getAnInitializer() { result = getInitializer(_) } + + /** + * Gets an entry in the constructor's initializer list, or a + * compiler-generated action which initializes a base class or member + * variable. The index specifies the order in which the initializer is + * to be evaluated. + */ + ConstructorInit getInitializer(int i) { + exprparents(unresolveElement(result), i, underlyingElement(this)) + } +} + +/** + * A function that defines an implicit conversion. + */ +abstract class ImplicitConversionFunction extends MemberFunction { + /** Gets the type this `ImplicitConversionFunction` takes as input. */ + abstract Type getSourceType(); + + /** Gets the type this `ImplicitConversionFunction` converts to. */ + abstract Type getDestType(); +} + +/** + * DEPRECATED: as of C++11 this class does not correspond perfectly with the + * language definition of a converting constructor. + * + * A C++ constructor that also defines an implicit conversion. For example the + * function `MyClass` in the following code is a `ConversionConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(const MyOtherClass &from) { + * ... + * } + * }; + * ``` + */ +deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction { + ConversionConstructor() { + strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and + not hasSpecifier("explicit") + } + + override string getAPrimaryQlClass() { + not this instanceof CopyConstructor and + not this instanceof MoveConstructor and + result = "ConversionConstructor" + } + + /** Gets the type this `ConversionConstructor` takes as input. */ + override Type getSourceType() { result = this.getParameter(0).getType() } + + /** Gets the type this `ConversionConstructor` is a constructor of. */ + override Type getDestType() { result = this.getDeclaringType() } +} + +private predicate hasCopySignature(MemberFunction f) { + f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType() +} + +private predicate hasMoveSignature(MemberFunction f) { + f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType() +} + +/** + * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in + * the following code is a `CopyConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(const MyClass &from) { + * ... + * } + * }; + * ``` + * + * As per the standard, a copy constructor of class `T` is a non-template + * constructor whose first parameter has type `T&`, `const T&`, `volatile + * T&`, or `const volatile T&`, and either there are no other parameters, + * or the rest of the parameters all have default values. + * + * For template classes, it can generally not be determined until instantiation + * whether a constructor is a copy constructor. For such classes, `CopyConstructor` + * over-approximates the set of copy constructors; if an under-approximation is + * desired instead, see the member predicate + * `mayNotBeCopyConstructorInInstantiation`. + */ +class CopyConstructor extends Constructor { + CopyConstructor() { + hasCopySignature(this) and + ( + // The rest of the parameters all have default values + forall(int i | i > 0 and exists(getParameter(i)) | 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 + ) and + not exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "CopyConstructor" } + + /** + * Holds if we cannot determine that this constructor will become a copy + * constructor in all instantiations. Depending on template parameters of the + * enclosing class, this may become an ordinary constructor or a copy + * constructor. + */ + predicate mayNotBeCopyConstructorInInstantiation() { + // In general, default arguments of template classes can only be + // 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 + } +} + +/** + * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in + * the following code is a `MoveConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(MyClass &&from) { + * ... + * } + * }; + * ``` + * + * As per the standard, a move constructor of class `T` is a non-template + * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`, + * or `const volatile T&&`, and either there are no other parameters, or + * the rest of the parameters all have default values. + * + * For template classes, it can generally not be determined until instantiation + * whether a constructor is a move constructor. For such classes, `MoveConstructor` + * over-approximates the set of move constructors; if an under-approximation is + * desired instead, see the member predicate + * `mayNotBeMoveConstructorInInstantiation`. + */ +class MoveConstructor extends Constructor { + MoveConstructor() { + hasMoveSignature(this) and + ( + // The rest of the parameters all have default values + forall(int i | i > 0 and exists(getParameter(i)) | 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 + ) and + not exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "MoveConstructor" } + + /** + * Holds if we cannot determine that this constructor will become a move + * constructor in all instantiations. Depending on template parameters of the + * enclosing class, this may become an ordinary constructor or a move + * constructor. + */ + predicate mayNotBeMoveConstructorInInstantiation() { + // In general, default arguments of template classes can only be + // 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 + } +} + +/** + * A C++ constructor that takes no arguments ('default' constructor). This + * is the constructor that is invoked when no initializer is given. For + * example the function `MyClass` in the following code is a + * `NoArgConstructor`: + * ``` + * class MyClass { + * public: + * MyClass() { + * ... + * } + * }; + * ``` + */ +class NoArgConstructor extends Constructor { + NoArgConstructor() { this.getNumberOfParameters() = 0 } +} + +/** + * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the + * following code is a destructor: + * ``` + * class MyClass { + * public: + * ~MyClass() { + * ... + * } + * }; + * ``` + */ +class Destructor extends MemberFunction { + Destructor() { functions(underlyingElement(this), _, 3) } + + override string getAPrimaryQlClass() { result = "Destructor" } + + /** + * Gets a compiler-generated action which destructs a base class or member + * variable. + */ + DestructorDestruction getADestruction() { result = getDestruction(_) } + + /** + * Gets a compiler-generated action which destructs a base class or member + * variable. The index specifies the order in which the destruction should + * be evaluated. + */ + DestructorDestruction getDestruction(int i) { + exprparents(unresolveElement(result), i, underlyingElement(this)) + } +} + +/** + * A C++ conversion operator [N4140 12.3.2]. For example the function + * `operator int` in the following code is a `ConversionOperator`: + * ``` + * class MyClass { + * public: + * operator int(); + * }; + * ``` + */ +class ConversionOperator extends MemberFunction, ImplicitConversionFunction { + ConversionOperator() { functions(underlyingElement(this), _, 4) } + + override string getAPrimaryQlClass() { result = "ConversionOperator" } + + override Type getSourceType() { result = this.getDeclaringType() } + + override Type getDestType() { result = this.getType() } +} + +/** + * A C++ copy assignment operator [N4140 12.8]. For example the function + * `operator=` in the following code is a `CopyAssignmentOperator`: + * ``` + * class MyClass { + * public: + * MyClass &operator=(const MyClass &other); + * }; + * ``` + * + * As per the standard, a copy assignment operator of class `T` is a + * non-template non-static member function with the name `operator=` that + * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile + * T&`, or `const volatile T&`. + */ +class CopyAssignmentOperator extends Operator { + CopyAssignmentOperator() { + hasName("operator=") and + ( + hasCopySignature(this) + or + // Unlike CopyConstructor, this member allows a non-reference + // parameter. + getParameter(0).getUnspecifiedType() = getDeclaringType() + ) and + not exists(this.getParameter(1)) and + not exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "CopyAssignmentOperator" } +} + +/** + * A C++ move assignment operator [N4140 12.8]. For example the function + * `operator=` in the following code is a `MoveAssignmentOperator`: + * ``` + * class MyClass { + * public: + * MyClass &operator=(MyClass &&other); + * }; + * ``` + * + * As per the standard, a move assignment operator of class `T` is a + * non-template non-static member function with the name `operator=` that + * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`, + * or `const volatile T&&`. + */ +class MoveAssignmentOperator extends Operator { + MoveAssignmentOperator() { + hasName("operator=") and + hasMoveSignature(this) and + not exists(this.getParameter(1)) and + not exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "MoveAssignmentOperator" } +} diff --git a/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll b/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll index eff2c9205bf9..042ee10700ad 100644 --- a/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll +++ b/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for working with name qualifiers such as the `N::` in + * `N::f()`. + */ + import cpp /** diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll index 9b9b12aaef06..6172c3af50ca 100644 --- a/cpp/ql/src/semmle/code/cpp/Namespace.qll +++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll @@ -1,3 +1,7 @@ +/** + * 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 @@ -127,7 +131,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl { */ Location getBodyLocation() { namespace_decls(underlyingElement(this), _, _, result) } - override string getCanonicalQLClass() { result = "NamespaceDeclarationEntry" } + override string getAPrimaryQlClass() { result = "NamespaceDeclarationEntry" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/NestedFields.qll b/cpp/ql/src/semmle/code/cpp/NestedFields.qll index c4be8b8b9ffd..ce67719a7e21 100644 --- a/cpp/ql/src/semmle/code/cpp/NestedFields.qll +++ b/cpp/ql/src/semmle/code/cpp/NestedFields.qll @@ -1,3 +1,8 @@ +/** + * Provides a class for reasoning about nested field accesses, for example + * the access `myLine.start.x`. + */ + import cpp /** @@ -25,7 +30,7 @@ private Expr getUltimateQualifier(FieldAccess fa) { } /** - * Accesses to nested fields. + * A nested field access, for example the access `myLine.start.x`. */ class NestedFieldAccess extends FieldAccess { Expr ultimateQualifier; @@ -35,6 +40,30 @@ class NestedFieldAccess extends FieldAccess { getTarget() = getANestedField(ultimateQualifier.getType().stripType()) } - /** Gets the ultimate qualifier of this nested field access. */ + /** + * Gets the outermost qualifier of this nested field access. In the + * following example, the access to `myLine.start.x` has outermost qualifier + * `myLine`: + * ``` + * struct Point + * { + * float x, y; + * }; + * + * struct Line + * { + * Point start, end; + * }; + * + * void init() + * { + * Line myLine; + * + * myLine.start.x = 0.0f; + * + * // ... + * } + * ``` + */ Expr getUltimateQualifier() { result = ultimateQualifier } } diff --git a/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll b/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll index 5c99a47e6747..17da273d5a73 100644 --- a/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll +++ b/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll @@ -1,3 +1,7 @@ +/** + * DEPRECATED: Objective-C is no longer supported. + */ + import semmle.code.cpp.Class private import semmle.code.cpp.internal.ResolveClass @@ -132,7 +136,7 @@ deprecated class ObjcTryStmt extends TryStmt { * DEPRECATED: Objective-C is no longer supported. * An Objective C `@finally` block. */ -deprecated class FinallyBlock extends Block { +deprecated class FinallyBlock extends BlockStmt { FinallyBlock() { none() } /** Gets the try statement corresponding to this finally block. */ diff --git a/cpp/ql/src/semmle/code/cpp/Parameter.qll b/cpp/ql/src/semmle/code/cpp/Parameter.qll index 1fbd8b0f071c..040b738591c4 100644 --- a/cpp/ql/src/semmle/code/cpp/Parameter.qll +++ b/cpp/ql/src/semmle/code/cpp/Parameter.qll @@ -1,3 +1,7 @@ +/** + * 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 @@ -45,7 +49,7 @@ class Parameter extends LocalScopeVariable, @parameter { result = "p#" + this.getIndex().toString() } - override string getCanonicalQLClass() { result = "Parameter" } + override string getAPrimaryQlClass() { result = "Parameter" } /** * Gets the name of this parameter, including it's type. @@ -94,7 +98,7 @@ class Parameter extends LocalScopeVariable, @parameter { * DEPRECATED: this method was used in a previous implementation of * getName, but is no longer in use. */ - deprecated string getNameInBlock(Block b) { + deprecated string getNameInBlock(BlockStmt b) { exists(ParameterDeclarationEntry pde | pde.getFunctionDeclarationEntry().getBlock() = b and this.getFunction().getBlock() = b and @@ -123,7 +127,7 @@ class Parameter extends LocalScopeVariable, @parameter { * Gets the catch block to which this parameter belongs, if it is a catch * block parameter. */ - Block getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) } + BlockStmt getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) } /** * Gets the zero-based index of this parameter. @@ -165,6 +169,7 @@ class Parameter extends LocalScopeVariable, @parameter { class ParameterIndex extends int { ParameterIndex() { exists(Parameter p | this = p.getIndex()) or - exists(Call c | exists(c.getArgument(this))) // permit indexing varargs + exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs + this = -1 // used for `this` } } diff --git a/cpp/ql/src/semmle/code/cpp/Preprocessor.qll b/cpp/ql/src/semmle/code/cpp/Preprocessor.qll index d0104900d605..a9609922b74d 100644 --- a/cpp/ql/src/semmle/code/cpp/Preprocessor.qll +++ b/cpp/ql/src/semmle/code/cpp/Preprocessor.qll @@ -33,11 +33,13 @@ class PreprocessorDirective extends Locatable, @preprocdirect { } } +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`. */ -abstract class PreprocessorBranchDirective extends PreprocessorDirective { +class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBranchDirective { /** * Gets the `#if`, `#ifdef` or `#ifndef` directive which matches this * branching directive. @@ -152,7 +154,7 @@ class PreprocessorIf extends PreprocessorBranch, @ppd_if { class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef { override string toString() { result = "#ifdef " + this.getHead() } - override string getCanonicalQLClass() { result = "PreprocessorIfdef" } + override string getAPrimaryQlClass() { result = "PreprocessorIfdef" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.qll b/cpp/ql/src/semmle/code/cpp/PrintAST.qll index 9f69d5644575..fb5232b3008a 100644 --- a/cpp/ql/src/semmle/code/cpp/PrintAST.qll +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.qll @@ -1,3 +1,11 @@ +/** + * Provides queries to pretty-print a C++ AST as a graph. + * + * By default, this will print the AST for all functions in the database. To change this behavior, + * extend `PrintASTConfiguration` and override `shouldPrintFunction` to hold for only the functions + * you wish to view the AST for. + */ + import cpp private import semmle.code.cpp.Print @@ -7,6 +15,9 @@ private newtype TPrintASTConfiguration = MkPrintASTConfiguration() * The query can extend this class to control which functions are printed. */ class PrintASTConfiguration extends TPrintASTConfiguration { + /** + * Gets a textual representation of this `PrintASTConfiguration`. + */ string toString() { result = "PrintASTConfiguration" } /** @@ -96,6 +107,9 @@ private newtype TPrintASTNode = * A node in the output tree. */ class PrintASTNode extends TPrintASTNode { + /** + * Gets a textual representation of this node in the PrintAST output tree. + */ abstract string toString(); /** @@ -155,7 +169,7 @@ class PrintASTNode extends TPrintASTNode { * Retrieves the canonical QL class(es) for entity `el` */ private string qlClass(ElementBase el) { - result = "[" + concat(el.getCanonicalQLClass(), ",") + "] " + result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] " // Alternative implementation -- do not delete. It is useful for QL class discovery. //result = "["+ concat(el.getAQlClass(), ",") + "] " } @@ -208,6 +222,9 @@ class ExprNode extends ASTNode { result = expr.getValueCategoryString() } + /** + * Gets the value of this expression, if it is a constant. + */ string getValue() { result = expr.getValue() } } @@ -373,6 +390,9 @@ class ParametersNode extends PrintASTNode, TParametersNode { override ASTNode getChild(int childIndex) { result.getAST() = func.getParameter(childIndex) } + /** + * Gets the `Function` for which this node represents the parameters. + */ final Function getFunction() { result = func } } @@ -392,6 +412,9 @@ class ConstructorInitializersNode extends PrintASTNode, TConstructorInitializers result.getAST() = ctor.getInitializer(childIndex) } + /** + * Gets the `Constructor` for which this node represents the initializer list. + */ final Constructor getConstructor() { result = ctor } } @@ -411,6 +434,9 @@ class DestructorDestructionsNode extends PrintASTNode, TDestructorDestructionsNo result.getAST() = dtor.getDestruction(childIndex) } + /** + * Gets the `Destructor` for which this node represents the destruction list. + */ final Destructor getDestructor() { result = dtor } } @@ -464,6 +490,9 @@ class FunctionNode extends ASTNode { key = "semmle.order" and result = getOrder().toString() } + /** + * Gets the `Function` this node represents. + */ final Function getFunction() { result = func } } @@ -499,11 +528,16 @@ class ArrayAggregateLiteralNode extends ExprNode { } } +/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */ query predicate nodes(PrintASTNode node, string key, string value) { node.shouldPrint() and value = node.getProperty(key) } +/** + * Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the + * given `value`. + */ query predicate edges(PrintASTNode source, PrintASTNode target, string key, string value) { exists(int childIndex | source.shouldPrint() and @@ -517,6 +551,7 @@ query predicate edges(PrintASTNode source, PrintASTNode target, string key, stri ) } +/** Holds if property `key` of the graph has the given `value`. */ query predicate graphProperties(string key, string value) { key = "semmle.graphKind" and value = "tree" } diff --git a/cpp/ql/src/semmle/code/cpp/Specifier.qll b/cpp/ql/src/semmle/code/cpp/Specifier.qll index f58f060623dd..3d68fb374f12 100644 --- a/cpp/ql/src/semmle/code/cpp/Specifier.qll +++ b/cpp/ql/src/semmle/code/cpp/Specifier.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling specifiers and attributes. + */ + import semmle.code.cpp.Element private import semmle.code.cpp.internal.ResolveClass @@ -12,7 +16,7 @@ class Specifier extends Element, @specifier { result instanceof UnknownDefaultLocation } - override string getCanonicalQLClass() { result = "Specifier" } + override string getAPrimaryQlClass() { result = "Specifier" } /** Gets the name of this specifier. */ string getName() { specifiers(underlyingElement(this), result) } @@ -33,7 +37,7 @@ class FunctionSpecifier extends Specifier { this.hasName("explicit") } - override string getCanonicalQLClass() { result = "FunctionSpecifier)" } + override string getAPrimaryQlClass() { result = "FunctionSpecifier)" } } /** @@ -49,7 +53,7 @@ class StorageClassSpecifier extends Specifier { this.hasName("mutable") } - override string getCanonicalQLClass() { result = "StorageClassSpecifier" } + override string getAPrimaryQlClass() { result = "StorageClassSpecifier" } } /** @@ -104,7 +108,7 @@ class AccessSpecifier extends Specifier { ) } - override string getCanonicalQLClass() { result = "AccessSpecifier" } + override string getAPrimaryQlClass() { result = "AccessSpecifier" } } /** @@ -234,7 +238,7 @@ class FormatAttribute extends GnuAttribute { ) } - override string getCanonicalQLClass() { result = "FormatAttribute" } + override string getAPrimaryQlClass() { result = "FormatAttribute" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/Struct.qll b/cpp/ql/src/semmle/code/cpp/Struct.qll index 6b95cdba1bd5..50a208894b43 100644 --- a/cpp/ql/src/semmle/code/cpp/Struct.qll +++ b/cpp/ql/src/semmle/code/cpp/Struct.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling `struct`s. + */ + import semmle.code.cpp.Type import semmle.code.cpp.Class @@ -18,7 +22,7 @@ import semmle.code.cpp.Class class Struct extends Class { Struct() { usertypes(underlyingElement(this), _, 1) or usertypes(underlyingElement(this), _, 3) } - override string getCanonicalQLClass() { result = "Struct" } + override string getAPrimaryQlClass() { result = "Struct" } override string explain() { result = "struct " + this.getName() } @@ -39,9 +43,7 @@ class Struct extends Class { class LocalStruct extends Struct { LocalStruct() { isLocal() } - override string getCanonicalQLClass() { - not this instanceof LocalUnion and result = "LocalStruct" - } + override string getAPrimaryQlClass() { not this instanceof LocalUnion and result = "LocalStruct" } } /** @@ -58,7 +60,7 @@ class LocalStruct extends Struct { class NestedStruct extends Struct { NestedStruct() { this.isMember() } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not this instanceof NestedUnion and result = "NestedStruct" } diff --git a/cpp/ql/src/semmle/code/cpp/TestFile.qll b/cpp/ql/src/semmle/code/cpp/TestFile.qll index 4348bddf59c0..b9e3fe3a614f 100644 --- a/cpp/ql/src/semmle/code/cpp/TestFile.qll +++ b/cpp/ql/src/semmle/code/cpp/TestFile.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for identifying files that contain test cases. It is often + * desirable to exclude these files from analysis. + */ + import semmle.code.cpp.File /** diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll index 55eb4f27d3dc..8273d0440d5c 100644 --- a/cpp/ql/src/semmle/code/cpp/Type.qll +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -1,5 +1,8 @@ +/** + * Provides a hierarchy of classes for modeling C/C++ types. + */ + import semmle.code.cpp.Element -import semmle.code.cpp.Member import semmle.code.cpp.Function private import semmle.code.cpp.internal.ResolveClass @@ -322,7 +325,7 @@ class BuiltInType extends Type, @builtintype { class ErroneousType extends BuiltInType { ErroneousType() { builtintypes(underlyingElement(this), _, 1, _, _, _) } - override string getCanonicalQLClass() { result = "ErroneousType" } + override string getAPrimaryQlClass() { result = "ErroneousType" } } /** @@ -342,7 +345,7 @@ class ErroneousType extends BuiltInType { class UnknownType extends BuiltInType { UnknownType() { builtintypes(underlyingElement(this), _, 2, _, _, _) } - override string getCanonicalQLClass() { result = "UnknownType" } + override string getAPrimaryQlClass() { result = "UnknownType" } } private predicate isArithmeticType(@builtintype type, int kind) { @@ -361,7 +364,7 @@ private predicate isArithmeticType(@builtintype type, int kind) { class ArithmeticType extends BuiltInType { ArithmeticType() { isArithmeticType(underlyingElement(this), _) } - override string getCanonicalQLClass() { result = "ArithmeticType" } + override string getAPrimaryQlClass() { result = "ArithmeticType" } } private predicate isIntegralType(@builtintype type, int kind) { @@ -561,7 +564,7 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType { class BoolType extends IntegralType { BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) } - override string getCanonicalQLClass() { result = "BoolType" } + override string getAPrimaryQlClass() { result = "BoolType" } } /** @@ -586,7 +589,7 @@ abstract class CharType extends IntegralType { } class PlainCharType extends CharType { PlainCharType() { builtintypes(underlyingElement(this), _, 5, _, _, _) } - override string getCanonicalQLClass() { result = "PlainCharType" } + override string getAPrimaryQlClass() { result = "PlainCharType" } } /** @@ -599,7 +602,7 @@ class PlainCharType extends CharType { class UnsignedCharType extends CharType { UnsignedCharType() { builtintypes(underlyingElement(this), _, 6, _, _, _) } - override string getCanonicalQLClass() { result = "UnsignedCharType" } + override string getAPrimaryQlClass() { result = "UnsignedCharType" } } /** @@ -612,7 +615,7 @@ class UnsignedCharType extends CharType { class SignedCharType extends CharType { SignedCharType() { builtintypes(underlyingElement(this), _, 7, _, _, _) } - override string getCanonicalQLClass() { result = "SignedCharType" } + override string getAPrimaryQlClass() { result = "SignedCharType" } } /** @@ -629,7 +632,7 @@ class ShortType extends IntegralType { builtintypes(underlyingElement(this), _, 10, _, _, _) } - override string getCanonicalQLClass() { result = "ShortType" } + override string getAPrimaryQlClass() { result = "ShortType" } } /** @@ -646,7 +649,7 @@ class IntType extends IntegralType { builtintypes(underlyingElement(this), _, 13, _, _, _) } - override string getCanonicalQLClass() { result = "IntType" } + override string getAPrimaryQlClass() { result = "IntType" } } /** @@ -663,7 +666,7 @@ class LongType extends IntegralType { builtintypes(underlyingElement(this), _, 16, _, _, _) } - override string getCanonicalQLClass() { result = "LongType" } + override string getAPrimaryQlClass() { result = "LongType" } } /** @@ -680,7 +683,7 @@ class LongLongType extends IntegralType { builtintypes(underlyingElement(this), _, 19, _, _, _) } - override string getCanonicalQLClass() { result = "LongLongType" } + override string getAPrimaryQlClass() { result = "LongLongType" } } /** @@ -698,7 +701,7 @@ class Int128Type extends IntegralType { builtintypes(underlyingElement(this), _, 37, _, _, _) } - override string getCanonicalQLClass() { result = "Int128Type" } + override string getAPrimaryQlClass() { result = "Int128Type" } } private newtype TTypeDomain = @@ -894,7 +897,7 @@ class DecimalFloatingPointType extends FloatingPointType { class FloatType extends RealNumberType, BinaryFloatingPointType { FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) } - override string getCanonicalQLClass() { result = "FloatType" } + override string getAPrimaryQlClass() { result = "FloatType" } } /** @@ -906,7 +909,7 @@ class FloatType extends RealNumberType, BinaryFloatingPointType { class DoubleType extends RealNumberType, BinaryFloatingPointType { DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) } - override string getCanonicalQLClass() { result = "DoubleType" } + override string getAPrimaryQlClass() { result = "DoubleType" } } /** @@ -918,7 +921,7 @@ class DoubleType extends RealNumberType, BinaryFloatingPointType { class LongDoubleType extends RealNumberType, BinaryFloatingPointType { LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) } - override string getCanonicalQLClass() { result = "LongDoubleType" } + override string getAPrimaryQlClass() { result = "LongDoubleType" } } /** @@ -930,7 +933,7 @@ class LongDoubleType extends RealNumberType, BinaryFloatingPointType { class Float128Type extends RealNumberType, BinaryFloatingPointType { Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) } - override string getCanonicalQLClass() { result = "Float128Type" } + override string getAPrimaryQlClass() { result = "Float128Type" } } /** @@ -942,7 +945,7 @@ class Float128Type extends RealNumberType, BinaryFloatingPointType { class Decimal32Type extends RealNumberType, DecimalFloatingPointType { Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) } - override string getCanonicalQLClass() { result = "Decimal32Type" } + override string getAPrimaryQlClass() { result = "Decimal32Type" } } /** @@ -954,7 +957,7 @@ class Decimal32Type extends RealNumberType, DecimalFloatingPointType { class Decimal64Type extends RealNumberType, DecimalFloatingPointType { Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) } - override string getCanonicalQLClass() { result = "Decimal64Type" } + override string getAPrimaryQlClass() { result = "Decimal64Type" } } /** @@ -966,7 +969,7 @@ class Decimal64Type extends RealNumberType, DecimalFloatingPointType { class Decimal128Type extends RealNumberType, DecimalFloatingPointType { Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) } - override string getCanonicalQLClass() { result = "Decimal128Type" } + override string getAPrimaryQlClass() { result = "Decimal128Type" } } /** @@ -978,7 +981,7 @@ class Decimal128Type extends RealNumberType, DecimalFloatingPointType { class VoidType extends BuiltInType { VoidType() { builtintypes(underlyingElement(this), _, 3, _, _, _) } - override string getCanonicalQLClass() { result = "VoidType" } + override string getAPrimaryQlClass() { result = "VoidType" } } /** @@ -994,7 +997,7 @@ class VoidType extends BuiltInType { class WideCharType extends IntegralType { WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) } - override string getCanonicalQLClass() { result = "WideCharType" } + override string getAPrimaryQlClass() { result = "WideCharType" } } /** @@ -1006,7 +1009,7 @@ class WideCharType extends IntegralType { class Char8Type extends IntegralType { Char8Type() { builtintypes(underlyingElement(this), _, 51, _, _, _) } - override string getCanonicalQLClass() { result = "Char8Type" } + override string getAPrimaryQlClass() { result = "Char8Type" } } /** @@ -1018,7 +1021,7 @@ class Char8Type extends IntegralType { class Char16Type extends IntegralType { Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) } - override string getCanonicalQLClass() { result = "Char16Type" } + override string getAPrimaryQlClass() { result = "Char16Type" } } /** @@ -1030,7 +1033,7 @@ class Char16Type extends IntegralType { class Char32Type extends IntegralType { Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) } - override string getCanonicalQLClass() { result = "Char32Type" } + override string getAPrimaryQlClass() { result = "Char32Type" } } /** @@ -1045,7 +1048,7 @@ class Char32Type extends IntegralType { class NullPointerType extends BuiltInType { NullPointerType() { builtintypes(underlyingElement(this), _, 34, _, _, _) } - override string getCanonicalQLClass() { result = "NullPointerType" } + override string getAPrimaryQlClass() { result = "NullPointerType" } } /** @@ -1080,22 +1083,46 @@ class DerivedType extends Type, @derivedtype { override Type stripType() { result = getBaseType().stripType() } - predicate isAutoReleasing() { + /** + * Holds if this type has the `__autoreleasing` specifier or if it points to + * a type with the `__autoreleasing` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. + */ + deprecated predicate isAutoReleasing() { this.hasSpecifier("__autoreleasing") or this.(PointerType).getBaseType().hasSpecifier("__autoreleasing") } - predicate isStrong() { + /** + * Holds if this type has the `__strong` specifier or if it points to + * a type with the `__strong` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. + */ + deprecated predicate isStrong() { this.hasSpecifier("__strong") or this.(PointerType).getBaseType().hasSpecifier("__strong") } - predicate isUnsafeRetained() { + /** + * Holds if this type has the `__unsafe_unretained` specifier or if it points + * to a type with the `__unsafe_unretained` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. + */ + deprecated predicate isUnsafeRetained() { this.hasSpecifier("__unsafe_unretained") or this.(PointerType).getBaseType().hasSpecifier("__unsafe_unretained") } - predicate isWeak() { + /** + * Holds if this type has the `__weak` specifier or if it points to + * a type with the `__weak` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. + */ + deprecated predicate isWeak() { this.hasSpecifier("__weak") or this.(PointerType).getBaseType().hasSpecifier("__weak") } @@ -1109,7 +1136,7 @@ class DerivedType extends Type, @derivedtype { * ``` */ class Decltype extends Type, @decltype { - override string getCanonicalQLClass() { result = "Decltype" } + override string getAPrimaryQlClass() { result = "Decltype" } /** * The expression whose type is being obtained by this decltype. @@ -1182,7 +1209,7 @@ class Decltype extends Type, @decltype { class PointerType extends DerivedType { PointerType() { derivedtypes(underlyingElement(this), _, 1, _) } - override string getCanonicalQLClass() { result = "PointerType" } + override string getAPrimaryQlClass() { result = "PointerType" } override int getPointerIndirectionLevel() { result = 1 + this.getBaseType().getPointerIndirectionLevel() @@ -1208,7 +1235,7 @@ class ReferenceType extends DerivedType { derivedtypes(underlyingElement(this), _, 2, _) or derivedtypes(underlyingElement(this), _, 8, _) } - override string getCanonicalQLClass() { result = "ReferenceType" } + override string getAPrimaryQlClass() { result = "ReferenceType" } override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } @@ -1235,7 +1262,7 @@ class ReferenceType extends DerivedType { class LValueReferenceType extends ReferenceType { LValueReferenceType() { derivedtypes(underlyingElement(this), _, 2, _) } - override string getCanonicalQLClass() { result = "LValueReferenceType" } + override string getAPrimaryQlClass() { result = "LValueReferenceType" } } /** @@ -1251,7 +1278,7 @@ class LValueReferenceType extends ReferenceType { class RValueReferenceType extends ReferenceType { RValueReferenceType() { derivedtypes(underlyingElement(this), _, 8, _) } - override string getCanonicalQLClass() { result = "RValueReferenceType" } + override string getAPrimaryQlClass() { result = "RValueReferenceType" } override string explain() { result = "rvalue " + super.explain() } } @@ -1266,7 +1293,7 @@ class RValueReferenceType extends ReferenceType { class SpecifiedType extends DerivedType { SpecifiedType() { derivedtypes(underlyingElement(this), _, 3, _) } - override string getCanonicalQLClass() { result = "SpecifiedType" } + override string getAPrimaryQlClass() { result = "SpecifiedType" } override int getSize() { result = this.getBaseType().getSize() } @@ -1314,8 +1341,12 @@ class SpecifiedType extends DerivedType { class ArrayType extends DerivedType { ArrayType() { derivedtypes(underlyingElement(this), _, 4, _) } - override string getCanonicalQLClass() { result = "ArrayType" } + override string getAPrimaryQlClass() { result = "ArrayType" } + /** + * Holds if this array is declared to be of a constant size. See + * `getArraySize` and `getByteSize` to get the size of the array. + */ predicate hasArraySize() { arraysizes(underlyingElement(this), _, _, _) } /** @@ -1381,7 +1412,7 @@ class GNUVectorType extends DerivedType { */ int getNumElements() { arraysizes(underlyingElement(this), result, _, _) } - override string getCanonicalQLClass() { result = "GNUVectorType" } + override string getAPrimaryQlClass() { result = "GNUVectorType" } /** * Gets the size, in bytes, of this vector type. @@ -1412,7 +1443,7 @@ class GNUVectorType extends DerivedType { class FunctionPointerType extends FunctionPointerIshType { FunctionPointerType() { derivedtypes(underlyingElement(this), _, 6, _) } - override string getCanonicalQLClass() { result = "FunctionPointerType" } + override string getAPrimaryQlClass() { result = "FunctionPointerType" } override int getPointerIndirectionLevel() { result = 1 } @@ -1430,7 +1461,7 @@ class FunctionPointerType extends FunctionPointerIshType { class FunctionReferenceType extends FunctionPointerIshType { FunctionReferenceType() { derivedtypes(underlyingElement(this), _, 7, _) } - override string getCanonicalQLClass() { result = "FunctionReferenceType" } + override string getAPrimaryQlClass() { result = "FunctionReferenceType" } override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } @@ -1519,7 +1550,7 @@ class PointerToMemberType extends Type, @ptrtomember { /** a printable representation of this named element */ override string toString() { result = this.getName() } - override string getCanonicalQLClass() { result = "PointerToMemberType" } + override string getAPrimaryQlClass() { result = "PointerToMemberType" } /** the name of this type */ override string getName() { result = "..:: *" } @@ -1564,16 +1595,25 @@ class RoutineType extends Type, @routinetype { /** a printable representation of this named element */ override string toString() { result = this.getName() } - override string getCanonicalQLClass() { result = "RoutineType" } + override string getAPrimaryQlClass() { result = "RoutineType" } override string getName() { result = "..()(..)" } + /** + * Gets the type of the `n`th parameter to this routine. + */ Type getParameterType(int n) { routinetypeargs(underlyingElement(this), n, unresolveElement(result)) } + /** + * Gets the type of a parameter to this routine. + */ Type getAParameterType() { routinetypeargs(underlyingElement(this), _, unresolveElement(result)) } + /** + * Gets the return type of this routine. + */ Type getReturnType() { routinetypes(underlyingElement(this), unresolveElement(result)) } override string explain() { @@ -1632,7 +1672,7 @@ class TemplateParameter extends UserType { usertypes(underlyingElement(this), _, 7) or usertypes(underlyingElement(this), _, 8) } - override string getCanonicalQLClass() { result = "TemplateParameter" } + override string getAPrimaryQlClass() { result = "TemplateParameter" } override predicate involvesTemplateParameter() { any() } } @@ -1650,7 +1690,7 @@ class TemplateParameter extends UserType { class TemplateTemplateParameter extends TemplateParameter { TemplateTemplateParameter() { usertypes(underlyingElement(this), _, 8) } - override string getCanonicalQLClass() { result = "TemplateTemplateParameter" } + override string getAPrimaryQlClass() { result = "TemplateTemplateParameter" } } /** @@ -1662,7 +1702,7 @@ class TemplateTemplateParameter extends TemplateParameter { class AutoType extends TemplateParameter { AutoType() { usertypes(underlyingElement(this), "auto", 7) } - override string getCanonicalQLClass() { result = "AutoType" } + override string getAPrimaryQlClass() { result = "AutoType" } override Location getLocation() { suppressUnusedThis(this) and @@ -1698,7 +1738,7 @@ private predicate suppressUnusedThis(Type t) { any() } class TypeMention extends Locatable, @type_mention { override string toString() { result = "type mention" } - override string getCanonicalQLClass() { result = "TypeMention" } + override string getAPrimaryQlClass() { result = "TypeMention" } /** * Gets the type being referenced by this type mention. diff --git a/cpp/ql/src/semmle/code/cpp/TypedefType.qll b/cpp/ql/src/semmle/code/cpp/TypedefType.qll index 504333aeedcc..aaf452ce4bb2 100644 --- a/cpp/ql/src/semmle/code/cpp/TypedefType.qll +++ b/cpp/ql/src/semmle/code/cpp/TypedefType.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling typedefs and type aliases. + */ + import semmle.code.cpp.Type private import semmle.code.cpp.internal.ResolveClass @@ -55,7 +59,7 @@ class TypedefType extends UserType { class CTypedefType extends TypedefType { CTypedefType() { usertypes(underlyingElement(this), _, 5) } - override string getCanonicalQLClass() { result = "CTypedefType" } + override string getAPrimaryQlClass() { result = "CTypedefType" } override string explain() { result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\"" @@ -71,7 +75,7 @@ class CTypedefType extends TypedefType { class UsingAliasTypedefType extends TypedefType { UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) } - override string getCanonicalQLClass() { result = "UsingAliasTypedefType" } + override string getAPrimaryQlClass() { result = "UsingAliasTypedefType" } override string explain() { result = "using {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\"" @@ -88,7 +92,7 @@ class UsingAliasTypedefType extends TypedefType { class LocalTypedefType extends TypedefType { LocalTypedefType() { isLocal() } - override string getCanonicalQLClass() { result = "LocalTypedefType" } + override string getAPrimaryQlClass() { result = "LocalTypedefType" } } /** @@ -101,7 +105,7 @@ class LocalTypedefType extends TypedefType { class NestedTypedefType extends TypedefType { NestedTypedefType() { this.isMember() } - override string getCanonicalQLClass() { result = "NestedTypedefType" } + override string getAPrimaryQlClass() { result = "NestedTypedefType" } /** * DEPRECATED: use `.hasSpecifier("private")` instead. diff --git a/cpp/ql/src/semmle/code/cpp/Union.qll b/cpp/ql/src/semmle/code/cpp/Union.qll index f1c033438ba5..6dcb2f0796c8 100644 --- a/cpp/ql/src/semmle/code/cpp/Union.qll +++ b/cpp/ql/src/semmle/code/cpp/Union.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling `union`s. + */ + import semmle.code.cpp.Type import semmle.code.cpp.Struct @@ -13,7 +17,7 @@ import semmle.code.cpp.Struct class Union extends Struct { Union() { usertypes(underlyingElement(this), _, 3) } - override string getCanonicalQLClass() { result = "Union" } + override string getAPrimaryQlClass() { result = "Union" } override string explain() { result = "union " + this.getName() } @@ -35,7 +39,7 @@ class Union extends Struct { class LocalUnion extends Union { LocalUnion() { isLocal() } - override string getCanonicalQLClass() { result = "LocalUnion" } + override string getAPrimaryQlClass() { result = "LocalUnion" } } /** @@ -53,7 +57,7 @@ class LocalUnion extends Union { class NestedUnion extends Union { NestedUnion() { this.isMember() } - override string getCanonicalQLClass() { result = "NestedUnion" } + override string getAPrimaryQlClass() { result = "NestedUnion" } /** Holds if this member is private. */ predicate isPrivate() { this.hasSpecifier("private") } diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll index 4484cde84deb..2ab0603f06cc 100644 --- a/cpp/ql/src/semmle/code/cpp/UserType.qll +++ b/cpp/ql/src/semmle/code/cpp/UserType.qll @@ -1,6 +1,10 @@ +/** + * Provides classes for modeling user-defined types such as classes, typedefs + * and enums. + */ + import semmle.code.cpp.Declaration import semmle.code.cpp.Type -import semmle.code.cpp.Member import semmle.code.cpp.Function private import semmle.code.cpp.internal.ResolveClass @@ -20,7 +24,7 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ */ override string getName() { usertypes(underlyingElement(this), result, _) } - override string getCanonicalQLClass() { result = "UserType" } + override string getAPrimaryQlClass() { result = "UserType" } /** * Gets the simple name of this type, without any template parameters. For example @@ -84,6 +88,9 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ * type exactly - but this is not apparent from its subclasses */ + /** + * Gets a child declaration within this user-defined type. + */ Declaration getADeclaration() { none() } override string explain() { result = this.getName() } @@ -104,7 +111,7 @@ class TypeDeclarationEntry extends DeclarationEntry, @type_decl { override string getName() { result = getType().getName() } - override string getCanonicalQLClass() { result = "TypeDeclarationEntry" } + override string getAPrimaryQlClass() { result = "TypeDeclarationEntry" } /** * The type which is being declared or defined. diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll index 54f39ab2bb6f..527d8879b96d 100644 --- a/cpp/ql/src/semmle/code/cpp/Variable.qll +++ b/cpp/ql/src/semmle/code/cpp/Variable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling variables and their declarations. + */ + import semmle.code.cpp.Element import semmle.code.cpp.exprs.Access import semmle.code.cpp.Initializer @@ -28,7 +32,7 @@ private import semmle.code.cpp.internal.ResolveClass * can have multiple declarations. */ class Variable extends Declaration, @variable { - override string getCanonicalQLClass() { result = "Variable" } + override string getAPrimaryQlClass() { result = "Variable" } /** Gets the initializer of this variable, if any. */ Initializer getInitializer() { result.getDeclaration() = this } @@ -140,6 +144,11 @@ class Variable extends Declaration, @variable { */ 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`. */ @@ -186,7 +195,7 @@ class Variable extends Declaration, @variable { class VariableDeclarationEntry extends DeclarationEntry, @var_decl { override Variable getDeclaration() { result = getVariable() } - override string getCanonicalQLClass() { result = "VariableDeclarationEntry" } + override string getAPrimaryQlClass() { result = "VariableDeclarationEntry" } /** * Gets the variable which is being declared or defined. @@ -245,7 +254,7 @@ class VariableDeclarationEntry extends DeclarationEntry, @var_decl { class ParameterDeclarationEntry extends VariableDeclarationEntry { ParameterDeclarationEntry() { param_decl_bind(underlyingElement(this), _, _) } - override string getCanonicalQLClass() { result = "ParameterDeclarationEntry" } + override string getAPrimaryQlClass() { result = "ParameterDeclarationEntry" } /** * Gets the function declaration or definition which this parameter @@ -321,7 +330,7 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry { */ class LocalScopeVariable extends Variable, @localscopevariable { /** Gets the function to which this variable belongs. */ - /*abstract*/ Function getFunction() { none() } + Function getFunction() { none() } // overridden in subclasses } /** @@ -359,7 +368,7 @@ class StackVariable extends LocalScopeVariable { * A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`. */ class LocalVariable extends LocalScopeVariable, @localvariable { - override string getCanonicalQLClass() { result = "LocalVariable" } + override string getAPrimaryQlClass() { result = "LocalVariable" } override string getName() { localvariables(underlyingElement(this), _, result) } @@ -460,7 +469,7 @@ class NamespaceVariable extends GlobalOrNamespaceVariable { exists(Namespace n | namespacembrs(unresolveElement(n), underlyingElement(this))) } - override string getCanonicalQLClass() { result = "NamespaceVariable" } + override string getAPrimaryQlClass() { result = "NamespaceVariable" } } /** @@ -481,7 +490,7 @@ class NamespaceVariable extends GlobalOrNamespaceVariable { class GlobalVariable extends GlobalOrNamespaceVariable { GlobalVariable() { not this instanceof NamespaceVariable } - override string getCanonicalQLClass() { result = "GlobalVariable" } + override string getAPrimaryQlClass() { result = "GlobalVariable" } } /** @@ -501,7 +510,7 @@ class GlobalVariable extends GlobalOrNamespaceVariable { class MemberVariable extends Variable, @membervariable { MemberVariable() { this.isMember() } - override string getCanonicalQLClass() { result = "MemberVariable" } + override string getAPrimaryQlClass() { result = "MemberVariable" } /** Holds if this member is private. */ predicate isPrivate() { this.hasSpecifier("private") } @@ -578,7 +587,7 @@ class TemplateVariable extends Variable { * float a; * } * - * template + * template * void myTemplateFunction() { * T b; * } diff --git a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll index f584a8e48027..253a27670774 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll @@ -6,7 +6,7 @@ import semmle.code.cpp.Type class CharPointerType extends PointerType { CharPointerType() { this.getBaseType() instanceof CharType } - override string getCanonicalQLClass() { result = "CharPointerType" } + override string getAPrimaryQlClass() { result = "CharPointerType" } } /** @@ -15,7 +15,7 @@ class CharPointerType extends PointerType { class IntPointerType extends PointerType { IntPointerType() { this.getBaseType() instanceof IntType } - override string getCanonicalQLClass() { result = "IntPointerType" } + override string getAPrimaryQlClass() { result = "IntPointerType" } } /** @@ -24,7 +24,7 @@ class IntPointerType extends PointerType { class VoidPointerType extends PointerType { VoidPointerType() { this.getBaseType() instanceof VoidType } - override string getCanonicalQLClass() { result = "VoidPointerType" } + override string getAPrimaryQlClass() { result = "VoidPointerType" } } /** @@ -36,7 +36,7 @@ class Size_t extends Type { this.hasName("size_t") } - override string getCanonicalQLClass() { result = "Size_t" } + override string getAPrimaryQlClass() { result = "Size_t" } } /** @@ -48,7 +48,7 @@ class Ssize_t extends Type { this.hasName("ssize_t") } - override string getCanonicalQLClass() { result = "Ssize_t" } + override string getAPrimaryQlClass() { result = "Ssize_t" } } /** @@ -60,7 +60,7 @@ class Ptrdiff_t extends Type { this.hasName("ptrdiff_t") } - override string getCanonicalQLClass() { result = "Ptrdiff_t" } + override string getAPrimaryQlClass() { result = "Ptrdiff_t" } } /** @@ -72,7 +72,7 @@ class Intmax_t extends Type { this.hasName("intmax_t") } - override string getCanonicalQLClass() { result = "Intmax_t" } + override string getAPrimaryQlClass() { result = "Intmax_t" } } /** @@ -84,7 +84,7 @@ class Uintmax_t extends Type { this.hasName("uintmax_t") } - override string getCanonicalQLClass() { result = "Uintmax_t" } + override string getAPrimaryQlClass() { result = "Uintmax_t" } } /** @@ -100,7 +100,7 @@ class Wchar_t extends Type { this.hasName("wchar_t") } - override string getCanonicalQLClass() { result = "Wchar_t" } + override string getAPrimaryQlClass() { result = "Wchar_t" } } /** @@ -176,5 +176,5 @@ class MicrosoftInt64Type extends IntegralType { class BuiltInVarArgsList extends Type { BuiltInVarArgsList() { this.hasName("__builtin_va_list") } - override string getCanonicalQLClass() { result = "BuiltInVarArgsList" } + override string getAPrimaryQlClass() { result = "BuiltInVarArgsList" } } diff --git a/cpp/ql/src/semmle/code/cpp/commons/Environment.qll b/cpp/ql/src/semmle/code/cpp/commons/Environment.qll index f3f1759dd5c0..3da19977f338 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/Environment.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/Environment.qll @@ -29,7 +29,7 @@ private predicate readsEnvironment(Expr read, string sourceDescription) { exists(FunctionCall call, string name | read = call and call.getTarget().hasGlobalOrStdName(name) and - (name = "getenv" or name = "secure_getenv" or name = "_wgetenv") and + name = ["getenv", "secure_getenv", "_wgetenv"] and sourceDescription = name ) } diff --git a/cpp/ql/src/semmle/code/cpp/commons/File.qll b/cpp/ql/src/semmle/code/cpp/commons/File.qll index 5808d704e385..acc5893d8106 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/File.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/File.qll @@ -1,3 +1,7 @@ +/** + * Provides predicates for identifying function calls that open or close a file. + */ + import cpp /** diff --git a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll index 32cea249214f..e441dd66adcc 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll @@ -9,10 +9,7 @@ import semmle.code.cpp.models.interfaces.FormattingFunction import semmle.code.cpp.models.implementations.Printf class PrintfFormatAttribute extends FormatAttribute { - PrintfFormatAttribute() { - getArchetype() = "printf" or - getArchetype() = "__printf__" - } + PrintfFormatAttribute() { getArchetype() = ["printf", "__printf__"] } } /** @@ -20,7 +17,7 @@ class PrintfFormatAttribute extends FormatAttribute { * function by its use of the GNU `format` attribute. */ class AttributeFormattingFunction extends FormattingFunction { - override string getCanonicalQLClass() { result = "AttributeFormattingFunction" } + override string getAPrimaryQlClass() { result = "AttributeFormattingFunction" } AttributeFormattingFunction() { exists(PrintfFormatAttribute printf_attrib | @@ -49,6 +46,18 @@ predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex) { ) } +/** + * A standard function such as `vsprintf` that has an output parameter + * and a variable argument list of type `va_arg`. + */ +private predicate primitiveVariadicFormatterOutput(TopLevelFunction f, int outputParamIndex) { + // note: this might look like the regular expression in `primitiveVariadicFormatter`, but + // there is one important difference: the [fs] part is not optional, as these classify + // the `printf` variants that write to a buffer. + // Conveniently, these buffer parameters are all at index 0. + f.getName().regexpMatch("_?_?va?[fs]n?w?printf(_s)?(_p)?(_l)?") and outputParamIndex = 0 +} + private predicate callsVariadicFormatter(Function f, int formatParamIndex) { exists(FunctionCall fc, int i | variadicFormatter(fc.getTarget(), i) and @@ -57,6 +66,26 @@ private predicate callsVariadicFormatter(Function f, int formatParamIndex) { ) } +private predicate callsVariadicFormatterOutput(Function f, int outputParamIndex) { + exists(FunctionCall fc, int i | + fc.getEnclosingFunction() = f and + variadicFormatterOutput(fc.getTarget(), i) and + fc.getArgument(i) = f.getParameter(outputParamIndex).getAnAccess() + ) +} + +/** + * Holds if `f` is a function such as `vprintf` that takes variable argument list + * of type `va_arg` and writes formatted output to a buffer given as a parameter at + * index `outputParamIndex`, if any. + */ +private predicate variadicFormatterOutput(Function f, int outputParamIndex) { + primitiveVariadicFormatterOutput(f, outputParamIndex) + or + not f.isVarargs() and + callsVariadicFormatterOutput(f, outputParamIndex) +} + /** * Holds if `f` is a function such as `vprintf` that has a format parameter * (at `formatParamIndex`) and a variable argument list of type `va_arg`. @@ -73,11 +102,13 @@ predicate variadicFormatter(Function f, int formatParamIndex) { * string and a variable number of arguments. */ class UserDefinedFormattingFunction extends FormattingFunction { - override string getCanonicalQLClass() { result = "UserDefinedFormattingFunction" } + override string getAPrimaryQlClass() { result = "UserDefinedFormattingFunction" } UserDefinedFormattingFunction() { isVarargs() and callsVariadicFormatter(this, _) } override int getFormatParameterIndex() { callsVariadicFormatter(this, result) } + + override int getOutputParameterIndex() { callsVariadicFormatterOutput(this, result) } } /** @@ -86,7 +117,7 @@ class UserDefinedFormattingFunction extends FormattingFunction { class FormattingFunctionCall extends Expr { FormattingFunctionCall() { this.(Call).getTarget() instanceof FormattingFunction } - override string getCanonicalQLClass() { result = "FormattingFunctionCall" } + override string getAPrimaryQlClass() { result = "FormattingFunctionCall" } /** * Gets the formatting function being called. @@ -567,12 +598,12 @@ class FormatLiteral extends Literal { or len = "l" and result = this.getLongType() or - (len = "ll" or len = "L" or len = "q") and + len = ["ll", "L", "q"] and result instanceof LongLongType or len = "j" and result = this.getIntmax_t() or - (len = "z" or len = "Z") and + len = ["z", "Z"] and (result = this.getSize_t() or result = this.getSsize_t()) or len = "t" and result = this.getPtrdiff_t() @@ -605,12 +636,12 @@ class FormatLiteral extends Literal { or len = "l" and result = this.getLongType() or - (len = "ll" or len = "L" or len = "q") and + len = ["ll", "L", "q"] and result instanceof LongLongType or len = "j" and result = this.getIntmax_t() or - (len = "z" or len = "Z") and + len = ["z", "Z"] and (result = this.getSize_t() or result = this.getSsize_t()) or len = "t" and result = this.getPtrdiff_t() @@ -636,9 +667,7 @@ class FormatLiteral extends Literal { FloatingPointType getFloatingPointConversion(int n) { exists(string len | len = this.getLength(n) and - if len = "L" or len = "ll" - then result instanceof LongDoubleType - else result instanceof DoubleType + if len = ["L", "ll"] then result instanceof LongDoubleType else result instanceof DoubleType ) } @@ -655,7 +684,7 @@ class FormatLiteral extends Literal { or len = "l" and base = this.getLongType() or - (len = "ll" or len = "L") and + len = ["ll", "L"] and base instanceof LongLongType or len = "q" and base instanceof LongLongType @@ -702,12 +731,12 @@ class FormatLiteral extends Literal { exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and ( - (conv = "c" or conv = "C") and + conv = ["c", "C"] and len = "h" and result instanceof PlainCharType or - (conv = "c" or conv = "C") and - (len = "l" or len = "w") and + conv = ["c", "C"] and + len = ["l", "w"] and result = getWideCharType() or conv = "c" and @@ -747,12 +776,12 @@ class FormatLiteral extends Literal { exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and ( - (conv = "s" or conv = "S") and + conv = ["s", "S"] and len = "h" and result.(PointerType).getBaseType() instanceof PlainCharType or - (conv = "s" or conv = "S") and - (len = "l" or len = "w") and + conv = ["s", "S"] and + len = ["l", "w"] and result.(PointerType).getBaseType() = getWideCharType() or conv = "s" and @@ -789,10 +818,7 @@ class FormatLiteral extends Literal { private Type getConversionType9(int n) { this.getConversionChar(n) = "Z" and - ( - this.getLength(n) = "l" or - this.getLength(n) = "w" - ) and + this.getLength(n) = ["l", "w"] and exists(Type t | t.getName() = "UNICODE_STRING" and result.(PointerType).getBaseType() = t @@ -945,10 +971,7 @@ class FormatLiteral extends Literal { len = (afterdot.maximum(1) + 6).maximum(1 + 1 + dot + afterdot + 1 + 1 + 3) ) // (e.g. "-1.59203e-319") or - ( - this.getConversionChar(n).toLowerCase() = "d" or - this.getConversionChar(n).toLowerCase() = "i" - ) and + this.getConversionChar(n).toLowerCase() = ["d", "i"] and // e.g. -2^31 = "-2147483648" exists(int sizeBits | sizeBits = diff --git a/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll b/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll index dce086bdd4ba..28408b82ffa6 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll @@ -8,14 +8,13 @@ import cpp */ class StrcatFunction extends Function { StrcatFunction() { - exists(string name | name = getName() | - name = "strcat" or // strcat(dst, src) - name = "strncat" or // strncat(dst, src, max_amount) - name = "wcscat" or // wcscat(dst, src) - name = "_mbscat" or // _mbscat(dst, src) - name = "wcsncat" or // wcsncat(dst, src, max_amount) - name = "_mbsncat" or // _mbsncat(dst, src, max_amount) - name = "_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale) - ) + getName() = + ["strcat", // strcat(dst, src) + "strncat", // strncat(dst, src, max_amount) + "wcscat", // wcscat(dst, src) + "_mbscat", // _mbscat(dst, src) + "wcsncat", // wcsncat(dst, src, max_amount) + "_mbsncat", // _mbsncat(dst, src, max_amount) + "_mbsncat_l"] // _mbsncat_l(dst, src, max_amount, locale) } } diff --git a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll index 92c09a3e6667..b54ff6d66e30 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll @@ -1,3 +1,7 @@ +/** + * Provides a class for calculating the possible length of string expressions. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.controlflow.SSA diff --git a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll index c680cfb073e8..c76413853939 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll @@ -23,8 +23,7 @@ abstract class MutexType extends Type { abstract predicate trylockAccess(FunctionCall fc, Expr arg); /** - * Holds if `fc` is a call that unlocks mutex `arg` - * of this type. + * Holds if `fc` is a call that unlocks mutex `arg` of this type. */ abstract predicate unlockAccess(FunctionCall fc, Expr arg); @@ -38,8 +37,7 @@ abstract class MutexType extends Type { } /** - * Holds if `fc` is a call that locks or tries to lock any - * mutex of this type. + * Gets a call that locks or tries to lock any mutex of this type. */ FunctionCall getLockAccess() { result = getMustlockAccess() or @@ -47,44 +45,44 @@ abstract class MutexType extends Type { } /** - * Holds if `fc` is a call that always locks any mutex of this type. + * Gets a call that always locks any mutex of this type. */ FunctionCall getMustlockAccess() { this.mustlockAccess(result, _) } /** - * Holds if `fc` is a call that tries to lock any mutex of this type, + * Gets a call that tries to lock any mutex of this type, * by may return without success. */ FunctionCall getTrylockAccess() { this.trylockAccess(result, _) } /** - * Holds if `fc` is a call that unlocks any mutex of this type. + * Gets a call that unlocks any mutex of this type. */ FunctionCall getUnlockAccess() { this.unlockAccess(result, _) } /** - * DEPRECATED: use mustlockAccess(fc, arg) instead + * DEPRECATED: use mustlockAccess(fc, arg) instead. */ deprecated Function getMustlockFunction() { result = getMustlockAccess().getTarget() } /** - * DEPRECATED: use trylockAccess(fc, arg) instead + * DEPRECATED: use trylockAccess(fc, arg) instead. */ deprecated Function getTrylockFunction() { result = getTrylockAccess().getTarget() } /** - * DEPRECATED: use lockAccess(fc, arg) instead + * DEPRECATED: use lockAccess(fc, arg) instead. */ deprecated Function getLockFunction() { result = getLockAccess().getTarget() } /** - * DEPRECATED: use unlockAccess(fc, arg) instead + * DEPRECATED: use unlockAccess(fc, arg) instead. */ deprecated Function getUnlockFunction() { result = getUnlockAccess().getTarget() } } /** - * A function that looks like a lock function. + * Gets a function that looks like a lock function. */ private Function mustlockCandidate() { exists(string name | name = result.getName() | @@ -94,7 +92,7 @@ private Function mustlockCandidate() { } /** - * A function that looks like a try-lock function. + * Gets a function that looks like a try-lock function. */ private Function trylockCandidate() { exists(string name | name = result.getName() | @@ -104,7 +102,7 @@ private Function trylockCandidate() { } /** - * A function that looks like an unlock function. + * Gets a function that looks like an unlock function. */ private Function unlockCandidate() { exists(string name | name = result.getName() | @@ -171,7 +169,10 @@ class DefaultMutexType extends MutexType { } } -/** Get the mutex argument of a call to lock or unlock. */ +/** + * Holds if `arg` is the mutex argument of a call to lock or unlock and + * `argType` is the type of the mutex. + */ private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) { argType = arg.getUnderlyingType().stripType() and ( @@ -184,18 +185,31 @@ private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) { // `MutexType.mustlockAccess`. } +/** + * Holds if `call` is a call that locks or tries to lock its argument `arg`. + */ predicate lockCall(Expr arg, FunctionCall call) { exists(MutexType t | lockArg(arg, t, call) and call = t.getLockAccess()) } +/** + * Holds if `call` is a call that always locks its argument `arg`. + */ predicate mustlockCall(Expr arg, FunctionCall call) { exists(MutexType t | lockArg(arg, t, call) and call = t.getMustlockAccess()) } +/** + * Holds if `call` is a call that tries to lock its argument `arg`, but may + * return without success. + */ predicate trylockCall(Expr arg, FunctionCall call) { exists(MutexType t | lockArg(arg, t, call) and call = t.getTrylockAccess()) } +/** + * Holds if `call` is a call that unlocks its argument `arg`. + */ predicate unlockCall(Expr arg, FunctionCall call) { exists(MutexType t | lockArg(arg, t, call) and call = t.getUnlockAccess()) } diff --git a/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll index 4b0b1299fd86..0a325442cf5a 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll @@ -25,7 +25,7 @@ private predicate exprInVoidContext(Expr e) { ( exists(ExprStmt s | s = e.getParent() and - not exists(StmtExpr se | s = se.getStmt().(Block).getLastStmt()) + not exists(StmtExpr se | s = se.getStmt().(BlockStmt).getLastStmt()) ) or exists(ConditionalExpr c | c.getThen() = e and c instanceof ExprInVoidContext) diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll index 1f79d6c5bc20..bac051f64746 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll @@ -65,7 +65,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase { * taken when this expression is true. */ ControlFlowNode getATrueSuccessor() { - truecond_base(this, result) and + qlCFGTrueSuccessor(this, result) and result = getASuccessor() } @@ -74,7 +74,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase { * taken when this expression is false. */ ControlFlowNode getAFalseSuccessor() { - falsecond_base(this, result) and + qlCFGFalseSuccessor(this, result) and result = getASuccessor() } @@ -95,18 +95,20 @@ import ControlFlowGraphPublic class ControlFlowNodeBase extends ElementBase, @cfgnode { } /** + * DEPRECATED: Use `ControlFlowNode.getATrueSuccessor()` instead. * Holds when `n2` is a control-flow node such that the control-flow * edge `(n1, n2)` may be taken when `n1` is an expression that is true. */ -predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) { +deprecated predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) { qlCFGTrueSuccessor(n1, n2) } /** + * DEPRECATED: Use `ControlFlowNode.getAFalseSuccessor()` instead. * Holds when `n2` is a control-flow node such that the control-flow * edge `(n1, n2)` may be taken when `n1` is an expression that is false. */ -predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) { +deprecated predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) { qlCFGFalseSuccessor(n1, n2) } @@ -134,7 +136,7 @@ abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase { /** * Holds if there is a control-flow edge from `source` to `target` in either * the extractor-generated control-flow graph or in a subclass of - * `AdditionalControlFlowEdge`. Use this relation instead of `successors`. + * `AdditionalControlFlowEdge`. Use this relation instead of `qlCFGSuccessor`. */ predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) { qlCFGSuccessor(source, target) diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll index 75211c631feb..656496325af2 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll @@ -13,6 +13,7 @@ import semmle.code.cpp.ir.IR * has the AST for the `Function` itself, which tends to confuse mapping between the AST `BasicBlock` * and the `IRBlock`. */ +pragma[noinline] private predicate isUnreachedBlock(IRBlock block) { block.getFirstInstruction() instanceof UnreachedInstruction } @@ -304,13 +305,13 @@ class IRGuardCondition extends Instruction { pred.getASuccessor() = succ and controls(pred, testIsTrue) or - hasBranchEdge(succ, testIsTrue) and + succ = getBranchSuccessor(testIsTrue) and branch.getCondition() = this and branch.getBlock() = pred } /** - * Holds if `branch` jumps directly to `succ` when this condition is `testIsTrue`. + * Gets the block to which `branch` jumps directly when this condition is `testIsTrue`. * * This predicate is intended to help with situations in which an inference can only be made * based on an edge between a block with multiple successors and a block with multiple @@ -324,14 +325,14 @@ class IRGuardCondition extends Instruction { * return x; * ``` */ - private predicate hasBranchEdge(IRBlock succ, boolean testIsTrue) { + private IRBlock getBranchSuccessor(boolean testIsTrue) { branch.getCondition() = this and ( testIsTrue = true and - succ.getFirstInstruction() = branch.getTrueSuccessor() + result.getFirstInstruction() = branch.getTrueSuccessor() or testIsTrue = false and - succ.getFirstInstruction() = branch.getFalseSuccessor() + result.getFirstInstruction() = branch.getFalseSuccessor() ) } @@ -405,20 +406,78 @@ class IRGuardCondition extends Instruction { */ private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) { not isUnreachedBlock(controlled) and - exists(IRBlock branchBlock | branchBlock.getAnInstruction() = branch | - exists(IRBlock succ | - testIsTrue = true and succ.getFirstInstruction() = branch.getTrueSuccessor() + // + // For this block to control the block `controlled` with `testIsTrue` the + // following must hold: Execution must have passed through the test; that + // is, `this` must strictly dominate `controlled`. Execution must have + // passed through the `testIsTrue` edge leaving `this`. + // + // Although "passed through the true edge" implies that + // `getBranchSuccessor(true)` dominates `controlled`, the reverse is not + // true, as flow may have passed through another edge to get to + // `getBranchSuccessor(true)`, so we need to assert that + // `getBranchSuccessor(true)` dominates `controlled` *and* that all + // predecessors of `getBranchSuccessor(true)` are either `this` or + // dominated by `getBranchSuccessor(true)`. + // + // For example, in the following snippet: + // + // if (x) + // controlled; + // false_successor; + // uncontrolled; + // + // `false_successor` dominates `uncontrolled`, but not all of its + // predecessors are `this` (`if (x)`) or dominated by itself. Whereas in + // the following code: + // + // if (x) + // while (controlled) + // also_controlled; + // false_successor; + // uncontrolled; + // + // the block `while (controlled)` is controlled because all of its + // predecessors are `this` (`if (x)`) or (in the case of `also_controlled`) + // dominated by itself. + // + // The additional constraint on the predecessors of the test successor implies + // that `this` strictly dominates `controlled` so that isn't necessary to check + // directly. + exists(IRBlock succ | + succ = this.getBranchSuccessor(testIsTrue) and + this.hasDominatingEdgeTo(succ) and + succ.dominates(controlled) + ) + } + + /** + * Holds if `(this, succ)` is an edge that dominates `succ`, that is, all other + * predecessors of `succ` are dominated by `succ`. This implies that `this` is the + * immediate dominator of `succ`. + * + * This is a necessary and sufficient condition for an edge to dominate anything, + * and in particular `bb1.hasDominatingEdgeTo(bb2) and bb2.dominates(bb3)` means + * that the edge `(bb1, bb2)` dominates `bb3`. + */ + private predicate hasDominatingEdgeTo(IRBlock succ) { + exists(IRBlock branchBlock | branchBlock = this.getBranchBlock() | + branchBlock.immediatelyDominates(succ) and + branchBlock.getASuccessor() = succ and + forall(IRBlock pred | pred = succ.getAPredecessor() and pred != branchBlock | + succ.dominates(pred) or - testIsTrue = false and succ.getFirstInstruction() = branch.getFalseSuccessor() - | - branch.getCondition() = this and - succ.dominates(controlled) and - forall(IRBlock pred | pred.getASuccessor() = succ | - pred = branchBlock or succ.dominates(pred) or not pred.isReachableFromFunctionEntry() - ) + // An unreachable `pred` is vacuously dominated by `succ` since all + // paths from the entry to `pred` go through `succ`. Such vacuous + // dominance is not included in the `dominates` predicate since that + // could cause quadratic blow-up. + not pred.isReachableFromFunctionEntry() ) ) } + + pragma[noinline] + private IRBlock getBranchBlock() { result = branch.getBlock() } } private ConditionalBranchInstruction get_branch_for_condition(Instruction guard) { diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll index 3ae1ed11e6d9..caae23c3bbd9 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll @@ -73,8 +73,20 @@ private predicate addressTakenVariable(StackVariable var) { ) } +/** + * Holds if `v` is a stack-allocated reference-typed local variable. We don't + * build SSA for such variables since they are likely to change values even + * when not syntactically mentioned. For the same reason, + * `addressTakenVariable` is used to prevent tracking variables that may be + * aliased by such a reference. + * + * Reference-typed parameters are treated as if they weren't references. + * That's because it's in practice highly unlikely that they alias other data + * accessible from the function body. + */ private predicate isReferenceVar(StackVariable v) { - v.getUnspecifiedType() instanceof ReferenceType + v.getUnspecifiedType() instanceof ReferenceType and + not v instanceof Parameter } /** diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll index b2cfc903a662..b47618de2e92 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll @@ -118,7 +118,7 @@ private predicate excludeNodeAndNodesBelow(Expr e) { or // Constructor init lists should be evaluated, and we can change this in // the future, but it would mean that a `Function` entry point is not - // always a `Block` or `FunctionTryStmt`. + // always a `BlockStmt` or `FunctionTryStmt`. e instanceof ConstructorInit or // Destructor field destructions should also be hooked into the CFG @@ -408,10 +408,10 @@ private Node getControlOrderChildSparse(Node n, int i) { // in-line in the block containing their corresponding DeclStmt but should // not be evaluated in the order implied by their position in the block. We // do the following. - // - Block skips all the VlaDeclStmt and VlaDimensionStmt children. + // - BlockStmt skips all the VlaDeclStmt and VlaDimensionStmt children. // - VlaDeclStmt is inserted as a child of DeclStmt // - VlaDimensionStmt is inserted as a child of VlaDeclStmt - result = n.(Block).getChild(i) and + result = n.(BlockStmt).getChild(i) and not result instanceof VlaDeclStmt and not result instanceof VlaDimensionStmt or @@ -557,7 +557,7 @@ private class Spec extends Pos { */ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) { scope = - any(Block b | + any(BlockStmt b | i = -1 and ni = b and spec.isAt() or if exists(getLastControlOrderChild(b)) @@ -734,7 +734,7 @@ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) { or // If the switch body is not a block then this step is skipped, and the // expression jumps directly to the cases. - i = 1 and ni = s.getStmt().(Block) and spec.isAt() + i = 1 and ni = s.getStmt().(BlockStmt) and spec.isAt() or i = 2 and ni = s.getASwitchCase() and spec.isBefore() or @@ -1010,7 +1010,7 @@ private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2) * The exact placement of that call in the CFG depends on the type of * `node` as follows: * - * - `Block`: after ordinary control flow falls off the end of the block + * - `BlockStmt`: after ordinary control flow falls off the end of the block * without jumps or exceptions. * - `ReturnStmt`: After the statement itself or after its operand (if * present). @@ -1376,8 +1376,6 @@ private module Cached { /** * Holds if `n2` is a successor of `n1` in the CFG. This includes also * true-successors and false-successors. - * - * This corresponds to the old `successors` dbscheme relation. */ cached predicate qlCFGSuccessor(Node n1, Node n2) { @@ -1390,9 +1388,8 @@ private module Cached { } /** - * Holds if `n2` is a true-successor of `n1` in the CFG. - * - * This corresponds to the old `truecond` dbscheme relation. + * Holds if `n2` is a control-flow node such that the control-flow + * edge `(n1, n2)` may be taken when `n1` is an expression that is true. */ cached predicate qlCFGTrueSuccessor(Node n1, Node n2) { @@ -1401,9 +1398,8 @@ private module Cached { } /** - * Holds if `n2` is a false-successor of `n1` in the CFG. - * - * This corresponds to the old `falsecond` dbscheme relation. + * Holds if `n2` is a control-flow node such that the control-flow + * edge `(n1, n2)` may be taken when `n1` is an expression that is false. */ cached predicate qlCFGFalseSuccessor(Node n1, Node n2) { diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll index c473f1560882..77e0f05ed020 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll @@ -1,5 +1,6 @@ import cpp private import PrimitiveBasicBlocks +private import semmle.code.cpp.controlflow.internal.CFG private class Node = ControlFlowNodeBase; @@ -153,8 +154,8 @@ private predicate nonAnalyzableFunction(Function f) { */ private predicate impossibleFalseEdge(Expr condition, Node succ) { conditionAlwaysTrue(condition) and - falsecond_base(condition, succ) and - not truecond_base(condition, succ) + qlCFGFalseSuccessor(condition, succ) and + not qlCFGTrueSuccessor(condition, succ) } /** @@ -162,8 +163,8 @@ private predicate impossibleFalseEdge(Expr condition, Node succ) { */ private predicate impossibleTrueEdge(Expr condition, Node succ) { conditionAlwaysFalse(condition) and - truecond_base(condition, succ) and - not falsecond_base(condition, succ) + qlCFGTrueSuccessor(condition, succ) and + not qlCFGFalseSuccessor(condition, succ) } /** @@ -181,7 +182,7 @@ private int switchCaseRangeEnd(SwitchCase sc) { * body `switchBlock`. There may be several such expressions: for example, if * the condition is `(x ? y : z)` then the result is {`y`, `z`}. */ -private Node getASwitchExpr(SwitchStmt switch, Block switchBlock) { +private Node getASwitchExpr(SwitchStmt switch, BlockStmt switchBlock) { switch.getStmt() = switchBlock and successors_extended(result, switchBlock) } @@ -191,7 +192,7 @@ private Node getASwitchExpr(SwitchStmt switch, Block switchBlock) { * from `switchBlock` to `sc` is impossible. This considers only non-`default` * switch cases. */ -private predicate impossibleSwitchEdge(Block switchBlock, SwitchCase sc) { +private predicate impossibleSwitchEdge(BlockStmt switchBlock, SwitchCase sc) { not sc instanceof DefaultCase and exists(SwitchStmt switch | switch = sc.getSwitchStmt() and @@ -214,7 +215,7 @@ private predicate impossibleSwitchEdge(Block switchBlock, SwitchCase sc) { * If a switch provably always chooses a non-default case, then the edge to * the default case is impossible. */ -private predicate impossibleDefaultSwitchEdge(Block switchBlock, DefaultCase dc) { +private predicate impossibleDefaultSwitchEdge(BlockStmt switchBlock, DefaultCase dc) { exists(SwitchStmt switch | switch = dc.getSwitchStmt() and switch.getStmt() = switchBlock and @@ -278,20 +279,62 @@ private predicate reachableRecursive(ControlFlowNode n) { reachableRecursive(n.getAPredecessor()) } +/** Holds if `e` is a compile time constant with integer value `val`. */ private predicate compileTimeConstantInt(Expr e, int val) { - val = e.getFullyConverted().getValue().toInt() and - not e instanceof StringLiteral and - not exists(Expr e1 | e1.getConversion() = e) // only values for fully converted expressions + ( + // If we have an integer value then we are done. + if exists(e.getValue().toInt()) + then val = e.getValue().toInt() + else + // Otherwise, if we are a conversion of another expression with an + // integer value, and that value can be converted into our type, + // then we have that value. + exists(Expr x, int valx | + x.getConversion() = e and + compileTimeConstantInt(x, valx) and + val = convertIntToType(valx, e.getType().getUnspecifiedType()) + ) + ) and + // If our unconverted expression is a string literal `"123"`, then we + // do not have integer value `123`. + not e.getUnconverted() instanceof StringLiteral } -library class CompileTimeConstantInt extends Expr { - CompileTimeConstantInt() { compileTimeConstantInt(this, _) } +/** + * Get `val` represented as type `t`, if that is possible without + * overflow or underflows. + */ +bindingset[val, t] +private int convertIntToType(int val, IntegralType t) { + if t instanceof BoolType + then if val = 0 then result = 0 else result = 1 + else + if t.isUnsigned() + then if val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 then result = val else none() + else + if val >= 0 and val.bitShiftRight(t.getSize() * 8 - 1) = 0 + then result = val + else + if (-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0 + then result = val + else none() +} + +/** + * INTERNAL: Do not use. + * An expression that has been found to have an integer value at compile + * time. + */ +class CompileTimeConstantInt extends Expr { + int val; + + CompileTimeConstantInt() { compileTimeConstantInt(this.getFullyConverted(), val) } - int getIntValue() { compileTimeConstantInt(this, result) } + int getIntValue() { result = val } } library class CompileTimeVariableExpr extends Expr { - CompileTimeVariableExpr() { not compileTimeConstantInt(this, _) } + CompileTimeVariableExpr() { not this instanceof CompileTimeConstantInt } } /** A helper class for evaluation of expressions. */ @@ -863,9 +906,9 @@ library class ConditionEvaluator extends ExprEvaluator { ConditionEvaluator() { this = 0 } override predicate interesting(Expr e) { - falsecond_base(e, _) + qlCFGFalseSuccessor(e, _) or - truecond_base(e, _) + qlCFGTrueSuccessor(e, _) } } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll index b3f8cd02828b..f9677a0654b7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll @@ -29,7 +29,7 @@ private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", [" */ private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") } -private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) { +private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) { lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr) or // When an object is implicitly converted to a reference to one of its base @@ -42,6 +42,10 @@ private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) { // such casts. lvalueIn.getConversion() = lvalueOut and lvalueOut.(CStyleCast).isImplicit() +} + +private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) { + lvalueToLvalueStepPure(lvalueIn, lvalueOut) or // C++ only lvalueIn = lvalueOut.(PrefixCrementOperation).getOperand().getFullyConverted() @@ -214,6 +218,69 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode ) } +private predicate lvalueFromVariableAccess(VariableAccess va, Expr lvalue) { + // Base case for non-reference types. + lvalue = va and + not va.getConversion() instanceof ReferenceDereferenceExpr + or + // Base case for reference types where we pretend that they are + // non-reference types. The type of the target of `va` can be `ReferenceType` + // or `FunctionReferenceType`. + lvalue = va.getConversion().(ReferenceDereferenceExpr) + or + // lvalue -> lvalue + exists(Expr prev | + lvalueFromVariableAccess(va, prev) and + lvalueToLvalueStep(prev, lvalue) + ) + or + // pointer -> lvalue + exists(Expr prev | + pointerFromVariableAccess(va, prev) and + pointerToLvalueStep(prev, lvalue) + ) + or + // reference -> lvalue + exists(Expr prev | + referenceFromVariableAccess(va, prev) and + referenceToLvalueStep(prev, lvalue) + ) +} + +private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) { + // pointer -> pointer + exists(Expr prev | + pointerFromVariableAccess(va, prev) and + pointerToPointerStep(prev, pointer) + ) + or + // reference -> pointer + exists(Expr prev | + referenceFromVariableAccess(va, prev) and + referenceToPointerStep(prev, pointer) + ) + or + // lvalue -> pointer + exists(Expr prev | + lvalueFromVariableAccess(va, prev) and + lvalueToPointerStep(prev, pointer) + ) +} + +private predicate referenceFromVariableAccess(VariableAccess va, Expr reference) { + // reference -> reference + exists(Expr prev | + referenceFromVariableAccess(va, prev) and + referenceToReferenceStep(prev, reference) + ) + or + // lvalue -> reference + exists(Expr prev | + lvalueFromVariableAccess(va, prev) and + lvalueToReferenceStep(prev, reference) + ) +} + /** * Holds if `node` is a control-flow node that may modify `inner` (or what it * points to) through `outer`. The two expressions may be `Conversion`s. Plain @@ -236,7 +303,7 @@ predicate valueToUpdate(Expr inner, Expr outer, ControlFlowNode node) { ( inner instanceof VariableAccess and // Don't track non-field assignments - (assignmentTo(outer, _) implies inner instanceof FieldAccess) + not (assignmentTo(outer, _) and outer.(VariableAccess).getTarget() instanceof StackVariable) or inner instanceof ThisExpr or @@ -245,3 +312,27 @@ predicate valueToUpdate(Expr inner, Expr outer, ControlFlowNode node) { // can't do anything useful with those at the moment. ) } + +/** + * Holds if `e` is a fully-converted expression that evaluates to an lvalue + * derived from `va` and is used for reading from or assigning to. This is in + * contrast with a variable access that is used for taking an address (`&x`) + * or simply discarding its value (`x;`). + * + * This analysis does not propagate across assignments or calls, and unlike + * `variableAccessedAsValue` in `semmle.code.cpp.dataflow.EscapesTree` it + * propagates through array accesses but not field accesses. The analysis is + * also not concerned with whether the lvalue `e` is converted to an rvalue -- + * to examine that, use the relevant member predicates on `Expr`. + * + * If `va` has reference type, the analysis concerns the value pointed to by + * the reference rather than the reference itself. The expression `e` may be a + * `Conversion`. + */ +predicate variablePartiallyAccessed(VariableAccess va, Expr e) { + lvalueFromVariableAccess(va, e) and + not lvalueToLvalueStepPure(e, _) and + not lvalueToPointerStep(e, _) and + not lvalueToReferenceStep(e, _) and + not e = any(ExprInVoidContext eivc | e = eivc.getConversion*()) +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll index 154430794d4a..e57af3b8d317 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll @@ -53,26 +53,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam } /** - * Holds if the call context `ctx` reduces the set of viable dispatch - * targets of `ma` in `c`. + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. */ -predicate reducedViableImplInCallContext(Call call, Function f, Call ctx) { none() } +predicate mayBenefitFromCallContext(Call call, Function f) { none() } /** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s for which the context makes a difference. + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. */ -Function prunedViableImplInCallContext(Call call, Call ctx) { none() } - -/** - * Holds if flow returning from `m` to `ma` might return further and if - * this path restricts the set of call sites that can be returned to. - */ -predicate reducedViableImplInReturn(Function f, Call call) { none() } - -/** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s and results for which the return flow from the - * result to `ma` restricts the possible context `ctx`. - */ -Function prunedViableImplInCallContextReverse(Call call, Call ctx) { none() } +Function viableImplInCallContext(Call call, Call ctx) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index 852f54974e24..892250f44bb8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -22,7 +22,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p)) + compatibleTypes(getNodeType(arg), getNodeType(p)) ) } @@ -147,174 +147,140 @@ private module Cached { } } - private module LocalFlowBigStep { - private predicate localFlowEntry(Node n) { - Cand::cand(_, n) and - ( - n instanceof ParameterNode or - n instanceof OutNode or - readStep(_, _, n) or - n instanceof CastNode - ) - } - - private predicate localFlowExit(Node n) { - Cand::cand(_, n) and - ( - n instanceof ArgumentNode - or - n instanceof ReturnNode - or - readStep(n, _, _) - or - n instanceof CastNode - or - n = - any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun)) - .getPreUpdateNode() - ) - } - - pragma[nomagic] - private predicate localFlowStepPlus(Node node1, Node node2) { - localFlowEntry(node1) and - simpleLocalFlowStep(node1, node2) and - node1 != node2 - or - exists(Node mid | - localFlowStepPlus(node1, mid) and - simpleLocalFlowStep(mid, node2) and - not mid instanceof CastNode - ) - } - - pragma[nomagic] - predicate localFlowBigStep(Node node1, Node node2) { - localFlowStepPlus(node1, node2) and - localFlowExit(node2) - } - } - /** * The final flow-through calculation: * - * - Input access paths are abstracted with a `ContentOption` parameter - * that represents the head of the access path. `TContentNone()` means that - * the access path is unrestricted. + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. * - Types are checked using the `compatibleTypes()` relation. */ private module Final { /** * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps, not taking call contexts into account. + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `p` that can flow to `node` - * (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { - parameterValueFlow0(p, node, contentIn) and + predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + read = TReadStepTypesNone() and + compatibleTypes(getNodeType(p), getNodeType(node)) or // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) - ) + compatibleTypes(read.getContentType(), getNodeType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() + read = TReadStepTypesNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn) and - LocalFlowBigStep::localFlowBigStep(mid, node) + parameterValueFlow(p, mid, read) and + simpleLocalFlowStep(mid, node) ) or // read - exists(Node mid, Content f | - parameterValueFlow(p, mid, TContentNone()) and - readStep(mid, f, node) and - contentIn.getContent() = f and + exists(Node mid | + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) + compatibleTypes(getNodeType(p), read.getContainerType()) ) or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ) { // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone()) and - argumentValueFlowsThrough(arg, contentIn, node) + parameterValueFlowArg(p, arg, mustBeNone) and + argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn) and - argumentValueFlowsThrough(arg, TContentNone(), node) + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn + ParameterNode p, ArgumentNode arg, ReadStepTypesOption read ) { - parameterValueFlow(p, arg, contentIn) and + parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn) + parameterValueFlowReturn(param, kind, read) ) } /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn) and + argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + read = TReadStepTypesNone() and + compatibleTypes(getNodeType(arg), getNodeType(out)) or // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) - ) + compatibleTypes(getNodeType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeType(out)) ) } + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and a single read step, not taking call + * contexts into account, thus representing a getter-step. + */ + predicate getterStep(ArgumentNode arg, Content c, Node out) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + /** * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps. + * callable using only value-preserving steps and possibly a single read + * step. * - * `contentIn` describes the content of `p` that can flow to the return - * node (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn + ParameterNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn) and + parameterValueFlow(p, ret, read) and kind = ret.getKind() ) } @@ -323,37 +289,107 @@ private module Cached { import Final } + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableCallable(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and + c = viableCallable(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + /** * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `f`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store(Node node1, Content f, Node node2) { - storeStep(node1, f, node2) and readStep(_, f, _) + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + storeStep(node1, c, node2) and + readStep(_, c, _) and + contentType = getNodeType(node1) and + containerType = getNodeType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and n2 = node2.(PostUpdateNode).getPreUpdateNode() | - argumentValueFlowsThrough(n2, TContentSome(f), n1) + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, f, n1) + readStep(n2, c, n1) and + contentType = getNodeType(n1) and + containerType = getNodeType(n2) ) } - import FlowThrough + /** + * Holds if data can flow from `node1` to `node2` via a direct assignment to + * `f`. + * + * This includes reverse steps through reads when the result of the read has + * been stored into, in order to handle cases like `x.f1.f2 = y`. + */ + cached + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) + } /** * Holds if the call context `call` either improves virtual dispatch in @@ -397,10 +433,13 @@ private module Cached { TBooleanNone() or TBooleanSome(boolean b) { b = true or b = false } + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } + cached newtype TAccessPathFront = TFrontNil(DataFlowType t) or - TFrontHead(Content f) + TFrontHead(TypedContent tc) cached newtype TAccessPathFrontOption = @@ -415,25 +454,38 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNodeExt + this instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + readStep(_, _, this) } } -newtype TContentOption = - TContentNone() or - TContentSome(Content f) +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + readStep(n1, c, n2) and + container = getNodeType(n1) and + content = getNodeType(n2) +} -private class ContentOption extends TContentOption { - Content getContent() { this = TContentSome(result) } +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) + } - predicate hasContent() { exists(this.getContent()) } +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } - string toString() { - result = this.getContent().toString() - or - not this.hasContent() and - result = "" - } + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } /** @@ -460,13 +512,19 @@ abstract class CallContext extends TCallContext { abstract predicate relevantFor(DataFlowCallable callable); } -class CallContextAny extends CallContext, TAnyCallContext { +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { override string toString() { result = "CcAny" } override predicate relevantFor(DataFlowCallable callable) { any() } } -abstract class CallContextCall extends CallContext { } +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} class CallContextSpecificCall extends CallContextCall, TSpecificCall { override string toString() { @@ -477,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { recordDataFlowCallSite(getCall(), callable) } + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + DataFlowCall getCall() { this = TSpecificCall(result) } } @@ -486,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override predicate relevantFor(DataFlowCallable callable) { exists(ParameterNode p | p.getEnclosingCallable() = callable) } + + override predicate matchesCall(DataFlowCall call) { any() } } -class CallContextReturn extends CallContext, TReturn { +class CallContextReturn extends CallContextNoCall, TReturn { override string toString() { exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") } @@ -678,9 +740,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -pragma[noinline] -DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } - predicate read = readStep/3; /** An optional Boolean value. */ @@ -692,6 +751,23 @@ class BooleanOption extends TBooleanOption { } } +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } +} + /** * The front of an access path. This is either a head or a nil. */ @@ -702,25 +778,36 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); - predicate headUsesContent(Content f) { this = TFrontHead(f) } + predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } + + predicate isClearedAt(Node n) { + exists(TypedContent tc | + this.headUsesContent(tc) and + clearsContent(n, tc.getContent()) + ) + } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - } + private DataFlowType t; + + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } - override DataFlowType getType() { this = TFrontNil(result) } + override DataFlowType getType() { result = t } override boolean toBoolNonEmpty() { result = false } } class AccessPathFrontHead extends AccessPathFront, TFrontHead { - override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + private TypedContent tc; - override DataFlowType getType() { - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } override boolean toBoolNonEmpty() { result = true } } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll index 0dc3b8eff450..4e1cd281488f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll @@ -37,21 +37,12 @@ module Consistency { ) } - query predicate uniqueTypeBound(Node n, string msg) { + query predicate uniqueType(Node n, string msg) { exists(int c | n instanceof RelevantNode and - c = count(n.getTypeBound()) and + c = count(getNodeType(n)) and c != 1 and - msg = "Node should have one type bound but has " + c + "." - ) - } - - query predicate uniqueTypeRepr(Node n, string msg) { - exists(int c | - n instanceof RelevantNode and - c = count(getErasedRepr(n.getTypeBound())) and - c != 1 and - msg = "Node should have one type representation but has " + c + "." + msg = "Node should have one type but has " + c + "." ) } @@ -104,7 +95,7 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } - private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { t = typeRepr() and @@ -132,8 +123,18 @@ module Consistency { n.getEnclosingCallable() != call.getEnclosingCallable() } + // This predicate helps the compiler forget that in some languages + // it is impossible for a result of `getPreUpdateNode` to be an + // instance of `PostUpdateNode`. + private Node getPre(PostUpdateNode n) { + result = n.getPreUpdateNode() + or + none() + } + query predicate postIsNotPre(PostUpdateNode n, string msg) { - n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node." + getPre(n) = n and + msg = "PostUpdateNode should not equal its pre-update node." } query predicate postHasUniquePre(PostUpdateNode n, string msg) { @@ -161,15 +162,14 @@ module Consistency { msg = "Origin of readStep is missing a PostUpdateNode." } - query predicate storeIsPostUpdate(Node n, string msg) { - storeStep(_, _, n) and - not n instanceof PostUpdateNode and - msg = "Store targets should be PostUpdateNodes." - } - query predicate argHasPostUpdate(ArgumentNode n, string msg) { not hasPost(n) and not isImmutableOrUnobservable(n) and msg = "ArgumentNode is missing PostUpdateNode." } + + query predicate postWithInFlow(PostUpdateNode n, string msg) { + simpleLocalFlowStep(_, n) and + msg = "PostUpdateNode should not be the target of local flow." + } } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index 43359fb329be..4562eb3fd194 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -1,6 +1,7 @@ private import cpp private import DataFlowUtil private import DataFlowDispatch +private import FlowVar /** Gets the instance argument of a non-static call. */ private Node getInstanceArgument(Call call) { @@ -106,7 +107,7 @@ private class ExprOutNode extends OutNode, ExprNode { override DataFlowCall getCall() { result = this.getExpr() } } -private class RefOutNode extends OutNode, DefinitionByReferenceNode { +private class RefOutNode extends OutNode, DefinitionByReferenceOrIteratorNode { /** Gets the underlying call. */ override DataFlowCall getCall() { result = this.getArgument().getParent() } } @@ -120,7 +121,7 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { kind = TNormalReturnKind() or exists(int i | - result.asDefiningArgument() = call.getArgument(i) and + result.(DefinitionByReferenceOrIteratorNode).getArgument() = call.getArgument(i) and kind = TRefReturnKind(i) ) } @@ -148,12 +149,6 @@ class Content extends TContent { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } - - /** Gets the type of the object containing this content. */ - abstract Type getContainerType(); - - /** Gets the type of this content. */ - abstract Type getType(); } private class FieldContent extends Content, TFieldContent { @@ -168,26 +163,14 @@ private class FieldContent extends Content, TFieldContent { override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { f.getLocation().hasLocationInfo(path, sl, sc, el, ec) } - - override Type getContainerType() { result = f.getDeclaringType() } - - override Type getType() { result = f.getType() } } private class CollectionContent extends Content, TCollectionContent { override string toString() { result = "collection" } - - override Type getContainerType() { none() } - - override Type getType() { none() } } private class ArrayContent extends Content, TArrayContent { override string toString() { result = "array" } - - override Type getContainerType() { none() } - - override Type getType() { none() } } /** @@ -235,16 +218,19 @@ predicate readStep(Node node1, Content f, Node node2) { } /** - * Gets a representative (boxed) type for `t` for the purpose of pruning - * possible flow. A single type is used for all numeric types to account for - * numeric conversions, and otherwise the erasure is used. + * Holds if values stored inside content `c` are cleared at node `n`. */ -Type getErasedRepr(Type t) { - suppressUnusedType(t) and +predicate clearsContent(Node n, Content c) { + none() // stub implementation +} + +/** Gets the type of `n` used for type pruning. */ +Type getNodeType(Node n) { + suppressUnusedNode(n) and result instanceof VoidType // stub implementation } -/** Gets a string representation of a type returned by `getErasedRepr`. */ +/** Gets a string representation of a type returned by `getNodeType`. */ string ppReprType(Type t) { none() } // stub implementation /** @@ -256,7 +242,7 @@ predicate compatibleTypes(Type t1, Type t2) { any() // stub implementation } -private predicate suppressUnusedType(Type t) { any() } +private predicate suppressUnusedNode(Node n) { any() } ////////////////////////////////////////////////////////////////////////////// // Java QL library compatibility wrappers @@ -314,3 +300,6 @@ predicate isImmutableOrUnobservable(Node n) { // The above list of cases isn't exhaustive, but it narrows down the // consistency alerts enough that most of them are interesting. } + +/** Holds if `n` should be hidden from path explanations. */ +predicate nodeIsHidden(Node n) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 47da6ca6e540..09409eb30f2a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -6,6 +6,7 @@ private import cpp private import semmle.code.cpp.dataflow.internal.FlowVar private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.controlflow.Guards +private import semmle.code.cpp.dataflow.internal.AddressFlow cached private newtype TNode = @@ -50,13 +51,25 @@ class Node extends TNode { /** Gets the type of this node. */ Type getType() { none() } // overridden in subclasses - /** Gets the expression corresponding to this node, if any. */ + /** + * Gets the expression corresponding to this node, if any. This predicate + * only has a result on nodes that represent the value of evaluating the + * expression. For data flowing _out of_ an expression, like when an + * argument is passed by reference, use `asDefiningArgument` instead of + * `asExpr`. + */ Expr asExpr() { result = this.(ExprNode).getExpr() } /** Gets the parameter corresponding to this node, if any. */ Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() } - /** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */ + /** + * Gets the argument that defines this `DefinitionByReferenceNode`, if any. + * This predicate should be used instead of `asExpr` when referring to the + * value of a reference argument _after_ the call has returned. For example, + * in `f(&x)`, this predicate will have `&x` as its result for the `Node` + * that represents the new value of `x`. + */ Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() } /** @@ -170,28 +183,29 @@ class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode { } /** - * A node that represents the value of a variable after a function call that - * may have changed the variable because it's passed by reference. + * INTERNAL: do not use. * - * A typical example would be a call `f(&x)`. Firstly, there will be flow into - * `x` from previous definitions of `x`. Secondly, there will be a - * `DefinitionByReferenceNode` to represent the value of `x` after the call has - * returned. This node will have its `getArgument()` equal to `&x`. + * A node that represents the value of a variable after a function call that + * may have changed the variable because it's passed by reference or because an + * iterator for it was passed by value or by reference. */ -class DefinitionByReferenceNode extends PartialDefinitionNode { +class DefinitionByReferenceOrIteratorNode extends PartialDefinitionNode { Expr inner; Expr argument; - DefinitionByReferenceNode() { - this.getPartialDefinition().(DefinitionByReference).definesExpressions(inner, argument) + DefinitionByReferenceOrIteratorNode() { + this.getPartialDefinition().definesExpressions(inner, argument) and + ( + this.getPartialDefinition() instanceof DefinitionByReference + or + this.getPartialDefinition() instanceof DefinitionByIterator + ) } override Function getFunction() { result = inner.getEnclosingFunction() } override Type getType() { result = inner.getType() } - override string toString() { result = "ref arg " + argument.toString() } - override Location getLocation() { result = argument.getLocation() } override ExprNode getPreUpdateNode() { result.getExpr() = argument } @@ -208,6 +222,21 @@ class DefinitionByReferenceNode extends PartialDefinitionNode { } } +/** + * A node that represents the value of a variable after a function call that + * may have changed the variable because it's passed by reference. + * + * A typical example would be a call `f(&x)`. Firstly, there will be flow into + * `x` from previous definitions of `x`. Secondly, there will be a + * `DefinitionByReferenceNode` to represent the value of `x` after the call has + * returned. This node will have its `getArgument()` equal to `&x`. + */ +class DefinitionByReferenceNode extends DefinitionByReferenceOrIteratorNode { + override VariablePartialDefinition pd; + + override string toString() { result = "ref arg " + argument.toString() } +} + /** * The value of an uninitialized local variable, viewed as a node in a data * flow graph. @@ -271,13 +300,11 @@ abstract class PostUpdateNode extends Node { override Location getLocation() { result = getPreUpdateNode().getLocation() } } -private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode { +abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode { PartialDefinition pd; PartialDefinitionNode() { this = TPartialDefinitionNode(pd) } - override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } - override Location getLocation() { result = pd.getActualLocation() } PartialDefinition getPartialDefinition() { result = pd } @@ -285,6 +312,24 @@ private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNo override string toString() { result = getPreUpdateNode().toString() + " [post update]" } } +private class VariablePartialDefinitionNode extends PartialDefinitionNode { + override VariablePartialDefinition pd; + + override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } +} + +/** + * INTERNAL: do not use. + * + * A synthetic data flow node used for flow into a collection when an iterator + * write occurs in a callee. + */ +class IteratorPartialDefinitionNode extends PartialDefinitionNode { + override IteratorPartialDefinition pd; + + override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } +} + /** * A post-update node on the `e->f` in `f(&e->f)` (and other forms). */ @@ -383,7 +428,9 @@ class PreConstructorInitThis extends Node, TPreConstructorInitThis { } /** - * Gets the `Node` corresponding to `e`. + * Gets the `Node` corresponding to the value of evaluating `e`. For data + * flowing _out of_ an expression, like when an argument is passed by + * reference, use `definitionByReferenceNodeFromArgument` instead. */ ExprNode exprNode(Expr e) { result.getExpr() = e } @@ -484,6 +531,17 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Expr -> Expr exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr()) or + // Assignment -> LValue post-update node + // + // This is used for assignments whose left-hand side is not a variable + // assignment or a storeStep but is still modeled by other means. It could be + // a call to `operator*` or `operator[]` where taint should flow to the + // post-update node of the qualifier. + exists(AssignExpr assign | + nodeFrom.asExpr() = assign and + nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue() + ) + or // Node -> FlowVar -> VariableAccess exists(FlowVar var | ( @@ -514,6 +572,19 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr() ) + or + // Reverse flow: data that flows from the post-update node of a reference + // returned by a function call, back into the qualifier of that function. + // This allows data to flow 'in' through references returned by a modeled + // function such as `operator[]`. + exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel | + call.getTarget() = f and + inModel.isReturnValueDeref() and + outModel.isQualifierObject() and + f.hasDataFlow(inModel, outModel) and + nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and + nodeTo.asDefiningArgument() = call.getQualifier() + ) } /** @@ -572,6 +643,15 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) { or toExpr.(AddressOfExpr).getOperand() = fromExpr or + // This rule enables flow from an array to its elements. Example: `a` to + // `a[i]` or `*a`, where `a` is an array type. It does not enable flow from a + // pointer to its indirection as in `p[i]` where `p` is a pointer type. + exists(Expr toConverted | + variablePartiallyAccessed(fromExpr, toConverted) and + toExpr = toConverted.getUnconverted() and + not toExpr = fromExpr + ) + or toExpr.(BuiltInOperationBuiltInAddressOf).getOperand() = fromExpr or // The following case is needed to track the qualifier object for flow @@ -591,14 +671,25 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) { // `ClassAggregateLiteral` (`{ capture1, ..., captureN }`). toExpr.(LambdaExpression).getInitializer() = fromExpr or + // Data flow through a function model. toExpr = any(Call call | - exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn | - call.getTarget() = f and + exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel | f.hasDataFlow(inModel, outModel) and - outModel.isReturnValue() and - inModel.isParameter(iIn) and - fromExpr = call.getArgument(iIn) + ( + exists(int iIn | + inModel.isParameter(iIn) and + fromExpr = call.getArgument(iIn) + ) + or + inModel.isQualifierObject() and + fromExpr = call.getQualifier() + or + inModel.isQualifierAddress() and + fromExpr = call.getQualifier() + ) and + call.getTarget() = f and + outModel.isReturnValue() ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 72033b0f72df..50395dbaafc0 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -6,6 +6,7 @@ import cpp private import semmle.code.cpp.controlflow.SSA private import semmle.code.cpp.dataflow.internal.SubBasicBlocks private import semmle.code.cpp.dataflow.internal.AddressFlow +private import semmle.code.cpp.models.implementations.Iterator /** * A conceptual variable that is assigned only once, like an SSA variable. This @@ -108,27 +109,26 @@ class FlowVar extends TFlowVar { * ``` */ private module PartialDefinitions { - class PartialDefinition extends Expr { - Expr innerDefinedExpr; + abstract class PartialDefinition extends Expr { ControlFlowNode node; - PartialDefinition() { - exists(Expr convertedInner | - valueToUpdate(convertedInner, this.getFullyConverted(), node) and - innerDefinedExpr = convertedInner.getUnconverted() and - not this instanceof Conversion - ) - } - - predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() } + abstract deprecated predicate partiallyDefines(Variable v); - predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e } + abstract deprecated predicate partiallyDefinesThis(ThisExpr e); /** * Gets the subBasicBlock where this `PartialDefinition` is defined. */ ControlFlowNode getSubBasicBlockStart() { result = node } + /** + * Holds if this `PartialDefinition` defines variable `v` at control-flow + * node `cfn`. + */ + // does this work with a dispred? + pragma[noinline] + abstract predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn); + /** * Holds if this partial definition may modify `inner` (or what it points * to) through `outer`. These expressions will never be `Conversion`s. @@ -137,10 +137,7 @@ private module PartialDefinitions { * - `inner` = `... .x`, `outer` = `&...` * - `inner` = `a`, `outer` = `*` */ - predicate definesExpressions(Expr inner, Expr outer) { - inner = innerDefinedExpr and - outer = this - } + abstract predicate definesExpressions(Expr inner, Expr outer); /** * Gets the location of this element, adjusted to avoid unknown locations @@ -156,10 +153,107 @@ private module PartialDefinitions { } } + class IteratorPartialDefinition extends PartialDefinition { + Variable collection; + Expr innerDefinedExpr; + + IteratorPartialDefinition() { + exists(Expr convertedInner | + not this instanceof Conversion and + valueToUpdate(convertedInner, this.getFullyConverted(), node) and + innerDefinedExpr = convertedInner.getUnconverted() and + ( + innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection) + or + innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and + collection instanceof IteratorParameter + ) and + innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator + ) + or + // iterators passed by value without a copy constructor + exists(Call call | + call = node and + call.getAnArgument() = innerDefinedExpr and + innerDefinedExpr = this and + this = getAnIteratorAccess(collection) and + not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator + ) + or + // iterators passed by value with a copy constructor + exists(Call call, ConstructorCall copy | + copy.getTarget() instanceof CopyConstructor and + call = node and + call.getAnArgument() = copy and + copy.getArgument(0) = getAnIteratorAccess(collection) and + innerDefinedExpr = this and + this = copy and + not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator + ) + } + + deprecated override predicate partiallyDefines(Variable v) { v = collection } + + deprecated override predicate partiallyDefinesThis(ThisExpr e) { none() } + + override predicate definesExpressions(Expr inner, Expr outer) { + inner = innerDefinedExpr and + outer = this + } + + override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { + v = collection and + cfn = node + } + } + + class VariablePartialDefinition extends PartialDefinition { + Expr innerDefinedExpr; + + VariablePartialDefinition() { + not this instanceof Conversion and + exists(Expr convertedInner | + valueToUpdate(convertedInner, this.getFullyConverted(), node) and + innerDefinedExpr = convertedInner.getUnconverted() + ) + } + + deprecated override predicate partiallyDefines(Variable v) { + innerDefinedExpr = v.getAnAccess() + } + + deprecated override predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e } + + /** + * Holds if this partial definition may modify `inner` (or what it points + * to) through `outer`. These expressions will never be `Conversion`s. + * + * For example, in `f(& (*a).x)`, there are two results: + * - `inner` = `... .x`, `outer` = `&...` + * - `inner` = `a`, `outer` = `*` + */ + override predicate definesExpressions(Expr inner, Expr outer) { + inner = innerDefinedExpr and + outer = this + } + + override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { + innerDefinedExpr = v.getAnAccess() and + cfn = node + } + } + + /** + * A partial definition that's a definition via an output iterator. + */ + class DefinitionByIterator extends IteratorPartialDefinition { + DefinitionByIterator() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) } + } + /** * A partial definition that's a definition by reference. */ - class DefinitionByReference extends PartialDefinition { + class DefinitionByReference extends VariablePartialDefinition { DefinitionByReference() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) } } } @@ -188,7 +282,7 @@ module FlowVar_internal { predicate fullySupportedSsaVariable(Variable v) { v = any(SsaDefinition def).getAVariable() and // A partially-defined variable is handled using the partial definitions logic. - not any(PartialDefinition p).partiallyDefines(v) and + not any(PartialDefinition p).partiallyDefinesVariableAt(v, _) and // SSA variables do not exist before their first assignment, but one // feature of this data flow library is to track where uninitialized data // ends up. @@ -201,7 +295,8 @@ module FlowVar_internal { // The SSA library has a theoretically accurate treatment of reference types, // treating them as immutable, but for data flow it gives better results in // practice to make the variable synonymous with its contents. - not v.getUnspecifiedType() instanceof ReferenceType + not v.getUnspecifiedType() instanceof ReferenceType and + not v instanceof IteratorParameter } /** @@ -230,9 +325,9 @@ module FlowVar_internal { ( initializer(v, sbb.getANode()) or - assignmentLikeOperation(sbb, v, _, _) + assignmentLikeOperation(sbb, v, _) or - sbb = any(PartialDefinition p | p.partiallyDefines(v)).getSubBasicBlockStart() + exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, sbb)) or blockVarDefinedByVariable(sbb, v) ) @@ -349,7 +444,7 @@ module FlowVar_internal { } override predicate definedByExpr(Expr e, ControlFlowNode node) { - assignmentLikeOperation(node, v, _, e) and + assignmentLikeOperation(node, v, e) and node = sbb or // We pick the defining `ControlFlowNode` of an `Initializer` to be its @@ -363,8 +458,7 @@ module FlowVar_internal { override predicate definedPartiallyAt(Expr e) { exists(PartialDefinition p | - p.partiallyDefines(v) and - sbb = p.getSubBasicBlockStart() and + p.partiallyDefinesVariableAt(v, sbb) and p.definesExpressions(_, e) ) } @@ -427,7 +521,7 @@ module FlowVar_internal { /** * Gets a variable that is assigned in this loop and read outside the loop. */ - private Variable getARelevantVariable() { + Variable getARelevantVariable() { result = this.getAVariableAssignedInLoop() and exists(VariableAccess va | va.getTarget() = result and @@ -440,7 +534,7 @@ module FlowVar_internal { pragma[noinline] private Variable getAVariableAssignedInLoop() { exists(BasicBlock bbAssign | - assignmentLikeOperation(bbAssign.getANode(), result, _, _) and + assignmentLikeOperation(bbAssign.getANode(), result, _) and this.bbInLoop(bbAssign) ) } @@ -472,10 +566,16 @@ module FlowVar_internal { reachesWithoutAssignment(bb.getAPredecessor(), v) and this.bbInLoop(bb) ) and - not assignmentLikeOperation(bb.getANode(), v, _, _) + not assignsToVar(bb, v) } } + pragma[noinline] + private predicate assignsToVar(BasicBlock bb, Variable v) { + assignmentLikeOperation(bb.getANode(), v, _) and + exists(AlwaysTrueUponEntryLoop loop | v = loop.getARelevantVariable()) + } + /** * Holds if `loop` always assigns to `v` before leaving through an edge * from `bbInside` in its condition to `bbOutside` outside the loop. Also, @@ -509,7 +609,7 @@ module FlowVar_internal { result = mid.getASuccessor() and variableLiveInSBB(result, v) and forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and - not assignmentLikeOperation(result, v, _, _) + not assignmentLikeOperation(result, v, _) ) } @@ -545,13 +645,15 @@ module FlowVar_internal { refType = p.getUnderlyingType() and not refType.getBaseType().isConst() ) + or + p instanceof IteratorParameter } /** * Holds if liveness of `v` should stop propagating backwards from `sbb`. */ private predicate variableNotLiveBefore(SubBasicBlock sbb, Variable v) { - assignmentLikeOperation(sbb, v, _, _) + assignmentLikeOperation(sbb, v, _) or // Liveness of `v` is killed when going backwards from a block that declares it exists(DeclStmt ds | ds.getADeclaration().(LocalVariable) = v and sbb.contains(ds)) @@ -671,21 +773,17 @@ module FlowVar_internal { * `node instanceof Initializer` is covered by `initializer` instead of this * predicate. */ - predicate assignmentLikeOperation( - ControlFlowNode node, Variable v, VariableAccess va, Expr assignedExpr - ) { + predicate assignmentLikeOperation(ControlFlowNode node, Variable v, Expr assignedExpr) { // Together, the two following cases cover `Assignment` node = any(AssignExpr ae | - va = ae.getLValue() and - v = va.getTarget() and + v.getAnAccess() = ae.getLValue() and assignedExpr = ae.getRValue() ) or node = any(AssignOperation ao | - va = ao.getLValue() and - v = va.getTarget() and + v.getAnAccess() = ao.getLValue() and // Here and in the `PrefixCrementOperation` case, we say that the assigned // expression is the operation itself. For example, we say that `x += 1` // assigns `x += 1` to `x`. The justification is that after this operation, @@ -697,12 +795,24 @@ module FlowVar_internal { // `PrefixCrementOperation` is itself a source node = any(CrementOperation op | - va = op.getOperand() and - v = va.getTarget() and + v.getAnAccess() = op.getOperand() and assignedExpr = op ) } + Expr getAnIteratorAccess(Variable collection) { + exists(Call c, SsaDefinition def, Variable iterator | + c.getQualifier() = collection.getAnAccess() and + c.getTarget() instanceof BeginOrEndFunction and + def.getAnUltimateDefiningValue(iterator) = c and + result = def.getAUse(iterator) + ) + } + + class IteratorParameter extends Parameter { + IteratorParameter() { this.getUnspecifiedType() instanceof Iterator } + } + /** * Holds if `v` is initialized to have value `assignedExpr`. */ @@ -734,9 +844,9 @@ module FlowVar_internal { class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode { DataFlowSubBasicBlockCutNode() { exists(Variable v | not fullySupportedSsaVariable(v) | - assignmentLikeOperation(this, v, _, _) + assignmentLikeOperation(this, v, _) or - this = any(PartialDefinition p | p.partiallyDefines(v)).getSubBasicBlockStart() + exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, this)) // It is not necessary to cut the basic blocks at `Initializer` nodes // because the affected variable can have no _other_ value before its // initializer. It is not necessary to cut basic blocks at procedure diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index 1812e3ffe7f9..1ef340c4f213 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -10,6 +10,7 @@ private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.models.interfaces.Taint +private import semmle.code.cpp.models.interfaces.Iterator private module DataFlow { import semmle.code.cpp.dataflow.internal.DataFlowUtil @@ -33,10 +34,10 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { } /** - * Holds if `node` should be a barrier in all global taint flow configurations + * Holds if `node` should be a sanitizer in all global taint flow configurations * but not in local taint. */ -predicate defaultTaintBarrier(DataFlow::Node node) { none() } +predicate defaultTaintSanitizer(DataFlow::Node node) { none() } /** * Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding @@ -65,6 +66,15 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT // tracking. The flow from expression `x` into `x++` etc. is handled in the // case above. exprTo = DataFlow::getAnAccessToAssignedVariable(exprFrom.(PostfixCrementOperation)) + or + // In `for (char c : s) { ... c ... }`, this rule propagates taint from `s` + // to `c`. + exists(RangeBasedForStmt rbf | + exprFrom = rbf.getRange() and + // It's guaranteed up to at least C++20 that the range-based for loop + // desugars to a variable with an initializer. + exprTo = rbf.getVariable().getInitializer().getExpr() + ) ) or // Taint can flow through modeled functions @@ -73,6 +83,26 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument()) or exprToPartialDefinitionStep(nodeFrom.asExpr(), nodeTo.asPartialDefinition()) + or + // Reverse taint: taint that flows from the post-update node of a reference + // returned by a function call, back into the qualifier of that function. + // This allows taint to flow 'in' through references returned by a modeled + // function such as `operator[]`. + exists(TaintFunction f, Call call, FunctionInput inModel, FunctionOutput outModel | + call.getTarget() = f and + inModel.isReturnValueDeref() and + nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = call and + f.hasTaintFlow(inModel, outModel) and + ( + outModel.isQualifierObject() and + nodeTo.asDefiningArgument() = call.getQualifier() + or + exists(int argOutIndex | + outModel.isParameterDeref(argOutIndex) and + nodeTo.asDefiningArgument() = call.getArgument(argOutIndex) + ) + ) + ) } /** @@ -226,4 +256,12 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) { exprIn = call.getArgument(argInIndex) ) ) + or + exists(Assignment a | + iteratorDereference(exprOut) and + a.getLValue() = exprOut and + a.getRValue() = exprIn + ) } + +private predicate iteratorDereference(Call c) { c.getTarget() instanceof IteratorReferenceFunction } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll index fe467be6fe4c..681fb41fa678 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling accesses including variable accesses, enum + * constant accesses and function accesses. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Variable import semmle.code.cpp.Enum @@ -36,7 +41,7 @@ class Access extends Expr, NameQualifiableElement, @access { * ``` */ class EnumConstantAccess extends Access, @varaccess { - override string getCanonicalQLClass() { result = "EnumConstantAccess" } + override string getAPrimaryQlClass() { result = "EnumConstantAccess" } EnumConstantAccess() { exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c))) @@ -61,7 +66,7 @@ class EnumConstantAccess extends Access, @varaccess { * ``` */ class VariableAccess extends Access, @varaccess { - override string getCanonicalQLClass() { result = "VariableAccess" } + override string getAPrimaryQlClass() { result = "VariableAccess" } VariableAccess() { not exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c))) @@ -166,7 +171,7 @@ class VariableAccess extends Access, @varaccess { * ``` */ class FieldAccess extends VariableAccess { - override string getCanonicalQLClass() { result = "FieldAccess" } + override string getAPrimaryQlClass() { result = "FieldAccess" } FieldAccess() { exists(Field f | varbind(underlyingElement(this), unresolveElement(f))) } @@ -194,7 +199,7 @@ class FieldAccess extends VariableAccess { * ``` */ class PointerFieldAccess extends FieldAccess { - override string getCanonicalQLClass() { result = "PointerFieldAccess" } + override string getAPrimaryQlClass() { result = "PointerFieldAccess" } PointerFieldAccess() { exists(PointerType t | @@ -211,7 +216,7 @@ class PointerFieldAccess extends FieldAccess { * distinguish whether or not the type of `obj` is a reference type. */ class DotFieldAccess extends FieldAccess { - override string getCanonicalQLClass() { result = "DotFieldAccess" } + override string getAPrimaryQlClass() { result = "DotFieldAccess" } DotFieldAccess() { exists(Class c | c = getQualifier().getFullyConverted().getUnspecifiedType()) } } @@ -232,7 +237,7 @@ class DotFieldAccess extends FieldAccess { * ``` */ class ReferenceFieldAccess extends DotFieldAccess { - override string getCanonicalQLClass() { result = "ReferenceFieldAccess" } + override string getAPrimaryQlClass() { result = "ReferenceFieldAccess" } ReferenceFieldAccess() { exprHasReferenceConversion(this.getQualifier()) } } @@ -253,7 +258,7 @@ class ReferenceFieldAccess extends DotFieldAccess { * ``` */ class ValueFieldAccess extends DotFieldAccess { - override string getCanonicalQLClass() { result = "ValueFieldAccess" } + override string getAPrimaryQlClass() { result = "ValueFieldAccess" } ValueFieldAccess() { not exprHasReferenceConversion(this.getQualifier()) } } @@ -307,7 +312,7 @@ private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.get * `ImplicitThisFieldAccess`. */ class ImplicitThisFieldAccess extends FieldAccess { - override string getCanonicalQLClass() { result = "ImplicitThisFieldAccess" } + override string getAPrimaryQlClass() { result = "ImplicitThisFieldAccess" } ImplicitThisFieldAccess() { not exists(this.getQualifier()) } } @@ -332,7 +337,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess { override predicate isConstant() { any() } - override string getCanonicalQLClass() { result = "PointerToFieldLiteral" } + override string getAPrimaryQlClass() { result = "PointerToFieldLiteral" } } /** @@ -349,7 +354,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess { class FunctionAccess extends Access, @routineexpr { FunctionAccess() { not iscall(underlyingElement(this), _) } - override string getCanonicalQLClass() { result = "FunctionAccess" } + override string getAPrimaryQlClass() { result = "FunctionAccess" } /** Gets the accessed function. */ override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) } @@ -399,7 +404,7 @@ class ParamAccessForType extends Expr, @param_ref { * ``` */ class TypeName extends Expr, @type_operand { - override string getCanonicalQLClass() { result = "TypeName" } + override string getAPrimaryQlClass() { result = "TypeName" } override string toString() { result = this.getType().getName() } } @@ -418,7 +423,7 @@ class TypeName extends Expr, @type_operand { * `OverloadedArrayExpr`. */ class ArrayExpr extends Expr, @subscriptexpr { - override string getCanonicalQLClass() { result = "ArrayExpr" } + override string getAPrimaryQlClass() { result = "ArrayExpr" } /** * Gets the array or pointer expression being subscripted. diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll index 278db89b41ef..b94c9cee724b 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling arithmetic operations such as `+`, `-`, `*` + * and `++`. + */ + import semmle.code.cpp.exprs.Expr /** @@ -14,7 +19,7 @@ class UnaryArithmeticOperation extends UnaryOperation, @un_arith_op_expr { } class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "UnaryMinusExpr" } + override string getAPrimaryQlClass() { result = "UnaryMinusExpr" } override int getPrecedence() { result = 16 } } @@ -28,7 +33,7 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr { class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "UnaryPlusExpr" } + override string getAPrimaryQlClass() { result = "UnaryPlusExpr" } override int getPrecedence() { result = 16 } } @@ -45,7 +50,7 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr { class ConjugationExpr extends UnaryArithmeticOperation, @conjugation { override string getOperator() { result = "~" } - override string getCanonicalQLClass() { result = "ConjugationExpr" } + override string getAPrimaryQlClass() { result = "ConjugationExpr" } } /** @@ -107,7 +112,7 @@ class PostfixCrementOperation extends CrementOperation, @postfix_crement_expr { class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preincrexpr { override string getOperator() { result = "++" } - override string getCanonicalQLClass() { result = "PrefixIncrExpr" } + override string getAPrimaryQlClass() { result = "PrefixIncrExpr" } override int getPrecedence() { result = 16 } } @@ -123,7 +128,7 @@ class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preinc class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predecrexpr { override string getOperator() { result = "--" } - override string getCanonicalQLClass() { result = "PrefixDecrExpr" } + override string getAPrimaryQlClass() { result = "PrefixDecrExpr" } override int getPrecedence() { result = 16 } } @@ -139,7 +144,7 @@ class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predec class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @postincrexpr { override string getOperator() { result = "++" } - override string getCanonicalQLClass() { result = "PostfixIncrExpr" } + override string getAPrimaryQlClass() { result = "PostfixIncrExpr" } override int getPrecedence() { result = 17 } @@ -157,7 +162,7 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @postdecrexpr { override string getOperator() { result = "--" } - override string getCanonicalQLClass() { result = "PostfixDecrExpr" } + override string getAPrimaryQlClass() { result = "PostfixDecrExpr" } override int getPrecedence() { result = 17 } @@ -175,7 +180,7 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr { override string getOperator() { result = "__real" } - override string getCanonicalQLClass() { result = "RealPartExpr" } + override string getAPrimaryQlClass() { result = "RealPartExpr" } } /** @@ -189,7 +194,7 @@ class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr { class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr { override string getOperator() { result = "__imag" } - override string getCanonicalQLClass() { result = "ImaginaryPartExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryPartExpr" } } /** @@ -208,7 +213,7 @@ class BinaryArithmeticOperation extends BinaryOperation, @bin_arith_op_expr { } class AddExpr extends BinaryArithmeticOperation, @addexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "AddExpr" } + override string getAPrimaryQlClass() { result = "AddExpr" } override int getPrecedence() { result = 13 } } @@ -222,7 +227,7 @@ class AddExpr extends BinaryArithmeticOperation, @addexpr { class SubExpr extends BinaryArithmeticOperation, @subexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "SubExpr" } + override string getAPrimaryQlClass() { result = "SubExpr" } override int getPrecedence() { result = 13 } } @@ -236,7 +241,7 @@ class SubExpr extends BinaryArithmeticOperation, @subexpr { class MulExpr extends BinaryArithmeticOperation, @mulexpr { override string getOperator() { result = "*" } - override string getCanonicalQLClass() { result = "MulExpr" } + override string getAPrimaryQlClass() { result = "MulExpr" } override int getPrecedence() { result = 14 } } @@ -250,7 +255,7 @@ class MulExpr extends BinaryArithmeticOperation, @mulexpr { class DivExpr extends BinaryArithmeticOperation, @divexpr { override string getOperator() { result = "/" } - override string getCanonicalQLClass() { result = "DivExpr" } + override string getAPrimaryQlClass() { result = "DivExpr" } override int getPrecedence() { result = 14 } } @@ -264,7 +269,7 @@ class DivExpr extends BinaryArithmeticOperation, @divexpr { class RemExpr extends BinaryArithmeticOperation, @remexpr { override string getOperator() { result = "%" } - override string getCanonicalQLClass() { result = "RemExpr" } + override string getAPrimaryQlClass() { result = "RemExpr" } override int getPrecedence() { result = 14 } } @@ -281,7 +286,7 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr { class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr { override string getOperator() { result = "*" } - override string getCanonicalQLClass() { result = "ImaginaryMulExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryMulExpr" } override int getPrecedence() { result = 14 } } @@ -298,7 +303,7 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr { class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr { override string getOperator() { result = "/" } - override string getCanonicalQLClass() { result = "ImaginaryDivExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryDivExpr" } override int getPrecedence() { result = 14 } } @@ -316,7 +321,7 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr { class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" } + override string getAPrimaryQlClass() { result = "RealImaginaryAddExpr" } override int getPrecedence() { result = 13 } } @@ -334,7 +339,7 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr { class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryRealAddExpr" } override int getPrecedence() { result = 13 } } @@ -352,7 +357,7 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr { class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "RealImaginarySubExpr" } + override string getAPrimaryQlClass() { result = "RealImaginarySubExpr" } override int getPrecedence() { result = 13 } } @@ -370,7 +375,7 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr { class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" } + override string getAPrimaryQlClass() { result = "ImaginaryRealSubExpr" } override int getPrecedence() { result = 13 } } @@ -384,7 +389,7 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr { class MinExpr extends BinaryArithmeticOperation, @minexpr { override string getOperator() { result = "?" } - override string getCanonicalQLClass() { result = "MaxExpr" } + override string getAPrimaryQlClass() { result = "MaxExpr" } } /** @@ -414,7 +419,7 @@ class PointerArithmeticOperation extends BinaryArithmeticOperation, @p_arith_op_ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr { override string getOperator() { result = "+" } - override string getCanonicalQLClass() { result = "PointerAddExpr" } + override string getAPrimaryQlClass() { result = "PointerAddExpr" } override int getPrecedence() { result = 13 } } @@ -429,7 +434,7 @@ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr { class PointerSubExpr extends PointerArithmeticOperation, @psubexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "PointerSubExpr" } + override string getAPrimaryQlClass() { result = "PointerSubExpr" } override int getPrecedence() { result = 13 } } @@ -444,7 +449,7 @@ class PointerSubExpr extends PointerArithmeticOperation, @psubexpr { class PointerDiffExpr extends PointerArithmeticOperation, @pdiffexpr { override string getOperator() { result = "-" } - override string getCanonicalQLClass() { result = "PointerDiffExpr" } + override string getAPrimaryQlClass() { result = "PointerDiffExpr" } override int getPrecedence() { result = 13 } } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll index 4d2f61f1b6d8..0c56d9a4d51f 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll @@ -38,7 +38,7 @@ class Assignment extends Operation, @assign_expr { class AssignExpr extends Assignment, @assignexpr { override string getOperator() { result = "=" } - override string getCanonicalQLClass() { result = "AssignExpr" } + override string getAPrimaryQlClass() { result = "AssignExpr" } /** Gets a textual representation of this assignment. */ override string toString() { result = "... = ..." } @@ -64,7 +64,7 @@ class AssignArithmeticOperation extends AssignOperation, @assign_arith_expr { } * ``` */ class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr { - override string getCanonicalQLClass() { result = "AssignAddExpr" } + override string getAPrimaryQlClass() { result = "AssignAddExpr" } override string getOperator() { result = "+=" } } @@ -76,7 +76,7 @@ class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr { * ``` */ class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr { - override string getCanonicalQLClass() { result = "AssignSubExpr" } + override string getAPrimaryQlClass() { result = "AssignSubExpr" } override string getOperator() { result = "-=" } } @@ -88,7 +88,7 @@ class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr { * ``` */ class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr { - override string getCanonicalQLClass() { result = "AssignMulExpr" } + override string getAPrimaryQlClass() { result = "AssignMulExpr" } override string getOperator() { result = "*=" } } @@ -100,7 +100,7 @@ class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr { * ``` */ class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr { - override string getCanonicalQLClass() { result = "AssignDivExpr" } + override string getAPrimaryQlClass() { result = "AssignDivExpr" } override string getOperator() { result = "/=" } } @@ -112,7 +112,7 @@ class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr { * ``` */ class AssignRemExpr extends AssignArithmeticOperation, @assignremexpr { - override string getCanonicalQLClass() { result = "AssignRemExpr" } + override string getAPrimaryQlClass() { result = "AssignRemExpr" } override string getOperator() { result = "%=" } } @@ -130,7 +130,7 @@ class AssignBitwiseOperation extends AssignOperation, @assign_bitwise_expr { } * ``` */ class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr { - override string getCanonicalQLClass() { result = "AssignAndExpr" } + override string getAPrimaryQlClass() { result = "AssignAndExpr" } override string getOperator() { result = "&=" } } @@ -142,7 +142,7 @@ class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr { * ``` */ class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr { - override string getCanonicalQLClass() { result = "AssignOrExpr" } + override string getAPrimaryQlClass() { result = "AssignOrExpr" } override string getOperator() { result = "|=" } } @@ -154,7 +154,7 @@ class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr { * ``` */ class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr { - override string getCanonicalQLClass() { result = "AssignXorExpr" } + override string getAPrimaryQlClass() { result = "AssignXorExpr" } override string getOperator() { result = "^=" } } @@ -166,7 +166,7 @@ class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr { * ``` */ class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr { - override string getCanonicalQLClass() { result = "AssignLShiftExpr" } + override string getAPrimaryQlClass() { result = "AssignLShiftExpr" } override string getOperator() { result = "<<=" } } @@ -178,7 +178,7 @@ class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr { * ``` */ class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr { - override string getCanonicalQLClass() { result = "AssignRShiftExpr" } + override string getAPrimaryQlClass() { result = "AssignRShiftExpr" } override string getOperator() { result = ">>=" } } @@ -190,7 +190,7 @@ class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr { * ``` */ class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr { - override string getCanonicalQLClass() { result = "AssignPointerAddExpr" } + override string getAPrimaryQlClass() { result = "AssignPointerAddExpr" } override string getOperator() { result = "+=" } } @@ -202,7 +202,7 @@ class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr { * ``` */ class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr { - override string getCanonicalQLClass() { result = "AssignPointerSubExpr" } + override string getAPrimaryQlClass() { result = "AssignPointerSubExpr" } override string getOperator() { result = "-=" } } @@ -227,7 +227,7 @@ class ConditionDeclExpr extends Expr, @condition_decl { */ deprecated Expr getExpr() { result = this.getChild(0) } - override string getCanonicalQLClass() { result = "ConditionDeclExpr" } + override string getAPrimaryQlClass() { result = "ConditionDeclExpr" } /** * Gets the compiler-generated variable access that conceptually occurs after diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll index 2d0b6cda0d6e..3a4f14d9ab81 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling bitwise operations such as `~`, `<<`, `&` and + * `|`. + */ + import semmle.code.cpp.exprs.Expr /** @@ -16,7 +21,7 @@ class ComplementExpr extends UnaryBitwiseOperation, @complementexpr { override int getPrecedence() { result = 16 } - override string getCanonicalQLClass() { result = "ComplementExpr" } + override string getAPrimaryQlClass() { result = "ComplementExpr" } } /** @@ -35,7 +40,7 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr { override int getPrecedence() { result = 12 } - override string getCanonicalQLClass() { result = "LShiftExpr" } + override string getAPrimaryQlClass() { result = "LShiftExpr" } } /** @@ -49,7 +54,7 @@ class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr { override int getPrecedence() { result = 12 } - override string getCanonicalQLClass() { result = "RShiftExpr" } + override string getAPrimaryQlClass() { result = "RShiftExpr" } } /** @@ -63,7 +68,7 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @andexpr { override int getPrecedence() { result = 8 } - override string getCanonicalQLClass() { result = "BitwiseAndExpr" } + override string getAPrimaryQlClass() { result = "BitwiseAndExpr" } } /** @@ -77,7 +82,7 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @orexpr { override int getPrecedence() { result = 6 } - override string getCanonicalQLClass() { result = "BitwiseOrExpr" } + override string getAPrimaryQlClass() { result = "BitwiseOrExpr" } } /** @@ -91,5 +96,5 @@ class BitwiseXorExpr extends BinaryBitwiseOperation, @xorexpr { override int getPrecedence() { result = 7 } - override string getCanonicalQLClass() { result = "BitwiseXorExpr" } + override string getAPrimaryQlClass() { result = "BitwiseXorExpr" } } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll index 5729a49086b4..3c627d712b44 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -1,11 +1,16 @@ +/** + * Provides classes for modeling built-in operations. Built-in operations are + * typically compiler specific and are used by libraries and generated code. + */ + import semmle.code.cpp.exprs.Expr /** - * A C/C++ builtin operation. This is the root QL class encompassing + * A C/C++ built-in operation. This is the root QL class encompassing * built-in functionality. */ class BuiltInOperation extends Expr, @builtin_op { - override string getCanonicalQLClass() { result = "BuiltInOperation" } + override string getAPrimaryQlClass() { result = "BuiltInOperation" } } /** @@ -25,7 +30,7 @@ class VarArgsExpr extends BuiltInOperation, @var_args_expr { } class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr { override string toString() { result = "__builtin_va_start" } - override string getCanonicalQLClass() { result = "BuiltInVarArgsStart" } + override string getAPrimaryQlClass() { result = "BuiltInVarArgsStart" } /** * Gets the `va_list` argument. @@ -50,7 +55,7 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr { class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr { override string toString() { result = "__builtin_va_end" } - override string getCanonicalQLClass() { result = "BuiltInVarArgsEnd" } + override string getAPrimaryQlClass() { result = "BuiltInVarArgsEnd" } /** * Gets the `va_list` argument. @@ -68,7 +73,7 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr { class BuiltInVarArg extends BuiltInOperation, @vaargexpr { override string toString() { result = "__builtin_va_arg" } - override string getCanonicalQLClass() { result = "BuiltInVarArg" } + override string getAPrimaryQlClass() { result = "BuiltInVarArg" } /** * Gets the `va_list` argument. @@ -88,7 +93,7 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr { class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr { override string toString() { result = "__builtin_va_copy" } - override string getCanonicalQLClass() { result = "BuiltInVarArgCopy" } + override string getAPrimaryQlClass() { result = "BuiltInVarArgCopy" } /** * Gets the destination `va_list` argument. @@ -110,7 +115,7 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr { class BuiltInNoOp extends BuiltInOperation, @noopexpr { override string toString() { result = "__noop" } - override string getCanonicalQLClass() { result = "BuiltInNoOp" } + override string getAPrimaryQlClass() { result = "BuiltInNoOp" } } /** @@ -132,7 +137,7 @@ deprecated class BuiltInOperationOffsetOf = BuiltInOperationBuiltInOffsetOf; class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr { override string toString() { result = "__builtin_offsetof" } - override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInOffsetOf" } + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInOffsetOf" } } /** @@ -149,7 +154,7 @@ class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr { class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr { override string toString() { result = "__INTADDR__" } - override string getCanonicalQLClass() { result = "BuiltInIntAddr" } + override string getAPrimaryQlClass() { result = "BuiltInIntAddr" } } /** @@ -164,7 +169,7 @@ class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr { class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr { override string toString() { result = "__has_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasAssign" } } /** @@ -179,7 +184,7 @@ class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr { class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { override string toString() { result = "__has_copy" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasCopy" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasCopy" } } /** @@ -195,7 +200,7 @@ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign { override string toString() { result = "__has_nothrow_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowAssign" } } /** @@ -211,7 +216,7 @@ class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassi class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothrowconstr { override string toString() { result = "__has_nothrow_constructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowConstructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowConstructor" } } /** @@ -226,7 +231,7 @@ class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothro class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy { override string toString() { result = "__has_nothrow_copy" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowCopy" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowCopy" } } /** @@ -242,7 +247,7 @@ class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy { class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassign { override string toString() { result = "__has_trivial_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialAssign" } } /** @@ -257,7 +262,7 @@ class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassi class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivialconstr { override string toString() { result = "__has_trivial_constructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialConstructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialConstructor" } } /** @@ -272,7 +277,7 @@ class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivia class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy { override string toString() { result = "__has_trivial_copy" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialCopy" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialCopy" } } /** @@ -287,7 +292,7 @@ class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy { class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivialdestructor { override string toString() { result = "__has_trivial_destructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialDestructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialDestructor" } } /** @@ -302,7 +307,7 @@ class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivial class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr { override string toString() { result = "__has_user_destructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasUserDestructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasUserDestructor" } } /** @@ -320,7 +325,7 @@ class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtualdestr { override string toString() { result = "__has_virtual_destructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasVirtualDestructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasVirtualDestructor" } } /** @@ -335,7 +340,7 @@ class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtual class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr { override string toString() { result = "__is_abstract" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsAbstract" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsAbstract" } } /** @@ -350,7 +355,7 @@ class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr { class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr { override string toString() { result = "__is_base_of" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsBaseOf" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsBaseOf" } } /** @@ -365,7 +370,7 @@ class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr { class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr { override string toString() { result = "__is_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsClass" } } /** @@ -380,7 +385,7 @@ class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr { class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr { override string toString() { result = "__is_convertible_to" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsConvertibleTo" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsConvertibleTo" } } /** @@ -395,7 +400,7 @@ class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr { class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr { override string toString() { result = "__is_empty" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsEmpty" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsEmpty" } } /** @@ -410,7 +415,7 @@ class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr { class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr { override string toString() { result = "__is_enum" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsEnum" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsEnum" } } /** @@ -427,7 +432,7 @@ class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr { class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr { override string toString() { result = "__is_pod" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsPod" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsPod" } } /** @@ -442,7 +447,7 @@ class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr { class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr { override string toString() { result = "__is_polymorphic" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsPolymorphic" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsPolymorphic" } } /** @@ -457,7 +462,7 @@ class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr { class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr { override string toString() { result = "__is_union" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsUnion" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsUnion" } } /** @@ -496,7 +501,7 @@ class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typesco class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshufflevector { override string toString() { result = "__builtin_shufflevector" } - override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInShuffleVector" } + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffleVector" } } /** @@ -516,7 +521,7 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtinconvertvector { override string toString() { result = "__builtin_convertvector" } - override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInConvertVector" } + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInConvertVector" } } /** @@ -538,7 +543,7 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation, result = this.getOperand().(ReferenceDereferenceExpr).getChild(0).(Access).getTarget() } - override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInAddressOf" } + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInAddressOf" } override string getOperator() { result = "__builtin_addressof" } } @@ -560,7 +565,7 @@ class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation, @istriviallyconstructibleexpr { override string toString() { result = "__is_trivially_constructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyConstructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyConstructible" } } /** @@ -577,7 +582,7 @@ class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation, class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleexpr { override string toString() { result = "__is_destructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsDestructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsDestructible" } } /** @@ -594,7 +599,7 @@ class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleex class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrowdestructibleexpr { override string toString() { result = "__is_nothrow_destructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowDestructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowDestructible" } } /** @@ -610,7 +615,7 @@ class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrow class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istriviallydestructibleexpr { override string toString() { result = "__is_trivially_destructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyDestructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyDestructible" } } /** @@ -629,7 +634,7 @@ class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istrivi class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr { override string toString() { result = "__is_trivially_assignable" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyAssignable" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyAssignable" } } /** @@ -645,7 +650,7 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr { override string toString() { result = "__is_nothrow_assignable" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowAssignable" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowAssignable" } } /** @@ -665,7 +670,7 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayoutexpr { override string toString() { result = "__is_standard_layout" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsStandardLayout" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsStandardLayout" } } /** @@ -679,7 +684,7 @@ class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayo class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr { override string toString() { result = "__is_trivially_copyable" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyCopyable" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyCopyable" } } /** @@ -699,7 +704,7 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr { override string toString() { result = "__is_literal_type" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsLiteralType" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsLiteralType" } } /** @@ -717,7 +722,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, @hastrivialmoveconstructorexpr { override string toString() { result = "__has_trivial_move_constructor" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialMoveConstructor" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialMoveConstructor" } } /** @@ -735,7 +740,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivialmoveassignexpr { override string toString() { result = "__has_trivial_move_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialMoveAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialMoveAssign" } } /** @@ -751,7 +756,7 @@ class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivial class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrowmoveassignexpr { override string toString() { result = "__has_nothrow_move_assign" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasNothrowMoveAssign" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasNothrowMoveAssign" } } /** @@ -770,7 +775,7 @@ class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrow class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructibleexpr { override string toString() { result = "__is_constructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsConstructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsConstructible" } } /** @@ -786,7 +791,7 @@ class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructible class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothrowconstructibleexpr { override string toString() { result = "__is_nothrow_constructible" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowConstructible" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowConstructible" } } /** @@ -801,7 +806,7 @@ class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothro class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr { override string toString() { result = "__has_finalizer" } - override string getCanonicalQLClass() { result = "BuiltInOperationHasFinalizer" } + override string getAPrimaryQlClass() { result = "BuiltInOperationHasFinalizer" } } /** @@ -815,7 +820,7 @@ class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr { class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr { override string toString() { result = "__is_delegate" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsDelegate" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsDelegate" } } /** @@ -828,7 +833,7 @@ class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr { class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfaceclassexpr { override string toString() { result = "__is_interface_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsInterfaceClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsInterfaceClass" } } /** @@ -845,7 +850,7 @@ class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfacecla class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr { override string toString() { result = "__is_ref_array" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsRefArray" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsRefArray" } } /** @@ -862,7 +867,7 @@ class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr { class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr { override string toString() { result = "__is_ref_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsRefClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsRefClass" } } /** @@ -880,7 +885,7 @@ class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr { class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr { override string toString() { result = "__is_sealed" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsSealed" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsSealed" } } /** @@ -899,7 +904,7 @@ class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr { class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalueclassexpr { override string toString() { result = "__is_simple_value_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsSimpleValueClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsSimpleValueClass" } } /** @@ -916,7 +921,7 @@ class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalu class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr { override string toString() { result = "__is_value_class" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsValueClass" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsValueClass" } } /** @@ -934,7 +939,7 @@ class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr { class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { override string toString() { result = "__is_final" } - override string getCanonicalQLClass() { result = "BuiltInOperationIsFinal" } + override string getAPrimaryQlClass() { result = "BuiltInOperationIsFinal" } } /** @@ -949,7 +954,7 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr { override string toString() { result = "__builtin_choose_expr" } - override string getCanonicalQLClass() { result = "BuiltInChooseExpr" } + override string getAPrimaryQlClass() { result = "BuiltInChooseExpr" } } /** @@ -966,7 +971,7 @@ class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr { class VectorFillOperation extends UnaryOperation, @vec_fill { override string getOperator() { result = "(vector fill)" } - override string getCanonicalQLClass() { result = "VectorFillOperation" } + override string getAPrimaryQlClass() { result = "VectorFillOperation" } } /** @@ -975,7 +980,7 @@ class VectorFillOperation extends UnaryOperation, @vec_fill { class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex { override string toString() { result = "__builtin_complex" } - override string getCanonicalQLClass() { result = "BuiltInComplexOperation" } + override string getAPrimaryQlClass() { result = "BuiltInComplexOperation" } /** Gets the operand corresponding to the real part of the complex number. */ Expr getRealOperand() { this.hasChild(result, 0) } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index abb26002091c..2f1c29be8bc9 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -1,13 +1,29 @@ +/** + * Provides classes for modeling call expressions including direct calls to + * functions, constructor and destructor calls, and calls made through function + * pointers. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Function private import semmle.code.cpp.dataflow.EscapesTree +private class TCall = @funbindexpr or @callexpr; + /** * A C/C++ call. - * - * This is the abstract root QL class for all types of calls. */ -abstract class Call extends Expr, NameQualifiableElement { +class Call extends Expr, NameQualifiableElement, TCall { + // `@funbindexpr` (which is the dbscheme type for FunctionCall) is a union type that includes + // `@routineexpr. This dbscheme type includes accesses to functions that are not necessarily calls to + // that function. That's why the charpred for `FunctionCall` requires: + // ``` + // iscall(underlyingElement(this), _) + // ``` + // So for the charpred for `Call` we include the requirement that if this is an instance of + // `@funbindexpr` it must be a _call_ to the function. + Call() { this instanceof @callexpr or iscall(underlyingElement(this), _) } + /** * Gets the number of arguments (actual parameters) of this call. The count * does _not_ include the qualifier of the call, if any. @@ -74,7 +90,7 @@ abstract class Call extends Expr, NameQualifiableElement { * method, and it might not exist. * - For a variable call, it never exists. */ - abstract Function getTarget(); + Function getTarget() { none() } // overridden in subclasses override int getPrecedence() { result = 17 } @@ -148,7 +164,7 @@ abstract class Call extends Expr, NameQualifiableElement { class FunctionCall extends Call, @funbindexpr { FunctionCall() { iscall(underlyingElement(this), _) } - override string getCanonicalQLClass() { result = "FunctionCall" } + override string getAPrimaryQlClass() { result = "FunctionCall" } /** Gets an explicit template argument for this call. */ Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) } @@ -297,7 +313,7 @@ class OverloadedPointerDereferenceExpr extends FunctionCall { getTarget().getEffectiveNumberOfParameters() = 1 } - override string getCanonicalQLClass() { result = "OverloadedPointerDereferenceExpr" } + override string getAPrimaryQlClass() { result = "OverloadedPointerDereferenceExpr" } /** * Gets the expression this operator * applies to. @@ -345,7 +361,7 @@ class OverloadedPointerDereferenceExpr extends FunctionCall { class OverloadedArrayExpr extends FunctionCall { OverloadedArrayExpr() { getTarget().hasName("operator[]") } - override string getCanonicalQLClass() { result = "OverloadedArrayExpr" } + override string getAPrimaryQlClass() { result = "OverloadedArrayExpr" } /** * Gets the expression being subscripted. @@ -377,7 +393,7 @@ class ExprCall extends Call, @callexpr { */ Expr getExpr() { result = this.getChild(0) } - override string getCanonicalQLClass() { result = "ExprCall" } + override string getAPrimaryQlClass() { result = "ExprCall" } override Expr getAnArgument() { exists(int i | result = this.getChild(i) and i >= 1) } @@ -401,7 +417,7 @@ class ExprCall extends Call, @callexpr { class VariableCall extends ExprCall { VariableCall() { this.getExpr() instanceof VariableAccess } - override string getCanonicalQLClass() { result = "VariableCall" } + override string getAPrimaryQlClass() { result = "VariableCall" } /** * Gets the variable which yields the function pointer to call. @@ -419,7 +435,7 @@ class VariableCall extends ExprCall { class ConstructorCall extends FunctionCall { ConstructorCall() { super.getTarget() instanceof Constructor } - override string getCanonicalQLClass() { result = "ConstructorCall" } + override string getAPrimaryQlClass() { result = "ConstructorCall" } /** Gets the constructor being called. */ override Constructor getTarget() { result = super.getTarget() } @@ -438,7 +454,7 @@ class ThrowExpr extends Expr, @throw_expr { */ Expr getExpr() { result = this.getChild(0) } - override string getCanonicalQLClass() { result = "ThrowExpr" } + override string getAPrimaryQlClass() { result = "ThrowExpr" } override string toString() { result = "throw ..." } @@ -454,7 +470,7 @@ class ThrowExpr extends Expr, @throw_expr { class ReThrowExpr extends ThrowExpr { ReThrowExpr() { this.getType() instanceof VoidType } - override string getCanonicalQLClass() { result = "ReThrowExpr" } + override string getAPrimaryQlClass() { result = "ReThrowExpr" } override string toString() { result = "re-throw exception " } } @@ -469,7 +485,7 @@ class ReThrowExpr extends ThrowExpr { class DestructorCall extends FunctionCall { DestructorCall() { super.getTarget() instanceof Destructor } - override string getCanonicalQLClass() { result = "DestructorCall" } + override string getAPrimaryQlClass() { result = "DestructorCall" } /** Gets the destructor being called. */ override Destructor getTarget() { result = super.getTarget() } @@ -493,7 +509,7 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call { */ Expr getQualifier() { result = this.getChild(0) } - override string getCanonicalQLClass() { result = "VacuousDestructorCall" } + override string getAPrimaryQlClass() { result = "VacuousDestructorCall" } override string toString() { result = "(vacuous destructor call)" } } @@ -506,7 +522,7 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call { * initializations. */ class ConstructorInit extends Expr, @ctorinit { - override string getCanonicalQLClass() { result = "ConstructorInit" } + override string getAPrimaryQlClass() { result = "ConstructorInit" } } /** @@ -514,7 +530,7 @@ class ConstructorInit extends Expr, @ctorinit { * initializer list or compiler-generated actions. */ class ConstructorBaseInit extends ConstructorInit, ConstructorCall { - override string getCanonicalQLClass() { result = "ConstructorBaseInit" } + override string getAPrimaryQlClass() { result = "ConstructorBaseInit" } } /** @@ -531,7 +547,7 @@ class ConstructorBaseInit extends ConstructorInit, ConstructorCall { * ``` */ class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit { - override string getCanonicalQLClass() { result = "ConstructorDirectInit" } + override string getAPrimaryQlClass() { result = "ConstructorDirectInit" } } /** @@ -551,7 +567,7 @@ class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit { * ``` */ class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit { - override string getCanonicalQLClass() { result = "ConstructorVirtualInit" } + override string getAPrimaryQlClass() { result = "ConstructorVirtualInit" } } /** @@ -566,7 +582,7 @@ class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit { * ``` */ class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit { - override string getCanonicalQLClass() { result = "ConstructorDelegationInit" } + override string getAPrimaryQlClass() { result = "ConstructorDelegationInit" } } /** @@ -585,7 +601,7 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit { /** Gets the field being initialized. */ Field getTarget() { varbind(underlyingElement(this), unresolveElement(result)) } - override string getCanonicalQLClass() { result = "ConstructorFieldInit" } + override string getAPrimaryQlClass() { result = "ConstructorFieldInit" } /** * Gets the expression to which the field is initialized. @@ -607,7 +623,7 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit { * compiler-generated actions. */ class DestructorDestruction extends Expr, @dtordestruct { - override string getCanonicalQLClass() { result = "DestructorDestruction" } + override string getAPrimaryQlClass() { result = "DestructorDestruction" } } /** @@ -615,7 +631,7 @@ class DestructorDestruction extends Expr, @dtordestruct { * compiler-generated actions. */ class DestructorBaseDestruction extends DestructorCall, DestructorDestruction { - override string getCanonicalQLClass() { result = "DestructorBaseDestruction" } + override string getAPrimaryQlClass() { result = "DestructorBaseDestruction" } } /** @@ -629,7 +645,7 @@ class DestructorBaseDestruction extends DestructorCall, DestructorDestruction { * ``` */ class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirectdestruct { - override string getCanonicalQLClass() { result = "DestructorDirectDestruction" } + override string getAPrimaryQlClass() { result = "DestructorDirectDestruction" } } /** @@ -646,7 +662,7 @@ class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirect * ``` */ class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtualdestruct { - override string getCanonicalQLClass() { result = "DestructorVirtualDestruction" } + override string getAPrimaryQlClass() { result = "DestructorVirtualDestruction" } } /** @@ -664,7 +680,7 @@ class DestructorFieldDestruction extends DestructorDestruction, @dtorfielddestru /** Gets the field being destructed. */ Field getTarget() { varbind(underlyingElement(this), unresolveElement(result)) } - override string getCanonicalQLClass() { result = "DestructorFieldDestruction" } + override string getAPrimaryQlClass() { result = "DestructorFieldDestruction" } /** Gets the compiler-generated call to the variable's destructor. */ DestructorCall getExpr() { result = this.getChild(0) } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll index a00014b9af75..e06ed095d401 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling C/C++ casts and conversions, as well as some + * type-related operators such as `sizeof` and `alignof`. + */ + import semmle.code.cpp.exprs.Expr private import semmle.code.cpp.internal.ResolveClass @@ -92,7 +97,7 @@ module CastConsistency { class CStyleCast extends Cast, @c_style_cast { override string toString() { result = "(" + this.getType().getName() + ")..." } - override string getCanonicalQLClass() { result = "CStyleCast" } + override string getAPrimaryQlClass() { result = "CStyleCast" } override int getPrecedence() { result = 16 } } @@ -111,7 +116,7 @@ class CStyleCast extends Cast, @c_style_cast { class StaticCast extends Cast, @static_cast { override string toString() { result = "static_cast<" + this.getType().getName() + ">..." } - override string getCanonicalQLClass() { result = "StaticCast" } + override string getAPrimaryQlClass() { result = "StaticCast" } override int getPrecedence() { result = 17 } } @@ -129,7 +134,7 @@ class StaticCast extends Cast, @static_cast { class ConstCast extends Cast, @const_cast { override string toString() { result = "const_cast<" + this.getType().getName() + ">..." } - override string getCanonicalQLClass() { result = "ConstCast" } + override string getAPrimaryQlClass() { result = "ConstCast" } override int getPrecedence() { result = 17 } } @@ -147,7 +152,7 @@ class ConstCast extends Cast, @const_cast { class ReinterpretCast extends Cast, @reinterpret_cast { override string toString() { result = "reinterpret_cast<" + this.getType().getName() + ">..." } - override string getCanonicalQLClass() { result = "ReinterpretCast" } + override string getAPrimaryQlClass() { result = "ReinterpretCast" } override int getPrecedence() { result = 17 } } @@ -203,7 +208,7 @@ class IntegralConversion extends ArithmeticConversion { isIntegralOrEnum(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "IntegralConversion" } @@ -223,7 +228,7 @@ class FloatingPointConversion extends ArithmeticConversion { getExpr().getUnspecifiedType() instanceof FloatingPointType } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "FloatingPointConversion" } @@ -243,7 +248,7 @@ class FloatingPointToIntegralConversion extends ArithmeticConversion { getExpr().getUnspecifiedType() instanceof FloatingPointType } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "FloatingPointToIntegralConversion" } @@ -263,7 +268,7 @@ class IntegralToFloatingPointConversion extends ArithmeticConversion { isIntegralOrEnum(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "IntegralToFloatingPointConversion" } @@ -289,9 +294,7 @@ class PointerConversion extends Cast { isPointerOrNullPointer(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { - not exists(qlCast(this)) and result = "PointerConversion" - } + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerConversion" } override string getSemanticConversionString() { result = "pointer conversion" } } @@ -325,7 +328,7 @@ class PointerToMemberConversion extends Cast { ) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerToMemberConversion" } @@ -346,7 +349,7 @@ class PointerToIntegralConversion extends Cast { isPointerOrNullPointer(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerToIntegralConversion" } @@ -367,7 +370,7 @@ class IntegralToPointerConversion extends Cast { isIntegralOrEnum(getExpr().getUnspecifiedType()) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "IntegralToPointerConversion" } @@ -385,7 +388,7 @@ class IntegralToPointerConversion extends Cast { class BoolConversion extends Cast { BoolConversion() { conversionkinds(underlyingElement(this), 1) } - override string getCanonicalQLClass() { not exists(qlCast(this)) and result = "BoolConversion" } + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "BoolConversion" } override string getSemanticConversionString() { result = "conversion to bool" } } @@ -403,7 +406,7 @@ class VoidConversion extends Cast { getUnspecifiedType() instanceof VoidType } - override string getCanonicalQLClass() { not exists(qlCast(this)) and result = "VoidConversion" } + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "VoidConversion" } override string getSemanticConversionString() { result = "conversion to void" } } @@ -479,7 +482,7 @@ private Class getConversionClass(Expr expr) { class BaseClassConversion extends InheritanceConversion { BaseClassConversion() { conversionkinds(underlyingElement(this), 2) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "BaseClassConversion" } @@ -506,7 +509,7 @@ class BaseClassConversion extends InheritanceConversion { class DerivedClassConversion extends InheritanceConversion { DerivedClassConversion() { conversionkinds(underlyingElement(this), 3) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "DerivedClassConversion" } @@ -528,7 +531,7 @@ class DerivedClassConversion extends InheritanceConversion { class PointerToMemberBaseClassConversion extends Cast { PointerToMemberBaseClassConversion() { conversionkinds(underlyingElement(this), 4) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerToMemberBaseClassConversion" } @@ -548,7 +551,7 @@ class PointerToMemberBaseClassConversion extends Cast { class PointerToMemberDerivedClassConversion extends Cast { PointerToMemberDerivedClassConversion() { conversionkinds(underlyingElement(this), 5) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerToMemberDerivedClassConversion" } @@ -569,9 +572,7 @@ class PointerToMemberDerivedClassConversion extends Cast { class GlvalueConversion extends Cast { GlvalueConversion() { conversionkinds(underlyingElement(this), 6) } - override string getCanonicalQLClass() { - not exists(qlCast(this)) and result = "GlvalueConversion" - } + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "GlvalueConversion" } override string getSemanticConversionString() { result = "glvalue conversion" } } @@ -597,7 +598,7 @@ class GlvalueConversion extends Cast { class PrvalueAdjustmentConversion extends Cast { PrvalueAdjustmentConversion() { conversionkinds(underlyingElement(this), 7) } - override string getCanonicalQLClass() { + override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PrvalueAdjustmentConversion" } @@ -620,7 +621,7 @@ class DynamicCast extends Cast, @dynamic_cast { override int getPrecedence() { result = 17 } - override string getCanonicalQLClass() { result = "DynamicCast" } + override string getAPrimaryQlClass() { result = "DynamicCast" } override string getSemanticConversionString() { result = "dynamic_cast" } } @@ -660,6 +661,9 @@ class UuidofOperator extends Expr, @uuidof { * ``` */ class TypeidOperator extends Expr, @type_id { + /** + * Gets the type that is returned by this typeid expression. + */ Type getResultType() { typeid_bind(underlyingElement(this), unresolveElement(result)) } /** @@ -669,7 +673,7 @@ class TypeidOperator extends Expr, @type_id { */ deprecated Type getSpecifiedType() { result = this.getResultType() } - override string getCanonicalQLClass() { result = "TypeidOperator" } + override string getAPrimaryQlClass() { result = "TypeidOperator" } /** * Gets the contained expression, if any (if this typeid contains @@ -699,7 +703,7 @@ class TypeidOperator extends Expr, @type_id { class SizeofPackOperator extends Expr, @sizeof_pack { override string toString() { result = "sizeof...(...)" } - override string getCanonicalQLClass() { result = "SizeofPackOperator" } + override string getAPrimaryQlClass() { result = "SizeofPackOperator" } override predicate mayBeImpure() { none() } @@ -722,7 +726,7 @@ class SizeofOperator extends Expr, @runtime_sizeof { class SizeofExprOperator extends SizeofOperator { SizeofExprOperator() { exists(Expr e | this.getChild(0) = e) } - override string getCanonicalQLClass() { result = "SizeofExprOperator" } + override string getAPrimaryQlClass() { result = "SizeofExprOperator" } /** Gets the contained expression. */ Expr getExprOperand() { result = this.getChild(0) } @@ -750,7 +754,7 @@ class SizeofExprOperator extends SizeofOperator { class SizeofTypeOperator extends SizeofOperator { SizeofTypeOperator() { sizeof_bind(underlyingElement(this), _) } - override string getCanonicalQLClass() { result = "SizeofTypeOperator" } + override string getAPrimaryQlClass() { result = "SizeofTypeOperator" } /** Gets the contained type. */ Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) } @@ -829,7 +833,7 @@ class ArrayToPointerConversion extends Conversion, @array_to_pointer { /** Gets a textual representation of this conversion. */ override string toString() { result = "array to pointer conversion" } - override string getCanonicalQLClass() { result = "ArrayToPointerConversion" } + override string getAPrimaryQlClass() { result = "ArrayToPointerConversion" } override predicate mayBeImpure() { none() } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll index a0688890a23b..0c84e07f5533 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling comparisons such as `==`, `!=` and `<`. + */ + import semmle.code.cpp.exprs.Expr /** @@ -21,7 +25,7 @@ class EqualityOperation extends ComparisonOperation, @eq_op_expr { * ``` */ class EQExpr extends EqualityOperation, @eqexpr { - override string getCanonicalQLClass() { result = "EQExpr" } + override string getAPrimaryQlClass() { result = "EQExpr" } override string getOperator() { result = "==" } } @@ -33,7 +37,7 @@ class EQExpr extends EqualityOperation, @eqexpr { * ``` */ class NEExpr extends EqualityOperation, @neexpr { - override string getCanonicalQLClass() { result = "NEExpr" } + override string getAPrimaryQlClass() { result = "NEExpr" } override string getOperator() { result = "!=" } } @@ -78,7 +82,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr { * ``` */ class GTExpr extends RelationalOperation, @gtexpr { - override string getCanonicalQLClass() { result = "GTExpr" } + override string getAPrimaryQlClass() { result = "GTExpr" } override string getOperator() { result = ">" } @@ -94,7 +98,7 @@ class GTExpr extends RelationalOperation, @gtexpr { * ``` */ class LTExpr extends RelationalOperation, @ltexpr { - override string getCanonicalQLClass() { result = "LTExpr" } + override string getAPrimaryQlClass() { result = "LTExpr" } override string getOperator() { result = "<" } @@ -110,7 +114,7 @@ class LTExpr extends RelationalOperation, @ltexpr { * ``` */ class GEExpr extends RelationalOperation, @geexpr { - override string getCanonicalQLClass() { result = "GEExpr" } + override string getAPrimaryQlClass() { result = "GEExpr" } override string getOperator() { result = ">=" } @@ -126,7 +130,7 @@ class GEExpr extends RelationalOperation, @geexpr { * ``` */ class LEExpr extends RelationalOperation, @leexpr { - override string getCanonicalQLClass() { result = "LEExpr" } + override string getAPrimaryQlClass() { result = "LEExpr" } override string getOperator() { result = "<=" } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll index fd15a22fbd22..a39b215b54a2 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -1,3 +1,7 @@ +/** + * Provides classes modeling C/C++ expressions. + */ + import semmle.code.cpp.Element private import semmle.code.cpp.Enclosing private import semmle.code.cpp.internal.ResolveClass @@ -23,7 +27,7 @@ class Expr extends StmtParent, @expr { Function getEnclosingFunction() { result = exprEnclosingElement(this) } /** Gets the nearest enclosing set of curly braces around this expression in the source, if any. */ - Block getEnclosingBlock() { result = getEnclosingStmt().getEnclosingBlock() } + BlockStmt getEnclosingBlock() { result = getEnclosingStmt().getEnclosingBlock() } override Stmt getEnclosingStmt() { result = this.getParent().(Expr).getEnclosingStmt() @@ -398,7 +402,7 @@ class Expr extends StmtParent, @expr { */ predicate hasImplicitConversion() { exists(Expr e | - exprconv(underlyingElement(this), unresolveElement(e)) and e.(Cast).isImplicit() + exprconv(underlyingElement(this), unresolveElement(e)) and e.(Conversion).isImplicit() ) } @@ -410,7 +414,7 @@ class Expr extends StmtParent, @expr { */ predicate hasExplicitConversion() { exists(Expr e | - exprconv(underlyingElement(this), unresolveElement(e)) and not e.(Cast).isImplicit() + exprconv(underlyingElement(this), unresolveElement(e)) and not e.(Conversion).isImplicit() ) } @@ -449,12 +453,14 @@ class Expr extends StmtParent, @expr { * cast from B to C. Only (1) and (2) would be included. */ Expr getExplicitlyConverted() { - // result is this or one of its conversions - result = this.getConversion*() and - // result is not an implicit conversion - it's either the expr or an explicit cast - (result = this or not result.(Cast).isImplicit()) and - // there is no further explicit conversion after result - not exists(Cast other | other = result.getConversion+() and not other.isImplicit()) + // For performance, we avoid a full transitive closure over `getConversion`. + // Since there can be several implicit conversions before and after an + // explicit conversion, use `getImplicitlyConverted` to step over them + // cheaply. Then, if there is an explicit conversion following the implict + // conversion sequence, recurse to handle multiple explicit conversions. + if this.getImplicitlyConverted().hasExplicitConversion() + then result = this.getImplicitlyConverted().getConversion().getExplicitlyConverted() + else result = this } /** @@ -535,6 +541,17 @@ class BinaryOperation extends Operation, @bin_op_expr { /** Gets the right operand of this binary operation. */ Expr getRightOperand() { this.hasChild(result, 1) } + /** + * Holds if `e1` and `e2` (in either order) are the two operands of this + * binary operation. + */ + predicate hasOperands(Expr e1, Expr e2) { + exists(int i | i in [0, 1] | + this.hasChild(e1, i) and + this.hasChild(e2, 1 - i) + ) + } + override string toString() { result = "... " + this.getOperator() + " ..." } override predicate mayBeImpure() { @@ -565,7 +582,7 @@ class BinaryOperation extends Operation, @bin_op_expr { class ParenthesizedBracedInitializerList extends Expr, @braced_init_list { override string toString() { result = "({...})" } - override string getCanonicalQLClass() { result = "ParenthesizedBracedInitializerList" } + override string getAPrimaryQlClass() { result = "ParenthesizedBracedInitializerList" } } /** @@ -580,7 +597,7 @@ class ParenthesizedBracedInitializerList extends Expr, @braced_init_list { class ParenthesisExpr extends Conversion, @parexpr { override string toString() { result = "(...)" } - override string getCanonicalQLClass() { result = "ParenthesisExpr" } + override string getAPrimaryQlClass() { result = "ParenthesisExpr" } } /** @@ -591,7 +608,7 @@ class ParenthesisExpr extends Conversion, @parexpr { class ErrorExpr extends Expr, @errorexpr { override string toString() { result = "" } - override string getCanonicalQLClass() { result = "ErrorExpr" } + override string getAPrimaryQlClass() { result = "ErrorExpr" } } /** @@ -606,7 +623,7 @@ class ErrorExpr extends Expr, @errorexpr { class AssumeExpr extends Expr, @assume { override string toString() { result = "__assume(...)" } - override string getCanonicalQLClass() { result = "AssumeExpr" } + override string getAPrimaryQlClass() { result = "AssumeExpr" } /** * Gets the operand of the `__assume` expressions. @@ -621,7 +638,7 @@ class AssumeExpr extends Expr, @assume { * ``` */ class CommaExpr extends Expr, @commaexpr { - override string getCanonicalQLClass() { result = "CommaExpr" } + override string getAPrimaryQlClass() { result = "CommaExpr" } /** * Gets the left operand, which is the one whose value is discarded. @@ -656,7 +673,7 @@ class CommaExpr extends Expr, @commaexpr { * ``` */ class AddressOfExpr extends UnaryOperation, @address_of { - override string getCanonicalQLClass() { result = "AddressOfExpr" } + override string getAPrimaryQlClass() { result = "AddressOfExpr" } /** Gets the function or variable whose address is taken. */ Declaration getAddressable() { @@ -688,7 +705,7 @@ class AddressOfExpr extends UnaryOperation, @address_of { class ReferenceToExpr extends Conversion, @reference_to { override string toString() { result = "(reference to)" } - override string getCanonicalQLClass() { result = "ReferenceToExpr" } + override string getAPrimaryQlClass() { result = "ReferenceToExpr" } override int getPrecedence() { result = 16 } } @@ -702,7 +719,7 @@ class ReferenceToExpr extends Conversion, @reference_to { * ``` */ class PointerDereferenceExpr extends UnaryOperation, @indirect { - override string getCanonicalQLClass() { result = "PointerDereferenceExpr" } + override string getAPrimaryQlClass() { result = "PointerDereferenceExpr" } /** * DEPRECATED: Use getOperand() instead. @@ -740,7 +757,7 @@ class PointerDereferenceExpr extends UnaryOperation, @indirect { class ReferenceDereferenceExpr extends Conversion, @ref_indirect { override string toString() { result = "(reference dereference)" } - override string getCanonicalQLClass() { result = "ReferenceDereferenceExpr" } + override string getAPrimaryQlClass() { result = "ReferenceDereferenceExpr" } } /** @@ -846,7 +863,7 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr { class NewExpr extends NewOrNewArrayExpr, @new_expr { override string toString() { result = "new" } - override string getCanonicalQLClass() { result = "NewExpr" } + override string getAPrimaryQlClass() { result = "NewExpr" } /** * Gets the type that is being allocated. @@ -876,7 +893,7 @@ class NewExpr extends NewOrNewArrayExpr, @new_expr { class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr { override string toString() { result = "new[]" } - override string getCanonicalQLClass() { result = "NewArrayExpr" } + override string getAPrimaryQlClass() { result = "NewArrayExpr" } /** * Gets the type that is being allocated. @@ -924,7 +941,7 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr { class DeleteExpr extends Expr, @delete_expr { override string toString() { result = "delete" } - override string getCanonicalQLClass() { result = "DeleteExpr" } + override string getAPrimaryQlClass() { result = "DeleteExpr" } override int getPrecedence() { result = 16 } @@ -998,7 +1015,7 @@ class DeleteExpr extends Expr, @delete_expr { class DeleteArrayExpr extends Expr, @delete_array_expr { override string toString() { result = "delete[]" } - override string getCanonicalQLClass() { result = "DeleteArrayExpr" } + override string getAPrimaryQlClass() { result = "DeleteArrayExpr" } override int getPrecedence() { result = 16 } @@ -1078,7 +1095,7 @@ class StmtExpr extends Expr, @expr_stmt { */ Stmt getStmt() { result.getParent() = this } - override string getCanonicalQLClass() { result = "StmtExpr" } + override string getAPrimaryQlClass() { result = "StmtExpr" } /** * Gets the result expression of the enclosed statement. For example, @@ -1094,7 +1111,7 @@ class StmtExpr extends Expr, @expr_stmt { /** Get the result expression of a statement. (Helper function for StmtExpr.) */ private Expr getStmtResultExpr(Stmt stmt) { result = stmt.(ExprStmt).getExpr() or - result = getStmtResultExpr(stmt.(Block).getLastStmt()) + result = getStmtResultExpr(stmt.(BlockStmt).getLastStmt()) } /** @@ -1103,7 +1120,7 @@ private Expr getStmtResultExpr(Stmt stmt) { class ThisExpr extends Expr, @thisaccess { override string toString() { result = "this" } - override string getCanonicalQLClass() { result = "ThisExpr" } + override string getAPrimaryQlClass() { result = "ThisExpr" } override predicate mayBeImpure() { none() } @@ -1139,7 +1156,7 @@ class BlockExpr extends Literal { class NoExceptExpr extends Expr, @noexceptexpr { override string toString() { result = "noexcept(...)" } - override string getCanonicalQLClass() { result = "NoExceptExpr" } + override string getAPrimaryQlClass() { result = "NoExceptExpr" } /** * Gets the expression inside this noexcept expression. @@ -1171,7 +1188,7 @@ class FoldExpr extends Expr, @foldexpr { ) } - override string getCanonicalQLClass() { result = "FoldExpr" } + override string getAPrimaryQlClass() { result = "FoldExpr" } /** Gets the binary operator used in this fold expression, as a string. */ string getOperatorString() { fold(underlyingElement(this), result, _) } @@ -1247,9 +1264,37 @@ private predicate constantTemplateLiteral(Expr e) { * ``` */ class SpaceshipExpr extends BinaryOperation, @spaceshipexpr { - override string getCanonicalQLClass() { result = "SpaceshipExpr" } + override string getAPrimaryQlClass() { result = "SpaceshipExpr" } override int getPrecedence() { result = 11 } override string getOperator() { result = "<=>" } } + +/** + * A C/C++ `co_await` expression. + * ``` + * co_await foo(); + * ``` + */ +class CoAwaitExpr extends UnaryOperation, @co_await { + override string getAPrimaryQlClass() { result = "CoAwaitExpr" } + + override string getOperator() { result = "co_await" } + + override int getPrecedence() { result = 16 } +} + +/** + * A C/C++ `co_yield` expression. + * ``` + * co_yield 1; + * ``` + */ +class CoYieldExpr extends UnaryOperation, @co_yield { + override string getAPrimaryQlClass() { result = "CoYieldExpr" } + + override string getOperator() { result = "co_yield" } + + override int getPrecedence() { result = 2 } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll index 4a5b1b21c8dd..8a51001f4d5e 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll @@ -1,3 +1,7 @@ +/** + * Provides classes for modeling lambda expressions and their captures. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Class @@ -15,7 +19,7 @@ import semmle.code.cpp.Class class LambdaExpression extends Expr, @lambdaexpr { override string toString() { result = "[...](...){...}" } - override string getCanonicalQLClass() { result = "LambdaExpression" } + override string getAPrimaryQlClass() { result = "LambdaExpression" } /** * Gets an implicitly or explicitly captured value of this lambda expression. @@ -75,7 +79,7 @@ class LambdaExpression extends Expr, @lambdaexpr { class Closure extends Class { Closure() { exists(LambdaExpression e | this = e.getType()) } - override string getCanonicalQLClass() { result = "Closure" } + override string getAPrimaryQlClass() { result = "Closure" } /** Gets the lambda expression of which this is the type. */ LambdaExpression getLambdaExpression() { result.getType() = this } @@ -101,7 +105,7 @@ class Closure extends Class { class LambdaCapture extends Locatable, @lambdacapture { override string toString() { result = getField().getName() } - override string getCanonicalQLClass() { result = "LambdaCapture" } + override string getAPrimaryQlClass() { result = "LambdaCapture" } /** * Holds if this capture was made implicitly. diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll index 3360be330c26..9ab944d2cc33 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling literals in the source code such as `0`, `'c'` + * or `"string"`. + */ + import semmle.code.cpp.exprs.Expr /** @@ -14,7 +19,7 @@ class Literal extends Expr, @literal { result = "Unknown literal" } - override string getCanonicalQLClass() { result = "Literal" } + override string getAPrimaryQlClass() { result = "Literal" } override predicate mayBeImpure() { none() } @@ -35,7 +40,7 @@ class Literal extends Expr, @literal { class LabelLiteral extends Literal { LabelLiteral() { jumpinfo(underlyingElement(this), _, _) } - override string getCanonicalQLClass() { result = "LabelLiteral" } + override string getAPrimaryQlClass() { result = "LabelLiteral" } /** Gets the corresponding label statement. */ LabelStmt getLabel() { jumpinfo(underlyingElement(this), _, unresolveElement(result)) } @@ -93,7 +98,7 @@ abstract class TextLiteral extends Literal { class CharLiteral extends TextLiteral { CharLiteral() { this.getValueText().regexpMatch("(?s)\\s*L?'.*") } - override string getCanonicalQLClass() { result = "CharLiteral" } + override string getAPrimaryQlClass() { result = "CharLiteral" } /** * Gets the character of this literal. For example `L'a'` has character `"a"`. @@ -115,7 +120,7 @@ class StringLiteral extends TextLiteral { // @aggregateliteral rather than @literal. } - override string getCanonicalQLClass() { result = "StringLiteral" } + override string getAPrimaryQlClass() { result = "StringLiteral" } } /** @@ -128,7 +133,7 @@ class StringLiteral extends TextLiteral { class OctalLiteral extends Literal { OctalLiteral() { super.getValueText().regexpMatch("\\s*0[0-7]+[uUlL]*\\s*") } - override string getCanonicalQLClass() { result = "OctalLiteral" } + override string getAPrimaryQlClass() { result = "OctalLiteral" } } /** @@ -140,14 +145,14 @@ class OctalLiteral extends Literal { class HexLiteral extends Literal { HexLiteral() { super.getValueText().regexpMatch("\\s*0[xX][0-9a-fA-F]+[uUlL]*\\s*") } - override string getCanonicalQLClass() { result = "HexLiteral" } + override string getAPrimaryQlClass() { result = "HexLiteral" } } /** * A C/C++ aggregate literal. */ class AggregateLiteral extends Expr, @aggregateliteral { - override string getCanonicalQLClass() { result = "AggregateLiteral" } + override string getAPrimaryQlClass() { result = "AggregateLiteral" } /** * DEPRECATED: Use ClassAggregateLiteral.getFieldExpr() instead. @@ -179,7 +184,7 @@ class ClassAggregateLiteral extends AggregateLiteral { ClassAggregateLiteral() { classType = this.getUnspecifiedType() } - override string getCanonicalQLClass() { result = "ClassAggregateLiteral" } + override string getAPrimaryQlClass() { result = "ClassAggregateLiteral" } /** * Gets the expression within the aggregate literal that is used to initialize @@ -299,7 +304,7 @@ class ArrayAggregateLiteral extends ArrayOrVectorAggregateLiteral { ArrayAggregateLiteral() { arrayType = this.getUnspecifiedType() } - override string getCanonicalQLClass() { result = "ArrayAggregateLiteral" } + override string getAPrimaryQlClass() { result = "ArrayAggregateLiteral" } override int getArraySize() { result = arrayType.getArraySize() } @@ -323,7 +328,7 @@ class VectorAggregateLiteral extends ArrayOrVectorAggregateLiteral { VectorAggregateLiteral() { vectorType = this.getUnspecifiedType() } - override string getCanonicalQLClass() { result = "VectorAggregateLiteral" } + override string getAPrimaryQlClass() { result = "VectorAggregateLiteral" } override int getArraySize() { result = vectorType.getNumElements() } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll index fa55c1e91eb4..d0e207f7b31d 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll @@ -1,3 +1,8 @@ +/** + * Provides classes for modeling logical operations such as `!`, `&&`, `||`, and + * the ternary `? :` expression. + */ + import semmle.code.cpp.exprs.Expr /** @@ -14,7 +19,7 @@ class UnaryLogicalOperation extends UnaryOperation, @un_log_op_expr { } class NotExpr extends UnaryLogicalOperation, @notexpr { override string getOperator() { result = "!" } - override string getCanonicalQLClass() { result = "NotExpr" } + override string getAPrimaryQlClass() { result = "NotExpr" } override int getPrecedence() { result = 16 } } @@ -46,7 +51,7 @@ class BinaryLogicalOperation extends BinaryOperation, @bin_log_op_expr { class LogicalAndExpr extends BinaryLogicalOperation, @andlogicalexpr { override string getOperator() { result = "&&" } - override string getCanonicalQLClass() { result = "LogicalAndExpr" } + override string getAPrimaryQlClass() { result = "LogicalAndExpr" } override int getPrecedence() { result = 5 } @@ -67,7 +72,7 @@ class LogicalAndExpr extends BinaryLogicalOperation, @andlogicalexpr { class LogicalOrExpr extends BinaryLogicalOperation, @orlogicalexpr { override string getOperator() { result = "||" } - override string getCanonicalQLClass() { result = "LogicalOrExpr" } + override string getAPrimaryQlClass() { result = "LogicalOrExpr" } override int getPrecedence() { result = 4 } @@ -89,7 +94,7 @@ class ConditionalExpr extends Operation, @conditionalexpr { /** Gets the condition of this conditional expression. */ Expr getCondition() { expr_cond_guard(underlyingElement(this), unresolveElement(result)) } - override string getCanonicalQLClass() { result = "ConditionalExpr" } + override string getAPrimaryQlClass() { result = "ConditionalExpr" } /** Gets the 'then' expression of this conditional expression. */ Expr getThen() { diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll b/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll index 61699023f8f1..c651ae9b1534 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll @@ -1,3 +1,7 @@ +/** + * DEPRECATED: Objective-C is no longer supported. + */ + import semmle.code.cpp.exprs.Expr import semmle.code.cpp.Class import semmle.code.cpp.ObjectiveC diff --git a/cpp/ql/src/semmle/code/cpp/ir/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/IR.qll index f019f20b6a83..381adad5e418 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + // Most queries should operate on the aliased SSA IR, so that's what we expose -// publically as the "IR". +// publicly as the "IR". import implementation.aliased_ssa.IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll index b5b7d7de7c24..b8abef8a5474 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll @@ -1 +1,5 @@ +/** + * Module used to configure the IR generation process. + */ + import implementation.IRConfiguration diff --git a/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll index 3ff802376357..c4ebf2f1eba2 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll @@ -1 +1,11 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + import implementation.aliased_ssa.PrintIR diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll index ddf43651727c..d735c8422480 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll @@ -70,7 +70,7 @@ private DataFlow::Node getNodeForSource(Expr source) { // // This case goes together with the similar (but not identical) rule in // `nodeIsBarrierIn`. - result = DataFlow::definitionByReferenceNode(source) and + result = DataFlow::definitionByReferenceNodeFromArgument(source) and not argv(source.(VariableAccess).getTarget()) ) } @@ -210,7 +210,7 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) { or // This case goes together with the similar (but not identical) rule in // `getNodeForSource`. - node = DataFlow::definitionByReferenceNode(source) + node = DataFlow::definitionByReferenceNodeFromArgument(source) ) } @@ -264,9 +264,6 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) { t instanceof Union or t instanceof ArrayType - or - // Buffers of unknown size - t instanceof UnknownType ) or exists(BinaryInstruction bin | diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll index d8904625e0ea..e927634fec23 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll @@ -226,28 +226,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam } /** - * Holds if the call context `ctx` reduces the set of viable dispatch - * targets of `ma` in `c`. + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. */ -predicate reducedViableImplInCallContext(CallInstruction call, Function f, CallInstruction ctx) { - none() -} - -/** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s for which the context makes a difference. - */ -Function prunedViableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() } - -/** - * Holds if flow returning from `m` to `ma` might return further and if - * this path restricts the set of call sites that can be returned to. - */ -predicate reducedViableImplInReturn(Function f, CallInstruction call) { none() } +predicate mayBenefitFromCallContext(CallInstruction call, Function f) { none() } /** - * Gets a viable dispatch target of `ma` in the context `ctx`. This is - * restricted to those `ma`s and results for which the return flow from the - * result to `ma` restricts the possible context `ctx`. + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. */ -Function prunedViableImplInCallContextReverse(CallInstruction call, CallInstruction ctx) { none() } +Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index 852f54974e24..892250f44bb8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -22,7 +22,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p)) + compatibleTypes(getNodeType(arg), getNodeType(p)) ) } @@ -147,174 +147,140 @@ private module Cached { } } - private module LocalFlowBigStep { - private predicate localFlowEntry(Node n) { - Cand::cand(_, n) and - ( - n instanceof ParameterNode or - n instanceof OutNode or - readStep(_, _, n) or - n instanceof CastNode - ) - } - - private predicate localFlowExit(Node n) { - Cand::cand(_, n) and - ( - n instanceof ArgumentNode - or - n instanceof ReturnNode - or - readStep(n, _, _) - or - n instanceof CastNode - or - n = - any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun)) - .getPreUpdateNode() - ) - } - - pragma[nomagic] - private predicate localFlowStepPlus(Node node1, Node node2) { - localFlowEntry(node1) and - simpleLocalFlowStep(node1, node2) and - node1 != node2 - or - exists(Node mid | - localFlowStepPlus(node1, mid) and - simpleLocalFlowStep(mid, node2) and - not mid instanceof CastNode - ) - } - - pragma[nomagic] - predicate localFlowBigStep(Node node1, Node node2) { - localFlowStepPlus(node1, node2) and - localFlowExit(node2) - } - } - /** * The final flow-through calculation: * - * - Input access paths are abstracted with a `ContentOption` parameter - * that represents the head of the access path. `TContentNone()` means that - * the access path is unrestricted. + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. * - Types are checked using the `compatibleTypes()` relation. */ private module Final { /** * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps, not taking call contexts into account. + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `p` that can flow to `node` - * (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { - parameterValueFlow0(p, node, contentIn) and + predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + read = TReadStepTypesNone() and + compatibleTypes(getNodeType(p), getNodeType(node)) or // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) - ) + compatibleTypes(read.getContentType(), getNodeType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() + read = TReadStepTypesNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn) and - LocalFlowBigStep::localFlowBigStep(mid, node) + parameterValueFlow(p, mid, read) and + simpleLocalFlowStep(mid, node) ) or // read - exists(Node mid, Content f | - parameterValueFlow(p, mid, TContentNone()) and - readStep(mid, f, node) and - contentIn.getContent() = f and + exists(Node mid | + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) + compatibleTypes(getNodeType(p), read.getContainerType()) ) or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ) { // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone()) and - argumentValueFlowsThrough(arg, contentIn, node) + parameterValueFlowArg(p, arg, mustBeNone) and + argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn) and - argumentValueFlowsThrough(arg, TContentNone(), node) + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn + ParameterNode p, ArgumentNode arg, ReadStepTypesOption read ) { - parameterValueFlow(p, arg, contentIn) and + parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn) + parameterValueFlowReturn(param, kind, read) ) } /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn) and + argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + read = TReadStepTypesNone() and + compatibleTypes(getNodeType(arg), getNodeType(out)) or // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) - ) + compatibleTypes(getNodeType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeType(out)) ) } + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and a single read step, not taking call + * contexts into account, thus representing a getter-step. + */ + predicate getterStep(ArgumentNode arg, Content c, Node out) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + /** * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps. + * callable using only value-preserving steps and possibly a single read + * step. * - * `contentIn` describes the content of `p` that can flow to the return - * node (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn + ParameterNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn) and + parameterValueFlow(p, ret, read) and kind = ret.getKind() ) } @@ -323,37 +289,107 @@ private module Cached { import Final } + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableCallable(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and + c = viableCallable(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + /** * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `f`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store(Node node1, Content f, Node node2) { - storeStep(node1, f, node2) and readStep(_, f, _) + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + storeStep(node1, c, node2) and + readStep(_, c, _) and + contentType = getNodeType(node1) and + containerType = getNodeType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and n2 = node2.(PostUpdateNode).getPreUpdateNode() | - argumentValueFlowsThrough(n2, TContentSome(f), n1) + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, f, n1) + readStep(n2, c, n1) and + contentType = getNodeType(n1) and + containerType = getNodeType(n2) ) } - import FlowThrough + /** + * Holds if data can flow from `node1` to `node2` via a direct assignment to + * `f`. + * + * This includes reverse steps through reads when the result of the read has + * been stored into, in order to handle cases like `x.f1.f2 = y`. + */ + cached + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) + } /** * Holds if the call context `call` either improves virtual dispatch in @@ -397,10 +433,13 @@ private module Cached { TBooleanNone() or TBooleanSome(boolean b) { b = true or b = false } + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } + cached newtype TAccessPathFront = TFrontNil(DataFlowType t) or - TFrontHead(Content f) + TFrontHead(TypedContent tc) cached newtype TAccessPathFrontOption = @@ -415,25 +454,38 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNodeExt + this instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + readStep(_, _, this) } } -newtype TContentOption = - TContentNone() or - TContentSome(Content f) +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + readStep(n1, c, n2) and + container = getNodeType(n1) and + content = getNodeType(n2) +} -private class ContentOption extends TContentOption { - Content getContent() { this = TContentSome(result) } +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) + } - predicate hasContent() { exists(this.getContent()) } +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } - string toString() { - result = this.getContent().toString() - or - not this.hasContent() and - result = "" - } + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } /** @@ -460,13 +512,19 @@ abstract class CallContext extends TCallContext { abstract predicate relevantFor(DataFlowCallable callable); } -class CallContextAny extends CallContext, TAnyCallContext { +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { override string toString() { result = "CcAny" } override predicate relevantFor(DataFlowCallable callable) { any() } } -abstract class CallContextCall extends CallContext { } +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} class CallContextSpecificCall extends CallContextCall, TSpecificCall { override string toString() { @@ -477,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { recordDataFlowCallSite(getCall(), callable) } + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + DataFlowCall getCall() { this = TSpecificCall(result) } } @@ -486,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override predicate relevantFor(DataFlowCallable callable) { exists(ParameterNode p | p.getEnclosingCallable() = callable) } + + override predicate matchesCall(DataFlowCall call) { any() } } -class CallContextReturn extends CallContext, TReturn { +class CallContextReturn extends CallContextNoCall, TReturn { override string toString() { exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") } @@ -678,9 +740,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -pragma[noinline] -DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } - predicate read = readStep/3; /** An optional Boolean value. */ @@ -692,6 +751,23 @@ class BooleanOption extends TBooleanOption { } } +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } +} + /** * The front of an access path. This is either a head or a nil. */ @@ -702,25 +778,36 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); - predicate headUsesContent(Content f) { this = TFrontHead(f) } + predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } + + predicate isClearedAt(Node n) { + exists(TypedContent tc | + this.headUsesContent(tc) and + clearsContent(n, tc.getContent()) + ) + } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - } + private DataFlowType t; + + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } - override DataFlowType getType() { this = TFrontNil(result) } + override DataFlowType getType() { result = t } override boolean toBoolNonEmpty() { result = false } } class AccessPathFrontHead extends AccessPathFront, TFrontHead { - override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + private TypedContent tc; - override DataFlowType getType() { - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } override boolean toBoolNonEmpty() { result = true } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll index 0dc3b8eff450..4e1cd281488f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll @@ -37,21 +37,12 @@ module Consistency { ) } - query predicate uniqueTypeBound(Node n, string msg) { + query predicate uniqueType(Node n, string msg) { exists(int c | n instanceof RelevantNode and - c = count(n.getTypeBound()) and + c = count(getNodeType(n)) and c != 1 and - msg = "Node should have one type bound but has " + c + "." - ) - } - - query predicate uniqueTypeRepr(Node n, string msg) { - exists(int c | - n instanceof RelevantNode and - c = count(getErasedRepr(n.getTypeBound())) and - c != 1 and - msg = "Node should have one type representation but has " + c + "." + msg = "Node should have one type but has " + c + "." ) } @@ -104,7 +95,7 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } - private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { t = typeRepr() and @@ -132,8 +123,18 @@ module Consistency { n.getEnclosingCallable() != call.getEnclosingCallable() } + // This predicate helps the compiler forget that in some languages + // it is impossible for a result of `getPreUpdateNode` to be an + // instance of `PostUpdateNode`. + private Node getPre(PostUpdateNode n) { + result = n.getPreUpdateNode() + or + none() + } + query predicate postIsNotPre(PostUpdateNode n, string msg) { - n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node." + getPre(n) = n and + msg = "PostUpdateNode should not equal its pre-update node." } query predicate postHasUniquePre(PostUpdateNode n, string msg) { @@ -161,15 +162,14 @@ module Consistency { msg = "Origin of readStep is missing a PostUpdateNode." } - query predicate storeIsPostUpdate(Node n, string msg) { - storeStep(_, _, n) and - not n instanceof PostUpdateNode and - msg = "Store targets should be PostUpdateNodes." - } - query predicate argHasPostUpdate(ArgumentNode n, string msg) { not hasPost(n) and not isImmutableOrUnobservable(n) and msg = "ArgumentNode is missing PostUpdateNode." } + + query predicate postWithInFlow(PostUpdateNode n, string msg) { + simpleLocalFlowStep(_, n) and + msg = "PostUpdateNode should not be the target of local flow." + } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 97118da117c8..e780c5c7eb36 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -5,35 +5,58 @@ private import DataFlowDispatch /** * A data flow node that occurs as the argument of a call and is passed as-is - * to the callable. Instance arguments (`this` pointer) are also included. + * to the callable. Instance arguments (`this` pointer) and read side effects + * on parameters are also included. */ -class ArgumentNode extends InstructionNode { - ArgumentNode() { - exists(CallInstruction call | - instr = call.getAnArgument() - or - instr.(ReadSideEffectInstruction).getPrimaryInstruction() = call - ) - } - +abstract class ArgumentNode extends OperandNode { /** * Holds if this argument occurs at the given position in the given call. * The instance argument is considered to have index `-1`. */ - predicate argumentOf(DataFlowCall call, int pos) { - instr = call.getPositionalArgument(pos) + abstract predicate argumentOf(DataFlowCall call, int pos); + + /** Gets the call in which this node is an argument. */ + DataFlowCall getCall() { this.argumentOf(result, _) } +} + +/** + * A data flow node that occurs as the argument to a call, or an + * implicit `this` pointer argument. + */ +private class PrimaryArgumentNode extends ArgumentNode { + override ArgumentOperand op; + + PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) } + + override predicate argumentOf(DataFlowCall call, int pos) { + op = call.getPositionalArgumentOperand(pos) or - instr = call.getThisArgument() and pos = -1 + op = call.getThisArgumentOperand() and pos = -1 + } + + override string toString() { + result = "Argument " + op.(PositionalArgumentOperand).getIndex() or - exists(ReadSideEffectInstruction read | - read = instr and - read.getPrimaryInstruction() = call and - pos = getArgumentPosOfSideEffect(read.getIndex()) - ) + op instanceof ThisArgumentOperand and result = "This argument" } +} - /** Gets the call in which this node is an argument. */ - DataFlowCall getCall() { this.argumentOf(result, _) } +/** + * A data flow node representing the read side effect of a call on a + * specific parameter. + */ +private class SideEffectArgumentNode extends ArgumentNode { + override SideEffectOperand op; + ReadSideEffectInstruction read; + + SideEffectArgumentNode() { op = read.getSideEffectOperand() } + + override predicate argumentOf(DataFlowCall call, int pos) { + read.getPrimaryInstruction() = call and + pos = getArgumentPosOfSideEffect(read.getIndex()) + } + + override string toString() { result = "Argument " + read.getIndex() + " indirection" } } private newtype TReturnKind = @@ -86,7 +109,12 @@ class ReturnValueNode extends ReturnNode { class ReturnIndirectionNode extends ReturnNode { override ReturnIndirectionInstruction primary; - override ReturnKind getKind() { result = TIndirectReturnKind(primary.getParameter().getIndex()) } + override ReturnKind getKind() { + result = TIndirectReturnKind(-1) and + primary.isThisIndirection() + or + result = TIndirectReturnKind(primary.getParameter().getIndex()) + } } /** A data flow node that represents the output of a call. */ @@ -123,8 +151,13 @@ private class SideEffectOutNode extends OutNode { * `kind`. */ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { - result.getCall() = call and - result.getReturnKind() = kind + // There should be only one `OutNode` for a given `(call, kind)` pair. Showing the optimizer that + // this is true helps it make better decisions downstream, especially in virtual dispatch. + result = + unique(OutNode outNode | + outNode.getCall() = call and + outNode.getReturnKind() = kind + ) } /** @@ -134,8 +167,23 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { */ predicate jumpStep(Node n1, Node n2) { none() } +/** + * Gets a field corresponding to the bit range `[startBit..endBit)` of class `c`, if any. + */ +private Field getAField(Class c, int startBit, int endBit) { + result.getDeclaringType() = c and + startBit = 8 * result.getByteOffset() and + endBit = 8 * result.getType().getSize() + startBit + or + exists(Field f, Class cInner | + f = c.getAField() and + cInner = f.getUnderlyingType() and + result = getAField(cInner, startBit - 8 * f.getByteOffset(), endBit - 8 * f.getByteOffset()) + ) +} + private newtype TContent = - TFieldContent(Field f) or + TFieldContent(Class c, int startBit, int endBit) { exists(getAField(c, startBit, endBit)) } or TCollectionContent() or TArrayContent() @@ -150,63 +198,92 @@ class Content extends TContent { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } - - /** Gets the type of the object containing this content. */ - abstract Type getContainerType(); - - /** Gets the type of this content. */ - abstract Type getType(); } private class FieldContent extends Content, TFieldContent { - Field f; - - FieldContent() { this = TFieldContent(f) } + Class c; + int startBit; + int endBit; - Field getField() { result = f } + FieldContent() { this = TFieldContent(c, startBit, endBit) } - override string toString() { result = f.toString() } + // Ensure that there's just 1 result for `toString`. + override string toString() { result = min(Field f | f = getAField() | f.toString()) } - override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - f.getLocation().hasLocationInfo(path, sl, sc, el, ec) - } - - override Type getContainerType() { result = f.getDeclaringType() } + predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit } - override Type getType() { result = f.getType() } + Field getAField() { result = getAField(c, startBit, endBit) } } private class CollectionContent extends Content, TCollectionContent { override string toString() { result = "collection" } - - override Type getContainerType() { none() } - - override Type getType() { none() } } private class ArrayContent extends Content, TArrayContent { - override string toString() { result = "array" } - - override Type getContainerType() { none() } + ArrayContent() { this = TArrayContent() } - override Type getType() { none() } + override string toString() { result = "array content" } } -private predicate storeStepNoChi(Node node1, Content f, PostUpdateNode node2) { - exists(FieldAddressInstruction fa, StoreInstruction store | +private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) { + exists(StoreInstruction store, Class c | store = node2.asInstruction() and - store.getDestinationAddress() = fa and store.getSourceValue() = node1.asInstruction() and - f.(FieldContent).getField() = fa.getField() + getWrittenField(store, f.(FieldContent).getAField(), c) and + f.hasOffset(c, _, _) + ) +} + +private FieldAddressInstruction getFieldInstruction(Instruction instr) { + result = instr or + result = instr.(CopyValueInstruction).getUnary() +} + +pragma[noinline] +private predicate getWrittenField(Instruction instr, Field f, Class c) { + exists(FieldAddressInstruction fa | + fa = + getFieldInstruction([instr.(StoreInstruction).getDestinationAddress(), + instr.(WriteSideEffectInstruction).getDestinationAddress()]) and + f = fa.getField() and + c = f.getDeclaringType() + ) +} + +private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode node2) { + exists(StoreInstruction store, ChiInstruction chi | + node1.asInstruction() = store and + node2.asInstruction() = chi and + chi.getPartial() = store and + exists(Class c | + c = chi.getResultType() and + exists(int startBit, int endBit | + chi.getUpdatedInterval(startBit, endBit) and + f.hasOffset(c, startBit, endBit) + ) + or + getWrittenField(store, f.getAField(), c) and + f.hasOffset(c, _, _) + ) ) } -private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) { - exists(FieldAddressInstruction fa, StoreInstruction store | +private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) { + a = TArrayContent() and + exists(StoreInstruction store | node1.asInstruction() = store and - store.getDestinationAddress() = fa and - node2.asInstruction().(ChiInstruction).getPartial() = store and - f.(FieldContent).getField() = fa.getField() + ( + // `x[i] = taint()` + // This matches the characteristic predicate in `ArrayStoreNode`. + store.getDestinationAddress() instanceof PointerAddInstruction + or + // `*p = taint()` + // This matches the characteristic predicate in `PointerStoreNode`. + store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction + ) and + // This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode` + // and `PointerStoreNode` require it in their characteristic predicates. + node2.asInstruction().(ChiInstruction).getPartial() = store ) } @@ -216,8 +293,37 @@ private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) { * value of `node1`. */ predicate storeStep(Node node1, Content f, PostUpdateNode node2) { - storeStepNoChi(node1, f, node2) or - storeStepChi(node1, f, node2) + fieldStoreStepNoChi(node1, f, node2) or + fieldStoreStepChi(node1, f, node2) or + arrayStoreStepChi(node1, f, node2) or + fieldStoreStepAfterArraySuppression(node1, f, node2) +} + +// This predicate pushes the correct `FieldContent` onto the access path when the +// `suppressArrayRead` predicate has popped off an `ArrayContent`. +private predicate fieldStoreStepAfterArraySuppression( + Node node1, FieldContent f, PostUpdateNode node2 +) { + exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi, Class c | + not chi.isResultConflated() and + node1.asInstruction() = chi and + node2.asInstruction() = chi and + chi.getPartial() = write and + getWrittenField(write, f.getAField(), c) and + f.hasOffset(c, _, _) + ) +} + +bindingset[result, i] +private int unbindInt(int i) { i <= result and i >= result } + +pragma[noinline] +private predicate getLoadedField(LoadInstruction load, Field f, Class c) { + exists(FieldAddressInstruction fa | + fa = load.getSourceAddress() and + f = fa.getField() and + c = f.getDeclaringType() + ) } /** @@ -225,38 +331,148 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) { * Thus, `node1` references an object with a field `f` whose value ends up in * `node2`. */ -predicate readStep(Node node1, Content f, Node node2) { - exists(FieldAddressInstruction fa, LoadInstruction load | - load.getSourceAddress() = fa and +private predicate fieldReadStep(Node node1, FieldContent f, Node node2) { + exists(LoadInstruction load | + node2.asInstruction() = load and + node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and + exists(Class c | + c = load.getSourceValueOperand().getAnyDef().getResultType() and + exists(int startBit, int endBit | + load.getSourceValueOperand().getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and + f.hasOffset(c, startBit, endBit) + ) + or + getLoadedField(load, f.getAField(), c) and + f.hasOffset(c, _, _) + ) + ) +} + +/** + * When a store step happens in a function that looks like an array write such as: + * ```cpp + * void f(int* pa) { + * pa = source(); + * } + * ``` + * it can be a write to an array, but it can also happen that `f` is called as `f(&a.x)`. If that is + * the case, the `ArrayContent` that was written by the call to `f` should be popped off the access + * path, and a `FieldContent` containing `x` should be pushed instead. + * So this case pops `ArrayContent` off the access path, and the `fieldStoreStepAfterArraySuppression` + * predicate in `storeStep` ensures that we push the right `FieldContent` onto the access path. + */ +predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) { + a = TArrayContent() and + exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi | + node1.asInstruction() = write and + node2.asInstruction() = chi and + chi.getPartial() = write and + getWrittenField(write, _, _) + ) +} + +private class ArrayToPointerConvertInstruction extends ConvertInstruction { + ArrayToPointerConvertInstruction() { + this.getUnary().getResultType() instanceof ArrayType and + this.getResultType() instanceof PointerType + } +} + +private Instruction skipOneCopyValueInstructionRec(CopyValueInstruction copy) { + copy.getUnary() = result and not result instanceof CopyValueInstruction + or + result = skipOneCopyValueInstructionRec(copy.getUnary()) +} + +private Instruction skipCopyValueInstructions(Instruction instr) { + not result instanceof CopyValueInstruction and result = instr + or + result = skipOneCopyValueInstructionRec(instr) +} + +private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) { + a = TArrayContent() and + // Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array. + exists(LoadInstruction load, Instruction address | + load.getSourceValueOperand().isDefinitionInexact() and node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and - fa.getField() = f.(FieldContent).getField() and - load = node2.asInstruction() + load = node2.asInstruction() and + address = skipCopyValueInstructions(load.getSourceAddress()) and + ( + address instanceof LoadInstruction or + address instanceof ArrayToPointerConvertInstruction or + address instanceof PointerOffsetInstruction + ) + ) +} + +/** + * In cases such as: + * ```cpp + * void f(int* pa) { + * *pa = source(); + * } + * ... + * int x; + * f(&x); + * use(x); + * ``` + * the load on `x` in `use(x)` will exactly overlap with its definition (in this case the definition + * is a `BufferMayWriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`) + * from the access path. + */ +private predicate exactReadStep(Node node1, ArrayContent a, Node node2) { + a = TArrayContent() and + exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi | + not chi.isResultConflated() and + chi.getPartial() = write and + node1.asInstruction() = write and + node2.asInstruction() = chi and + // To distinquish this case from the `arrayReadStep` case we require that the entire variable was + // overwritten by the `BufferMayWriteSideEffectInstruction` (i.e., there is a load that reads the + // entire variable). + exists(LoadInstruction load | load.getSourceValue() = chi) ) } /** - * Gets a representative (boxed) type for `t` for the purpose of pruning - * possible flow. A single type is used for all numeric types to account for - * numeric conversions, and otherwise the erasure is used. + * Holds if data can flow from `node1` to `node2` via a read of `f`. + * Thus, `node1` references an object with a field `f` whose value ends up in + * `node2`. + */ +predicate readStep(Node node1, Content f, Node node2) { + fieldReadStep(node1, f, node2) or + arrayReadStep(node1, f, node2) or + exactReadStep(node1, f, node2) or + suppressArrayRead(node1, f, node2) +} + +/** + * Holds if values stored inside content `c` are cleared at node `n`. */ -Type getErasedRepr(Type t) { - suppressUnusedType(t) and - result instanceof VoidType // stub implementation +predicate clearsContent(Node n, Content c) { + none() // stub implementation } -/** Gets a string representation of a type returned by `getErasedRepr`. */ -string ppReprType(Type t) { none() } // stub implementation +/** Gets the type of `n` used for type pruning. */ +IRType getNodeType(Node n) { + suppressUnusedNode(n) and + result instanceof IRVoidType // stub implementation +} + +/** Gets a string representation of a type returned by `getNodeType`. */ +string ppReprType(IRType t) { none() } // stub implementation /** * Holds if `t1` and `t2` are compatible, that is, whether data can flow from * a node of type `t1` to a node of type `t2`. */ pragma[inline] -predicate compatibleTypes(Type t1, Type t2) { +predicate compatibleTypes(IRType t1, IRType t2) { any() // stub implementation } -private predicate suppressUnusedType(Type t) { any() } +private predicate suppressUnusedNode(Node n) { any() } ////////////////////////////////////////////////////////////////////////////// // Java QL library compatibility wrappers @@ -276,7 +492,7 @@ class DataFlowCallable = Declaration; class DataFlowExpr = Expr; -class DataFlowType = Type; +class DataFlowType = IRType; /** A function call relevant for data flow. */ class DataFlowCall extends CallInstruction { @@ -306,3 +522,6 @@ predicate isImmutableOrUnobservable(Node n) { // complex to model here. any() } + +/** Holds if `n` should be hidden from path explanations. */ +predicate nodeIsHidden(Node n) { n instanceof OperandNode } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index beb3c8d954d1..1270b8d67da5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -11,8 +11,10 @@ private import semmle.code.cpp.ir.IR private import semmle.code.cpp.controlflow.IRGuards private import semmle.code.cpp.models.interfaces.DataFlow +cached private newtype TIRDataFlowNode = TInstructionNode(Instruction i) or + TOperandNode(Operand op) or TVariableNode(Variable var) /** @@ -32,15 +34,23 @@ class Node extends TIRDataFlowNode { Function getFunction() { none() } // overridden in subclasses /** Gets the type of this node. */ - Type getType() { none() } // overridden in subclasses + IRType getType() { none() } // overridden in subclasses /** Gets the instruction corresponding to this node, if any. */ Instruction asInstruction() { result = this.(InstructionNode).getInstruction() } + /** Gets the operands corresponding to this node, if any. */ + Operand asOperand() { result = this.(OperandNode).getOperand() } + /** - * Gets the non-conversion expression corresponding to this node, if any. If - * this node strictly (in the sense of `asConvertedExpr`) corresponds to a - * `Conversion`, then the result is that `Conversion`'s non-`Conversion` base + * Gets the non-conversion expression corresponding to this node, if any. + * This predicate only has a result on nodes that represent the value of + * evaluating the expression. For data flowing _out of_ an expression, like + * when an argument is passed by reference, use `asDefiningArgument` instead + * of `asExpr`. + * + * If this node strictly (in the sense of `asConvertedExpr`) corresponds to + * a `Conversion`, then the result is the underlying non-`Conversion` base * expression. */ Expr asExpr() { result = this.(ExprNode).getExpr() } @@ -51,7 +61,13 @@ class Node extends TIRDataFlowNode { */ Expr asConvertedExpr() { result = this.(ExprNode).getConvertedExpr() } - /** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */ + /** + * Gets the argument that defines this `DefinitionByReferenceNode`, if any. + * This predicate should be used instead of `asExpr` when referring to the + * value of a reference argument _after_ the call has returned. For example, + * in `f(&x)`, this predicate will have `&x` as its result for the `Node` + * that represents the new value of `x`. + */ Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() } /** Gets the positional parameter corresponding to this node, if any. */ @@ -59,7 +75,7 @@ class Node extends TIRDataFlowNode { /** * Gets the variable corresponding to this node, if any. This can be used for - * modelling flow in and out of global variables. + * modeling flow in and out of global variables. */ Variable asVariable() { result = this.(VariableNode).getVariable() } @@ -84,7 +100,7 @@ class Node extends TIRDataFlowNode { /** * Gets an upper bound on the type of this node. */ - Type getTypeBound() { result = getType() } + IRType getTypeBound() { result = getType() } /** Gets the location of this element. */ Location getLocation() { none() } // overridden by subclasses @@ -121,7 +137,7 @@ class InstructionNode extends Node, TInstructionNode { override Function getFunction() { result = instr.getEnclosingFunction() } - override Type getType() { result = instr.getResultType() } + override IRType getType() { result = instr.getResultIRType() } override Location getLocation() { result = instr.getLocation() } @@ -132,6 +148,28 @@ class InstructionNode extends Node, TInstructionNode { } } +/** + * An operand, viewed as a node in a data flow graph. + */ +class OperandNode extends Node, TOperandNode { + Operand op; + + OperandNode() { this = TOperandNode(op) } + + /** Gets the operand corresponding to this node. */ + Operand getOperand() { result = op } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override Function getFunction() { result = op.getUse().getEnclosingFunction() } + + override IRType getType() { result = op.getIRType() } + + override Location getLocation() { result = op.getLocation() } + + override string toString() { result = this.getOperand().toString() } +} + /** * An expression, viewed as a node in a data flow graph. */ @@ -291,29 +329,36 @@ abstract class PostUpdateNode extends InstructionNode { * setY(&x); // a partial definition of the object `x`. * ``` */ -abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode { +abstract private class PartialDefinitionNode extends PostUpdateNode { abstract Expr getDefinedExpr(); } private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode { override ChiInstruction instr; - FieldAddressInstruction field; + StoreInstruction store; ExplicitFieldStoreQualifierNode() { not instr.isResultConflated() and - exists(StoreInstruction store | - instr.getPartial() = store and field = store.getDestinationAddress() + instr.getPartial() = store and + ( + instr.getUpdatedInterval(_, _) or + store.getDestinationAddress() instanceof FieldAddressInstruction ) } - // There might be multiple `ChiInstructions` that has a particular instruction as - // the total operand - so this definition gives consistency errors in - // DataFlowImplConsistency::Consistency. However, it's not clear what (if any) implications - // this consistency failure has. - override Node getPreUpdateNode() { result.asInstruction() = instr.getTotal() } + // By using an operand as the result of this predicate we avoid the dataflow inconsistency errors + // caused by having multiple nodes sharing the same pre update node. This inconsistency error can cause + // a tuple explosion in the big step dataflow relation since it can make many nodes be the entry node + // into a big step. + override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() } override Expr getDefinedExpr() { - result = field.getObjectAddress().getUnconvertedResultExpression() + result = + store + .getDestinationAddress() + .(FieldAddressInstruction) + .getObjectAddress() + .getUnconvertedResultExpression() } } @@ -325,20 +370,93 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode { */ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode { override StoreInstruction instr; - FieldAddressInstruction field; ExplicitSingleFieldStoreQualifierNode() { - field = instr.getDestinationAddress() and - not exists(ChiInstruction chi | chi.getPartial() = instr) + not exists(ChiInstruction chi | chi.getPartial() = instr) and + // Without this condition any store would create a `PostUpdateNode`. + instr.getDestinationAddress() instanceof FieldAddressInstruction } override Node getPreUpdateNode() { none() } + override Expr getDefinedExpr() { + result = + instr + .getDestinationAddress() + .(FieldAddressInstruction) + .getObjectAddress() + .getUnconvertedResultExpression() + } +} + +private FieldAddressInstruction getFieldInstruction(Instruction instr) { + result = instr or + result = instr.(CopyValueInstruction).getUnary() +} + +/** + * The target of a `fieldStoreStepAfterArraySuppression` store step, which is used to convert + * an `ArrayContent` to a `FieldContent` when the `BufferMayWriteSideEffect` instruction stores + * into a field. See the QLDoc for `suppressArrayRead` for an example of where such a conversion + * is inserted. + */ +private class BufferMayWriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode { + override ChiInstruction instr; + BufferMayWriteSideEffectInstruction write; + FieldAddressInstruction field; + + BufferMayWriteSideEffectFieldStoreQualifierNode() { + not instr.isResultConflated() and + instr.getPartial() = write and + field = getFieldInstruction(write.getDestinationAddress()) + } + + override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() } + override Expr getDefinedExpr() { result = field.getObjectAddress().getUnconvertedResultExpression() } } +/** + * The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden + * `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`. + */ +private class ArrayStoreNode extends PartialDefinitionNode { + override ChiInstruction instr; + PointerAddInstruction add; + + ArrayStoreNode() { + not instr.isResultConflated() and + exists(StoreInstruction store | + instr.getPartial() = store and + add = store.getDestinationAddress() + ) + } + + override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() } + + override Expr getDefinedExpr() { result = add.getLeft().getUnconvertedResultExpression() } +} + +/** + * The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden + * `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`. + */ +private class PointerStoreNode extends PostUpdateNode { + override ChiInstruction instr; + + PointerStoreNode() { + not instr.isResultConflated() and + exists(StoreInstruction store | + instr.getPartial() = store and + store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction + ) + } + + override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() } +} + /** * A node that represents the value of a variable after a function call that * may have changed the variable because it's passed by reference. @@ -352,7 +470,7 @@ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNod class DefinitionByReferenceNode extends InstructionNode { override WriteSideEffectInstruction instr; - /** Gets the argument corresponding to this node. */ + /** Gets the unconverted argument corresponding to this node. */ Expr getArgument() { result = instr @@ -387,22 +505,10 @@ class DefinitionByReferenceNode extends InstructionNode { } } -/** - * A node representing the memory pointed to by a function argument. - * - * This class exists only in order to override `toString`, which would - * otherwise be the default implementation inherited from `InstructionNode`. - */ -private class ArgumentIndirectionNode extends InstructionNode { - override ReadSideEffectInstruction instr; - - override string toString() { result = "Argument " + instr.getIndex() + " indirection" } -} - /** * A `Node` corresponding to a variable in the program, as opposed to the * value of that variable at some particular point. This can be used for - * modelling flow in and out of global variables. + * modeling flow in and out of global variables. */ class VariableNode extends Node, TVariableNode { Variable v; @@ -423,7 +529,7 @@ class VariableNode extends Node, TVariableNode { result = v } - override Type getType() { result = v.getType() } + override IRType getType() { result.getCanonicalLanguageType().hasUnspecifiedType(v.getType(), _) } override Location getLocation() { result = v.getLocation() } @@ -436,20 +542,26 @@ class VariableNode extends Node, TVariableNode { InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr } /** + * DEPRECATED: use `definitionByReferenceNodeFromArgument` instead. + * * Gets the `Node` corresponding to a definition by reference of the variable * that is passed as `argument` of a call. */ -DefinitionByReferenceNode definitionByReferenceNode(Expr e) { result.getArgument() = e } +deprecated DefinitionByReferenceNode definitionByReferenceNode(Expr e) { result.getArgument() = e } /** - * Gets a `Node` corresponding to `e` or any of its conversions. There is no - * result if `e` is a `Conversion`. + * Gets the `Node` corresponding to the value of evaluating `e` or any of its + * conversions. There is no result if `e` is a `Conversion`. For data flowing + * _out of_ an expression, like when an argument is passed by reference, use + * `definitionByReferenceNodeFromArgument` instead. */ ExprNode exprNode(Expr e) { result.getExpr() = e } /** - * Gets the `Node` corresponding to `e`, if any. Here, `e` may be a - * `Conversion`. + * Gets the `Node` corresponding to the value of evaluating `e`. Here, `e` may + * be a `Conversion`. For data flowing _out of_ an expression, like when an + * argument is passed by reference, use + * `definitionByReferenceNodeFromArgument` instead. */ ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e } @@ -458,6 +570,14 @@ ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e } */ ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p } +/** + * Gets the `Node` corresponding to a definition by reference of the variable + * that is passed as unconverted `argument` of a call. + */ +DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) { + result.getArgument() = argument +} + /** Gets the `VariableNode` corresponding to the variable `v`. */ VariableNode variableNode(Variable v) { result.getVariable() = v } @@ -481,29 +601,39 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr * This is the local flow predicate that's used as a building block in global * data flow. It may have less flow than the `localFlowStep` predicate. */ +cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { - simpleInstructionLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asInstruction()) + // Operand -> Instruction flow + simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction()) + or + // Instruction -> Operand flow + simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand()) } pragma[noinline] private predicate getFieldSizeOfClass(Class c, Type type, int size) { exists(Field f | f.getDeclaringType() = c and - f.getType() = type and + f.getUnderlyingType() = type and type.getSize() = size ) } -cached -private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction iTo) { - iTo.(CopyInstruction).getSourceValue() = iFrom - or - iTo.(PhiInstruction).getAnOperand().getDef() = iFrom +private predicate isSingleFieldClass(Type type, Operand op) { + exists(int size, Class c | + c = op.getType().getUnderlyingType() and + c.getSize() = size and + getFieldSizeOfClass(c, type, size) + ) +} + +private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) { + // Propagate flow from an instruction to its exact uses. + opTo.getDef() = iFrom or - // A read side effect is almost never exact since we don't know exactly how - // much memory the callee will read. - iTo.(ReadSideEffectInstruction).getSideEffectOperand().getAnyDef() = iFrom and - not iFrom.isResultConflated() + opTo = any(ReadSideEffectInstruction read).getSideEffectOperand() and + not iFrom.isResultConflated() and + iFrom = opTo.getAnyDef() or // Loading a single `int` from an `int *` parameter is not an exact load since // the parameter may point to an entire array rather than a single `int`. The @@ -517,20 +647,33 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction // leads to a phi node. exists(InitializeIndirectionInstruction init | iFrom = init and - iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = init and + opTo.(LoadOperand).getAnyDef() = init and // Check that the types match. Otherwise we can get flow from an object to // its fields, which leads to field conflation when there's flow from other // fields to the object elsewhere. init.getParameter().getType().getUnspecifiedType().(DerivedType).getBaseType() = - iTo.getResultType().getUnspecifiedType() + opTo.getType().getUnspecifiedType() ) or + // Flow from stores to structs with a single field to a load of that field. + exists(LoadInstruction load | + load.getSourceValueOperand() = opTo and + opTo.getAnyDef() = iFrom and + isSingleFieldClass(iFrom.getResultType(), opTo) + ) +} + +private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) { + iTo.(CopyInstruction).getSourceValueOperand() = opFrom + or + iTo.(PhiInstruction).getAnInputOperand() = opFrom + or // Treat all conversions as flow, even conversions between different numeric types. - iTo.(ConvertInstruction).getUnary() = iFrom + iTo.(ConvertInstruction).getUnaryOperand() = opFrom or - iTo.(CheckedConvertOrNullInstruction).getUnary() = iFrom + iTo.(CheckedConvertOrNullInstruction).getUnaryOperand() = opFrom or - iTo.(InheritanceConversionInstruction).getUnary() = iFrom + iTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom or // A chi instruction represents a point where a new value (the _partial_ // operand) may overwrite an old value (the _total_ operand), but the alias @@ -543,7 +686,7 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction // // Flow through the partial operand belongs in the taint-tracking libraries // for now. - iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom + iTo.getAnOperand().(ChiTotalOperand) = opFrom or // Add flow from write side-effects to non-conflated chi instructions through their // partial operands. From there, a `readStep` will find subsequent reads of that field. @@ -558,23 +701,16 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction // Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to // `setX`, which will be melded into `p` through a chi instruction. exists(ChiInstruction chi | chi = iTo | - chi.getPartialOperand().getDef() = iFrom.(WriteSideEffectInstruction) and + opFrom.getAnyDef() instanceof WriteSideEffectInstruction and + chi.getPartialOperand() = opFrom and not chi.isResultConflated() ) or - // Flow from stores to structs with a single field to a load of that field. - iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = iFrom and - exists(int size, Type type | - type = iFrom.getResultType() and - iTo.getResultType().getSize() = size and - getFieldSizeOfClass(iTo.getResultType(), type, size) - ) - or // Flow through modeled functions - modelFlow(iFrom, iTo) + modelFlow(opFrom, iTo) } -private predicate modelFlow(Instruction iFrom, Instruction iTo) { +private predicate modelFlow(Operand opFrom, Instruction iTo) { exists( CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut | @@ -594,23 +730,33 @@ private predicate modelFlow(Instruction iFrom, Instruction iTo) { iTo = outNode and outNode = getSideEffectFor(call, index) ) - // TODO: add write side effects for qualifiers + or + exists(WriteSideEffectInstruction outNode | + modelOut.isQualifierObject() and + iTo = outNode and + outNode = getSideEffectFor(call, -1) + ) ) and ( exists(int index | modelIn.isParameter(index) and - iFrom = call.getPositionalArgument(index) + opFrom = call.getPositionalArgumentOperand(index) ) or exists(int index, ReadSideEffectInstruction read | modelIn.isParameterDeref(index) and read = getSideEffectFor(call, index) and - iFrom = read.getSideEffectOperand().getAnyDef() + opFrom = read.getSideEffectOperand() ) or modelIn.isQualifierAddress() and - iFrom = call.getThisArgument() - // TODO: add read side effects for qualifiers + opFrom = call.getThisArgumentOperand() + or + exists(ReadSideEffectInstruction read | + modelIn.isQualifierObject() and + read = getSideEffectFor(call, -1) and + opFrom = read.getSideEffectOperand() + ) ) ) } @@ -658,13 +804,20 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2) */ class BarrierGuard extends IRGuardCondition { /** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */ - abstract predicate checks(Instruction instr, boolean b); + predicate checksInstr(Instruction instr, boolean b) { none() } + + /** Override this predicate to hold if this guard validates `expr` upon evaluating to `b`. */ + predicate checks(Expr e, boolean b) { none() } /** Gets a node guarded by this guard. */ final Node getAGuardedNode() { exists(ValueNumber value, boolean edge | + ( + this.checksInstr(value.getAnInstruction(), edge) + or + this.checks(value.getAnInstruction().getConvertedResultExpression(), edge) + ) and result.asInstruction() = value.getAnInstruction() and - this.checks(value.getAnInstruction(), edge) and this.controls(result.asInstruction().getBlock(), edge) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll index d1cafb28f1c1..93d74519ca5c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll @@ -9,24 +9,31 @@ private import semmle.code.cpp.ir.dataflow.DataFlow /** * Gets the instruction that goes into `input` for `call`. */ -Instruction callInput(CallInstruction call, FunctionInput input) { +DataFlow::Node callInput(CallInstruction call, FunctionInput input) { // A positional argument exists(int index | - result = call.getPositionalArgument(index) and + result.asInstruction() = call.getPositionalArgument(index) and input.isParameter(index) ) or // A value pointed to by a positional argument exists(ReadSideEffectInstruction read | - result = read and + result.asOperand() = read.getSideEffectOperand() and read.getPrimaryInstruction() = call and input.isParameterDeref(read.getIndex()) ) or // The qualifier pointer - result = call.getThisArgument() and + result.asInstruction() = call.getThisArgument() and input.isQualifierAddress() - //TODO: qualifier deref + or + // The qualifier object + exists(ReadSideEffectInstruction read | + result.asOperand() = read.getSideEffectOperand() and + read.getPrimaryInstruction() = call and + read.getIndex() = -1 and + input.isQualifierObject() + ) } /** @@ -43,5 +50,13 @@ Instruction callOutput(CallInstruction call, FunctionOutput output) { effect.getPrimaryInstruction() = call and output.isParameterDeref(effect.getIndex()) ) - // TODO: qualifiers, return value dereference + or + // The side effect of a call on the qualifier object + exists(WriteSideEffectInstruction effect | + result = effect and + effect.getPrimaryInstruction() = call and + effect.getIndex() = -1 and + output.isQualifierObject() + ) + // TODO: return value dereference } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll new file mode 100644 index 000000000000..edbb11db2f37 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll @@ -0,0 +1,152 @@ +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil + +/** + * Gets a short ID for an IR dataflow node. + * - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`). + * - For `Operand`s, this is the label of the operand, prefixed with the result ID of the + * instruction and a dot (e.g. `m128.left`). + * - For `Variable`s, this is the qualified name of the variable. + */ +private string nodeId(DataFlow::Node node, int order1, int order2) { + exists(Instruction instruction | instruction = node.asInstruction() | + result = instruction.getResultId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + exists(Operand operand, Instruction instruction | + operand = node.asOperand() and + instruction = operand.getUse() + | + result = instruction.getResultId() + "." + operand.getDumpId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + result = "var(" + node.asVariable().getQualifiedName() + ")" and + order1 = 1000000 and + order2 = 0 +} + +/** + * Gets the local dataflow from other nodes in the same function to this node. + */ +private string getFromFlow(DataFlow::Node useNode, int order1, int order2) { + exists(DataFlow::Node defNode, string prefix | + ( + simpleLocalFlowStep(defNode, useNode) and prefix = "" + or + any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and + defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and + prefix = "+" + ) and + if defNode.asInstruction() = useNode.asOperand().getAnyDef() + then + // Shorthand for flow from the def of this operand. + result = prefix + "def" and + order1 = -1 and + order2 = 0 + else + if defNode.asOperand().getUse() = useNode.asInstruction() + then + // Shorthand for flow from an operand of this instruction + result = prefix + defNode.asOperand().getDumpId() and + order1 = -1 and + order2 = defNode.asOperand().getDumpSortOrder() + else result = prefix + nodeId(defNode, order1, order2) + ) +} + +/** + * Gets the local dataflow from this node to other nodes in the same function. + */ +private string getToFlow(DataFlow::Node defNode, int order1, int order2) { + exists(DataFlow::Node useNode, string prefix | + ( + simpleLocalFlowStep(defNode, useNode) and prefix = "" + or + any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and + defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and + prefix = "+" + ) and + if useNode.asInstruction() = defNode.asOperand().getUse() + then + // Shorthand for flow to this operand's instruction. + result = prefix + "result" and + order1 = -1 and + order2 = 0 + else result = prefix + nodeId(useNode, order1, order2) + ) +} + +/** + * Gets the properties of the dataflow node `node`. + */ +private string getNodeProperty(DataFlow::Node node, string key) { + // List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow + // out of this node is printed as `@->dest`. + key = "flow" and + result = + strictconcat(string flow, boolean to, int order1, int order2 | + flow = getFromFlow(node, order1, order2) + "->@" and to = false + or + flow = "@->" + getToFlow(node, order1, order2) and to = true + | + flow, ", " order by to, order1, order2, flow + ) + or + // Is this node a dataflow sink? + key = "sink" and + any(DataFlow::Configuration cfg).isSink(node) and + result = "true" + or + // Is this node a dataflow source? + key = "source" and + any(DataFlow::Configuration cfg).isSource(node) and + result = "true" + or + // Is this node a dataflow barrier, and if so, what kind? + key = "barrier" and + result = + strictconcat(string kind | + any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full" + or + any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in" + or + any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out" + or + exists(DataFlow::BarrierGuard guard | + any(DataFlow::Configuration cfg).isBarrierGuard(guard) and + node = guard.getAGuardedNode() and + kind = "guard(" + guard.getResultId() + ")" + ) + | + kind, ", " + ) +} + +/** + * Property provider for local IR dataflow. + */ +class LocalFlowPropertyProvider extends IRPropertyProvider { + override string getOperandProperty(Operand operand, string key) { + exists(DataFlow::Node node | + operand = node.asOperand() and + result = getNodeProperty(node, key) + ) + } + + override string getInstructionProperty(Instruction instruction, string key) { + exists(DataFlow::Node node | + instruction = node.asInstruction() and + result = getNodeProperty(node, key) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll index 2290bab05713..202d3f1c14ef 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll @@ -19,8 +19,11 @@ predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent * different objects. */ +cached predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { localInstructionTaintStep(nodeFrom.asInstruction(), nodeTo.asInstruction()) + or + modeledTaintStep(nodeFrom, nodeTo) } /** @@ -49,8 +52,6 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no or nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom or - modeledInstructionTaintStep(nodeFrom, nodeTo) - or // Flow through partial reads of arrays and unions nodeTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = nodeFrom and not nodeFrom.isResultConflated() and @@ -100,19 +101,26 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { } /** - * Holds if `node` should be a barrier in all global taint flow configurations + * Holds if `node` should be a sanitizer in all global taint flow configurations * but not in local taint. */ -predicate defaultTaintBarrier(DataFlow::Node node) { none() } +predicate defaultTaintSanitizer(DataFlow::Node node) { none() } /** * Holds if taint can flow from `instrIn` to `instrOut` through a call to a * modeled function. */ -predicate modeledInstructionTaintStep(Instruction instrIn, Instruction instrOut) { +predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) { exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut | - instrIn = callInput(call, modelIn) and - instrOut = callOutput(call, modelOut) and + ( + nodeIn = callInput(call, modelIn) + or + exists(int n | + modelIn.isParameterDeref(n) and + nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n)) + ) + ) and + nodeOut.asInstruction() = callOutput(call, modelOut) and call.getStaticCallTarget() = func and func.hasTaintFlow(modelIn, modelOut) ) @@ -126,8 +134,8 @@ predicate modeledInstructionTaintStep(Instruction instrIn, Instruction instrOut) CallInstruction call, Function func, FunctionInput modelIn, OutParameterDeref modelMidOut, int indexMid, InParameter modelMidIn, OutReturnValue modelOut | - instrIn = callInput(call, modelIn) and - instrOut = callOutput(call, modelOut) and + nodeIn = callInput(call, modelIn) and + nodeOut.asInstruction() = callOutput(call, modelOut) and call.getStaticCallTarget() = func and func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll index 54059fb5b82f..32e36bb67876 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that specify the conditions under which control flows along a given edge. + */ + private import internal.EdgeKindInternal private newtype TEdgeKind = @@ -77,9 +81,15 @@ class CaseEdge extends EdgeKind, TCaseEdge { else result = "Case[" + minValue + ".." + maxValue + "]" } - string getMinValue() { result = minValue } + /** + * Gets the smallest value of the switch expression for which control will flow along this edge. + */ + final string getMinValue() { result = minValue } - string getMaxValue() { result = maxValue } + /** + * Gets the largest value of the switch expression for which control will flow along this edge. + */ + final string getMaxValue() { result = maxValue } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll index 71bc8ec2b0f2..37ac2fccdd94 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll @@ -10,6 +10,7 @@ private newtype TIRConfiguration = MkIRConfiguration() * The query can extend this class to control which functions have IR generated for them. */ class IRConfiguration extends TIRConfiguration { + /** Gets a textual representation of this element. */ string toString() { result = "IRConfiguration" } /** @@ -17,6 +18,13 @@ class IRConfiguration extends TIRConfiguration { */ predicate shouldCreateIRForFunction(Language::Function func) { any() } + /** + * Holds if the strings used as part of an IR dump should be generated for function `func`. + * + * This predicate is overridden in `PrintIR.qll` to avoid the expense of generating a large number + * of debug strings for IR that will not be dumped. We still generate the actual IR for these + * functions, however, to preserve the results of any interprocedural analysis. + */ predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() } } @@ -26,6 +34,7 @@ private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration * The query can extend this class to control what escape analysis is used when generating SSA. */ class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration { + /** Gets a textual representation of this element. */ string toString() { result = "IREscapeAnalysisConfiguration" } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll index d196cdce0ab2..3bf3bf2e2766 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll @@ -32,6 +32,7 @@ private newtype TIRType = * all pointer types map to the same instance of `IRAddressType`. */ class IRType extends TIRType { + /** Gets a textual representation of this type. */ string toString() { none() } /** @@ -111,6 +112,8 @@ private class IRSizedType extends IRType { this = TIRFunctionAddressType(byteSize) or this = TIROpaqueType(_, byteSize) } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** @@ -128,7 +131,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType { } /** - * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and + * A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and * `IRFloatingPointType`. */ class IRNumericType extends IRSizedType { @@ -137,13 +140,33 @@ class IRNumericType extends IRSizedType { this = TIRUnsignedIntegerType(byteSize) or this = TIRFloatingPointType(byteSize, _, _) } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. +} + +/** + * An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`. + */ +class IRIntegerType extends IRNumericType { + IRIntegerType() { + this = TIRSignedIntegerType(byteSize) or + this = TIRUnsignedIntegerType(byteSize) + } + + /** Holds if this integer type is signed. */ + predicate isSigned() { none() } + + /** Holds if this integer type is unsigned. */ + predicate isUnsigned() { none() } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed * integer, as well as character types whose representation is signed. */ -class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType { +class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType { final override string toString() { result = "int" + byteSize.toString() } final override Language::LanguageType getCanonicalLanguageType() { @@ -152,13 +175,15 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType { pragma[noinline] final override int getByteSize() { result = byteSize } + + override predicate isSigned() { any() } } /** * An unsigned two's-complement integer. Also used to represent enums whose underlying type is an * unsigned integer, as well as character types whose representation is unsigned. */ -class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType { +class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType { final override string toString() { result = "uint" + byteSize.toString() } final override Language::LanguageType getCanonicalLanguageType() { @@ -167,6 +192,8 @@ class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType { pragma[noinline] final override int getByteSize() { result = byteSize } + + override predicate isUnsigned() { any() } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll index 6852a9654017..5e11a310e2fb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll @@ -1,3 +1,9 @@ +/** + * Provides classes that describe how a particular `Instruction` or its operands access memory. + */ + +private import IRConfiguration + private newtype TMemoryAccessKind = TIndirectMemoryAccess() or TBufferMemoryAccess() or @@ -14,6 +20,7 @@ private newtype TMemoryAccessKind = * memory result. */ class MemoryAccessKind extends TMemoryAccessKind { + /** Gets a textual representation of this access kind. */ string toString() { none() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index c0b8adbe56be..c4134d240aba 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -1,3 +1,8 @@ +/** + * Provides `Opcode`s that specify the operation performed by an `Instruction`, as well as metadata + * about those opcodes, such as operand kinds and memory accesses. + */ + private import internal.OpcodeImports as Imports private import internal.OperandTag import Imports::MemoryAccessKind @@ -45,7 +50,7 @@ private newtype TOpcode = TConvertToDerived() or TCheckedConvertOrNull() or TCheckedConvertOrThrow() or - TDynamicCastToVoid() or + TCompleteObjectAddress() or TVariableAddress() or TFieldAddress() or TFunctionAddress() or @@ -86,7 +91,11 @@ private newtype TOpcode = TUnreached() or TNewObj() +/** + * An opcode that specifies the operation performed by an `Instruction`. + */ class Opcode extends TOpcode { + /** Gets a textual representation of this element. */ string toString() { result = "UnknownOpcode" } /** @@ -139,10 +148,20 @@ class Opcode extends TOpcode { predicate hasOperandInternal(OperandTag tag) { none() } } +/** + * The `Opcode` for a `UnaryInstruction`. + * + * See the `UnaryInstruction` documentation for more details. + */ abstract class UnaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag } } +/** + * The `Opcode` for a `BinaryInstruction`. + * + * See the `BinaryInstruction` documentation for more details. + */ abstract class BinaryOpcode extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof LeftOperandTag or @@ -150,44 +169,127 @@ abstract class BinaryOpcode extends Opcode { } } +/** + * The `Opcode` for a `PointerArithmeticInstruction`. + * + * See the `PointerArithmeticInstruction` documentation for more details. + */ abstract class PointerArithmeticOpcode extends BinaryOpcode { } +/** + * The `Opcode` for a `PointerOffsetInstruction`. + * + * See the `PointerOffsetInstruction` documentation for more details. + */ abstract class PointerOffsetOpcode extends PointerArithmeticOpcode { } +/** + * The `Opcode` for an `ArithmeticInstruction`. + * + * See the `ArithmeticInstruction` documentation for more details. + */ abstract class ArithmeticOpcode extends Opcode { } +/** + * The `Opcode` for a `BinaryArithmeticInstruction`. + * + * See the `BinaryArithmeticInstruction` documentation for more details. + */ abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode { } +/** + * The `Opcode` for a `UnaryArithmeticInstruction`. + * + * See the `UnaryArithmeticInstruction` documentation for more details. + */ abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode { } +/** + * The `Opcode` for a `BitwiseInstruction`. + * + * See the `BitwiseInstruction` documentation for more details. + */ abstract class BitwiseOpcode extends Opcode { } +/** + * The `Opcode` for a `BinaryBitwiseInstruction`. + * + * See the `BinaryBitwiseInstruction` documentation for more details. + */ abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode { } +/** + * The `Opcode` for a `UnaryBitwiseInstruction`. + * + * See the `UnaryBitwiseInstruction` documentation for more details. + */ abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode { } +/** + * The `Opcode` for a `CompareInstruction`. + * + * See the `CompareInstruction` documentation for more details. + */ abstract class CompareOpcode extends BinaryOpcode { } +/** + * The `Opcode` for a `RelationalInstruction`. + * + * See the `RelationalInstruction` documentation for more details. + */ abstract class RelationalOpcode extends CompareOpcode { } +/** + * The `Opcode` for a `CopyInstruction`. + * + * See the `CopyInstruction` documentation for more details. + */ abstract class CopyOpcode extends Opcode { } +/** + * The `Opcode` for a `ConvertToBaseInstruction`. + * + * See the `ConvertToBaseInstruction` documentation for more details. + */ abstract class ConvertToBaseOpcode extends UnaryOpcode { } -abstract class MemoryAccessOpcode extends Opcode { } - +/** + * The `Opcode` for a `ReturnInstruction`. + * + * See the `ReturnInstruction` documentation for more details. + */ abstract class ReturnOpcode extends Opcode { } +/** + * The `Opcode` for a `ThrowInstruction`. + * + * See the `ThrowInstruction` documentation for more details. + */ abstract class ThrowOpcode extends Opcode { } +/** + * The `Opcode` for a `CatchInstruction`. + * + * See the `CatchInstruction` documentation for more details. + */ abstract class CatchOpcode extends Opcode { } -abstract class OpcodeWithCondition extends Opcode { +abstract private class OpcodeWithCondition extends Opcode { final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag } } +/** + * The `Opcode` for a `BuiltInOperationInstruction`. + * + * See the `BuiltInOperationInstruction` documentation for more details. + */ abstract class BuiltInOperationOpcode extends Opcode { } +/** + * The `Opcode` for a `SideEffectInstruction`. + * + * See the `SideEffectInstruction` documentation for more details. + */ abstract class SideEffectOpcode extends Opcode { } /** @@ -323,7 +425,9 @@ abstract class OpcodeWithLoad extends IndirectReadOpcode { } /** - * An opcode that reads from a set of memory locations as a side effect. + * The `Opcode` for a `ReadSideEffectInstruction`. + * + * See the `ReadSideEffectInstruction` documentation for more details. */ abstract class ReadSideEffectOpcode extends SideEffectOpcode { final override predicate hasOperandInternal(OperandTag tag) { @@ -332,51 +436,111 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { } /** - * An opcode that writes to a set of memory locations as a side effect. + * The `Opcode` for a `WriteSideEffectInstruction`. + * + * See the `WriteSideEffectInstruction` documentation for more details. */ abstract class WriteSideEffectOpcode extends SideEffectOpcode { } +/** + * Provides `Opcode`s that specify the operation performed by an `Instruction`. + */ module Opcode { + /** + * The `Opcode` for a `NoOpInstruction`. + * + * See the `NoOpInstruction` documentation for more details. + */ class NoOp extends Opcode, TNoOp { final override string toString() { result = "NoOp" } } + /** + * The `Opcode` for an `UninitializedInstruction`. + * + * See the `UninitializedInstruction` documentation for more details. + */ class Uninitialized extends IndirectWriteOpcode, TUninitialized { final override string toString() { result = "Uninitialized" } } + /** + * The `Opcode` for an `ErrorInstruction`. + * + * See the `ErrorInstruction` documentation for more details. + */ class Error extends Opcode, TError { final override string toString() { result = "Error" } } + /** + * The `Opcode` for an `InitializeParameterInstruction`. + * + * See the `InitializeParameterInstruction` documentation for more details. + */ class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter { final override string toString() { result = "InitializeParameter" } } + /** + * The `Opcode` for an `InitializeIndirectionInstruction`. + * + * See the `InitializeIndirectionInstruction` documentation for more details. + */ class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { final override string toString() { result = "InitializeIndirection" } } + /** + * The `Opcode` for an `InitializeThisInstruction`. + * + * See the `InitializeThisInstruction` documentation for more details. + */ class InitializeThis extends Opcode, TInitializeThis { final override string toString() { result = "InitializeThis" } } + /** + * The `Opcode` for an `EnterFunctionInstruction`. + * + * See the `EnterFunctionInstruction` documentation for more details. + */ class EnterFunction extends Opcode, TEnterFunction { final override string toString() { result = "EnterFunction" } } + /** + * The `Opcode` for an `ExitFunctionInstruction`. + * + * See the `ExitFunctionInstruction` documentation for more details. + */ class ExitFunction extends Opcode, TExitFunction { final override string toString() { result = "ExitFunction" } } + /** + * The `Opcode` for a `ReturnValueInstruction`. + * + * See the `ReturnValueInstruction` documentation for more details. + */ class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue { final override string toString() { result = "ReturnValue" } } + /** + * The `Opcode` for a `ReturnVoidInstruction`. + * + * See the `ReturnVoidInstruction` documentation for more details. + */ class ReturnVoid extends ReturnOpcode, TReturnVoid { final override string toString() { result = "ReturnVoid" } } + /** + * The `Opcode` for a `ReturnIndirectionInstruction`. + * + * See the `ReturnIndirectionInstruction` documentation for more details. + */ class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { final override string toString() { result = "ReturnIndirection" } @@ -385,14 +549,29 @@ module Opcode { } } + /** + * The `Opcode` for a `CopyValueInstruction`. + * + * See the `CopyValueInstruction` documentation for more details. + */ class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { final override string toString() { result = "CopyValue" } } + /** + * The `Opcode` for a `LoadInstruction`. + * + * See the `LoadInstruction` documentation for more details. + */ class Load extends CopyOpcode, OpcodeWithLoad, TLoad { final override string toString() { result = "Load" } } + /** + * The `Opcode` for a `StoreInstruction`. + * + * See the `StoreInstruction` documentation for more details. + */ class Store extends CopyOpcode, IndirectWriteOpcode, TStore { final override string toString() { result = "Store" } @@ -401,154 +580,344 @@ module Opcode { } } + /** + * The `Opcode` for an `AddInstruction`. + * + * See the `AddInstruction` documentation for more details. + */ class Add extends BinaryArithmeticOpcode, TAdd { final override string toString() { result = "Add" } } + /** + * The `Opcode` for a `SubInstruction`. + * + * See the `SubInstruction` documentation for more details. + */ class Sub extends BinaryArithmeticOpcode, TSub { final override string toString() { result = "Sub" } } + /** + * The `Opcode` for a `MulInstruction`. + * + * See the `MulInstruction` documentation for more details. + */ class Mul extends BinaryArithmeticOpcode, TMul { final override string toString() { result = "Mul" } } + /** + * The `Opcode` for a `DivInstruction`. + * + * See the `DivInstruction` documentation for more details. + */ class Div extends BinaryArithmeticOpcode, TDiv { final override string toString() { result = "Div" } } + /** + * The `Opcode` for a `RemInstruction`. + * + * See the `RemInstruction` documentation for more details. + */ class Rem extends BinaryArithmeticOpcode, TRem { final override string toString() { result = "Rem" } } + /** + * The `Opcode` for a `NegateInstruction`. + * + * See the `NegateInstruction` documentation for more details. + */ class Negate extends UnaryArithmeticOpcode, TNegate { final override string toString() { result = "Negate" } } + /** + * The `Opcode` for a `ShiftLeftInstruction`. + * + * See the `ShiftLeftInstruction` documentation for more details. + */ class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { final override string toString() { result = "ShiftLeft" } } + /** + * The `Opcode` for a `ShiftRightInstruction`. + * + * See the `ShiftRightInstruction` documentation for more details. + */ class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { final override string toString() { result = "ShiftRight" } } + /** + * The `Opcode` for a `BitAndInstruction`. + * + * See the `BitAndInstruction` documentation for more details. + */ class BitAnd extends BinaryBitwiseOpcode, TBitAnd { final override string toString() { result = "BitAnd" } } + /** + * The `Opcode` for a `BitOrInstruction`. + * + * See the `BitOrInstruction` documentation for more details. + */ class BitOr extends BinaryBitwiseOpcode, TBitOr { final override string toString() { result = "BitOr" } } + /** + * The `Opcode` for a `BitXorInstruction`. + * + * See the `BitXorInstruction` documentation for more details. + */ class BitXor extends BinaryBitwiseOpcode, TBitXor { final override string toString() { result = "BitXor" } } + /** + * The `Opcode` for a `BitComplementInstruction`. + * + * See the `BitComplementInstruction` documentation for more details. + */ class BitComplement extends UnaryBitwiseOpcode, TBitComplement { final override string toString() { result = "BitComplement" } } + /** + * The `Opcode` for a `LogicalNotInstruction`. + * + * See the `LogicalNotInstruction` documentation for more details. + */ class LogicalNot extends UnaryOpcode, TLogicalNot { final override string toString() { result = "LogicalNot" } } + /** + * The `Opcode` for a `CompareEQInstruction`. + * + * See the `CompareEQInstruction` documentation for more details. + */ class CompareEQ extends CompareOpcode, TCompareEQ { final override string toString() { result = "CompareEQ" } } + /** + * The `Opcode` for a `CompareNEInstruction`. + * + * See the `CompareNEInstruction` documentation for more details. + */ class CompareNE extends CompareOpcode, TCompareNE { final override string toString() { result = "CompareNE" } } + /** + * The `Opcode` for a `CompareLTInstruction`. + * + * See the `CompareLTInstruction` documentation for more details. + */ class CompareLT extends RelationalOpcode, TCompareLT { final override string toString() { result = "CompareLT" } } + /** + * The `Opcode` for a `CompareGTInstruction`. + * + * See the `CompareGTInstruction` documentation for more details. + */ class CompareGT extends RelationalOpcode, TCompareGT { final override string toString() { result = "CompareGT" } } + /** + * The `Opcode` for a `CompareLEInstruction`. + * + * See the `CompareLEInstruction` documentation for more details. + */ class CompareLE extends RelationalOpcode, TCompareLE { final override string toString() { result = "CompareLE" } } + /** + * The `Opcode` for a `CompareGEInstruction`. + * + * See the `CompareGEInstruction` documentation for more details. + */ class CompareGE extends RelationalOpcode, TCompareGE { final override string toString() { result = "CompareGE" } } + /** + * The `Opcode` for a `PointerAddInstruction`. + * + * See the `PointerAddInstruction` documentation for more details. + */ class PointerAdd extends PointerOffsetOpcode, TPointerAdd { final override string toString() { result = "PointerAdd" } } + /** + * The `Opcode` for a `PointerSubInstruction`. + * + * See the `PointerSubInstruction` documentation for more details. + */ class PointerSub extends PointerOffsetOpcode, TPointerSub { final override string toString() { result = "PointerSub" } } + /** + * The `Opcode` for a `PointerDiffInstruction`. + * + * See the `PointerDiffInstruction` documentation for more details. + */ class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { final override string toString() { result = "PointerDiff" } } + /** + * The `Opcode` for a `ConvertInstruction`. + * + * See the `ConvertInstruction` documentation for more details. + */ class Convert extends UnaryOpcode, TConvert { final override string toString() { result = "Convert" } } + /** + * The `Opcode` for a `ConvertToNonVirtualBaseInstruction`. + * + * See the `ConvertToNonVirtualBaseInstruction` documentation for more details. + */ class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase { final override string toString() { result = "ConvertToNonVirtualBase" } } + /** + * The `Opcode` for a `ConvertToVirtualBaseInstruction`. + * + * See the `ConvertToVirtualBaseInstruction` documentation for more details. + */ class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase { final override string toString() { result = "ConvertToVirtualBase" } } + /** + * The `Opcode` for a `ConvertToDerivedInstruction`. + * + * See the `ConvertToDerivedInstruction` documentation for more details. + */ class ConvertToDerived extends UnaryOpcode, TConvertToDerived { final override string toString() { result = "ConvertToDerived" } } + /** + * The `Opcode` for a `CheckedConvertOrNullInstruction`. + * + * See the `CheckedConvertOrNullInstruction` documentation for more details. + */ class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { final override string toString() { result = "CheckedConvertOrNull" } } + /** + * The `Opcode` for a `CheckedConvertOrThrowInstruction`. + * + * See the `CheckedConvertOrThrowInstruction` documentation for more details. + */ class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { final override string toString() { result = "CheckedConvertOrThrow" } } - class DynamicCastToVoid extends UnaryOpcode, TDynamicCastToVoid { - final override string toString() { result = "DynamicCastToVoid" } + /** + * The `Opcode` for a `CompleteObjectAddressInstruction`. + * + * See the `CompleteObjectAddressInstruction` documentation for more details. + */ + class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress { + final override string toString() { result = "CompleteObjectAddress" } } + /** + * The `Opcode` for a `VariableAddressInstruction`. + * + * See the `VariableAddressInstruction` documentation for more details. + */ class VariableAddress extends Opcode, TVariableAddress { final override string toString() { result = "VariableAddress" } } + /** + * The `Opcode` for a `FieldAddressInstruction`. + * + * See the `FieldAddressInstruction` documentation for more details. + */ class FieldAddress extends UnaryOpcode, TFieldAddress { final override string toString() { result = "FieldAddress" } } + /** + * The `Opcode` for an `ElementsAddressInstruction`. + * + * See the `ElementsAddressInstruction` documentation for more details. + */ class ElementsAddress extends UnaryOpcode, TElementsAddress { final override string toString() { result = "ElementsAddress" } } + /** + * The `Opcode` for a `FunctionAddressInstruction`. + * + * See the `FunctionAddressInstruction` documentation for more details. + */ class FunctionAddress extends Opcode, TFunctionAddress { final override string toString() { result = "FunctionAddress" } } + /** + * The `Opcode` for a `ConstantInstruction`. + * + * See the `ConstantInstruction` documentation for more details. + */ class Constant extends Opcode, TConstant { final override string toString() { result = "Constant" } } + /** + * The `Opcode` for a `StringConstantInstruction`. + * + * See the `StringConstantInstruction` documentation for more details. + */ class StringConstant extends Opcode, TStringConstant { final override string toString() { result = "StringConstant" } } + /** + * The `Opcode` for a `ConditionalBranchInstruction`. + * + * See the `ConditionalBranchInstruction` documentation for more details. + */ class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { final override string toString() { result = "ConditionalBranch" } } + /** + * The `Opcode` for a `SwitchInstruction`. + * + * See the `SwitchInstruction` documentation for more details. + */ class Switch extends OpcodeWithCondition, TSwitch { final override string toString() { result = "Switch" } } + /** + * The `Opcode` for a `CallInstruction`. + * + * See the `CallInstruction` documentation for more details. + */ class Call extends Opcode, TCall { final override string toString() { result = "Call" } @@ -557,32 +926,67 @@ module Opcode { } } + /** + * The `Opcode` for a `CatchByTypeInstruction`. + * + * See the `CatchByTypeInstruction` documentation for more details. + */ class CatchByType extends CatchOpcode, TCatchByType { final override string toString() { result = "CatchByType" } } + /** + * The `Opcode` for a `CatchAnyInstruction`. + * + * See the `CatchAnyInstruction` documentation for more details. + */ class CatchAny extends CatchOpcode, TCatchAny { final override string toString() { result = "CatchAny" } } + /** + * The `Opcode` for a `ThrowValueInstruction`. + * + * See the `ThrowValueInstruction` documentation for more details. + */ class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue { final override string toString() { result = "ThrowValue" } } + /** + * The `Opcode` for a `ReThrowInstruction`. + * + * See the `ReThrowInstruction` documentation for more details. + */ class ReThrow extends ThrowOpcode, TReThrow { final override string toString() { result = "ReThrow" } } + /** + * The `Opcode` for an `UnwindInstruction`. + * + * See the `UnwindInstruction` documentation for more details. + */ class Unwind extends Opcode, TUnwind { final override string toString() { result = "Unwind" } } + /** + * The `Opcode` for an `AliasedDefinitionInstruction`. + * + * See the `AliasedDefinitionInstruction` documentation for more details. + */ class AliasedDefinition extends Opcode, TAliasedDefinition { final override string toString() { result = "AliasedDefinition" } final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess } } + /** + * The `Opcode` for an `InitializeNonLocalInstruction`. + * + * See the `InitializeNonLocalInstruction` documentation for more details. + */ class InitializeNonLocal extends Opcode, TInitializeNonLocal { final override string toString() { result = "InitializeNonLocal" } @@ -591,6 +995,11 @@ module Opcode { } } + /** + * The `Opcode` for an `AliasedUseInstruction`. + * + * See the `AliasedUseInstruction` documentation for more details. + */ class AliasedUse extends Opcode, TAliasedUse { final override string toString() { result = "AliasedUse" } @@ -601,92 +1010,187 @@ module Opcode { } } + /** + * The `Opcode` for a `PhiInstruction`. + * + * See the `PhiInstruction` documentation for more details. + */ class Phi extends Opcode, TPhi { final override string toString() { result = "Phi" } final override MemoryAccessKind getWriteMemoryAccess() { result instanceof PhiMemoryAccess } } + /** + * The `Opcode` for a `BuiltInInstruction`. + * + * See the `BuiltInInstruction` documentation for more details. + */ class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { final override string toString() { result = "BuiltIn" } } + /** + * The `Opcode` for a `VarArgsStartInstruction`. + * + * See the `VarArgsStartInstruction` documentation for more details. + */ class VarArgsStart extends UnaryOpcode, TVarArgsStart { final override string toString() { result = "VarArgsStart" } } + /** + * The `Opcode` for a `VarArgsEndInstruction`. + * + * See the `VarArgsEndInstruction` documentation for more details. + */ class VarArgsEnd extends UnaryOpcode, TVarArgsEnd { final override string toString() { result = "VarArgsEnd" } } + /** + * The `Opcode` for a `VarArgInstruction`. + * + * See the `VarArgInstruction` documentation for more details. + */ class VarArg extends UnaryOpcode, TVarArg { final override string toString() { result = "VarArg" } } + /** + * The `Opcode` for a `NextVarArgInstruction`. + * + * See the `NextVarArgInstruction` documentation for more details. + */ class NextVarArg extends UnaryOpcode, TNextVarArg { final override string toString() { result = "NextVarArg" } } + /** + * The `Opcode` for a `CallSideEffectInstruction`. + * + * See the `CallSideEffectInstruction` documentation for more details. + */ class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode, ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect { final override string toString() { result = "CallSideEffect" } } + /** + * The `Opcode` for a `CallReadSideEffectInstruction`. + * + * See the `CallReadSideEffectInstruction` documentation for more details. + */ class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallReadSideEffect { final override string toString() { result = "CallReadSideEffect" } } + /** + * The `Opcode` for an `IndirectReadSideEffectInstruction`. + * + * See the `IndirectReadSideEffectInstruction` documentation for more details. + */ class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode, TIndirectReadSideEffect { final override string toString() { result = "IndirectReadSideEffect" } } + /** + * The `Opcode` for an `IndirectMustWriteSideEffectInstruction`. + * + * See the `IndirectMustWriteSideEffectInstruction` documentation for more details. + */ class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, TIndirectMustWriteSideEffect { final override string toString() { result = "IndirectMustWriteSideEffect" } } + /** + * The `Opcode` for an `IndirectMayWriteSideEffectInstruction`. + * + * See the `IndirectMayWriteSideEffectInstruction` documentation for more details. + */ class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, MayWriteOpcode, TIndirectMayWriteSideEffect { final override string toString() { result = "IndirectMayWriteSideEffect" } } + /** + * The `Opcode` for a `BufferReadSideEffectInstruction`. + * + * See the `BufferReadSideEffectInstruction` documentation for more details. + */ class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode, TBufferReadSideEffect { final override string toString() { result = "BufferReadSideEffect" } } + /** + * The `Opcode` for a `BufferMustWriteSideEffectInstruction`. + * + * See the `BufferMustWriteSideEffectInstruction` documentation for more details. + */ class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, TBufferMustWriteSideEffect { final override string toString() { result = "BufferMustWriteSideEffect" } } + /** + * The `Opcode` for a `BufferMayWriteSideEffectInstruction`. + * + * See the `BufferMayWriteSideEffectInstruction` documentation for more details. + */ class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, MayWriteOpcode, TBufferMayWriteSideEffect { final override string toString() { result = "BufferMayWriteSideEffect" } } + /** + * The `Opcode` for a `SizedBufferReadSideEffectInstruction`. + * + * See the `SizedBufferReadSideEffectInstruction` documentation for more details. + */ class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode, TSizedBufferReadSideEffect { final override string toString() { result = "SizedBufferReadSideEffect" } } + /** + * The `Opcode` for a `SizedBufferMustWriteSideEffectInstruction`. + * + * See the `SizedBufferMustWriteSideEffectInstruction` documentation for more details. + */ class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, TSizedBufferMustWriteSideEffect { final override string toString() { result = "SizedBufferMustWriteSideEffect" } } + /** + * The `Opcode` for a `SizedBufferMayWriteSideEffectInstruction`. + * + * See the `SizedBufferMayWriteSideEffectInstruction` documentation for more details. + */ class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, MayWriteOpcode, TSizedBufferMayWriteSideEffect { final override string toString() { result = "SizedBufferMayWriteSideEffect" } } + /** + * The `Opcode` for an `InitializeDynamicAllocationInstruction`. + * + * See the `InitializeDynamicAllocationInstruction` documentation for more details. + */ class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode, TInitializeDynamicAllocation { final override string toString() { result = "InitializeDynamicAllocation" } } + /** + * The `Opcode` for a `ChiInstruction`. + * + * See the `ChiInstruction` documentation for more details. + */ class Chi extends Opcode, TChi { final override string toString() { result = "Chi" } @@ -701,6 +1205,11 @@ module Opcode { } } + /** + * The `Opcode` for an `InlineAsmInstruction`. + * + * See the `InlineAsmInstruction` documentation for more details. + */ class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode, MayReadOpcode, TInlineAsm { final override string toString() { result = "InlineAsm" } @@ -710,10 +1219,20 @@ module Opcode { } } + /** + * The `Opcode` for an `UnreachedInstruction`. + * + * See the `UnreachedInstruction` documentation for more details. + */ class Unreached extends Opcode, TUnreached { final override string toString() { result = "Unreached" } } + /** + * The `Opcode` for a `NewObjInstruction`. + * + * See the `NewObjInstruction` documentation for more details. + */ class NewObj extends Opcode, TNewObj { final override string toString() { result = "NewObj" } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll index a0c0ca675307..5f230de560d6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll @@ -12,5 +12,6 @@ private import Imports::TempVariableTag * computed on each branch. The set of possible `TempVariableTag`s is language-dependent. */ class TempVariableTag extends TTempVariableTag { + /** Gets a textual representation of this tag. */ string toString() { result = getTempVariableTagId(this) } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll index badd48552a5f..c96783fe6e81 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + import IRFunction import Instruction import IRBlock @@ -11,11 +55,12 @@ import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() /** - * Class that provides additional properties to be dumped for IR instructions and blocks when using + * A class that provides additional properties to be dumped for IR instructions and blocks when using * the PrintIR module. Libraries that compute additional facts about IR elements can extend the * single instance of this class to specify the additional properties computed by the library. */ class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ string toString() { result = "IRPropertyProvider" } /** @@ -27,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index 94ef73b27692..d827ed3cf82d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -1,3 +1,7 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + private import internal.IRInternal import Instruction private import internal.IRBlockImports as Imports @@ -16,15 +20,27 @@ private import Cached * Most consumers should use the class `IRBlock`. */ class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ final string toString() { result = getFirstInstruction(this).toString() } + /** Gets the source location of the first non-`Phi` instruction in this block. */ final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + /** + * INTERNAL: Do not use. + * + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. + * INTERNAL: Do not use. + * + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. */ int getDisplayIndex() { exists(IRConfiguration::IRConfiguration config | @@ -42,27 +58,51 @@ class IRBlockBase extends TIRBlock { ) } + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ final Instruction getInstruction(int index) { result = getInstruction(this, index) } + /** + * Get the `Phi` instructions that appear at the start of this block. + */ final PhiInstruction getAPhiInstruction() { Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() } + /** + * Gets an instruction in this block. This includes `Phi` instructions. + */ final Instruction getAnInstruction() { result = getInstruction(_) or result = getAPhiInstruction() } + /** + * Gets the first non-`Phi` instruction in this block. + */ final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + /** + * Gets the last instruction in this block. + */ final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + /** + * Gets the number of non-`Phi` instructions in this block. + */ final int getInstructionCount() { result = getInstructionCount(this) } + /** + * Gets the `IRFunction` that contains this block. + */ final IRFunction getEnclosingIRFunction() { result = getFirstInstruction(this).getEnclosingIRFunction() } + /** + * Gets the `Function` that contains this block. + */ final Language::Function getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } @@ -74,20 +114,57 @@ class IRBlockBase extends TIRBlock { * instruction of another block. */ class IRBlock extends IRBlockBase { + /** + * Gets a block to which control flows directly from this block. + */ final IRBlock getASuccessor() { blockSuccessor(this, result) } + /** + * Gets a block from which control flows directly to this block. + */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + /** + * Gets a block on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ pragma[noinline] final IRBlock dominanceFrontier() { dominates(result.getAPredecessor()) and @@ -95,7 +172,7 @@ class IRBlock extends IRBlockBase { } /** - * Holds if this block is reachable from the entry point of its function + * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { this = getEnclosingIRFunction().getEntryBlock() or @@ -210,4 +287,4 @@ private module Cached { idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) } -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll index 65af34942b6b..6a87b9b4b5fd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll @@ -8,10 +8,79 @@ module InstructionConsistency { private import Imports::Overlap private import internal.IRInternal + private newtype TOptionalIRFunction = + TPresentIRFunction(IRFunction irFunc) or + TMissingIRFunction() + + /** + * An `IRFunction` that might not exist. This is used so that we can produce consistency failures + * for IR that also incorrectly lacks a `getEnclosingIRFunction()`. + */ + abstract private class OptionalIRFunction extends TOptionalIRFunction { + abstract string toString(); + + abstract Language::Location getLocation(); + } + + private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction { + private IRFunction irFunc; + + PresentIRFunction() { this = TPresentIRFunction(irFunc) } + + override string toString() { + result = concat(Language::getIdentityString(irFunc.getFunction()), "; ") + } + + override Language::Location getLocation() { + // To avoid an overwhelming number of results when the extractor merges functions with the + // same name, just pick a single location. + result = + rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString()) + } + } + + private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction { + override string toString() { result = "" } + + override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation } + } + + private OptionalIRFunction getInstructionIRFunction(Instruction instr) { + result = TPresentIRFunction(instr.getEnclosingIRFunction()) + or + not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) { + result = getInstructionIRFunction(instr) and + irFuncText = result.toString() + } + + private OptionalIRFunction getOperandIRFunction(Operand operand) { + result = TPresentIRFunction(operand.getEnclosingIRFunction()) + or + not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) { + result = getOperandIRFunction(operand) and + irFuncText = result.toString() + } + + private OptionalIRFunction getBlockIRFunction(IRBlock block) { + result = TPresentIRFunction(block.getEnclosingIRFunction()) + or + not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + /** * Holds if instruction `instr` is missing an expected operand with tag `tag`. */ - query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) { + query predicate missingOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { exists(OperandTag tag | instr.getOpcode().hasOperand(tag) and not exists(NonPhiOperand operand | @@ -21,32 +90,39 @@ module InstructionConsistency { message = "Instruction '" + instr.getOpcode().toString() + "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(instr, irFuncText) ) } /** * Holds if instruction `instr` has an unexpected operand with tag `tag`. */ - query predicate unexpectedOperand(Instruction instr, OperandTag tag) { - exists(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - not instr.getOpcode().hasOperand(tag) and - not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and - not ( - instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag - ) and - not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) + query predicate unexpectedOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag | + exists(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + not instr.getOpcode().hasOperand(tag) and + not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and + not ( + instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag + ) and + not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and + message = + "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() + + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if instruction `instr` has multiple operands with tag `tag`. */ query predicate duplicateOperand( - Instruction instr, string message, IRFunction func, string funcText + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(OperandTag tag, int operandCount | operandCount = @@ -58,8 +134,7 @@ module InstructionConsistency { message = "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + " in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(instr, irFuncText) ) } @@ -67,100 +142,136 @@ module InstructionConsistency { * Holds if `Phi` instruction `instr` is missing an operand corresponding to * the predecessor block `pred`. */ - query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) { - pred = instr.getBlock().getAPredecessor() and - not exists(PhiInputOperand operand | - operand = instr.getAnOperand() and - operand.getPredecessorBlock() = pred + query predicate missingPhiOperand( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRBlock pred | + pred = instr.getBlock().getAPredecessor() and + not exists(PhiInputOperand operand | + operand = instr.getAnOperand() and + operand.getPredecessorBlock() = pred + ) and + message = + "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" + + pred.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } - query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func, Instruction use | + query predicate missingOperandType( + Operand operand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Instruction use | not exists(operand.getType()) and use = operand.getUse() and - func = use.getEnclosingFunction() and message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + - "' missing type in function '" + Language::getIdentityString(func) + "'." + "' is missing a type in function '$@'." and + irFunc = getOperandIRFunction(operand, irFuncText) ) } query predicate duplicateChiOperand( - ChiInstruction chi, string message, IRFunction func, string funcText + ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText ) { chi.getTotal() = chi.getPartial() and message = "Chi instruction for " + chi.getPartial().toString() + - " has duplicate operands in function $@" and - func = chi.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + " has duplicate operands in function '$@'." and + irFunc = getInstructionIRFunction(chi, irFuncText) } query predicate sideEffectWithoutPrimary( - SideEffectInstruction instr, string message, IRFunction func, string funcText + SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { not exists(instr.getPrimaryInstruction()) and - message = "Side effect instruction missing primary instruction in function $@" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + message = + "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } /** * Holds if an instruction, other than `ExitFunction`, has no successors. */ - query predicate instructionWithoutSuccessor(Instruction instr) { + query predicate instructionWithoutSuccessor( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { not exists(instr.getASuccessor()) and not instr instanceof ExitFunctionInstruction and // Phi instructions aren't linked into the instruction-level flow graph. not instr instanceof PhiInstruction and - not instr instanceof UnreachedInstruction + not instr instanceof UnreachedInstruction and + message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } /** - * Holds if there are multiple (`n`) edges of kind `kind` from `source`, - * where `target` is among the targets of those edges. + * Holds if there are multiple edges of the same kind from `source`. */ - query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) { - n = strictcount(Instruction t | source.getSuccessor(kind) = t) and - n > 1 and - source.getSuccessor(kind) = target + query predicate ambiguousSuccessors( + Instruction source, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(EdgeKind kind, int n | + n = strictcount(Instruction t | source.getSuccessor(kind) = t) and + n > 1 and + message = + "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" + + kind.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(source, irFuncText) + ) } /** - * Holds if `instr` in `f` is part of a loop even though the AST of `f` + * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function * contains no element that can cause loops. */ - query predicate unexplainedLoop(Language::Function f, Instruction instr) { - exists(IRBlock block | - instr.getBlock() = block and - block.getEnclosingFunction() = f and - block.getASuccessor+() = block - ) and - not Language::hasPotentialLoop(f) + query predicate unexplainedLoop( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Language::Function f | + exists(IRBlock block | + instr.getBlock() = block and + block.getEnclosingFunction() = f and + block.getASuccessor+() = block + ) and + not Language::hasPotentialLoop(f) and + message = + "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if a `Phi` instruction is present in a block with fewer than two * predecessors. */ - query predicate unnecessaryPhiInstruction(PhiInstruction instr) { - count(instr.getBlock().getAPredecessor()) < 2 + query predicate unnecessaryPhiInstruction( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int n | + n = count(instr.getBlock().getAPredecessor()) and + n < 2 and + message = + "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() + + " predecessors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if a memory operand is connected to a definition with an unmodeled result. */ query predicate memoryOperandDefinitionIsUnmodeled( - Instruction instr, string message, IRFunction func, string funcText + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(MemoryOperand operand, Instruction def | operand = instr.getAnOperand() and def = operand.getAnyDef() and not def.isResultModeled() and - message = "Memory operand definition has unmodeled result in function '$@'" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + message = + "Memory operand definition on instruction '" + instr.toString() + + "' has unmodeled result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } @@ -168,18 +279,37 @@ module InstructionConsistency { * Holds if operand `operand` consumes a value that was defined in * a different function. */ - query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) { - operand.getUse() = instr and - operand.getAnyDef() = defInstr and - instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction() + query predicate operandAcrossFunctions( + Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText, + OptionalIRFunction defIRFunc, string defIRFuncText + ) { + exists(Instruction useInstr, Instruction defInstr | + operand.getUse() = useInstr and + operand.getAnyDef() = defInstr and + useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and + defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and + useIRFunc != defIRFunc and + message = + "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() + + "' in function '$@', but is defined on instruction '" + defInstr.toString() + + "' in function '$@'." + ) } /** * Holds if instruction `instr` is not in exactly one block. */ - query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { - blockCount = count(instr.getBlock()) and - blockCount != 1 + query predicate instructionWithoutUniqueBlock( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int blockCount | + blockCount = count(instr.getBlock()) and + blockCount != 1 and + message = + "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() + + " blocks in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } private predicate forwardEdge(IRBlock b1, IRBlock b2) { @@ -192,10 +322,11 @@ module InstructionConsistency { * * This check ensures we don't have too _few_ back edges. */ - query predicate containsLoopOfForwardEdges(IRFunction f) { + query predicate containsLoopOfForwardEdges(IRFunction f, string message) { exists(IRBlock block | forwardEdge+(block, block) and - block.getEnclosingIRFunction() = f + block.getEnclosingIRFunction() = f and + message = "Function contains a loop consisting of only forward edges." ) } @@ -207,12 +338,19 @@ module InstructionConsistency { * * This check ensures we don't have too _many_ back edges. */ - query predicate lostReachability(IRBlock block) { + query predicate lostReachability( + IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText + ) { exists(IRFunction f, IRBlock entry | entry = f.getEntryBlock() and entry.getASuccessor+() = block and not forwardEdge+(entry, block) and - not Language::hasGoto(f.getFunction()) + not Language::hasGoto(f.getFunction()) and + message = + "Block '" + block.toString() + + "' is not reachable by traversing only forward edges in function '$@'." and + irFunc = TPresentIRFunction(f) and + irFuncText = irFunc.toString() ) } @@ -220,16 +358,22 @@ module InstructionConsistency { * Holds if the number of back edges differs between the `Instruction` graph * and the `IRBlock` graph. */ - query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) { - fromInstr = - count(Instruction i1, Instruction i2 | - i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2 - ) and - fromBlock = - count(IRBlock b1, IRBlock b2 | - b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2 - ) and - fromInstr != fromBlock + query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) { + exists(int fromInstr, int fromBlock | + fromInstr = + count(Instruction i1, Instruction i2 | + getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2 + ) and + fromBlock = + count(IRBlock b1, IRBlock b2 | + getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2 + ) and + fromInstr != fromBlock and + message = + "The instruction graph for function '" + irFunc.toString() + "' contains " + + fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString() + + " back edges." + ) } /** @@ -251,7 +395,7 @@ module InstructionConsistency { * Holds if `useOperand` has a definition that does not dominate the use. */ query predicate useNotDominatedByDefinition( - Operand useOperand, string message, IRFunction func, string funcText + Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | pointOfEvaluation(useOperand, useBlock, useIndex) and @@ -272,19 +416,17 @@ module InstructionConsistency { message = "Operand '" + useOperand.toString() + "' is not dominated by its definition in function '$@'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getOperandIRFunction(useOperand, irFuncText) ) } query predicate switchInstructionWithoutDefaultEdge( - SwitchInstruction switchInstr, string message, IRFunction func, string funcText + SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText ) { not exists(switchInstr.getDefaultSuccessor()) and message = "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and - func = switchInstr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(switchInstr, irFuncText) } /** @@ -305,18 +447,30 @@ module InstructionConsistency { instr.getOpcode() instanceof Opcode::InitializeNonLocal } - query predicate notMarkedAsConflated(Instruction instr) { + query predicate notMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { shouldBeConflated(instr) and - not instr.isResultConflated() + not instr.isResultConflated() and + message = + "Instruction '" + instr.toString() + + "' should be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } - query predicate wronglyMarkedAsConflated(Instruction instr) { + query predicate wronglyMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { instr.isResultConflated() and - not shouldBeConflated(instr) + not shouldBeConflated(instr) and + message = + "Instruction '" + instr.toString() + + "' should not be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } query predicate invalidOverlap( - MemoryOperand useOperand, string message, IRFunction func, string funcText + MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(Overlap overlap | overlap = useOperand.getDefinitionOverlap() and @@ -324,8 +478,20 @@ module InstructionConsistency { message = "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" + overlap.toString() + "'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getOperandIRFunction(useOperand, irFuncText) + ) + } + + query predicate nonUniqueEnclosingIRFunction( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int irFuncCount | + irFuncCount = count(instr.getEnclosingIRFunction()) and + irFuncCount != 1 and + message = + "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() + + " results for `getEnclosingIRFunction()` in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll index 9aea3e00d666..5968e58f90bf 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll @@ -1,29 +1,17 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase import Instruction -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - /** - * Represents the IR for a function. + * The IR for a function. */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - +class IRFunction extends IRFunctionBase { /** * Gets the entry point for this function. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index 9f2a0d4ea281..146fc2707383 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + private import internal.IRInternal import IRFunction private import internal.IRVariableImports as Imports @@ -7,15 +11,11 @@ private import Imports::TTempVariableTag private import Imports::TIRVariable private import Imports::IRType -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - /** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { Language::Function func; @@ -27,6 +27,7 @@ class IRVariable extends TIRVariable { this = TIRDynamicInitializationFlag(func, _, _) } + /** Gets a textual representation of this element. */ string toString() { none() } /** @@ -162,20 +163,30 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } + /** + * INTERNAL: Do not use. + * + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ final string getLocationString() { result = ast.getLocation().getStartLine().toString() + ":" + ast.getLocation().getStartColumn().toString() } + /** + * INTERNAL: Do not use. + * + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ string getBaseString() { none() } } -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - /** * A temporary variable introduced by IR construction. The most common examples are the variable * generated to hold the return value of a function, or the variable generated to hold the result of @@ -190,6 +201,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa result = "Temp: " + Construction::getTempVariableUniqueId(this) } + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ final TempVariableTag getTag() { result = tag } override string getBaseString() { result = "#temp" } @@ -217,19 +232,23 @@ class IRThrowVariable extends IRTempVariable { * A temporary variable generated to hold the contents of all arguments passed to the `...` of a * function that accepts a variable number of arguments. */ -class IREllipsisVariable extends IRTempVariable { +class IREllipsisVariable extends IRTempVariable, IRParameter { IREllipsisVariable() { tag = EllipsisTempVar() } final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } } /** * A temporary variable generated to hold the `this` pointer. */ -class IRThisVariable extends IRTempVariable { +class IRThisVariable extends IRTempVariable, IRParameter { IRThisVariable() { tag = ThisTempVar() } final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } } /** @@ -249,6 +268,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { final override string getBaseString() { result = "#string" } + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ final Language::StringLiteral getLiteral() { result = literal } } @@ -266,6 +288,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string toString() { result = var.toString() + "#init" } + /** + * Gets variable whose initialization is guarded by this flag. + */ final Language::Variable getVariable() { result = var } final override string getUniqueId() { @@ -274,3 +299,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string getBaseString() { result = "#init:" + var.toString() + ":" } } + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +/** + * An IR variable representing a positional parameter. + */ +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 9c83a3d99f0c..620b23b942e0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + private import internal.IRInternal import IRFunction import IRBlock @@ -27,9 +31,16 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * Represents a single operation in the IR. + * A single instruction in the IR. */ -class Instruction extends Construction::TInstruction { +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + + /** Gets a textual representation of this element. */ final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -194,16 +205,25 @@ class Instruction extends Construction::TInstruction { * conversion. */ final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) + result = Raw::getInstructionConvertedResultExpression(this) } /** * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. */ final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) + result = Raw::getInstructionUnconvertedResultExpression(this) } + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ final Language::LanguageType getResultLanguageType() { result = Construction::getInstructionResultType(this) } @@ -212,6 +232,7 @@ class Instruction extends Construction::TInstruction { * Gets the type of the result produced by this instruction. If the instruction does not produce * a result, its result type will be `IRVoidType`. */ + cached final IRType getResultIRType() { result = getResultLanguageType().getIRType() } /** @@ -240,17 +261,19 @@ class Instruction extends Construction::TInstruction { * given by `getResultType()`. * * For example, the statement `y = x;` generates the following IR: + * ``` * r1_0(glval: int) = VariableAddress[x] * r1_1(int) = Load r1_0, mu0_1 * r1_2(glval: int) = VariableAddress[y] * mu1_3(int) = Store r1_2, r1_1 + * ``` * * The result of each `VariableAddress` instruction is a glvalue of type * `int`, representing the address of the corresponding integer variable. The * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -259,7 +282,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } + final int getResultSize() { result = getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -392,13 +415,27 @@ class Instruction extends Construction::TInstruction { final Instruction getAPredecessor() { result = getPredecessor(_) } } +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ class VariableInstruction extends Instruction { IRVariable var; - VariableInstruction() { var = Construction::getInstructionVariable(this) } + VariableInstruction() { var = Raw::getInstructionVariable(this) } override string getImmediateString() { result = var.toString() } + /** + * Gets the variable that this instruction references. + */ final IRVariable getIRVariable() { result = var } /** @@ -407,63 +444,156 @@ class VariableInstruction extends Instruction { final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ class FieldInstruction extends Instruction { Language::Field field; - FieldInstruction() { field = Construction::getInstructionField(this) } + FieldInstruction() { field = Raw::getInstructionField(this) } final override string getImmediateString() { result = field.toString() } + /** + * Gets the field that this instruction references. + */ final Language::Field getField() { result = field } } +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ class FunctionInstruction extends Instruction { Language::Function funcSymbol; - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } final override string getImmediateString() { result = funcSymbol.toString() } + /** + * Gets the function that this instruction references. + */ final Language::Function getFunctionSymbol() { result = funcSymbol } } +/** + * An instruction whose result is a compile-time constant value. + */ class ConstantValueInstruction extends Instruction { string value; - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } final override string getImmediateString() { result = value } + /** + * Gets the constant value of this instruction's result. + */ final string getValue() { result = value } } +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ class IndexedInstruction extends Instruction { int index; - IndexedInstruction() { index = Construction::getInstructionIndex(this) } + IndexedInstruction() { index = Raw::getInstructionIndex(this) } final override string getImmediateString() { result = index.toString() } + /** + * Gets the zero-based index of the argument that this instruction references. + */ final int getIndex() { result = index } } +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ class EnterFunctionInstruction extends Instruction { EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } } +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ class InitializeParameterInstruction extends VariableInstruction { InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirectionInstruction extends VariableInstruction { InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } @@ -474,14 +604,42 @@ class InitializeThisInstruction extends Instruction { InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } } +/** + * An instruction that computes the address of a non-static field of an object. + */ class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + /** + * Gets the operand that provides the address of the object containing the field. + */ final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + /** * An instruction that produces a well-defined but unknown result and has * unknown side effects, including side effects that are not conservatively @@ -496,6 +654,12 @@ class ErrorInstruction extends Instruction { ErrorInstruction() { getOpcode() instanceof Opcode::Error } } +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } @@ -505,35 +669,94 @@ class UninitializedInstruction extends VariableInstruction { final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ class NoOpInstruction extends Instruction { NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } } +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ class ReturnInstruction extends Instruction { ReturnInstruction() { getOpcode() instanceof ReturnOpcode } } +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ class ReturnVoidInstruction extends ReturnInstruction { ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } } +/** + * An instruction that returns control to the caller of the function, including a return value. + */ class ReturnValueInstruction extends ReturnInstruction { ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + /** + * Gets the operand that provides the value being returned by the function. + */ final LoadOperand getReturnValueOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } } +/** + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. + */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + /** + * Gets the operand that provides the value of the pointed-to memory. + */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + /** + * Gets the operand that provides the address of the pointed-to memory. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } /** @@ -541,87 +764,191 @@ class ReturnIndirectionInstruction extends VariableInstruction { * function. */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } } +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ class CopyInstruction extends Instruction { CopyInstruction() { getOpcode() instanceof CopyOpcode } + /** + * Gets the operand that provides the input value of the copy. + */ Operand getSourceValueOperand() { none() } + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } } +/** + * An instruction that returns a register result containing a copy of its register operand. + */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ class LoadInstruction extends CopyInstruction { LoadInstruction() { getOpcode() instanceof Opcode::Load } + /** + * Gets the operand that provides the address of the value being loaded. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } final override LoadOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ class StoreInstruction extends CopyInstruction { StoreInstruction() { getOpcode() instanceof Opcode::Store } + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranchInstruction extends Instruction { ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ final ConditionOperand getConditionOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ final Instruction getCondition() { result = getConditionOperand().getDef() } + /** + * Gets the instruction to which control will flow if the condition is true. + */ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + /** + * Gets the instruction to which control will flow if the condition is false. + */ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } } +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no + * successors. + */ class ExitFunctionInstruction extends Instruction { ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } } +/** + * An instruction whose result is a constant value. + */ class ConstantInstruction extends ConstantValueInstruction { ConstantInstruction() { getOpcode() instanceof Opcode::Constant } } +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } } +/** + * An instruction whose result is a constant value of floating-point type. + */ class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } +/** + * An instruction whose result is the address of a string literal. + */ class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + /** + * Gets the string literal whose address is returned by this instruction. + */ final Language::StringLiteral getValue() { result = var.getLiteral() } } +/** + * An instruction whose result is computed from two operands. + */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + /** + * Gets the left operand of this binary instruction. + */ final LeftOperand getLeftOperand() { result = getAnOperand() } + /** + * Gets the right operand of this binary instruction. + */ final RightOperand getRightOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ final Instruction getLeft() { result = getLeftOperand().getDef() } + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ final Instruction getRight() { result = getRightOperand().getDef() } /** @@ -634,121 +961,301 @@ class BinaryInstruction extends Instruction { } } +/** + * An instruction that computes the result of an arithmetic operation. + */ class ArithmeticInstruction extends Instruction { ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } } +/** + * An instruction that performs an arithmetic operation on two numeric operands. + */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * numeric operand. + */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } +/** + * An instruction that computes the sum of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ class AddInstruction extends BinaryArithmeticInstruction { AddInstruction() { getOpcode() instanceof Opcode::Add } } +/** + * An instruction that computes the difference of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ class SubInstruction extends BinaryArithmeticInstruction { SubInstruction() { getOpcode() instanceof Opcode::Sub } } +/** + * An instruction that computes the product of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ class MulInstruction extends BinaryArithmeticInstruction { MulInstruction() { getOpcode() instanceof Opcode::Mul } } +/** + * An instruction that computes the quotient of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ class DivInstruction extends BinaryArithmeticInstruction { DivInstruction() { getOpcode() instanceof Opcode::Div } } +/** + * An instruction that computes the remainder of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ class RemInstruction extends BinaryArithmeticInstruction { RemInstruction() { getOpcode() instanceof Opcode::Rem } } +/** + * An instruction that negates a single numeric operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ class NegateInstruction extends UnaryArithmeticInstruction { NegateInstruction() { getOpcode() instanceof Opcode::Negate } } +/** + * An instruction that computes the result of a bitwise operation. + */ class BitwiseInstruction extends Instruction { BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } } +/** + * An instruction that performs a bitwise operation on two integer operands. + */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } +/** + * An instruction that performs a bitwise operation on a single integer operand. + */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } +/** + * An instruction that computes the bitwise "and" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitAndInstruction extends BinaryBitwiseInstruction { BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } } +/** + * An instruction that computes the bitwise "or" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitOrInstruction extends BinaryBitwiseInstruction { BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } } +/** + * An instruction that computes the bitwise "xor" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitXorInstruction extends BinaryBitwiseInstruction { BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } } +/** + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } } +/** + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ class ShiftRightInstruction extends BinaryBitwiseInstruction { ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } } +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; PointerArithmeticInstruction() { getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) + elementSize = Raw::getInstructionElementSize(this) } final override string getImmediateString() { result = elementSize.toString() } + /** + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. + */ final int getElementSize() { result = elementSize } } +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ class PointerOffsetInstruction extends PointerArithmeticInstruction { PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } } +/** + * An instruction that adds an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ class PointerAddInstruction extends PointerOffsetInstruction { PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } } +/** + * An instruction that subtracts an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ class PointerSubInstruction extends PointerOffsetInstruction { PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } } +/** + * An instruction that computes the difference between two pointers. + * + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. + */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } } +/** + * An instruction whose result is computed from a single operand. + */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + /** + * Gets the sole operand of this instruction. + */ final UnaryOperand getUnaryOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ final Instruction getUnary() { result = getUnaryOperand().getDef() } } +/** + * An instruction that converts the value of its operand to a value of a different type. + */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } } +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ class CheckedConvertOrNullInstruction extends UnaryInstruction { CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** - * Represents an instruction that converts between two addresses - * related by inheritance. + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. */ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class baseClass; Language::Class derivedClass; InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) + Raw::getInstructionInheritance(this, baseClass, derivedClass) } final override string getImmediateString() { @@ -778,59 +1285,91 @@ class InheritanceConversionInstruction extends UnaryInstruction { } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. + * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } } +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ class BitComplementInstruction extends UnaryBitwiseInstruction { BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } } +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ class LogicalNotInstruction extends UnaryInstruction { LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } } +/** + * An instruction that compares two numeric operands. + */ class CompareInstruction extends BinaryInstruction { CompareInstruction() { getOpcode() instanceof CompareOpcode } } +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareEQInstruction extends CompareInstruction { CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } } +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ class CompareNEInstruction extends CompareInstruction { CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } } /** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. + * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { RelationalInstruction() { getOpcode() instanceof RelationalOpcode } @@ -857,6 +1396,13 @@ class RelationalInstruction extends CompareInstruction { predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLTInstruction extends RelationalInstruction { CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } @@ -867,6 +1413,13 @@ class CompareLTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGTInstruction extends RelationalInstruction { CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } @@ -877,6 +1430,14 @@ class CompareGTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLEInstruction extends RelationalInstruction { CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } @@ -887,6 +1448,14 @@ class CompareLEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGEInstruction extends RelationalInstruction { CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } @@ -897,15 +1466,32 @@ class CompareGEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ class SwitchInstruction extends Instruction { SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + /** Gets the operand that provides the integer value controlling the switch. */ final ConditionOperand getExpressionOperand() { result = getAnOperand() } + /** Gets the instruction whose result provides the integer value controlling the switch. */ final Instruction getExpression() { result = getExpressionOperand().getDef() } + /** Gets the successor instructions along the case edges of the switch. */ final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + /** Gets the successor instruction along the default edge of the switch, if any. */ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } } @@ -936,7 +1522,7 @@ class CallInstruction extends Instruction { * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** @@ -981,6 +1567,9 @@ class CallInstruction extends Instruction { class SideEffectInstruction extends Instruction { SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + /** + * Gets the instruction whose execution causes this side effect. + */ final Instruction getPrimaryInstruction() { result = Construction::getPrimaryInstructionForSideEffect(this) } @@ -996,9 +1585,10 @@ class CallSideEffectInstruction extends SideEffectInstruction { /** * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } @@ -1046,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { getOpcode() instanceof Opcode::SizedBufferReadSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1056,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } } /** @@ -1087,11 +1693,20 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** * An instruction representing the potential write of an indirect parameter within a function call. + * * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. * written. */ @@ -1103,6 +1718,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1111,6 +1727,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1118,11 +1735,19 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a + * An instruction representing the initial value of newly allocated memory, such as the result of a * call to `malloc`. */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { @@ -1211,7 +1836,7 @@ class CatchByTypeInstruction extends CatchInstruction { CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) + exceptionType = Raw::getInstructionExceptionType(this) } final override string getImmediateString() { result = exceptionType.toString() } @@ -1337,29 +1962,43 @@ class ChiInstruction extends Instruction { * Gets the operand that represents the new value written by the memory write. */ final Instruction getPartial() { result = getPartialOperand().getDef() } + + /** + * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. + */ + final predicate getUpdatedInterval(int startBit, int endBit) { + Construction::getIntervalUpdatedByChi(this, startBit, endBit) + } } /** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } } /** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. */ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; BuiltInOperationInstruction() { getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) + operation = Raw::getInstructionBuiltInOperation(this) } + /** + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is + * performed by this instruction. + */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } } @@ -1372,3 +2011,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction { final override string getImmediateString() { result = getBuiltInOperation().toString() } } + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index f82704094c8e..a12e35d471b8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + private import internal.IRInternal private import Instruction private import IRBlock @@ -75,13 +79,21 @@ private PhiOperandBase phiOperand( } /** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) */ class Operand extends TOperand { + /** Gets a textual representation of this element. */ string toString() { result = "Operand" } + /** + * Gets the location of the source code for this operand. + */ final Language::Location getLocation() { result = getUse().getLocation() } + /** + * Gets the function that contains this operand. + */ final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } /** @@ -139,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -268,8 +285,13 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ final OperandTag getOperandTag() { result = tag } } @@ -292,6 +314,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase { } } +/** + * A memory operand other than the operand of a `Phi` instruction. + */ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { override MemoryOperandTag tag; @@ -311,8 +336,19 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper not Construction::isInCycle(useInstr) and strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1 } + + /** + * Holds if the operand totally overlaps with its definition and consumes the + * bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition. + */ + predicate getUsedInterval(int startBitOffset, int endBitOffset) { + Construction::getUsedInterval(this, startBitOffset, endBitOffset) + } } +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; @@ -416,6 +452,9 @@ class PositionalArgumentOperand extends ArgumentOperand { final int getIndex() { result = tag.getArgIndex() } } +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; } @@ -445,6 +484,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index d9c0df44e12e..59dadee71545 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -1,3 +1,13 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + private import internal.IRInternal private import IR private import internal.PrintIRImports as Imports @@ -9,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration() * The query can extend this class to control which functions are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ string toString() { result = "PrintIRConfiguration" } /** @@ -39,6 +50,37 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -47,7 +89,7 @@ private newtype TPrintableIRNode = /** * A node to be emitted in the IR graph. */ -abstract class PrintableIRNode extends TPrintableIRNode { +abstract private class PrintableIRNode extends TPrintableIRNode { abstract string toString(); /** @@ -98,7 +140,7 @@ abstract class PrintableIRNode extends TPrintableIRNode { /** * An IR graph node representing a `IRFunction` object. */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { IRFunction irFunc; PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } @@ -129,7 +171,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { /** * An IR graph node representing an `IRBlock` object. */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { IRBlock block; PrintableIRBlock() { this = TPrintableIRBlock(block) } @@ -161,7 +203,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { /** * An IR graph node representing an `Instruction`. */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { Instruction instr; PrintableInstruction() { this = TPrintableInstruction(instr) } @@ -179,7 +221,7 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -199,6 +241,22 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { @@ -224,6 +282,9 @@ private string getPaddingString(int n) { n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " } +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ query predicate nodes(PrintableIRNode node, string key, string value) { value = node.getProperty(key) } @@ -237,6 +298,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) { ) } +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | predBlock = pred.getBlock() and @@ -256,6 +321,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, ) } +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ query predicate parents(PrintableIRNode child, PrintableIRNode parent) { parent = child.getParent() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index 1612e0065b77..19fb0490f808 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -196,16 +196,17 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { bitOffset = Ints::unknown() } -private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { +private predicate isArgumentForParameter( + CallInstruction ci, Operand operand, InitializeParameterInstruction init +) { exists(Language::Function f | ci = operand.getUse() and f = ci.getStaticCallTarget() and ( - init.(InitializeParameterInstruction).getParameter() = - f.getParameter(operand.(PositionalArgumentOperand).getIndex()) + init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex()) or - init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and - init.getEnclosingFunction() = f and + init.getIRVariable() instanceof IRThisVariable and + unique( | | init.getEnclosingFunction()) = f and operand instanceof ThisArgumentOperand ) and not Language::isFunctionVirtual(f) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll index 9cd0384f924c..1e034051f05e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -133,6 +133,12 @@ abstract class MemoryLocation extends TMemoryLocation { predicate isAlwaysAllocatedOnStack() { none() } } +/** + * Represents a set of `MemoryLocation`s that cannot overlap with + * `MemoryLocation`s outside of the set. The `VirtualVariable` will be + * represented by a `MemoryLocation` that totally overlaps all other + * `MemoryLocations` in the set. + */ abstract class VirtualVariable extends MemoryLocation { } abstract class AllocationMemoryLocation extends MemoryLocation { @@ -617,3 +623,9 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { ) ) } + +/** Gets the start bit offset of a `MemoryLocation`, if any. */ +int getStartBitOffset(VariableMemoryLocation location) { result = location.getStartBitOffset() } + +/** Gets the end bit offset of a `MemoryLocation`, if any. */ +int getEndBitOffset(VariableMemoryLocation location) { result = location.getEndBitOffset() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll new file mode 100644 index 000000000000..8ec63b7c1cbd --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll index 4cc52d3bbf99..3a7a08accc0d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language import SSAConstruction as Construction import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 30414bb5db3a..a6cb78b2b3a7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -1,5 +1,11 @@ import SSAConstructionInternal -private import SSAConstructionImports +private import SSAConstructionImports as Imports +private import Imports::Opcode +private import Imports::OperandTag +private import Imports::Overlap +private import Imports::TInstruction +private import Imports::RawIR as RawIR +private import SSAInstructions private import NewIR private class OldBlock = Reachability::ReachableBlock; @@ -10,52 +16,45 @@ import Cached cached private module Cached { - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached + predicate hasPhiInstructionCached( + OldInstruction blockStartInstr, Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + blockStartInstr = oldBlock.getFirstInstruction() + ) } cached - predicate functionHasIR(Language::Function func) { - exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func) + predicate hasChiInstructionCached(OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) } cached - OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) } - - private IRVariable getNewIRVariable(OldIR::IRVariable var) { - // This is just a type cast. Both classes derive from the same newtype. - result = var + predicate hasUnreachedInstructionCached(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) } - cached - newtype TInstruction = - WrappedInstruction(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction - } or - Phi(OldBlock block, Alias::MemoryLocation defLocation) { - definitionHasPhiNode(defLocation, block) - } or - Chi(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction and - hasChiNode(_, oldInstruction) - } or - Unreached(Language::Function function) { - exists(OldInstruction oldInstruction | - function = oldInstruction.getEnclosingFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } + class TStageInstruction = + TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; cached - predicate hasTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type - ) { - exists(OldIR::IRTempVariable var | - var.getEnclosingFunction() = func and - var.getAST() = ast and - var.getTag() = tag and - var.getLanguageType() = type - ) + predicate hasInstruction(TStageInstruction instr) { + instr instanceof TRawInstruction and instr instanceof OldInstruction + or + instr instanceof TPhiInstruction + or + instr instanceof TChiInstruction + or + instr instanceof TUnreachedInstruction + } + + private IRBlock getNewBlock(OldBlock oldBlock) { + result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } cached @@ -73,7 +72,7 @@ private module Cached { or // Chi instructions track virtual variables, and therefore a chi instruction is // conflated if it's associated with the aliased virtual variable. - exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) | Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof Alias::AliasedVirtualVariable ) @@ -81,7 +80,7 @@ private module Cached { // Phi instructions track locations, and therefore a phi instruction is // conflated if it's associated with a conflated location. exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and + instruction = getPhi(_, location) and not exists(location.getAllocation()) ) } @@ -128,7 +127,7 @@ private module Cached { hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result) ) or - instruction = Chi(getOldInstruction(result)) and + instruction = getChi(getOldInstruction(result)) and tag instanceof ChiPartialOperandTag and overlap instanceof MustExactlyOverlap or @@ -150,6 +149,34 @@ private module Cached { ) } + /** + * Holds if the partial operand of this `ChiInstruction` updates the bit range + * `[startBitOffset, endBitOffset)` of the total operand. + */ + cached + predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBitOffset, int endBitOffset) { + exists(Alias::MemoryLocation location, OldInstruction oldInstruction | + oldInstruction = getOldInstruction(chi.getPartial()) and + location = Alias::getResultMemoryLocation(oldInstruction) and + startBitOffset = Alias::getStartBitOffset(location) and + endBitOffset = Alias::getEndBitOffset(location) + ) + } + + /** + * Holds if `operand` totally overlaps with its definition and consumes the bit range + * `[startBitOffset, endBitOffset)`. + */ + cached + predicate getUsedInterval(NonPhiMemoryOperand operand, int startBitOffset, int endBitOffset) { + exists(Alias::MemoryLocation location, OldIR::NonPhiMemoryOperand oldOperand | + oldOperand = operand.getUse().(OldInstruction).getAnOperand() and + location = Alias::getOperandMemoryLocation(oldOperand) and + startBitOffset = Alias::getStartBitOffset(location) and + endBitOffset = Alias::getEndBitOffset(location) + ) + } + /** * Holds if `instr` is part of a cycle in the operand graph that doesn't go * through a phi instruction and therefore should be impossible. @@ -172,13 +199,15 @@ private module Cached { pragma[noopt] cached - Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) { + Instruction getPhiOperandDefinition( + PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation | hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and - instr = Phi(phiBlock, useLocation) and + instr = getPhi(phiBlock, useLocation) and newPredecessorBlock = getNewBlock(predBlock) and result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) @@ -191,7 +220,7 @@ private module Cached { Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank | - chiInstr = Chi(oldInstr) and + chiInstr = getChi(oldInstr) and vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and hasUseAtRank(vvar, useBlock, useRank, oldInstr) and @@ -203,21 +232,11 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = Phi(oldBlock, _) and + instr = getPhi(oldBlock, _) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } - cached - Language::Expr getInstructionConvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getConvertedResultExpression() - } - - cached - Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getUnconvertedResultExpression() - } - /* * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, * that node is its successor in the new successor relation, and the Chi node's successors are @@ -228,20 +247,20 @@ private module Cached { Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { if hasChiNode(_, getOldInstruction(instruction)) then - result = Chi(getOldInstruction(instruction)) and + result = getChi(getOldInstruction(instruction)) and kind instanceof GotoEdge else ( exists(OldInstruction oldInstruction | oldInstruction = getOldInstruction(instruction) and ( if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) - then result = Unreached(instruction.getEnclosingFunction()) + then result = unreachedInstruction(instruction.getEnclosingIRFunction()) else result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) or exists(OldInstruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) @@ -260,137 +279,73 @@ private module Cached { // `oldInstruction`, in which case the back edge should come out of the // chi node instead. if hasChiNode(_, oldInstruction) - then instruction = Chi(oldInstruction) + then instruction = getChi(oldInstruction) else instruction = getNewInstruction(oldInstruction) ) } cached - Language::AST getInstructionAST(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result = oldInstruction.getAST() + Language::AST getInstructionAST(Instruction instr) { + result = getOldInstruction(instr).getAST() + or + exists(RawIR::Instruction blockStartInstr | + instr = phiInstruction(blockStartInstr, _) and + result = blockStartInstr.getAST() ) or - exists(OldBlock block | - instruction = Phi(block, _) and - result = block.getFirstInstruction().getAST() + exists(RawIR::Instruction primaryInstr | + instr = chiInstruction(primaryInstr) and + result = primaryInstr.getAST() ) or - instruction = Unreached(result) + exists(IRFunctionBase irFunc | + instr = unreachedInstruction(irFunc) and result = irFunc.getFunction() + ) } cached - Language::LanguageType getInstructionResultType(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getResultLanguageType() - ) + Language::LanguageType getInstructionResultType(Instruction instr) { + result = instr.(RawIR::Instruction).getResultLanguageType() or - exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | - instruction = Chi(oldInstruction) and - hasChiNode(vvar, oldInstruction) and - result = vvar.getType() + exists(Alias::MemoryLocation defLocation | + instr = phiInstruction(_, defLocation) and + result = defLocation.getType() ) or - exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and - result = location.getType() + exists(Instruction primaryInstr, Alias::VirtualVariable vvar | + instr = chiInstruction(primaryInstr) and + hasChiNode(vvar, primaryInstr) and + result = vvar.getType() ) or - instruction = Unreached(_) and - result = Language::getVoidType() + instr = unreachedInstruction(_) and result = Language::getVoidType() } cached - Opcode getInstructionOpcode(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getOpcode() - ) + Opcode getInstructionOpcode(Instruction instr) { + result = getOldInstruction(instr).getOpcode() or - instruction instanceof Chi and - result instanceof Opcode::Chi + instr = phiInstruction(_, _) and result instanceof Opcode::Phi or - instruction instanceof Phi and - result instanceof Opcode::Phi + instr = chiInstruction(_) and result instanceof Opcode::Chi or - instruction instanceof Unreached and - result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result.getFunction() = oldInstruction.getEnclosingFunction() - ) + IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) { + result = getOldInstruction(instr).getEnclosingIRFunction() or - exists(OldBlock block | - instruction = Phi(block, _) and - result.getFunction() = block.getEnclosingFunction() + exists(OldInstruction blockStartInstr | + instr = phiInstruction(blockStartInstr, _) and + result = blockStartInstr.getEnclosingIRFunction() ) or - instruction = Unreached(result.getFunction()) - } - - cached - IRVariable getInstructionVariable(Instruction instruction) { - result = - getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable()) - } - - cached - Language::Field getInstructionField(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() - } - - cached - int getInstructionIndex(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex() - } - - cached - Language::Function getInstructionFunction(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue() - } - - cached - Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { - result = - getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation() - } - - cached - Language::LanguageType getInstructionExceptionType(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() - } - - cached - int getInstructionElementSize(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() - } - - cached - predicate getInstructionInheritance( - Instruction instruction, Language::Class baseClass, Language::Class derivedClass - ) { - exists(OldIR::InheritanceConversionInstruction oldInstr | - oldInstr = getOldInstruction(instruction) and - baseClass = oldInstr.getBaseClass() and - derivedClass = oldInstr.getDerivedClass() + exists(OldInstruction primaryInstr | + instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction() ) + or + instr = unreachedInstruction(result) } cached @@ -401,7 +356,7 @@ private module Cached { ) or exists(OldIR::Instruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction) ) } @@ -409,6 +364,14 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } +private OldInstruction getOldInstruction(Instruction instr) { instr = result } + +private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) } + +private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { + result = phiInstruction(defBlock.getFirstInstruction(), defLocation) +} + /** * Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition * of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the @@ -588,7 +551,7 @@ module DefUse { | // An odd offset corresponds to the `Chi` instruction. defOffset = oldOffset * 2 + 1 and - result = Chi(oldInstr) and + result = getChi(oldInstr) and ( defLocation = Alias::getResultMemoryLocation(oldInstr) or defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() @@ -607,7 +570,7 @@ module DefUse { or defOffset = -1 and hasDefinition(_, defLocation, defBlock, defOffset) and - result = Phi(defBlock, defLocation) and + result = getPhi(defBlock, defLocation) and actualDefLocation = defLocation } @@ -891,7 +854,7 @@ private module CachedForDebugging { ) or exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity | - instr = Phi(phiBlock, location) and + instr = getPhi(phiBlock, location) and result = "Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and if location instanceof Alias::VirtualVariable @@ -901,7 +864,7 @@ private module CachedForDebugging { else specificity = "s" ) or - instr = Unreached(_) and + instr = unreachedInstruction(_) and result = "Unreached" } @@ -961,3 +924,19 @@ module SSAConsistency { ) } } + +/** + * Provides the portion of the parameterized IR interface that is used to construct the SSA stages + * of the IR. The raw stage of the IR does not expose these predicates. + * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures + * that all of SSA construction will be evaluated in the same stage. + */ +module SSA { + class MemoryLocation = Alias::MemoryLocation; + + predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2; + + predicate hasChiInstruction = Cached::hasChiInstructionCached/1; + + predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll index 00f12020a29d..f347df86ba1e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll @@ -1,3 +1,5 @@ -import semmle.code.cpp.ir.implementation.Opcode -import semmle.code.cpp.ir.implementation.internal.OperandTag -import semmle.code.cpp.ir.internal.Overlap +import semmle.code.cpp.ir.implementation.Opcode as Opcode +import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag +import semmle.code.cpp.ir.internal.Overlap as Overlap +import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction +import semmle.code.cpp.ir.implementation.raw.IR as RawIR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll index c0922aff8917..bb068bdd489c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll @@ -2,5 +2,6 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR +import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions import semmle.code.cpp.ir.internal.IRCppLanguage as Language import AliasedSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll new file mode 100644 index 000000000000..60895ce3d266 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll @@ -0,0 +1,27 @@ +/** + * Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`. + */ + +private import IRFunctionBaseInternal + +private newtype TIRFunction = + MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } + +/** + * The IR for a function. This base class contains only the predicates that are the same between all + * phases of the IR. Each instantiation of `IRFunction` extends this class. + */ +class IRFunctionBase extends TIRFunction { + Language::Function func; + + IRFunctionBase() { this = MkIRFunction(func) } + + /** Gets a textual representation of this element. */ + final string toString() { result = "IR: " + func.toString() } + + /** Gets the function whose IR is represented. */ + final Language::Function getFunction() { result = func } + + /** Gets the location of the function. */ + final Language::Location getLocation() { result = func.getLocation() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll new file mode 100644 index 000000000000..cc1bdb6444b6 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.internal.IRCppLanguage as Language +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll index ac284440648d..21dfedd95cd4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll @@ -40,7 +40,17 @@ abstract class OperandTag extends TOperandTag { /** * Gets a label that will appear before the operand when the IR is printed. */ - string getLabel() { result = "" } + final string getLabel() { if alwaysPrintLabel() then result = getId() + ":" else result = "" } + + /** + * Gets an identifier that uniquely identifies this operand within its instruction. + */ + abstract string getId(); + + /** + * Holds if the operand should always be prefixed with its label in the dump of its instruction. + */ + predicate alwaysPrintLabel() { none() } } /** @@ -69,7 +79,9 @@ class AddressOperandTag extends RegisterOperandTag, TAddressOperand { final override int getSortOrder() { result = 0 } - final override string getLabel() { result = "&:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "&" } } AddressOperandTag addressOperand() { result = TAddressOperand() } @@ -82,6 +94,8 @@ class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand { final override string toString() { result = "BufferSize" } final override int getSortOrder() { result = 1 } + + final override string getId() { result = "size" } } BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() } @@ -93,6 +107,8 @@ class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand { final override string toString() { result = "SideEffect" } final override int getSortOrder() { result = 2 } + + final override string getId() { result = "side_effect" } } SideEffectOperandTag sideEffectOperand() { result = TSideEffectOperand() } @@ -105,6 +121,8 @@ class LoadOperandTag extends TypedOperandTag, TLoadOperand { final override string toString() { result = "Load" } final override int getSortOrder() { result = 3 } + + final override string getId() { result = "load" } } LoadOperandTag loadOperand() { result = TLoadOperand() } @@ -116,6 +134,8 @@ class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand { final override string toString() { result = "StoreValue" } final override int getSortOrder() { result = 4 } + + final override string getId() { result = "store" } } StoreValueOperandTag storeValueOperand() { result = TStoreValueOperand() } @@ -127,6 +147,8 @@ class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand { final override string toString() { result = "Unary" } final override int getSortOrder() { result = 5 } + + final override string getId() { result = "unary" } } UnaryOperandTag unaryOperand() { result = TUnaryOperand() } @@ -138,6 +160,8 @@ class LeftOperandTag extends RegisterOperandTag, TLeftOperand { final override string toString() { result = "Left" } final override int getSortOrder() { result = 6 } + + final override string getId() { result = "left" } } LeftOperandTag leftOperand() { result = TLeftOperand() } @@ -149,6 +173,8 @@ class RightOperandTag extends RegisterOperandTag, TRightOperand { final override string toString() { result = "Right" } final override int getSortOrder() { result = 7 } + + final override string getId() { result = "right" } } RightOperandTag rightOperand() { result = TRightOperand() } @@ -160,6 +186,8 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand { final override string toString() { result = "Condition" } final override int getSortOrder() { result = 8 } + + final override string getId() { result = "cond" } } ConditionOperandTag conditionOperand() { result = TConditionOperand() } @@ -172,7 +200,9 @@ class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand { final override int getSortOrder() { result = 10 } - final override string getLabel() { result = "func:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "func" } } CallTargetOperandTag callTargetOperand() { result = TCallTargetOperand() } @@ -195,7 +225,9 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand { final override int getSortOrder() { result = 11 } - final override string getLabel() { result = "this:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "this" } } ThisArgumentOperandTag thisArgumentOperand() { result = TThisArgumentOperand() } @@ -212,9 +244,11 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, TPositionalArgume final override int getSortOrder() { result = 12 + argIndex } - final override string getLabel() { result = argIndex.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } final int getArgIndex() { result = argIndex } + + final override string getId() { result = argIndex.toString() } } PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) { @@ -228,7 +262,9 @@ class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand { final override int getSortOrder() { result = 13 } - final override string getLabel() { result = "total:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "total" } } ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() } @@ -238,7 +274,9 @@ class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand { final override int getSortOrder() { result = 14 } - final override string getLabel() { result = "partial:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "partial" } } ChiPartialOperandTag chiPartialOperand() { result = TChiPartialOperand() } @@ -252,7 +290,9 @@ class AsmOperandTag extends RegisterOperandTag, TAsmOperand { final override int getSortOrder() { result = 15 + index } - final override string getLabel() { result = index.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = index.toString() } } AsmOperandTag asmOperand(int index) { result = TAsmOperand(index) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll index 362274f387c0..7984c4883fd5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll @@ -1,5 +1,5 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language -import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as Construction +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Construction private import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag_ module Imports { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll new file mode 100644 index 000000000000..e16b71733b5a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -0,0 +1,97 @@ +private import TInstructionInternal +private import IRFunctionBase +private import TInstructionImports as Imports +private import Imports::IRType +private import Imports::Opcode + +/** + * An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual + * branches of this type for instructions created directly from the AST (`TRawInstruction`) and for + * instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`, + * `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of + * all of the branches that can appear in that particular stage. The public `Instruction` class for + * each phase extends the `TStageInstruction` type for that stage. + */ +cached +newtype TInstruction = + TRawInstruction( + IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 + ) { + IRConstruction::Raw::hasInstruction(tag1, tag2) + } or + TUnaliasedSSAPhiInstruction( + TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation + ) { + UnaliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation) + } or + TUnaliasedSSAChiInstruction(TRawInstruction primaryInstruction) { none() } or + TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { + UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc) + } or + TAliasedSSAPhiInstruction( + TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation + ) { + AliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation) + } or + TAliasedSSAChiInstruction(TRawInstruction primaryInstruction) { + AliasedSSA::SSA::hasChiInstruction(primaryInstruction) + } or + TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { + AliasedSSA::SSA::hasUnreachedInstruction(irFunc) + } + +/** + * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the + * unaliased SSA stage. + * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via + * a class alias. + */ +module UnaliasedSSAInstructions { + class TPhiInstruction = TUnaliasedSSAPhiInstruction; + + TPhiInstruction phiInstruction( + TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation + ) { + result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation) + } + + class TChiInstruction = TUnaliasedSSAChiInstruction; + + TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { + result = TUnaliasedSSAChiInstruction(primaryInstruction) + } + + class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction; + + TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) { + result = TUnaliasedSSAUnreachedInstruction(irFunc) + } +} + +/** + * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the + * aliased SSA stage. + * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via + * a class alias. + */ +module AliasedSSAInstructions { + class TPhiInstruction = TAliasedSSAPhiInstruction; + + TPhiInstruction phiInstruction( + TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation + ) { + result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation) + } + + class TChiInstruction = TAliasedSSAChiInstruction; + + TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { + result = TAliasedSSAChiInstruction(primaryInstruction) + } + + class TUnreachedInstruction = TAliasedSSAUnreachedInstruction; + + TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) { + result = TAliasedSSAUnreachedInstruction(irFunc) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll new file mode 100644 index 000000000000..e008ce7d8d34 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.implementation.IRType as IRType +import semmle.code.cpp.ir.implementation.Opcode as Opcode diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll new file mode 100644 index 000000000000..adaaaca9cd82 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll @@ -0,0 +1,4 @@ +import semmle.code.cpp.ir.internal.IRCppLanguage as Language +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction +import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA +import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSSA diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll index badd48552a5f..c96783fe6e81 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + import IRFunction import Instruction import IRBlock @@ -11,11 +55,12 @@ import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() /** - * Class that provides additional properties to be dumped for IR instructions and blocks when using + * A class that provides additional properties to be dumped for IR instructions and blocks when using * the PrintIR module. Libraries that compute additional facts about IR elements can extend the * single instance of this class to specify the additional properties computed by the library. */ class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ string toString() { result = "IRPropertyProvider" } /** @@ -27,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index 94ef73b27692..d827ed3cf82d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -1,3 +1,7 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + private import internal.IRInternal import Instruction private import internal.IRBlockImports as Imports @@ -16,15 +20,27 @@ private import Cached * Most consumers should use the class `IRBlock`. */ class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ final string toString() { result = getFirstInstruction(this).toString() } + /** Gets the source location of the first non-`Phi` instruction in this block. */ final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + /** + * INTERNAL: Do not use. + * + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. + * INTERNAL: Do not use. + * + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. */ int getDisplayIndex() { exists(IRConfiguration::IRConfiguration config | @@ -42,27 +58,51 @@ class IRBlockBase extends TIRBlock { ) } + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ final Instruction getInstruction(int index) { result = getInstruction(this, index) } + /** + * Get the `Phi` instructions that appear at the start of this block. + */ final PhiInstruction getAPhiInstruction() { Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() } + /** + * Gets an instruction in this block. This includes `Phi` instructions. + */ final Instruction getAnInstruction() { result = getInstruction(_) or result = getAPhiInstruction() } + /** + * Gets the first non-`Phi` instruction in this block. + */ final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + /** + * Gets the last instruction in this block. + */ final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + /** + * Gets the number of non-`Phi` instructions in this block. + */ final int getInstructionCount() { result = getInstructionCount(this) } + /** + * Gets the `IRFunction` that contains this block. + */ final IRFunction getEnclosingIRFunction() { result = getFirstInstruction(this).getEnclosingIRFunction() } + /** + * Gets the `Function` that contains this block. + */ final Language::Function getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } @@ -74,20 +114,57 @@ class IRBlockBase extends TIRBlock { * instruction of another block. */ class IRBlock extends IRBlockBase { + /** + * Gets a block to which control flows directly from this block. + */ final IRBlock getASuccessor() { blockSuccessor(this, result) } + /** + * Gets a block from which control flows directly to this block. + */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + /** + * Gets a block on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ pragma[noinline] final IRBlock dominanceFrontier() { dominates(result.getAPredecessor()) and @@ -95,7 +172,7 @@ class IRBlock extends IRBlockBase { } /** - * Holds if this block is reachable from the entry point of its function + * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { this = getEnclosingIRFunction().getEntryBlock() or @@ -210,4 +287,4 @@ private module Cached { idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) } -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll index 65af34942b6b..6a87b9b4b5fd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll @@ -8,10 +8,79 @@ module InstructionConsistency { private import Imports::Overlap private import internal.IRInternal + private newtype TOptionalIRFunction = + TPresentIRFunction(IRFunction irFunc) or + TMissingIRFunction() + + /** + * An `IRFunction` that might not exist. This is used so that we can produce consistency failures + * for IR that also incorrectly lacks a `getEnclosingIRFunction()`. + */ + abstract private class OptionalIRFunction extends TOptionalIRFunction { + abstract string toString(); + + abstract Language::Location getLocation(); + } + + private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction { + private IRFunction irFunc; + + PresentIRFunction() { this = TPresentIRFunction(irFunc) } + + override string toString() { + result = concat(Language::getIdentityString(irFunc.getFunction()), "; ") + } + + override Language::Location getLocation() { + // To avoid an overwhelming number of results when the extractor merges functions with the + // same name, just pick a single location. + result = + rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString()) + } + } + + private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction { + override string toString() { result = "" } + + override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation } + } + + private OptionalIRFunction getInstructionIRFunction(Instruction instr) { + result = TPresentIRFunction(instr.getEnclosingIRFunction()) + or + not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) { + result = getInstructionIRFunction(instr) and + irFuncText = result.toString() + } + + private OptionalIRFunction getOperandIRFunction(Operand operand) { + result = TPresentIRFunction(operand.getEnclosingIRFunction()) + or + not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) { + result = getOperandIRFunction(operand) and + irFuncText = result.toString() + } + + private OptionalIRFunction getBlockIRFunction(IRBlock block) { + result = TPresentIRFunction(block.getEnclosingIRFunction()) + or + not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + /** * Holds if instruction `instr` is missing an expected operand with tag `tag`. */ - query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) { + query predicate missingOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { exists(OperandTag tag | instr.getOpcode().hasOperand(tag) and not exists(NonPhiOperand operand | @@ -21,32 +90,39 @@ module InstructionConsistency { message = "Instruction '" + instr.getOpcode().toString() + "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(instr, irFuncText) ) } /** * Holds if instruction `instr` has an unexpected operand with tag `tag`. */ - query predicate unexpectedOperand(Instruction instr, OperandTag tag) { - exists(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - not instr.getOpcode().hasOperand(tag) and - not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and - not ( - instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag - ) and - not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) + query predicate unexpectedOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag | + exists(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + not instr.getOpcode().hasOperand(tag) and + not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and + not ( + instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag + ) and + not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and + message = + "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() + + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if instruction `instr` has multiple operands with tag `tag`. */ query predicate duplicateOperand( - Instruction instr, string message, IRFunction func, string funcText + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(OperandTag tag, int operandCount | operandCount = @@ -58,8 +134,7 @@ module InstructionConsistency { message = "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + " in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(instr, irFuncText) ) } @@ -67,100 +142,136 @@ module InstructionConsistency { * Holds if `Phi` instruction `instr` is missing an operand corresponding to * the predecessor block `pred`. */ - query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) { - pred = instr.getBlock().getAPredecessor() and - not exists(PhiInputOperand operand | - operand = instr.getAnOperand() and - operand.getPredecessorBlock() = pred + query predicate missingPhiOperand( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRBlock pred | + pred = instr.getBlock().getAPredecessor() and + not exists(PhiInputOperand operand | + operand = instr.getAnOperand() and + operand.getPredecessorBlock() = pred + ) and + message = + "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" + + pred.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } - query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func, Instruction use | + query predicate missingOperandType( + Operand operand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Instruction use | not exists(operand.getType()) and use = operand.getUse() and - func = use.getEnclosingFunction() and message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + - "' missing type in function '" + Language::getIdentityString(func) + "'." + "' is missing a type in function '$@'." and + irFunc = getOperandIRFunction(operand, irFuncText) ) } query predicate duplicateChiOperand( - ChiInstruction chi, string message, IRFunction func, string funcText + ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText ) { chi.getTotal() = chi.getPartial() and message = "Chi instruction for " + chi.getPartial().toString() + - " has duplicate operands in function $@" and - func = chi.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + " has duplicate operands in function '$@'." and + irFunc = getInstructionIRFunction(chi, irFuncText) } query predicate sideEffectWithoutPrimary( - SideEffectInstruction instr, string message, IRFunction func, string funcText + SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { not exists(instr.getPrimaryInstruction()) and - message = "Side effect instruction missing primary instruction in function $@" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + message = + "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } /** * Holds if an instruction, other than `ExitFunction`, has no successors. */ - query predicate instructionWithoutSuccessor(Instruction instr) { + query predicate instructionWithoutSuccessor( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { not exists(instr.getASuccessor()) and not instr instanceof ExitFunctionInstruction and // Phi instructions aren't linked into the instruction-level flow graph. not instr instanceof PhiInstruction and - not instr instanceof UnreachedInstruction + not instr instanceof UnreachedInstruction and + message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } /** - * Holds if there are multiple (`n`) edges of kind `kind` from `source`, - * where `target` is among the targets of those edges. + * Holds if there are multiple edges of the same kind from `source`. */ - query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) { - n = strictcount(Instruction t | source.getSuccessor(kind) = t) and - n > 1 and - source.getSuccessor(kind) = target + query predicate ambiguousSuccessors( + Instruction source, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(EdgeKind kind, int n | + n = strictcount(Instruction t | source.getSuccessor(kind) = t) and + n > 1 and + message = + "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" + + kind.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(source, irFuncText) + ) } /** - * Holds if `instr` in `f` is part of a loop even though the AST of `f` + * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function * contains no element that can cause loops. */ - query predicate unexplainedLoop(Language::Function f, Instruction instr) { - exists(IRBlock block | - instr.getBlock() = block and - block.getEnclosingFunction() = f and - block.getASuccessor+() = block - ) and - not Language::hasPotentialLoop(f) + query predicate unexplainedLoop( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Language::Function f | + exists(IRBlock block | + instr.getBlock() = block and + block.getEnclosingFunction() = f and + block.getASuccessor+() = block + ) and + not Language::hasPotentialLoop(f) and + message = + "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if a `Phi` instruction is present in a block with fewer than two * predecessors. */ - query predicate unnecessaryPhiInstruction(PhiInstruction instr) { - count(instr.getBlock().getAPredecessor()) < 2 + query predicate unnecessaryPhiInstruction( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int n | + n = count(instr.getBlock().getAPredecessor()) and + n < 2 and + message = + "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() + + " predecessors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if a memory operand is connected to a definition with an unmodeled result. */ query predicate memoryOperandDefinitionIsUnmodeled( - Instruction instr, string message, IRFunction func, string funcText + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(MemoryOperand operand, Instruction def | operand = instr.getAnOperand() and def = operand.getAnyDef() and not def.isResultModeled() and - message = "Memory operand definition has unmodeled result in function '$@'" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + message = + "Memory operand definition on instruction '" + instr.toString() + + "' has unmodeled result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } @@ -168,18 +279,37 @@ module InstructionConsistency { * Holds if operand `operand` consumes a value that was defined in * a different function. */ - query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) { - operand.getUse() = instr and - operand.getAnyDef() = defInstr and - instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction() + query predicate operandAcrossFunctions( + Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText, + OptionalIRFunction defIRFunc, string defIRFuncText + ) { + exists(Instruction useInstr, Instruction defInstr | + operand.getUse() = useInstr and + operand.getAnyDef() = defInstr and + useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and + defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and + useIRFunc != defIRFunc and + message = + "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() + + "' in function '$@', but is defined on instruction '" + defInstr.toString() + + "' in function '$@'." + ) } /** * Holds if instruction `instr` is not in exactly one block. */ - query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { - blockCount = count(instr.getBlock()) and - blockCount != 1 + query predicate instructionWithoutUniqueBlock( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int blockCount | + blockCount = count(instr.getBlock()) and + blockCount != 1 and + message = + "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() + + " blocks in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } private predicate forwardEdge(IRBlock b1, IRBlock b2) { @@ -192,10 +322,11 @@ module InstructionConsistency { * * This check ensures we don't have too _few_ back edges. */ - query predicate containsLoopOfForwardEdges(IRFunction f) { + query predicate containsLoopOfForwardEdges(IRFunction f, string message) { exists(IRBlock block | forwardEdge+(block, block) and - block.getEnclosingIRFunction() = f + block.getEnclosingIRFunction() = f and + message = "Function contains a loop consisting of only forward edges." ) } @@ -207,12 +338,19 @@ module InstructionConsistency { * * This check ensures we don't have too _many_ back edges. */ - query predicate lostReachability(IRBlock block) { + query predicate lostReachability( + IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText + ) { exists(IRFunction f, IRBlock entry | entry = f.getEntryBlock() and entry.getASuccessor+() = block and not forwardEdge+(entry, block) and - not Language::hasGoto(f.getFunction()) + not Language::hasGoto(f.getFunction()) and + message = + "Block '" + block.toString() + + "' is not reachable by traversing only forward edges in function '$@'." and + irFunc = TPresentIRFunction(f) and + irFuncText = irFunc.toString() ) } @@ -220,16 +358,22 @@ module InstructionConsistency { * Holds if the number of back edges differs between the `Instruction` graph * and the `IRBlock` graph. */ - query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) { - fromInstr = - count(Instruction i1, Instruction i2 | - i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2 - ) and - fromBlock = - count(IRBlock b1, IRBlock b2 | - b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2 - ) and - fromInstr != fromBlock + query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) { + exists(int fromInstr, int fromBlock | + fromInstr = + count(Instruction i1, Instruction i2 | + getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2 + ) and + fromBlock = + count(IRBlock b1, IRBlock b2 | + getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2 + ) and + fromInstr != fromBlock and + message = + "The instruction graph for function '" + irFunc.toString() + "' contains " + + fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString() + + " back edges." + ) } /** @@ -251,7 +395,7 @@ module InstructionConsistency { * Holds if `useOperand` has a definition that does not dominate the use. */ query predicate useNotDominatedByDefinition( - Operand useOperand, string message, IRFunction func, string funcText + Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | pointOfEvaluation(useOperand, useBlock, useIndex) and @@ -272,19 +416,17 @@ module InstructionConsistency { message = "Operand '" + useOperand.toString() + "' is not dominated by its definition in function '$@'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getOperandIRFunction(useOperand, irFuncText) ) } query predicate switchInstructionWithoutDefaultEdge( - SwitchInstruction switchInstr, string message, IRFunction func, string funcText + SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText ) { not exists(switchInstr.getDefaultSuccessor()) and message = "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and - func = switchInstr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(switchInstr, irFuncText) } /** @@ -305,18 +447,30 @@ module InstructionConsistency { instr.getOpcode() instanceof Opcode::InitializeNonLocal } - query predicate notMarkedAsConflated(Instruction instr) { + query predicate notMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { shouldBeConflated(instr) and - not instr.isResultConflated() + not instr.isResultConflated() and + message = + "Instruction '" + instr.toString() + + "' should be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } - query predicate wronglyMarkedAsConflated(Instruction instr) { + query predicate wronglyMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { instr.isResultConflated() and - not shouldBeConflated(instr) + not shouldBeConflated(instr) and + message = + "Instruction '" + instr.toString() + + "' should not be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } query predicate invalidOverlap( - MemoryOperand useOperand, string message, IRFunction func, string funcText + MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(Overlap overlap | overlap = useOperand.getDefinitionOverlap() and @@ -324,8 +478,20 @@ module InstructionConsistency { message = "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" + overlap.toString() + "'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getOperandIRFunction(useOperand, irFuncText) + ) + } + + query predicate nonUniqueEnclosingIRFunction( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int irFuncCount | + irFuncCount = count(instr.getEnclosingIRFunction()) and + irFuncCount != 1 and + message = + "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() + + " results for `getEnclosingIRFunction()` in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll index 9aea3e00d666..5968e58f90bf 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll @@ -1,29 +1,17 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase import Instruction -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - /** - * Represents the IR for a function. + * The IR for a function. */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - +class IRFunction extends IRFunctionBase { /** * Gets the entry point for this function. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index 9f2a0d4ea281..146fc2707383 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + private import internal.IRInternal import IRFunction private import internal.IRVariableImports as Imports @@ -7,15 +11,11 @@ private import Imports::TTempVariableTag private import Imports::TIRVariable private import Imports::IRType -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - /** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { Language::Function func; @@ -27,6 +27,7 @@ class IRVariable extends TIRVariable { this = TIRDynamicInitializationFlag(func, _, _) } + /** Gets a textual representation of this element. */ string toString() { none() } /** @@ -162,20 +163,30 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } + /** + * INTERNAL: Do not use. + * + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ final string getLocationString() { result = ast.getLocation().getStartLine().toString() + ":" + ast.getLocation().getStartColumn().toString() } + /** + * INTERNAL: Do not use. + * + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ string getBaseString() { none() } } -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - /** * A temporary variable introduced by IR construction. The most common examples are the variable * generated to hold the return value of a function, or the variable generated to hold the result of @@ -190,6 +201,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa result = "Temp: " + Construction::getTempVariableUniqueId(this) } + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ final TempVariableTag getTag() { result = tag } override string getBaseString() { result = "#temp" } @@ -217,19 +232,23 @@ class IRThrowVariable extends IRTempVariable { * A temporary variable generated to hold the contents of all arguments passed to the `...` of a * function that accepts a variable number of arguments. */ -class IREllipsisVariable extends IRTempVariable { +class IREllipsisVariable extends IRTempVariable, IRParameter { IREllipsisVariable() { tag = EllipsisTempVar() } final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } } /** * A temporary variable generated to hold the `this` pointer. */ -class IRThisVariable extends IRTempVariable { +class IRThisVariable extends IRTempVariable, IRParameter { IRThisVariable() { tag = ThisTempVar() } final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } } /** @@ -249,6 +268,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { final override string getBaseString() { result = "#string" } + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ final Language::StringLiteral getLiteral() { result = literal } } @@ -266,6 +288,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string toString() { result = var.toString() + "#init" } + /** + * Gets variable whose initialization is guarded by this flag. + */ final Language::Variable getVariable() { result = var } final override string getUniqueId() { @@ -274,3 +299,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string getBaseString() { result = "#init:" + var.toString() + ":" } } + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +/** + * An IR variable representing a positional parameter. + */ +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 9c83a3d99f0c..620b23b942e0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + private import internal.IRInternal import IRFunction import IRBlock @@ -27,9 +31,16 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * Represents a single operation in the IR. + * A single instruction in the IR. */ -class Instruction extends Construction::TInstruction { +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + + /** Gets a textual representation of this element. */ final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -194,16 +205,25 @@ class Instruction extends Construction::TInstruction { * conversion. */ final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) + result = Raw::getInstructionConvertedResultExpression(this) } /** * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. */ final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) + result = Raw::getInstructionUnconvertedResultExpression(this) } + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ final Language::LanguageType getResultLanguageType() { result = Construction::getInstructionResultType(this) } @@ -212,6 +232,7 @@ class Instruction extends Construction::TInstruction { * Gets the type of the result produced by this instruction. If the instruction does not produce * a result, its result type will be `IRVoidType`. */ + cached final IRType getResultIRType() { result = getResultLanguageType().getIRType() } /** @@ -240,17 +261,19 @@ class Instruction extends Construction::TInstruction { * given by `getResultType()`. * * For example, the statement `y = x;` generates the following IR: + * ``` * r1_0(glval: int) = VariableAddress[x] * r1_1(int) = Load r1_0, mu0_1 * r1_2(glval: int) = VariableAddress[y] * mu1_3(int) = Store r1_2, r1_1 + * ``` * * The result of each `VariableAddress` instruction is a glvalue of type * `int`, representing the address of the corresponding integer variable. The * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -259,7 +282,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } + final int getResultSize() { result = getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -392,13 +415,27 @@ class Instruction extends Construction::TInstruction { final Instruction getAPredecessor() { result = getPredecessor(_) } } +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ class VariableInstruction extends Instruction { IRVariable var; - VariableInstruction() { var = Construction::getInstructionVariable(this) } + VariableInstruction() { var = Raw::getInstructionVariable(this) } override string getImmediateString() { result = var.toString() } + /** + * Gets the variable that this instruction references. + */ final IRVariable getIRVariable() { result = var } /** @@ -407,63 +444,156 @@ class VariableInstruction extends Instruction { final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ class FieldInstruction extends Instruction { Language::Field field; - FieldInstruction() { field = Construction::getInstructionField(this) } + FieldInstruction() { field = Raw::getInstructionField(this) } final override string getImmediateString() { result = field.toString() } + /** + * Gets the field that this instruction references. + */ final Language::Field getField() { result = field } } +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ class FunctionInstruction extends Instruction { Language::Function funcSymbol; - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } final override string getImmediateString() { result = funcSymbol.toString() } + /** + * Gets the function that this instruction references. + */ final Language::Function getFunctionSymbol() { result = funcSymbol } } +/** + * An instruction whose result is a compile-time constant value. + */ class ConstantValueInstruction extends Instruction { string value; - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } final override string getImmediateString() { result = value } + /** + * Gets the constant value of this instruction's result. + */ final string getValue() { result = value } } +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ class IndexedInstruction extends Instruction { int index; - IndexedInstruction() { index = Construction::getInstructionIndex(this) } + IndexedInstruction() { index = Raw::getInstructionIndex(this) } final override string getImmediateString() { result = index.toString() } + /** + * Gets the zero-based index of the argument that this instruction references. + */ final int getIndex() { result = index } } +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ class EnterFunctionInstruction extends Instruction { EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } } +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ class InitializeParameterInstruction extends VariableInstruction { InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirectionInstruction extends VariableInstruction { InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } @@ -474,14 +604,42 @@ class InitializeThisInstruction extends Instruction { InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } } +/** + * An instruction that computes the address of a non-static field of an object. + */ class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + /** + * Gets the operand that provides the address of the object containing the field. + */ final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + /** * An instruction that produces a well-defined but unknown result and has * unknown side effects, including side effects that are not conservatively @@ -496,6 +654,12 @@ class ErrorInstruction extends Instruction { ErrorInstruction() { getOpcode() instanceof Opcode::Error } } +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } @@ -505,35 +669,94 @@ class UninitializedInstruction extends VariableInstruction { final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ class NoOpInstruction extends Instruction { NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } } +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ class ReturnInstruction extends Instruction { ReturnInstruction() { getOpcode() instanceof ReturnOpcode } } +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ class ReturnVoidInstruction extends ReturnInstruction { ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } } +/** + * An instruction that returns control to the caller of the function, including a return value. + */ class ReturnValueInstruction extends ReturnInstruction { ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + /** + * Gets the operand that provides the value being returned by the function. + */ final LoadOperand getReturnValueOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } } +/** + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. + */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + /** + * Gets the operand that provides the value of the pointed-to memory. + */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + /** + * Gets the operand that provides the address of the pointed-to memory. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } /** @@ -541,87 +764,191 @@ class ReturnIndirectionInstruction extends VariableInstruction { * function. */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } } +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ class CopyInstruction extends Instruction { CopyInstruction() { getOpcode() instanceof CopyOpcode } + /** + * Gets the operand that provides the input value of the copy. + */ Operand getSourceValueOperand() { none() } + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } } +/** + * An instruction that returns a register result containing a copy of its register operand. + */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ class LoadInstruction extends CopyInstruction { LoadInstruction() { getOpcode() instanceof Opcode::Load } + /** + * Gets the operand that provides the address of the value being loaded. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } final override LoadOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ class StoreInstruction extends CopyInstruction { StoreInstruction() { getOpcode() instanceof Opcode::Store } + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranchInstruction extends Instruction { ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ final ConditionOperand getConditionOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ final Instruction getCondition() { result = getConditionOperand().getDef() } + /** + * Gets the instruction to which control will flow if the condition is true. + */ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + /** + * Gets the instruction to which control will flow if the condition is false. + */ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } } +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no + * successors. + */ class ExitFunctionInstruction extends Instruction { ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } } +/** + * An instruction whose result is a constant value. + */ class ConstantInstruction extends ConstantValueInstruction { ConstantInstruction() { getOpcode() instanceof Opcode::Constant } } +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } } +/** + * An instruction whose result is a constant value of floating-point type. + */ class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } +/** + * An instruction whose result is the address of a string literal. + */ class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + /** + * Gets the string literal whose address is returned by this instruction. + */ final Language::StringLiteral getValue() { result = var.getLiteral() } } +/** + * An instruction whose result is computed from two operands. + */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + /** + * Gets the left operand of this binary instruction. + */ final LeftOperand getLeftOperand() { result = getAnOperand() } + /** + * Gets the right operand of this binary instruction. + */ final RightOperand getRightOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ final Instruction getLeft() { result = getLeftOperand().getDef() } + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ final Instruction getRight() { result = getRightOperand().getDef() } /** @@ -634,121 +961,301 @@ class BinaryInstruction extends Instruction { } } +/** + * An instruction that computes the result of an arithmetic operation. + */ class ArithmeticInstruction extends Instruction { ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } } +/** + * An instruction that performs an arithmetic operation on two numeric operands. + */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * numeric operand. + */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } +/** + * An instruction that computes the sum of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ class AddInstruction extends BinaryArithmeticInstruction { AddInstruction() { getOpcode() instanceof Opcode::Add } } +/** + * An instruction that computes the difference of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ class SubInstruction extends BinaryArithmeticInstruction { SubInstruction() { getOpcode() instanceof Opcode::Sub } } +/** + * An instruction that computes the product of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ class MulInstruction extends BinaryArithmeticInstruction { MulInstruction() { getOpcode() instanceof Opcode::Mul } } +/** + * An instruction that computes the quotient of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ class DivInstruction extends BinaryArithmeticInstruction { DivInstruction() { getOpcode() instanceof Opcode::Div } } +/** + * An instruction that computes the remainder of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ class RemInstruction extends BinaryArithmeticInstruction { RemInstruction() { getOpcode() instanceof Opcode::Rem } } +/** + * An instruction that negates a single numeric operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ class NegateInstruction extends UnaryArithmeticInstruction { NegateInstruction() { getOpcode() instanceof Opcode::Negate } } +/** + * An instruction that computes the result of a bitwise operation. + */ class BitwiseInstruction extends Instruction { BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } } +/** + * An instruction that performs a bitwise operation on two integer operands. + */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } +/** + * An instruction that performs a bitwise operation on a single integer operand. + */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } +/** + * An instruction that computes the bitwise "and" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitAndInstruction extends BinaryBitwiseInstruction { BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } } +/** + * An instruction that computes the bitwise "or" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitOrInstruction extends BinaryBitwiseInstruction { BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } } +/** + * An instruction that computes the bitwise "xor" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitXorInstruction extends BinaryBitwiseInstruction { BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } } +/** + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } } +/** + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ class ShiftRightInstruction extends BinaryBitwiseInstruction { ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } } +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; PointerArithmeticInstruction() { getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) + elementSize = Raw::getInstructionElementSize(this) } final override string getImmediateString() { result = elementSize.toString() } + /** + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. + */ final int getElementSize() { result = elementSize } } +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ class PointerOffsetInstruction extends PointerArithmeticInstruction { PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } } +/** + * An instruction that adds an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ class PointerAddInstruction extends PointerOffsetInstruction { PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } } +/** + * An instruction that subtracts an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ class PointerSubInstruction extends PointerOffsetInstruction { PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } } +/** + * An instruction that computes the difference between two pointers. + * + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. + */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } } +/** + * An instruction whose result is computed from a single operand. + */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + /** + * Gets the sole operand of this instruction. + */ final UnaryOperand getUnaryOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ final Instruction getUnary() { result = getUnaryOperand().getDef() } } +/** + * An instruction that converts the value of its operand to a value of a different type. + */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } } +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ class CheckedConvertOrNullInstruction extends UnaryInstruction { CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** - * Represents an instruction that converts between two addresses - * related by inheritance. + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. */ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class baseClass; Language::Class derivedClass; InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) + Raw::getInstructionInheritance(this, baseClass, derivedClass) } final override string getImmediateString() { @@ -778,59 +1285,91 @@ class InheritanceConversionInstruction extends UnaryInstruction { } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. + * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } } +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ class BitComplementInstruction extends UnaryBitwiseInstruction { BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } } +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ class LogicalNotInstruction extends UnaryInstruction { LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } } +/** + * An instruction that compares two numeric operands. + */ class CompareInstruction extends BinaryInstruction { CompareInstruction() { getOpcode() instanceof CompareOpcode } } +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareEQInstruction extends CompareInstruction { CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } } +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ class CompareNEInstruction extends CompareInstruction { CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } } /** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. + * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { RelationalInstruction() { getOpcode() instanceof RelationalOpcode } @@ -857,6 +1396,13 @@ class RelationalInstruction extends CompareInstruction { predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLTInstruction extends RelationalInstruction { CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } @@ -867,6 +1413,13 @@ class CompareLTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGTInstruction extends RelationalInstruction { CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } @@ -877,6 +1430,14 @@ class CompareGTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLEInstruction extends RelationalInstruction { CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } @@ -887,6 +1448,14 @@ class CompareLEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGEInstruction extends RelationalInstruction { CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } @@ -897,15 +1466,32 @@ class CompareGEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ class SwitchInstruction extends Instruction { SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + /** Gets the operand that provides the integer value controlling the switch. */ final ConditionOperand getExpressionOperand() { result = getAnOperand() } + /** Gets the instruction whose result provides the integer value controlling the switch. */ final Instruction getExpression() { result = getExpressionOperand().getDef() } + /** Gets the successor instructions along the case edges of the switch. */ final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + /** Gets the successor instruction along the default edge of the switch, if any. */ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } } @@ -936,7 +1522,7 @@ class CallInstruction extends Instruction { * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** @@ -981,6 +1567,9 @@ class CallInstruction extends Instruction { class SideEffectInstruction extends Instruction { SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + /** + * Gets the instruction whose execution causes this side effect. + */ final Instruction getPrimaryInstruction() { result = Construction::getPrimaryInstructionForSideEffect(this) } @@ -996,9 +1585,10 @@ class CallSideEffectInstruction extends SideEffectInstruction { /** * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } @@ -1046,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { getOpcode() instanceof Opcode::SizedBufferReadSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1056,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } } /** @@ -1087,11 +1693,20 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** * An instruction representing the potential write of an indirect parameter within a function call. + * * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. * written. */ @@ -1103,6 +1718,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1111,6 +1727,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1118,11 +1735,19 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a + * An instruction representing the initial value of newly allocated memory, such as the result of a * call to `malloc`. */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { @@ -1211,7 +1836,7 @@ class CatchByTypeInstruction extends CatchInstruction { CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) + exceptionType = Raw::getInstructionExceptionType(this) } final override string getImmediateString() { result = exceptionType.toString() } @@ -1337,29 +1962,43 @@ class ChiInstruction extends Instruction { * Gets the operand that represents the new value written by the memory write. */ final Instruction getPartial() { result = getPartialOperand().getDef() } + + /** + * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. + */ + final predicate getUpdatedInterval(int startBit, int endBit) { + Construction::getIntervalUpdatedByChi(this, startBit, endBit) + } } /** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } } /** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. */ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; BuiltInOperationInstruction() { getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) + operation = Raw::getInstructionBuiltInOperation(this) } + /** + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is + * performed by this instruction. + */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } } @@ -1372,3 +2011,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction { final override string getImmediateString() { result = getBuiltInOperation().toString() } } + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index f82704094c8e..a12e35d471b8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + private import internal.IRInternal private import Instruction private import IRBlock @@ -75,13 +79,21 @@ private PhiOperandBase phiOperand( } /** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) */ class Operand extends TOperand { + /** Gets a textual representation of this element. */ string toString() { result = "Operand" } + /** + * Gets the location of the source code for this operand. + */ final Language::Location getLocation() { result = getUse().getLocation() } + /** + * Gets the function that contains this operand. + */ final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } /** @@ -139,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -268,8 +285,13 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ final OperandTag getOperandTag() { result = tag } } @@ -292,6 +314,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase { } } +/** + * A memory operand other than the operand of a `Phi` instruction. + */ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { override MemoryOperandTag tag; @@ -311,8 +336,19 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper not Construction::isInCycle(useInstr) and strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1 } + + /** + * Holds if the operand totally overlaps with its definition and consumes the + * bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition. + */ + predicate getUsedInterval(int startBitOffset, int endBitOffset) { + Construction::getUsedInterval(this, startBitOffset, endBitOffset) + } } +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; @@ -416,6 +452,9 @@ class PositionalArgumentOperand extends ArgumentOperand { final int getIndex() { result = tag.getArgIndex() } } +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; } @@ -445,6 +484,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index d9c0df44e12e..59dadee71545 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -1,3 +1,13 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + private import internal.IRInternal private import IR private import internal.PrintIRImports as Imports @@ -9,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration() * The query can extend this class to control which functions are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ string toString() { result = "PrintIRConfiguration" } /** @@ -39,6 +50,37 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -47,7 +89,7 @@ private newtype TPrintableIRNode = /** * A node to be emitted in the IR graph. */ -abstract class PrintableIRNode extends TPrintableIRNode { +abstract private class PrintableIRNode extends TPrintableIRNode { abstract string toString(); /** @@ -98,7 +140,7 @@ abstract class PrintableIRNode extends TPrintableIRNode { /** * An IR graph node representing a `IRFunction` object. */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { IRFunction irFunc; PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } @@ -129,7 +171,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { /** * An IR graph node representing an `IRBlock` object. */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { IRBlock block; PrintableIRBlock() { this = TPrintableIRBlock(block) } @@ -161,7 +203,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { /** * An IR graph node representing an `Instruction`. */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { Instruction instr; PrintableInstruction() { this = TPrintableInstruction(instr) } @@ -179,7 +221,7 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -199,6 +241,22 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { @@ -224,6 +282,9 @@ private string getPaddingString(int n) { n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " } +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ query predicate nodes(PrintableIRNode node, string key, string value) { value = node.getProperty(key) } @@ -237,6 +298,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) { ) } +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | predBlock = pred.getBlock() and @@ -256,6 +321,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, ) } +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ query predicate parents(PrintableIRNode child, PrintableIRNode parent) { parent = child.getParent() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 5200da91a55c..f6e27e080f7d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -1,6 +1,9 @@ private import cpp import semmle.code.cpp.ir.implementation.raw.IR private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.implementation.internal.IRFunctionBase +private import semmle.code.cpp.ir.implementation.internal.TInstruction +private import semmle.code.cpp.ir.implementation.internal.TIRVariable private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.ir.internal.Overlap private import semmle.code.cpp.ir.internal.TempVariableTag @@ -12,34 +15,36 @@ private import TranslatedStmt private import TranslatedFunction TranslatedElement getInstructionTranslatedElement(Instruction instruction) { - instruction = MkInstruction(result, _) + instruction = TRawInstruction(result, _) } -InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) } - -import Cached +InstructionTag getInstructionTag(Instruction instruction) { + instruction = TRawInstruction(_, result) +} +/** + * Provides the portion of the parameterized IR interface that is used to construct the initial + * "raw" stage of the IR. The other stages of the IR do not expose these predicates. + */ cached -private module Cached { +module Raw { + class InstructionTag1 = TranslatedElement; + + class InstructionTag2 = InstructionTag; + cached predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) } cached - newtype TInstruction = - MkInstruction(TranslatedElement element, InstructionTag tag) { - element.hasInstruction(_, tag, _) - } + predicate hasInstruction(TranslatedElement element, InstructionTag tag) { + element.hasInstruction(_, tag, _) + } cached predicate hasUserVariable(Function func, Variable var, CppType type) { getTranslatedFunction(func).hasUserVariable(var, type) } - cached - predicate hasThisVariable(Function func, CppType type) { - type = getTypeForGLValue(getTranslatedFunction(func).getThisType()) - } - cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) { exists(TranslatedElement element | @@ -64,232 +69,7 @@ private module Cached { } cached - predicate hasModeledMemoryResult(Instruction instruction) { none() } - - cached - predicate hasConflatedMemoryResult(Instruction instruction) { - instruction instanceof AliasedDefinitionInstruction - or - instruction.getOpcode() instanceof Opcode::InitializeNonLocal - } - - cached - Expr getInstructionConvertedResultExpression(Instruction instruction) { - exists(TranslatedExpr translatedExpr | - translatedExpr = getTranslatedExpr(result) and - instruction = translatedExpr.getResult() and - // Only associate `instruction` with this expression if the translated - // expression actually produced the instruction; not if it merely - // forwarded the result of another translated expression. - instruction = translatedExpr.getInstruction(_) - ) - } - - cached - Expr getInstructionUnconvertedResultExpression(Instruction instruction) { - result = getInstructionConvertedResultExpression(instruction).getUnconverted() - } - - cached - Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionRegisterOperand(getInstructionTag(instruction), tag) - } - - cached - Instruction getMemoryOperandDefinition( - Instruction instruction, MemoryOperandTag tag, Overlap overlap - ) { - none() - } - - /** Gets a non-phi instruction that defines an operand of `instr`. */ - private Instruction getNonPhiOperandDef(Instruction instr) { - result = getRegisterOperandDefinition(instr, _) - or - result = getMemoryOperandDefinition(instr, _, _) - } - - /** - * Gets a non-phi instruction that defines an operand of `instr` but only if - * both `instr` and the result have neighbor on the other side of the edge - * between them. This is a necessary condition for being in a cycle, and it - * removes about two thirds of the tuples that would otherwise be in this - * predicate. - */ - private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) { - result = getNonPhiOperandDef(instr) and - exists(getNonPhiOperandDef(result)) and - instr = getNonPhiOperandDef(_) - } - - /** - * Holds if `instr` is part of a cycle in the operand graph that doesn't go - * through a phi instruction and therefore should be impossible. - * - * If such cycles are present, either due to a programming error in the IR - * generation or due to a malformed database, it can cause infinite loops in - * analyses that assume a cycle-free graph of non-phi operands. Therefore it's - * better to remove these operands than to leave cycles in the operand graph. - */ - pragma[noopt] - cached - predicate isInCycle(Instruction instr) { - instr instanceof Instruction and - getNonPhiOperandDefOfIntermediate+(instr) = instr - } - - cached - CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { - // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as - // the result type of the load. - tag instanceof LoadOperandTag and - result = instruction.(LoadInstruction).getResultLanguageType() - or - not instruction instanceof LoadInstruction and - result = - getInstructionTranslatedElement(instruction) - .getInstructionMemoryOperandType(getInstructionTag(instruction), tag) - } - - cached - Instruction getPhiOperandDefinition( - PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap - ) { - none() - } - - cached - Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() } - - cached - Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionSuccessor(getInstructionTag(instruction), kind) - } - - /** - * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`--> - * `targetInstruction` is a back edge under the condition that - * `requiredAncestor` is an ancestor of `sourceElement`. - */ - private predicate backEdgeCandidate( - TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor, - Instruction targetInstruction, EdgeKind kind - ) { - // While loop: - // Any edge from within the body of the loop to the condition of the loop - // is a back edge. This includes edges from `continue` and the fall-through - // edge(s) after the last instruction(s) in the body. - exists(TranslatedWhileStmt s | - targetInstruction = s.getFirstConditionInstruction() and - targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and - requiredAncestor = s.getBody() - ) - or - // Do-while loop: - // The back edge should be the edge(s) from the condition to the - // body. This ensures that it's the back edge that will be pruned in a `do - // { ... } while (0)` statement. Note that all `continue` statements in a - // do-while loop produce forward edges. - exists(TranslatedDoStmt s | - targetInstruction = s.getBody().getFirstInstruction() and - targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and - requiredAncestor = s.getCondition() - ) - or - // For loop: - // Any edge from within the body or update of the loop to the condition of - // the loop is a back edge. When there is no loop update expression, this - // includes edges from `continue` and the fall-through edge(s) after the - // last instruction(s) in the body. A for loop may not have a condition, in - // which case `getFirstConditionInstruction` returns the body instead. - exists(TranslatedForStmt s | - targetInstruction = s.getFirstConditionInstruction() and - targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and - ( - requiredAncestor = s.getUpdate() - or - not exists(s.getUpdate()) and - requiredAncestor = s.getBody() - ) - ) - or - // Range-based for loop: - // Any edge from within the update of the loop to the condition of - // the loop is a back edge. - exists(TranslatedRangeBasedForStmt s | - targetInstruction = s.getCondition().getFirstInstruction() and - targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and - requiredAncestor = s.getUpdate() - ) - } - - private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) { - backEdgeCandidate(jumpSource, _, _, _, _) and - ancestor = jumpSource - or - // For performance, we don't want a fastTC here - jumpSourceHasAncestor(jumpSource, ancestor.getAChild()) - } - - cached - Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) { - exists( - TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor - | - backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and - jumpSourceHasAncestor(sourceElement, requiredAncestor) and - instruction = sourceElement.getInstruction(sourceTag) - ) - or - // Goto statement: - // As a conservative approximation, any edge out of `goto` is a back edge - // unless it goes strictly forward in the program text. A `goto` whose - // source and target are both inside a macro will be seen as having the - // same location for source and target, so we conservatively assume that - // such a `goto` creates a back edge. - exists(TranslatedElement s, GotoStmt goto | - not isStrictlyForwardGoto(goto) and - goto = s.getAST() and - exists(InstructionTag tag | - result = s.getInstructionSuccessor(tag, kind) and - instruction = s.getInstruction(tag) - ) - ) - } - - /** Holds if `goto` jumps strictly forward in the program text. */ - private predicate isStrictlyForwardGoto(GotoStmt goto) { - goto.getLocation().isBefore(goto.getTarget().getLocation()) - } - - cached - Locatable getInstructionAST(Instruction instruction) { - result = getInstructionTranslatedElement(instruction).getAST() - } - - cached - CppType getInstructionResultType(Instruction instruction) { - getInstructionTranslatedElement(instruction) - .hasInstruction(_, getInstructionTag(instruction), result) - } - - cached - Opcode getInstructionOpcode(Instruction instruction) { - getInstructionTranslatedElement(instruction) - .hasInstruction(result, getInstructionTag(instruction), _) - } - - cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - result.getFunction() = getInstructionTranslatedElement(instruction).getFunction() - } - - cached - IRVariable getInstructionVariable(Instruction instruction) { + TIRVariable getInstructionVariable(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | element = getInstructionTranslatedElement(instruction) and tag = getInstructionTag(instruction) and @@ -302,10 +82,9 @@ private module Cached { cached Field getInstructionField(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionField(tag) - ) + result = + getInstructionTranslatedElement(instruction) + .getInstructionField(getInstructionTag(instruction)) } cached @@ -324,10 +103,9 @@ private module Cached { cached int getInstructionIndex(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionIndex(tag) - ) + result = + getInstructionTranslatedElement(instruction) + .getInstructionIndex(getInstructionTag(instruction)) } cached @@ -350,20 +128,11 @@ private module Cached { .getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass) } - pragma[noinline] - private predicate instructionOrigin( - Instruction instruction, TranslatedElement element, InstructionTag tag - ) { - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) - } - cached int getInstructionElementSize(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionElementSize(tag) - ) + result = + getInstructionTranslatedElement(instruction) + .getInstructionElementSize(getInstructionTag(instruction)) } cached @@ -372,22 +141,237 @@ private module Cached { } cached - int getInstructionResultSize(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionResultSize(tag) + Expr getInstructionConvertedResultExpression(Instruction instruction) { + exists(TranslatedExpr translatedExpr | + translatedExpr = getTranslatedExpr(result) and + instruction = translatedExpr.getResult() and + // Only associate `instruction` with this expression if the translated + // expression actually produced the instruction; not if it merely + // forwarded the result of another translated expression. + instruction = translatedExpr.getInstruction(_) ) } cached - Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getPrimaryInstructionForSideEffect(tag) - ) + Expr getInstructionUnconvertedResultExpression(Instruction instruction) { + result = getInstructionConvertedResultExpression(instruction).getUnconverted() } } +class TStageInstruction = TRawInstruction; + +predicate hasInstruction(TRawInstruction instr) { any() } + +predicate hasModeledMemoryResult(Instruction instruction) { none() } + +predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal +} + +Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionRegisterOperand(getInstructionTag(instruction), tag) +} + +Instruction getMemoryOperandDefinition( + Instruction instruction, MemoryOperandTag tag, Overlap overlap +) { + none() +} + +/** + * Holds if the partial operand of this `ChiInstruction` updates the bit range + * `[startBitOffset, endBitOffset)` of the total operand. + */ +predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBit, int endBit) { none() } + +/** + * Holds if the operand totally overlaps with its definition and consumes the + * bit range `[startBitOffset, endBitOffset)`. + */ +predicate getUsedInterval(Operand operand, int startBit, int endBit) { none() } + +/** Gets a non-phi instruction that defines an operand of `instr`. */ +private Instruction getNonPhiOperandDef(Instruction instr) { + result = getRegisterOperandDefinition(instr, _) + or + result = getMemoryOperandDefinition(instr, _, _) +} + +/** + * Gets a non-phi instruction that defines an operand of `instr` but only if + * both `instr` and the result have neighbor on the other side of the edge + * between them. This is a necessary condition for being in a cycle, and it + * removes about two thirds of the tuples that would otherwise be in this + * predicate. + */ +private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) { + result = getNonPhiOperandDef(instr) and + exists(getNonPhiOperandDef(result)) and + instr = getNonPhiOperandDef(_) +} + +/** + * Holds if `instr` is part of a cycle in the operand graph that doesn't go + * through a phi instruction and therefore should be impossible. + * + * If such cycles are present, either due to a programming error in the IR + * generation or due to a malformed database, it can cause infinite loops in + * analyses that assume a cycle-free graph of non-phi operands. Therefore it's + * better to remove these operands than to leave cycles in the operand graph. + */ +pragma[noopt] +predicate isInCycle(Instruction instr) { + instr instanceof Instruction and + getNonPhiOperandDefOfIntermediate+(instr) = instr +} + +CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { + // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as + // the result type of the load. + tag instanceof LoadOperandTag and + result = instruction.(LoadInstruction).getResultLanguageType() + or + not instruction instanceof LoadInstruction and + result = + getInstructionTranslatedElement(instruction) + .getInstructionMemoryOperandType(getInstructionTag(instruction), tag) +} + +Instruction getPhiOperandDefinition( + PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap +) { + none() +} + +Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() } + +Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionSuccessor(getInstructionTag(instruction), kind) +} + +/** + * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`--> + * `targetInstruction` is a back edge under the condition that + * `requiredAncestor` is an ancestor of `sourceElement`. + */ +private predicate backEdgeCandidate( + TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor, + Instruction targetInstruction, EdgeKind kind +) { + // While loop: + // Any edge from within the body of the loop to the condition of the loop + // is a back edge. This includes edges from `continue` and the fall-through + // edge(s) after the last instruction(s) in the body. + exists(TranslatedWhileStmt s | + targetInstruction = s.getFirstConditionInstruction() and + targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and + requiredAncestor = s.getBody() + ) + or + // Do-while loop: + // The back edge should be the edge(s) from the condition to the + // body. This ensures that it's the back edge that will be pruned in a `do + // { ... } while (0)` statement. Note that all `continue` statements in a + // do-while loop produce forward edges. + exists(TranslatedDoStmt s | + targetInstruction = s.getBody().getFirstInstruction() and + targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and + requiredAncestor = s.getCondition() + ) + or + // For loop: + // Any edge from within the body or update of the loop to the condition of + // the loop is a back edge. When there is no loop update expression, this + // includes edges from `continue` and the fall-through edge(s) after the + // last instruction(s) in the body. A for loop may not have a condition, in + // which case `getFirstConditionInstruction` returns the body instead. + exists(TranslatedForStmt s | + targetInstruction = s.getFirstConditionInstruction() and + targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and + ( + requiredAncestor = s.getUpdate() + or + not exists(s.getUpdate()) and + requiredAncestor = s.getBody() + ) + ) + or + // Range-based for loop: + // Any edge from within the update of the loop to the condition of + // the loop is a back edge. + exists(TranslatedRangeBasedForStmt s | + targetInstruction = s.getCondition().getFirstInstruction() and + targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and + requiredAncestor = s.getUpdate() + ) +} + +private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) { + backEdgeCandidate(jumpSource, _, _, _, _) and + ancestor = jumpSource + or + // For performance, we don't want a fastTC here + jumpSourceHasAncestor(jumpSource, ancestor.getAChild()) +} + +Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) { + exists( + TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor + | + backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and + jumpSourceHasAncestor(sourceElement, requiredAncestor) and + instruction = sourceElement.getInstruction(sourceTag) + ) + or + // Goto statement: + // As a conservative approximation, any edge out of `goto` is a back edge + // unless it goes strictly forward in the program text. A `goto` whose + // source and target are both inside a macro will be seen as having the + // same location for source and target, so we conservatively assume that + // such a `goto` creates a back edge. + exists(TranslatedElement s, GotoStmt goto | + not isStrictlyForwardGoto(goto) and + goto = s.getAST() and + exists(InstructionTag tag | + result = s.getInstructionSuccessor(tag, kind) and + instruction = s.getInstruction(tag) + ) + ) +} + +/** Holds if `goto` jumps strictly forward in the program text. */ +private predicate isStrictlyForwardGoto(GotoStmt goto) { + goto.getLocation().isBefore(goto.getTarget().getLocation()) +} + +Locatable getInstructionAST(TStageInstruction instr) { + result = getInstructionTranslatedElement(instr).getAST() +} + +CppType getInstructionResultType(TStageInstruction instr) { + getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result) +} + +Opcode getInstructionOpcode(TStageInstruction instr) { + getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _) +} + +IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { + result.getFunction() = getInstructionTranslatedElement(instr).getFunction() +} + +Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getPrimaryInstructionForSideEffect(getInstructionTag(instruction)) +} + import CachedForDebugging cached diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll new file mode 100644 index 000000000000..8ec63b7c1cbd --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll index 8fd2f662f346..82cc38ac0927 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language import IRConstruction as Construction import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration +import IRConstruction::Raw as Raw diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 15bb66940ea1..f3c8816c19d6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -24,6 +24,16 @@ private Element getRealParent(Expr expr) { result.(Destructor).getADestruction() = expr } +IRUserVariable getIRUserVariable(Function func, Variable var) { + result.getVariable() = var and + result.getEnclosingFunction() = func +} + +IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) { + result.getAST() = ast and + result.getTag() = tag +} + /** * Holds if `expr` is a constant of a type that can be replaced directly with * its value in the IR. This does not include address constants as we have no @@ -415,8 +425,11 @@ newtype TTranslatedElement = } or TTranslatedEllipsisParameter(Function func) { translateFunction(func) and func.isVarargs() } or TTranslatedReadEffects(Function func) { translateFunction(func) } or + TTranslatedThisReadEffect(Function func) { + translateFunction(func) and func.isMember() and not func.isStatic() + } or // The read side effects in a function's return block - TTranslatedReadEffect(Parameter param) { + TTranslatedParameterReadEffect(Parameter param) { translateFunction(param.getFunction()) and exists(Type t | t = param.getUnspecifiedType() | t instanceof ArrayType or @@ -463,7 +476,7 @@ newtype TTranslatedElement = ) } or // The side effects of an allocation, i.e. `new`, `new[]` or `malloc` - TTranslatedAllocationSideEffects(AllocationExpr expr) or + TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or // A precise side effect of an argument to a `Call` TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) { ( @@ -702,12 +715,8 @@ abstract class TranslatedElement extends TTranslatedElement { int getInstructionElementSize(InstructionTag tag) { none() } /** - * If the instruction specified by `tag` has a result of type `UnknownType`, - * gets the size of the result in bytes. If the result does not have a knonwn - * constant size, this predicate does not hold. + * Holds if the generated IR refers to an opaque type with size `byteSize`. */ - int getInstructionResultSize(InstructionTag tag) { none() } - predicate needsUnknownOpaqueType(int byteSize) { none() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll index 75e70d1986f4..6da9193fd580 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -1011,7 +1011,7 @@ class TranslatedDynamicCast extends TranslatedSingleInstructionConversion { if resultType instanceof PointerType then if resultType.(PointerType).getBaseType() instanceof VoidType - then result instanceof Opcode::DynamicCastToVoid + then result instanceof Opcode::CompleteObjectAddress else result instanceof Opcode::CheckedConvertOrNull else result instanceof Opcode::CheckedConvertOrThrow ) @@ -2905,7 +2905,7 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) { private predicate exprImmediatelyDiscarded(Expr expr) { exists(ExprStmt s | s = expr.getParent() and - not exists(StmtExpr se | s = se.getStmt().(Block).getLastStmt()) + not exists(StmtExpr se | s = se.getStmt().(BlockStmt).getLastStmt()) ) or exists(CommaExpr c | c.getLeftOperand() = expr) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll index 6d34830a0bd4..f55d661b202e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll @@ -676,14 +676,17 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects { override string toString() { result = "read effects: " + func.toString() } override TranslatedElement getChild(int id) { - result = getTranslatedReadEffect(func.getParameter(id)) + result = getTranslatedThisReadEffect(func) and + id = -1 + or + result = getTranslatedParameterReadEffect(func.getParameter(id)) } override Instruction getFirstInstruction() { if exists(getAChild()) then result = - min(TranslatedReadEffect child, int id | child = getChild(id) | child order by id) + min(TranslatedElement child, int id | child = getChild(id) | child order by id) .getFirstInstruction() else result = getParent().getChildSuccessor(this) } @@ -709,17 +712,15 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects { override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() } } -private TranslatedReadEffect getTranslatedReadEffect(Parameter param) { result.getAST() = param } - -class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect { - Parameter param; - - TranslatedReadEffect() { this = TTranslatedReadEffect(param) } - - override Locatable getAST() { result = param } +private TranslatedThisReadEffect getTranslatedThisReadEffect(Function func) { + result.getAST() = func +} - override string toString() { result = "read effect: " + param.toString() } +private TranslatedParameterReadEffect getTranslatedParameterReadEffect(Parameter param) { + result.getAST() = param +} +abstract class TranslatedReadEffect extends TranslatedElement { override TranslatedElement getChild(int id) { none() } override Instruction getChildSuccessor(TranslatedElement child) { none() } @@ -732,20 +733,12 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect { override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } - override Function getFunction() { result = param.getFunction() } - override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { opcode instanceof Opcode::ReturnIndirection and tag = OnlyInstructionTag() and resultType = getVoidType() } - final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { - tag = OnlyInstructionTag() and - operandTag = addressOperand() and - result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag()) - } - final override CppType getInstructionMemoryOperandType( InstructionTag tag, TypedOperandTag operandTag ) { @@ -753,6 +746,47 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect { operandTag = sideEffectOperand() and result = getUnknownType() } +} + +class TranslatedThisReadEffect extends TranslatedReadEffect, TTranslatedThisReadEffect { + Function func; + + TranslatedThisReadEffect() { this = TTranslatedThisReadEffect(func) } + + override Locatable getAST() { result = func } + + override Function getFunction() { result = func } + + override string toString() { result = "read effect: this" } + + final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag = addressOperand() and + result = getTranslatedThisParameter(func).getInstruction(InitializerIndirectAddressTag()) + } + + final override IRVariable getInstructionVariable(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = getTranslatedFunction(func).getThisVariable() + } +} + +class TranslatedParameterReadEffect extends TranslatedReadEffect, TTranslatedParameterReadEffect { + Parameter param; + + TranslatedParameterReadEffect() { this = TTranslatedParameterReadEffect(param) } + + override Locatable getAST() { result = param } + + override string toString() { result = "read effect: " + param.toString() } + + override Function getFunction() { result = param.getFunction() } + + final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag = addressOperand() and + result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag()) + } final override IRVariable getInstructionVariable(InstructionTag tag) { tag = OnlyInstructionTag() and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll index 8e2947d709f0..4b6538654db8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -415,17 +415,6 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati ) } - override int getInstructionResultSize(InstructionTag tag) { - exists(int elementCount | - zeroInitRange(_, elementCount) and - ( - tag = ZeroPadStringConstantTag() or - tag = ZeroPadStringStoreTag() - ) and - result = elementCount * getElementType().getSize() - ) - } - private Type getElementType() { result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType() } @@ -772,15 +761,6 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati result = getZeroValue(getElementType()) } - override int getInstructionResultSize(InstructionTag tag) { - elementCount > 1 and - ( - tag = getElementDefaultValueTag() or - tag = getElementDefaultValueStoreTag() - ) and - result = elementCount * getElementType().getSize() - } - override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll index 3339046d3915..ce08fc9367fc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll @@ -290,7 +290,7 @@ class TranslatedTryStmt extends TranslatedStmt { } class TranslatedBlock extends TranslatedStmt { - override Block stmt; + override BlockStmt stmt; override TranslatedElement getChild(int id) { result = getStmt(id) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll index badd48552a5f..c96783fe6e81 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll @@ -1,3 +1,47 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + import IRFunction import Instruction import IRBlock @@ -11,11 +55,12 @@ import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() /** - * Class that provides additional properties to be dumped for IR instructions and blocks when using + * A class that provides additional properties to be dumped for IR instructions and blocks when using * the PrintIR module. Libraries that compute additional facts about IR elements can extend the * single instance of this class to specify the additional properties computed by the library. */ class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ string toString() { result = "IRPropertyProvider" } /** @@ -27,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index 94ef73b27692..d827ed3cf82d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -1,3 +1,7 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + private import internal.IRInternal import Instruction private import internal.IRBlockImports as Imports @@ -16,15 +20,27 @@ private import Cached * Most consumers should use the class `IRBlock`. */ class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ final string toString() { result = getFirstInstruction(this).toString() } + /** Gets the source location of the first non-`Phi` instruction in this block. */ final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + /** + * INTERNAL: Do not use. + * + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. + * INTERNAL: Do not use. + * + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. */ int getDisplayIndex() { exists(IRConfiguration::IRConfiguration config | @@ -42,27 +58,51 @@ class IRBlockBase extends TIRBlock { ) } + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ final Instruction getInstruction(int index) { result = getInstruction(this, index) } + /** + * Get the `Phi` instructions that appear at the start of this block. + */ final PhiInstruction getAPhiInstruction() { Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() } + /** + * Gets an instruction in this block. This includes `Phi` instructions. + */ final Instruction getAnInstruction() { result = getInstruction(_) or result = getAPhiInstruction() } + /** + * Gets the first non-`Phi` instruction in this block. + */ final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + /** + * Gets the last instruction in this block. + */ final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + /** + * Gets the number of non-`Phi` instructions in this block. + */ final int getInstructionCount() { result = getInstructionCount(this) } + /** + * Gets the `IRFunction` that contains this block. + */ final IRFunction getEnclosingIRFunction() { result = getFirstInstruction(this).getEnclosingIRFunction() } + /** + * Gets the `Function` that contains this block. + */ final Language::Function getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } @@ -74,20 +114,57 @@ class IRBlockBase extends TIRBlock { * instruction of another block. */ class IRBlock extends IRBlockBase { + /** + * Gets a block to which control flows directly from this block. + */ final IRBlock getASuccessor() { blockSuccessor(this, result) } + /** + * Gets a block from which control flows directly to this block. + */ final IRBlock getAPredecessor() { blockSuccessor(result, this) } + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + /** + * Gets a block on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ pragma[noinline] final IRBlock dominanceFrontier() { dominates(result.getAPredecessor()) and @@ -95,7 +172,7 @@ class IRBlock extends IRBlockBase { } /** - * Holds if this block is reachable from the entry point of its function + * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { this = getEnclosingIRFunction().getEntryBlock() or @@ -210,4 +287,4 @@ private module Cached { idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) } -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll index 65af34942b6b..6a87b9b4b5fd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll @@ -8,10 +8,79 @@ module InstructionConsistency { private import Imports::Overlap private import internal.IRInternal + private newtype TOptionalIRFunction = + TPresentIRFunction(IRFunction irFunc) or + TMissingIRFunction() + + /** + * An `IRFunction` that might not exist. This is used so that we can produce consistency failures + * for IR that also incorrectly lacks a `getEnclosingIRFunction()`. + */ + abstract private class OptionalIRFunction extends TOptionalIRFunction { + abstract string toString(); + + abstract Language::Location getLocation(); + } + + private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction { + private IRFunction irFunc; + + PresentIRFunction() { this = TPresentIRFunction(irFunc) } + + override string toString() { + result = concat(Language::getIdentityString(irFunc.getFunction()), "; ") + } + + override Language::Location getLocation() { + // To avoid an overwhelming number of results when the extractor merges functions with the + // same name, just pick a single location. + result = + rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString()) + } + } + + private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction { + override string toString() { result = "" } + + override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation } + } + + private OptionalIRFunction getInstructionIRFunction(Instruction instr) { + result = TPresentIRFunction(instr.getEnclosingIRFunction()) + or + not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) { + result = getInstructionIRFunction(instr) and + irFuncText = result.toString() + } + + private OptionalIRFunction getOperandIRFunction(Operand operand) { + result = TPresentIRFunction(operand.getEnclosingIRFunction()) + or + not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) { + result = getOperandIRFunction(operand) and + irFuncText = result.toString() + } + + private OptionalIRFunction getBlockIRFunction(IRBlock block) { + result = TPresentIRFunction(block.getEnclosingIRFunction()) + or + not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + /** * Holds if instruction `instr` is missing an expected operand with tag `tag`. */ - query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) { + query predicate missingOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { exists(OperandTag tag | instr.getOpcode().hasOperand(tag) and not exists(NonPhiOperand operand | @@ -21,32 +90,39 @@ module InstructionConsistency { message = "Instruction '" + instr.getOpcode().toString() + "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(instr, irFuncText) ) } /** * Holds if instruction `instr` has an unexpected operand with tag `tag`. */ - query predicate unexpectedOperand(Instruction instr, OperandTag tag) { - exists(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - not instr.getOpcode().hasOperand(tag) and - not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and - not ( - instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag - ) and - not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) + query predicate unexpectedOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag | + exists(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + not instr.getOpcode().hasOperand(tag) and + not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and + not ( + instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag + ) and + not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and + message = + "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() + + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if instruction `instr` has multiple operands with tag `tag`. */ query predicate duplicateOperand( - Instruction instr, string message, IRFunction func, string funcText + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(OperandTag tag, int operandCount | operandCount = @@ -58,8 +134,7 @@ module InstructionConsistency { message = "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + " in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(instr, irFuncText) ) } @@ -67,100 +142,136 @@ module InstructionConsistency { * Holds if `Phi` instruction `instr` is missing an operand corresponding to * the predecessor block `pred`. */ - query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) { - pred = instr.getBlock().getAPredecessor() and - not exists(PhiInputOperand operand | - operand = instr.getAnOperand() and - operand.getPredecessorBlock() = pred + query predicate missingPhiOperand( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRBlock pred | + pred = instr.getBlock().getAPredecessor() and + not exists(PhiInputOperand operand | + operand = instr.getAnOperand() and + operand.getPredecessorBlock() = pred + ) and + message = + "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" + + pred.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } - query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func, Instruction use | + query predicate missingOperandType( + Operand operand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Instruction use | not exists(operand.getType()) and use = operand.getUse() and - func = use.getEnclosingFunction() and message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + - "' missing type in function '" + Language::getIdentityString(func) + "'." + "' is missing a type in function '$@'." and + irFunc = getOperandIRFunction(operand, irFuncText) ) } query predicate duplicateChiOperand( - ChiInstruction chi, string message, IRFunction func, string funcText + ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText ) { chi.getTotal() = chi.getPartial() and message = "Chi instruction for " + chi.getPartial().toString() + - " has duplicate operands in function $@" and - func = chi.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + " has duplicate operands in function '$@'." and + irFunc = getInstructionIRFunction(chi, irFuncText) } query predicate sideEffectWithoutPrimary( - SideEffectInstruction instr, string message, IRFunction func, string funcText + SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { not exists(instr.getPrimaryInstruction()) and - message = "Side effect instruction missing primary instruction in function $@" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + message = + "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } /** * Holds if an instruction, other than `ExitFunction`, has no successors. */ - query predicate instructionWithoutSuccessor(Instruction instr) { + query predicate instructionWithoutSuccessor( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { not exists(instr.getASuccessor()) and not instr instanceof ExitFunctionInstruction and // Phi instructions aren't linked into the instruction-level flow graph. not instr instanceof PhiInstruction and - not instr instanceof UnreachedInstruction + not instr instanceof UnreachedInstruction and + message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } /** - * Holds if there are multiple (`n`) edges of kind `kind` from `source`, - * where `target` is among the targets of those edges. + * Holds if there are multiple edges of the same kind from `source`. */ - query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) { - n = strictcount(Instruction t | source.getSuccessor(kind) = t) and - n > 1 and - source.getSuccessor(kind) = target + query predicate ambiguousSuccessors( + Instruction source, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(EdgeKind kind, int n | + n = strictcount(Instruction t | source.getSuccessor(kind) = t) and + n > 1 and + message = + "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" + + kind.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(source, irFuncText) + ) } /** - * Holds if `instr` in `f` is part of a loop even though the AST of `f` + * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function * contains no element that can cause loops. */ - query predicate unexplainedLoop(Language::Function f, Instruction instr) { - exists(IRBlock block | - instr.getBlock() = block and - block.getEnclosingFunction() = f and - block.getASuccessor+() = block - ) and - not Language::hasPotentialLoop(f) + query predicate unexplainedLoop( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Language::Function f | + exists(IRBlock block | + instr.getBlock() = block and + block.getEnclosingFunction() = f and + block.getASuccessor+() = block + ) and + not Language::hasPotentialLoop(f) and + message = + "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if a `Phi` instruction is present in a block with fewer than two * predecessors. */ - query predicate unnecessaryPhiInstruction(PhiInstruction instr) { - count(instr.getBlock().getAPredecessor()) < 2 + query predicate unnecessaryPhiInstruction( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int n | + n = count(instr.getBlock().getAPredecessor()) and + n < 2 and + message = + "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() + + " predecessors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } /** * Holds if a memory operand is connected to a definition with an unmodeled result. */ query predicate memoryOperandDefinitionIsUnmodeled( - Instruction instr, string message, IRFunction func, string funcText + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(MemoryOperand operand, Instruction def | operand = instr.getAnOperand() and def = operand.getAnyDef() and not def.isResultModeled() and - message = "Memory operand definition has unmodeled result in function '$@'" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + message = + "Memory operand definition on instruction '" + instr.toString() + + "' has unmodeled result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } @@ -168,18 +279,37 @@ module InstructionConsistency { * Holds if operand `operand` consumes a value that was defined in * a different function. */ - query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) { - operand.getUse() = instr and - operand.getAnyDef() = defInstr and - instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction() + query predicate operandAcrossFunctions( + Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText, + OptionalIRFunction defIRFunc, string defIRFuncText + ) { + exists(Instruction useInstr, Instruction defInstr | + operand.getUse() = useInstr and + operand.getAnyDef() = defInstr and + useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and + defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and + useIRFunc != defIRFunc and + message = + "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() + + "' in function '$@', but is defined on instruction '" + defInstr.toString() + + "' in function '$@'." + ) } /** * Holds if instruction `instr` is not in exactly one block. */ - query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { - blockCount = count(instr.getBlock()) and - blockCount != 1 + query predicate instructionWithoutUniqueBlock( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int blockCount | + blockCount = count(instr.getBlock()) and + blockCount != 1 and + message = + "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() + + " blocks in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) } private predicate forwardEdge(IRBlock b1, IRBlock b2) { @@ -192,10 +322,11 @@ module InstructionConsistency { * * This check ensures we don't have too _few_ back edges. */ - query predicate containsLoopOfForwardEdges(IRFunction f) { + query predicate containsLoopOfForwardEdges(IRFunction f, string message) { exists(IRBlock block | forwardEdge+(block, block) and - block.getEnclosingIRFunction() = f + block.getEnclosingIRFunction() = f and + message = "Function contains a loop consisting of only forward edges." ) } @@ -207,12 +338,19 @@ module InstructionConsistency { * * This check ensures we don't have too _many_ back edges. */ - query predicate lostReachability(IRBlock block) { + query predicate lostReachability( + IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText + ) { exists(IRFunction f, IRBlock entry | entry = f.getEntryBlock() and entry.getASuccessor+() = block and not forwardEdge+(entry, block) and - not Language::hasGoto(f.getFunction()) + not Language::hasGoto(f.getFunction()) and + message = + "Block '" + block.toString() + + "' is not reachable by traversing only forward edges in function '$@'." and + irFunc = TPresentIRFunction(f) and + irFuncText = irFunc.toString() ) } @@ -220,16 +358,22 @@ module InstructionConsistency { * Holds if the number of back edges differs between the `Instruction` graph * and the `IRBlock` graph. */ - query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) { - fromInstr = - count(Instruction i1, Instruction i2 | - i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2 - ) and - fromBlock = - count(IRBlock b1, IRBlock b2 | - b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2 - ) and - fromInstr != fromBlock + query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) { + exists(int fromInstr, int fromBlock | + fromInstr = + count(Instruction i1, Instruction i2 | + getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2 + ) and + fromBlock = + count(IRBlock b1, IRBlock b2 | + getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2 + ) and + fromInstr != fromBlock and + message = + "The instruction graph for function '" + irFunc.toString() + "' contains " + + fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString() + + " back edges." + ) } /** @@ -251,7 +395,7 @@ module InstructionConsistency { * Holds if `useOperand` has a definition that does not dominate the use. */ query predicate useNotDominatedByDefinition( - Operand useOperand, string message, IRFunction func, string funcText + Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | pointOfEvaluation(useOperand, useBlock, useIndex) and @@ -272,19 +416,17 @@ module InstructionConsistency { message = "Operand '" + useOperand.toString() + "' is not dominated by its definition in function '$@'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getOperandIRFunction(useOperand, irFuncText) ) } query predicate switchInstructionWithoutDefaultEdge( - SwitchInstruction switchInstr, string message, IRFunction func, string funcText + SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText ) { not exists(switchInstr.getDefaultSuccessor()) and message = "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and - func = switchInstr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getInstructionIRFunction(switchInstr, irFuncText) } /** @@ -305,18 +447,30 @@ module InstructionConsistency { instr.getOpcode() instanceof Opcode::InitializeNonLocal } - query predicate notMarkedAsConflated(Instruction instr) { + query predicate notMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { shouldBeConflated(instr) and - not instr.isResultConflated() + not instr.isResultConflated() and + message = + "Instruction '" + instr.toString() + + "' should be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } - query predicate wronglyMarkedAsConflated(Instruction instr) { + query predicate wronglyMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { instr.isResultConflated() and - not shouldBeConflated(instr) + not shouldBeConflated(instr) and + message = + "Instruction '" + instr.toString() + + "' should not be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) } query predicate invalidOverlap( - MemoryOperand useOperand, string message, IRFunction func, string funcText + MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText ) { exists(Overlap overlap | overlap = useOperand.getDefinitionOverlap() and @@ -324,8 +478,20 @@ module InstructionConsistency { message = "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" + overlap.toString() + "'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) + irFunc = getOperandIRFunction(useOperand, irFuncText) + ) + } + + query predicate nonUniqueEnclosingIRFunction( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int irFuncCount | + irFuncCount = count(instr.getEnclosingIRFunction()) and + irFuncCount != 1 and + message = + "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() + + " results for `getEnclosingIRFunction()` in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) ) } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll index 9aea3e00d666..5968e58f90bf 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll @@ -1,29 +1,17 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase import Instruction -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - /** - * Represents the IR for a function. + * The IR for a function. */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - +class IRFunction extends IRFunctionBase { /** * Gets the entry point for this function. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index 9f2a0d4ea281..146fc2707383 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + private import internal.IRInternal import IRFunction private import internal.IRVariableImports as Imports @@ -7,15 +11,11 @@ private import Imports::TTempVariableTag private import Imports::TIRVariable private import Imports::IRType -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - /** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { Language::Function func; @@ -27,6 +27,7 @@ class IRVariable extends TIRVariable { this = TIRDynamicInitializationFlag(func, _, _) } + /** Gets a textual representation of this element. */ string toString() { none() } /** @@ -162,20 +163,30 @@ class IRGeneratedVariable extends IRVariable { override string getUniqueId() { none() } + /** + * INTERNAL: Do not use. + * + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ final string getLocationString() { result = ast.getLocation().getStartLine().toString() + ":" + ast.getLocation().getStartColumn().toString() } + /** + * INTERNAL: Do not use. + * + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ string getBaseString() { none() } } -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - /** * A temporary variable introduced by IR construction. The most common examples are the variable * generated to hold the return value of a function, or the variable generated to hold the result of @@ -190,6 +201,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa result = "Temp: " + Construction::getTempVariableUniqueId(this) } + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ final TempVariableTag getTag() { result = tag } override string getBaseString() { result = "#temp" } @@ -217,19 +232,23 @@ class IRThrowVariable extends IRTempVariable { * A temporary variable generated to hold the contents of all arguments passed to the `...` of a * function that accepts a variable number of arguments. */ -class IREllipsisVariable extends IRTempVariable { +class IREllipsisVariable extends IRTempVariable, IRParameter { IREllipsisVariable() { tag = EllipsisTempVar() } final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } } /** * A temporary variable generated to hold the `this` pointer. */ -class IRThisVariable extends IRTempVariable { +class IRThisVariable extends IRTempVariable, IRParameter { IRThisVariable() { tag = ThisTempVar() } final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } } /** @@ -249,6 +268,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { final override string getBaseString() { result = "#string" } + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ final Language::StringLiteral getLiteral() { result = literal } } @@ -266,6 +288,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string toString() { result = var.toString() + "#init" } + /** + * Gets variable whose initialization is guarded by this flag. + */ final Language::Variable getVariable() { result = var } final override string getUniqueId() { @@ -274,3 +299,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial final override string getBaseString() { result = "#init:" + var.toString() + ":" } } + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +/** + * An IR variable representing a positional parameter. + */ +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 9c83a3d99f0c..620b23b942e0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + private import internal.IRInternal import IRFunction import IRBlock @@ -27,9 +31,16 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil } /** - * Represents a single operation in the IR. + * A single instruction in the IR. */ -class Instruction extends Construction::TInstruction { +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + + /** Gets a textual representation of this element. */ final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } /** @@ -194,16 +205,25 @@ class Instruction extends Construction::TInstruction { * conversion. */ final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) + result = Raw::getInstructionConvertedResultExpression(this) } /** * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. */ final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) + result = Raw::getInstructionUnconvertedResultExpression(this) } + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ final Language::LanguageType getResultLanguageType() { result = Construction::getInstructionResultType(this) } @@ -212,6 +232,7 @@ class Instruction extends Construction::TInstruction { * Gets the type of the result produced by this instruction. If the instruction does not produce * a result, its result type will be `IRVoidType`. */ + cached final IRType getResultIRType() { result = getResultLanguageType().getIRType() } /** @@ -240,17 +261,19 @@ class Instruction extends Construction::TInstruction { * given by `getResultType()`. * * For example, the statement `y = x;` generates the following IR: + * ``` * r1_0(glval: int) = VariableAddress[x] * r1_1(int) = Load r1_0, mu0_1 * r1_2(glval: int) = VariableAddress[y] * mu1_3(int) = Store r1_2, r1_1 + * ``` * * The result of each `VariableAddress` instruction is a glvalue of type * `int`, representing the address of the corresponding integer variable. The * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -259,7 +282,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } + final int getResultSize() { result = getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -392,13 +415,27 @@ class Instruction extends Construction::TInstruction { final Instruction getAPredecessor() { result = getPredecessor(_) } } +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ class VariableInstruction extends Instruction { IRVariable var; - VariableInstruction() { var = Construction::getInstructionVariable(this) } + VariableInstruction() { var = Raw::getInstructionVariable(this) } override string getImmediateString() { result = var.toString() } + /** + * Gets the variable that this instruction references. + */ final IRVariable getIRVariable() { result = var } /** @@ -407,63 +444,156 @@ class VariableInstruction extends Instruction { final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ class FieldInstruction extends Instruction { Language::Field field; - FieldInstruction() { field = Construction::getInstructionField(this) } + FieldInstruction() { field = Raw::getInstructionField(this) } final override string getImmediateString() { result = field.toString() } + /** + * Gets the field that this instruction references. + */ final Language::Field getField() { result = field } } +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ class FunctionInstruction extends Instruction { Language::Function funcSymbol; - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } final override string getImmediateString() { result = funcSymbol.toString() } + /** + * Gets the function that this instruction references. + */ final Language::Function getFunctionSymbol() { result = funcSymbol } } +/** + * An instruction whose result is a compile-time constant value. + */ class ConstantValueInstruction extends Instruction { string value; - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } final override string getImmediateString() { result = value } + /** + * Gets the constant value of this instruction's result. + */ final string getValue() { result = value } } +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ class IndexedInstruction extends Instruction { int index; - IndexedInstruction() { index = Construction::getInstructionIndex(this) } + IndexedInstruction() { index = Raw::getInstructionIndex(this) } final override string getImmediateString() { result = index.toString() } + /** + * Gets the zero-based index of the argument that this instruction references. + */ final int getIndex() { result = index } } +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ class EnterFunctionInstruction extends Instruction { EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } } +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ class VariableAddressInstruction extends VariableInstruction { VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } } +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ class InitializeParameterInstruction extends VariableInstruction { InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ class InitializeIndirectionInstruction extends VariableInstruction { InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + /** + * Gets the parameter initialized by this instruction. + */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } } @@ -474,14 +604,42 @@ class InitializeThisInstruction extends Instruction { InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } } +/** + * An instruction that computes the address of a non-static field of an object. + */ class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + /** + * Gets the operand that provides the address of the object containing the field. + */ final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } } +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + /** * An instruction that produces a well-defined but unknown result and has * unknown side effects, including side effects that are not conservatively @@ -496,6 +654,12 @@ class ErrorInstruction extends Instruction { ErrorInstruction() { getOpcode() instanceof Opcode::Error } } +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } @@ -505,35 +669,94 @@ class UninitializedInstruction extends VariableInstruction { final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } } +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ class NoOpInstruction extends Instruction { NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } } +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ class ReturnInstruction extends Instruction { ReturnInstruction() { getOpcode() instanceof ReturnOpcode } } +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ class ReturnVoidInstruction extends ReturnInstruction { ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } } +/** + * An instruction that returns control to the caller of the function, including a return value. + */ class ReturnValueInstruction extends ReturnInstruction { ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + /** + * Gets the operand that provides the value being returned by the function. + */ final LoadOperand getReturnValueOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } } +/** + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. + */ class ReturnIndirectionInstruction extends VariableInstruction { ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + /** + * Gets the operand that provides the value of the pointed-to memory. + */ final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + /** + * Gets the operand that provides the address of the pointed-to memory. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } /** @@ -541,87 +764,191 @@ class ReturnIndirectionInstruction extends VariableInstruction { * function. */ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } } +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ class CopyInstruction extends Instruction { CopyInstruction() { getOpcode() instanceof CopyOpcode } + /** + * Gets the operand that provides the input value of the copy. + */ Operand getSourceValueOperand() { none() } + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } } +/** + * An instruction that returns a register result containing a copy of its register operand. + */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ class LoadInstruction extends CopyInstruction { LoadInstruction() { getOpcode() instanceof Opcode::Load } + /** + * Gets the operand that provides the address of the value being loaded. + */ final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } final override LoadOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ class StoreInstruction extends CopyInstruction { StoreInstruction() { getOpcode() instanceof Opcode::Store } + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } } +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ class ConditionalBranchInstruction extends Instruction { ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ final ConditionOperand getConditionOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ final Instruction getCondition() { result = getConditionOperand().getDef() } + /** + * Gets the instruction to which control will flow if the condition is true. + */ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + /** + * Gets the instruction to which control will flow if the condition is false. + */ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } } +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no + * successors. + */ class ExitFunctionInstruction extends Instruction { ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } } +/** + * An instruction whose result is a constant value. + */ class ConstantInstruction extends ConstantValueInstruction { ConstantInstruction() { getOpcode() instanceof Opcode::Constant } } +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } } +/** + * An instruction whose result is a constant value of floating-point type. + */ class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } } +/** + * An instruction whose result is the address of a string literal. + */ class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + /** + * Gets the string literal whose address is returned by this instruction. + */ final Language::StringLiteral getValue() { result = var.getLiteral() } } +/** + * An instruction whose result is computed from two operands. + */ class BinaryInstruction extends Instruction { BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + /** + * Gets the left operand of this binary instruction. + */ final LeftOperand getLeftOperand() { result = getAnOperand() } + /** + * Gets the right operand of this binary instruction. + */ final RightOperand getRightOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ final Instruction getLeft() { result = getLeftOperand().getDef() } + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ final Instruction getRight() { result = getRightOperand().getDef() } /** @@ -634,121 +961,301 @@ class BinaryInstruction extends Instruction { } } +/** + * An instruction that computes the result of an arithmetic operation. + */ class ArithmeticInstruction extends Instruction { ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } } +/** + * An instruction that performs an arithmetic operation on two numeric operands. + */ class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * numeric operand. + */ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } +/** + * An instruction that computes the sum of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ class AddInstruction extends BinaryArithmeticInstruction { AddInstruction() { getOpcode() instanceof Opcode::Add } } +/** + * An instruction that computes the difference of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ class SubInstruction extends BinaryArithmeticInstruction { SubInstruction() { getOpcode() instanceof Opcode::Sub } } +/** + * An instruction that computes the product of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ class MulInstruction extends BinaryArithmeticInstruction { MulInstruction() { getOpcode() instanceof Opcode::Mul } } +/** + * An instruction that computes the quotient of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ class DivInstruction extends BinaryArithmeticInstruction { DivInstruction() { getOpcode() instanceof Opcode::Div } } +/** + * An instruction that computes the remainder of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ class RemInstruction extends BinaryArithmeticInstruction { RemInstruction() { getOpcode() instanceof Opcode::Rem } } +/** + * An instruction that negates a single numeric operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ class NegateInstruction extends UnaryArithmeticInstruction { NegateInstruction() { getOpcode() instanceof Opcode::Negate } } +/** + * An instruction that computes the result of a bitwise operation. + */ class BitwiseInstruction extends Instruction { BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } } +/** + * An instruction that performs a bitwise operation on two integer operands. + */ class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } +/** + * An instruction that performs a bitwise operation on a single integer operand. + */ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } +/** + * An instruction that computes the bitwise "and" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitAndInstruction extends BinaryBitwiseInstruction { BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } } +/** + * An instruction that computes the bitwise "or" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitOrInstruction extends BinaryBitwiseInstruction { BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } } +/** + * An instruction that computes the bitwise "xor" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ class BitXorInstruction extends BinaryBitwiseInstruction { BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } } +/** + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } } +/** + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ class ShiftRightInstruction extends BinaryBitwiseInstruction { ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } } +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; PointerArithmeticInstruction() { getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) + elementSize = Raw::getInstructionElementSize(this) } final override string getImmediateString() { result = elementSize.toString() } + /** + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. + */ final int getElementSize() { result = elementSize } } +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ class PointerOffsetInstruction extends PointerArithmeticInstruction { PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } } +/** + * An instruction that adds an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ class PointerAddInstruction extends PointerOffsetInstruction { PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } } +/** + * An instruction that subtracts an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ class PointerSubInstruction extends PointerOffsetInstruction { PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } } +/** + * An instruction that computes the difference between two pointers. + * + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. + */ class PointerDiffInstruction extends PointerArithmeticInstruction { PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } } +/** + * An instruction whose result is computed from a single operand. + */ class UnaryInstruction extends Instruction { UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + /** + * Gets the sole operand of this instruction. + */ final UnaryOperand getUnaryOperand() { result = getAnOperand() } + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ final Instruction getUnary() { result = getUnaryOperand().getDef() } } +/** + * An instruction that converts the value of its operand to a value of a different type. + */ class ConvertInstruction extends UnaryInstruction { ConvertInstruction() { getOpcode() instanceof Opcode::Convert } } +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ class CheckedConvertOrNullInstruction extends UnaryInstruction { CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** - * Represents an instruction that converts between two addresses - * related by inheritance. + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. */ class InheritanceConversionInstruction extends UnaryInstruction { Language::Class baseClass; Language::Class derivedClass; InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) + Raw::getInstructionInheritance(this, baseClass, derivedClass) } final override string getImmediateString() { @@ -778,59 +1285,91 @@ class InheritanceConversionInstruction extends UnaryInstruction { } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. + * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } } /** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } } +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ class BitComplementInstruction extends UnaryBitwiseInstruction { BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } } +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ class LogicalNotInstruction extends UnaryInstruction { LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } } +/** + * An instruction that compares two numeric operands. + */ class CompareInstruction extends BinaryInstruction { CompareInstruction() { getOpcode() instanceof CompareOpcode } } +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareEQInstruction extends CompareInstruction { CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } } +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ class CompareNEInstruction extends CompareInstruction { CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } } /** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. + * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { RelationalInstruction() { getOpcode() instanceof RelationalOpcode } @@ -857,6 +1396,13 @@ class RelationalInstruction extends CompareInstruction { predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLTInstruction extends RelationalInstruction { CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } @@ -867,6 +1413,13 @@ class CompareLTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGTInstruction extends RelationalInstruction { CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } @@ -877,6 +1430,14 @@ class CompareGTInstruction extends RelationalInstruction { override predicate isStrict() { any() } } +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareLEInstruction extends RelationalInstruction { CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } @@ -887,6 +1448,14 @@ class CompareLEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ class CompareGEInstruction extends RelationalInstruction { CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } @@ -897,15 +1466,32 @@ class CompareGEInstruction extends RelationalInstruction { override predicate isStrict() { none() } } +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ class SwitchInstruction extends Instruction { SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + /** Gets the operand that provides the integer value controlling the switch. */ final ConditionOperand getExpressionOperand() { result = getAnOperand() } + /** Gets the instruction whose result provides the integer value controlling the switch. */ final Instruction getExpression() { result = getExpressionOperand().getDef() } + /** Gets the successor instructions along the case edges of the switch. */ final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + /** Gets the successor instruction along the default edge of the switch, if any. */ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } } @@ -936,7 +1522,7 @@ class CallInstruction extends Instruction { * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** @@ -981,6 +1567,9 @@ class CallInstruction extends Instruction { class SideEffectInstruction extends Instruction { SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + /** + * Gets the instruction whose execution causes this side effect. + */ final Instruction getPrimaryInstruction() { result = Construction::getPrimaryInstructionForSideEffect(this) } @@ -996,9 +1585,10 @@ class CallSideEffectInstruction extends SideEffectInstruction { /** * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } @@ -1046,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { getOpcode() instanceof Opcode::SizedBufferReadSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** @@ -1056,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } } /** @@ -1087,11 +1693,20 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** * An instruction representing the potential write of an indirect parameter within a function call. + * * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. * written. */ @@ -1103,6 +1718,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1111,6 +1727,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { /** * An instruction representing the write of an indirect buffer parameter within a function call. + * * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { @@ -1118,11 +1735,19 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } } /** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a + * An instruction representing the initial value of newly allocated memory, such as the result of a * call to `malloc`. */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { @@ -1211,7 +1836,7 @@ class CatchByTypeInstruction extends CatchInstruction { CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) + exceptionType = Raw::getInstructionExceptionType(this) } final override string getImmediateString() { result = exceptionType.toString() } @@ -1337,29 +1962,43 @@ class ChiInstruction extends Instruction { * Gets the operand that represents the new value written by the memory write. */ final Instruction getPartial() { result = getPartialOperand().getDef() } + + /** + * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. + */ + final predicate getUpdatedInterval(int startBit, int endBit) { + Construction::getIntervalUpdatedByChi(this, startBit, endBit) + } } /** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } } /** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. */ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; BuiltInOperationInstruction() { getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) + operation = Raw::getInstructionBuiltInOperation(this) } + /** + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is + * performed by this instruction. + */ final Language::BuiltInOperation getBuiltInOperation() { result = operation } } @@ -1372,3 +2011,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction { final override string getImmediateString() { result = getBuiltInOperation().toString() } } + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index f82704094c8e..a12e35d471b8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + private import internal.IRInternal private import Instruction private import IRBlock @@ -75,13 +79,21 @@ private PhiOperandBase phiOperand( } /** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) */ class Operand extends TOperand { + /** Gets a textual representation of this element. */ string toString() { result = "Operand" } + /** + * Gets the location of the source code for this operand. + */ final Language::Location getLocation() { result = getUse().getLocation() } + /** + * Gets the function that contains this operand. + */ final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } /** @@ -139,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -268,8 +285,13 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ final OperandTag getOperandTag() { result = tag } } @@ -292,6 +314,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase { } } +/** + * A memory operand other than the operand of a `Phi` instruction. + */ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { override MemoryOperandTag tag; @@ -311,8 +336,19 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper not Construction::isInCycle(useInstr) and strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1 } + + /** + * Holds if the operand totally overlaps with its definition and consumes the + * bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition. + */ + predicate getUsedInterval(int startBitOffset, int endBitOffset) { + Construction::getUsedInterval(this, startBitOffset, endBitOffset) + } } +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; @@ -416,6 +452,9 @@ class PositionalArgumentOperand extends ArgumentOperand { final int getIndex() { result = tag.getArgIndex() } } +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; } @@ -445,6 +484,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index d9c0df44e12e..59dadee71545 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -1,3 +1,13 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + private import internal.IRInternal private import IR private import internal.PrintIRImports as Imports @@ -9,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration() * The query can extend this class to control which functions are printed. */ class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ string toString() { result = "PrintIRConfiguration" } /** @@ -39,6 +50,37 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -47,7 +89,7 @@ private newtype TPrintableIRNode = /** * A node to be emitted in the IR graph. */ -abstract class PrintableIRNode extends TPrintableIRNode { +abstract private class PrintableIRNode extends TPrintableIRNode { abstract string toString(); /** @@ -98,7 +140,7 @@ abstract class PrintableIRNode extends TPrintableIRNode { /** * An IR graph node representing a `IRFunction` object. */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { IRFunction irFunc; PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } @@ -129,7 +171,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { /** * An IR graph node representing an `IRBlock` object. */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { IRBlock block; PrintableIRBlock() { this = TPrintableIRBlock(block) } @@ -161,7 +203,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { /** * An IR graph node representing an `Instruction`. */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { Instruction instr; PrintableInstruction() { this = TPrintableInstruction(instr) } @@ -179,7 +221,7 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -199,6 +241,22 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { @@ -224,6 +282,9 @@ private string getPaddingString(int n) { n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " } +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ query predicate nodes(PrintableIRNode node, string key, string value) { value = node.getProperty(key) } @@ -237,6 +298,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) { ) } +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | predBlock = pred.getBlock() and @@ -256,6 +321,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, ) } +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ query predicate parents(PrintableIRNode child, PrintableIRNode parent) { parent = child.getParent() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 1612e0065b77..19fb0490f808 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -196,16 +196,17 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { bitOffset = Ints::unknown() } -private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { +private predicate isArgumentForParameter( + CallInstruction ci, Operand operand, InitializeParameterInstruction init +) { exists(Language::Function f | ci = operand.getUse() and f = ci.getStaticCallTarget() and ( - init.(InitializeParameterInstruction).getParameter() = - f.getParameter(operand.(PositionalArgumentOperand).getIndex()) + init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex()) or - init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and - init.getEnclosingFunction() = f and + init.getIRVariable() instanceof IRThisVariable and + unique( | | init.getEnclosingFunction()) = f and operand instanceof ThisArgumentOperand ) and not Language::isFunctionVirtual(f) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll new file mode 100644 index 000000000000..8ec63b7c1cbd --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll index 4cc52d3bbf99..3a7a08accc0d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language import SSAConstruction as Construction import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 30414bb5db3a..a6cb78b2b3a7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -1,5 +1,11 @@ import SSAConstructionInternal -private import SSAConstructionImports +private import SSAConstructionImports as Imports +private import Imports::Opcode +private import Imports::OperandTag +private import Imports::Overlap +private import Imports::TInstruction +private import Imports::RawIR as RawIR +private import SSAInstructions private import NewIR private class OldBlock = Reachability::ReachableBlock; @@ -10,52 +16,45 @@ import Cached cached private module Cached { - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached + predicate hasPhiInstructionCached( + OldInstruction blockStartInstr, Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + blockStartInstr = oldBlock.getFirstInstruction() + ) } cached - predicate functionHasIR(Language::Function func) { - exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func) + predicate hasChiInstructionCached(OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) } cached - OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) } - - private IRVariable getNewIRVariable(OldIR::IRVariable var) { - // This is just a type cast. Both classes derive from the same newtype. - result = var + predicate hasUnreachedInstructionCached(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) } - cached - newtype TInstruction = - WrappedInstruction(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction - } or - Phi(OldBlock block, Alias::MemoryLocation defLocation) { - definitionHasPhiNode(defLocation, block) - } or - Chi(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction and - hasChiNode(_, oldInstruction) - } or - Unreached(Language::Function function) { - exists(OldInstruction oldInstruction | - function = oldInstruction.getEnclosingFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } + class TStageInstruction = + TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; cached - predicate hasTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type - ) { - exists(OldIR::IRTempVariable var | - var.getEnclosingFunction() = func and - var.getAST() = ast and - var.getTag() = tag and - var.getLanguageType() = type - ) + predicate hasInstruction(TStageInstruction instr) { + instr instanceof TRawInstruction and instr instanceof OldInstruction + or + instr instanceof TPhiInstruction + or + instr instanceof TChiInstruction + or + instr instanceof TUnreachedInstruction + } + + private IRBlock getNewBlock(OldBlock oldBlock) { + result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } cached @@ -73,7 +72,7 @@ private module Cached { or // Chi instructions track virtual variables, and therefore a chi instruction is // conflated if it's associated with the aliased virtual variable. - exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) | Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof Alias::AliasedVirtualVariable ) @@ -81,7 +80,7 @@ private module Cached { // Phi instructions track locations, and therefore a phi instruction is // conflated if it's associated with a conflated location. exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and + instruction = getPhi(_, location) and not exists(location.getAllocation()) ) } @@ -128,7 +127,7 @@ private module Cached { hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result) ) or - instruction = Chi(getOldInstruction(result)) and + instruction = getChi(getOldInstruction(result)) and tag instanceof ChiPartialOperandTag and overlap instanceof MustExactlyOverlap or @@ -150,6 +149,34 @@ private module Cached { ) } + /** + * Holds if the partial operand of this `ChiInstruction` updates the bit range + * `[startBitOffset, endBitOffset)` of the total operand. + */ + cached + predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBitOffset, int endBitOffset) { + exists(Alias::MemoryLocation location, OldInstruction oldInstruction | + oldInstruction = getOldInstruction(chi.getPartial()) and + location = Alias::getResultMemoryLocation(oldInstruction) and + startBitOffset = Alias::getStartBitOffset(location) and + endBitOffset = Alias::getEndBitOffset(location) + ) + } + + /** + * Holds if `operand` totally overlaps with its definition and consumes the bit range + * `[startBitOffset, endBitOffset)`. + */ + cached + predicate getUsedInterval(NonPhiMemoryOperand operand, int startBitOffset, int endBitOffset) { + exists(Alias::MemoryLocation location, OldIR::NonPhiMemoryOperand oldOperand | + oldOperand = operand.getUse().(OldInstruction).getAnOperand() and + location = Alias::getOperandMemoryLocation(oldOperand) and + startBitOffset = Alias::getStartBitOffset(location) and + endBitOffset = Alias::getEndBitOffset(location) + ) + } + /** * Holds if `instr` is part of a cycle in the operand graph that doesn't go * through a phi instruction and therefore should be impossible. @@ -172,13 +199,15 @@ private module Cached { pragma[noopt] cached - Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) { + Instruction getPhiOperandDefinition( + PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation | hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and - instr = Phi(phiBlock, useLocation) and + instr = getPhi(phiBlock, useLocation) and newPredecessorBlock = getNewBlock(predBlock) and result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) @@ -191,7 +220,7 @@ private module Cached { Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank | - chiInstr = Chi(oldInstr) and + chiInstr = getChi(oldInstr) and vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and hasUseAtRank(vvar, useBlock, useRank, oldInstr) and @@ -203,21 +232,11 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = Phi(oldBlock, _) and + instr = getPhi(oldBlock, _) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } - cached - Language::Expr getInstructionConvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getConvertedResultExpression() - } - - cached - Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getUnconvertedResultExpression() - } - /* * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, * that node is its successor in the new successor relation, and the Chi node's successors are @@ -228,20 +247,20 @@ private module Cached { Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { if hasChiNode(_, getOldInstruction(instruction)) then - result = Chi(getOldInstruction(instruction)) and + result = getChi(getOldInstruction(instruction)) and kind instanceof GotoEdge else ( exists(OldInstruction oldInstruction | oldInstruction = getOldInstruction(instruction) and ( if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) - then result = Unreached(instruction.getEnclosingFunction()) + then result = unreachedInstruction(instruction.getEnclosingIRFunction()) else result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) or exists(OldInstruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) @@ -260,137 +279,73 @@ private module Cached { // `oldInstruction`, in which case the back edge should come out of the // chi node instead. if hasChiNode(_, oldInstruction) - then instruction = Chi(oldInstruction) + then instruction = getChi(oldInstruction) else instruction = getNewInstruction(oldInstruction) ) } cached - Language::AST getInstructionAST(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result = oldInstruction.getAST() + Language::AST getInstructionAST(Instruction instr) { + result = getOldInstruction(instr).getAST() + or + exists(RawIR::Instruction blockStartInstr | + instr = phiInstruction(blockStartInstr, _) and + result = blockStartInstr.getAST() ) or - exists(OldBlock block | - instruction = Phi(block, _) and - result = block.getFirstInstruction().getAST() + exists(RawIR::Instruction primaryInstr | + instr = chiInstruction(primaryInstr) and + result = primaryInstr.getAST() ) or - instruction = Unreached(result) + exists(IRFunctionBase irFunc | + instr = unreachedInstruction(irFunc) and result = irFunc.getFunction() + ) } cached - Language::LanguageType getInstructionResultType(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getResultLanguageType() - ) + Language::LanguageType getInstructionResultType(Instruction instr) { + result = instr.(RawIR::Instruction).getResultLanguageType() or - exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | - instruction = Chi(oldInstruction) and - hasChiNode(vvar, oldInstruction) and - result = vvar.getType() + exists(Alias::MemoryLocation defLocation | + instr = phiInstruction(_, defLocation) and + result = defLocation.getType() ) or - exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and - result = location.getType() + exists(Instruction primaryInstr, Alias::VirtualVariable vvar | + instr = chiInstruction(primaryInstr) and + hasChiNode(vvar, primaryInstr) and + result = vvar.getType() ) or - instruction = Unreached(_) and - result = Language::getVoidType() + instr = unreachedInstruction(_) and result = Language::getVoidType() } cached - Opcode getInstructionOpcode(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getOpcode() - ) + Opcode getInstructionOpcode(Instruction instr) { + result = getOldInstruction(instr).getOpcode() or - instruction instanceof Chi and - result instanceof Opcode::Chi + instr = phiInstruction(_, _) and result instanceof Opcode::Phi or - instruction instanceof Phi and - result instanceof Opcode::Phi + instr = chiInstruction(_) and result instanceof Opcode::Chi or - instruction instanceof Unreached and - result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result.getFunction() = oldInstruction.getEnclosingFunction() - ) + IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) { + result = getOldInstruction(instr).getEnclosingIRFunction() or - exists(OldBlock block | - instruction = Phi(block, _) and - result.getFunction() = block.getEnclosingFunction() + exists(OldInstruction blockStartInstr | + instr = phiInstruction(blockStartInstr, _) and + result = blockStartInstr.getEnclosingIRFunction() ) or - instruction = Unreached(result.getFunction()) - } - - cached - IRVariable getInstructionVariable(Instruction instruction) { - result = - getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable()) - } - - cached - Language::Field getInstructionField(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() - } - - cached - int getInstructionIndex(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex() - } - - cached - Language::Function getInstructionFunction(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue() - } - - cached - Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { - result = - getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation() - } - - cached - Language::LanguageType getInstructionExceptionType(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() - } - - cached - int getInstructionElementSize(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() - } - - cached - predicate getInstructionInheritance( - Instruction instruction, Language::Class baseClass, Language::Class derivedClass - ) { - exists(OldIR::InheritanceConversionInstruction oldInstr | - oldInstr = getOldInstruction(instruction) and - baseClass = oldInstr.getBaseClass() and - derivedClass = oldInstr.getDerivedClass() + exists(OldInstruction primaryInstr | + instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction() ) + or + instr = unreachedInstruction(result) } cached @@ -401,7 +356,7 @@ private module Cached { ) or exists(OldIR::Instruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction) ) } @@ -409,6 +364,14 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } +private OldInstruction getOldInstruction(Instruction instr) { instr = result } + +private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) } + +private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { + result = phiInstruction(defBlock.getFirstInstruction(), defLocation) +} + /** * Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition * of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the @@ -588,7 +551,7 @@ module DefUse { | // An odd offset corresponds to the `Chi` instruction. defOffset = oldOffset * 2 + 1 and - result = Chi(oldInstr) and + result = getChi(oldInstr) and ( defLocation = Alias::getResultMemoryLocation(oldInstr) or defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() @@ -607,7 +570,7 @@ module DefUse { or defOffset = -1 and hasDefinition(_, defLocation, defBlock, defOffset) and - result = Phi(defBlock, defLocation) and + result = getPhi(defBlock, defLocation) and actualDefLocation = defLocation } @@ -891,7 +854,7 @@ private module CachedForDebugging { ) or exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity | - instr = Phi(phiBlock, location) and + instr = getPhi(phiBlock, location) and result = "Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and if location instanceof Alias::VirtualVariable @@ -901,7 +864,7 @@ private module CachedForDebugging { else specificity = "s" ) or - instr = Unreached(_) and + instr = unreachedInstruction(_) and result = "Unreached" } @@ -961,3 +924,19 @@ module SSAConsistency { ) } } + +/** + * Provides the portion of the parameterized IR interface that is used to construct the SSA stages + * of the IR. The raw stage of the IR does not expose these predicates. + * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures + * that all of SSA construction will be evaluated in the same stage. + */ +module SSA { + class MemoryLocation = Alias::MemoryLocation; + + predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2; + + predicate hasChiInstruction = Cached::hasChiInstructionCached/1; + + predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll index 00f12020a29d..f347df86ba1e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll @@ -1,3 +1,5 @@ -import semmle.code.cpp.ir.implementation.Opcode -import semmle.code.cpp.ir.implementation.internal.OperandTag -import semmle.code.cpp.ir.internal.Overlap +import semmle.code.cpp.ir.implementation.Opcode as Opcode +import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag +import semmle.code.cpp.ir.internal.Overlap as Overlap +import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction +import semmle.code.cpp.ir.implementation.raw.IR as RawIR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll index 4cfbdfe831e1..73b08d1286b5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll @@ -2,5 +2,7 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR +import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage +import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions import semmle.code.cpp.ir.internal.IRCppLanguage as Language import SimpleSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index aee4959513ed..a7b9160bdc75 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -59,6 +59,12 @@ class MemoryLocation extends TMemoryLocation { final string getUniqueId() { result = var.getUniqueId() } } +/** + * Represents a set of `MemoryLocation`s that cannot overlap with + * `MemoryLocation`s outside of the set. The `VirtualVariable` will be + * represented by a `MemoryLocation` that totally overlaps all other + * `MemoryLocations` in the set. + */ class VirtualVariable extends MemoryLocation { } /** A virtual variable that groups all escaped memory within a function. */ @@ -79,3 +85,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { result = getMemoryLocation(getAddressOperandAllocation(operand.getAddressOperand())) } + +/** Gets the start bit offset of a `MemoryLocation`, if any. */ +int getStartBitOffset(MemoryLocation location) { none() } + +/** Gets the end bit offset of a `MemoryLocation`, if any. */ +int getEndBitOffset(MemoryLocation location) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll index f8c4ceaf9049..2ce23f098a2d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll @@ -1,7 +1,7 @@ private import cpp private import semmle.code.cpp.Print private import semmle.code.cpp.ir.implementation.IRType -private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction +private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw private int getPointerSize() { result = max(any(NullPointerType t).getSize()) } @@ -143,7 +143,7 @@ private predicate isOpaqueType(Type type) { predicate hasOpaqueType(Type tag, int byteSize) { isOpaqueType(tag) and byteSize = getTypeSize(tag) or - tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize) + tag instanceof UnknownType and Raw::needsUnknownOpaqueType(byteSize) } /** @@ -191,7 +191,7 @@ private newtype TCppType = TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or TFunctionGLValueType() or TGLValueAddressType(Type type) or - TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or + TUnknownOpaqueType(int byteSize) { Raw::needsUnknownOpaqueType(byteSize) } or TUnknownType() /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll index 8ce0549b2b4d..f9a0c574f8c3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll @@ -3,18 +3,33 @@ private newtype TOverlap = TMustTotallyOverlap() or TMustExactlyOverlap() +/** + * Represents a possible overlap between two memory ranges. + */ abstract class Overlap extends TOverlap { abstract string toString(); } +/** + * Represents a partial overlap between two memory ranges, which may or may not + * actually occur in practice. + */ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { final override string toString() { result = "MayPartiallyOverlap" } } +/** + * Represents an overlap in which the first memory range is known to include all + * bits of the second memory range, but may be larger or have a different type. + */ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { final override string toString() { result = "MustTotallyOverlap" } } +/** + * Represents an overlap between two memory ranges that have the same extent and + * the same type. + */ class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { final override string toString() { result = "MustExactlyOverlap" } } diff --git a/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll b/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll index 3dbb290d71a7..45036cfddf33 100644 --- a/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll @@ -334,7 +334,7 @@ private predicate branchingExpr(Expr expr) { * Gets the number of branching statements and expressions in a block. This is * for computing cyclomatic complexity. */ -int cyclomaticComplexityBranches(Block b) { +int cyclomaticComplexityBranches(BlockStmt b) { result = count(Stmt stmt | branchingStmt(stmt) and @@ -373,7 +373,7 @@ private predicate skipParent(Stmt s) { exists(Stmt parent | parent = s.getParentStmt() | s instanceof IfStmt and parent.(IfStmt).getElse() = s or - parent instanceof Block + parent instanceof BlockStmt or exists(File f, int startLine, int startCol | startsAt(s, f, startLine, startCol) and diff --git a/cpp/ql/src/semmle/code/cpp/models/Models.qll b/cpp/ql/src/semmle/code/cpp/models/Models.qll index 82ae1fdc4f0a..75e81fdc318e 100644 --- a/cpp/ql/src/semmle/code/cpp/models/Models.qll +++ b/cpp/ql/src/semmle/code/cpp/models/Models.qll @@ -4,6 +4,8 @@ private import implementations.Fread private import implementations.Gets private import implementations.IdentityFunction private import implementations.Inet +private import implementations.Iterator +private import implementations.MemberFunction private import implementations.Memcpy private import implementations.Memset private import implementations.Printf @@ -12,6 +14,11 @@ private import implementations.Strcat private import implementations.Strcpy private import implementations.Strdup private import implementations.Strftime +private import implementations.StdContainer +private import implementations.StdPair +private import implementations.StdMap +private import implementations.StdSet private import implementations.StdString private import implementations.Swap private import implementations.GetDelim +private import implementations.SmartPointer diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll index 782800d0fa24..1f82b90bff42 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling various methods of allocation + * Provides implementation classes modeling various methods of allocation * (`malloc`, `new` etc). See `semmle.code.cpp.models.interfaces.Allocation` * for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll index 2ef355bf3982..4f0341b673ee 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll @@ -1,5 +1,5 @@ /** - * Provides implementation classes modelling various methods of deallocation + * Provides implementation classes modeling various methods of deallocation * (`free`, `delete` etc). See `semmle.code.cpp.models.interfaces.Deallocation` * for usage information. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll index e5e45729e0dc..d22f2f376806 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modeling `gets` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint import semmle.code.cpp.models.interfaces.ArrayFunction @@ -11,11 +16,10 @@ import semmle.code.cpp.models.interfaces.FlowSource class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction, SideEffectFunction, RemoteFlowFunction { GetsFunction() { - exists(string name | hasGlobalOrStdName(name) | - name = "gets" or // gets(str) - name = "fgets" or // fgets(str, num, stream) - name = "fgetws" // fgetws(wstr, num, stream) - ) + // gets(str) + // fgets(str, num, stream) + // fgetws(wstr, num, stream) + hasGlobalOrStdName(["gets", "fgets", "fgetws"]) } override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { @@ -48,4 +52,17 @@ class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, Alias output.isParameterDeref(0) and description = "String read by " + this.getName() } + + override predicate hasArrayWithVariableSize(int bufParam, int countParam) { + not hasGlobalOrStdName("gets") and + bufParam = 0 and + countParam = 1 + } + + override predicate hasArrayWithUnknownSize(int bufParam) { + hasGlobalOrStdName("gets") and + bufParam = 0 + } + + override predicate hasArrayOutput(int bufParam) { bufParam = 0 } } diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll index 865a74b55717..f9eab7aba939 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll @@ -4,16 +4,13 @@ import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.SideEffect /** - * The standard function templates `std::move` and `std::identity` + * The standard function templates `std::move` and `std::forward`. */ class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction { IdentityFunction() { this.getNamespace().getParentNamespace() instanceof GlobalNamespace and this.getNamespace().getName() = "std" and - ( - this.getName() = "move" or - this.getName() = "forward" - ) + this.getName() = ["move", "forward"] } override predicate hasOnlySpecificReadSideEffects() { any() } diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll new file mode 100644 index 000000000000..71453467d8a7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll @@ -0,0 +1,293 @@ +/** + * Provides implementation classes modeling C++ iterators, including + * `std::iterator`, `std::iterator_traits`, and types meeting the + * `LegacyIterator` named requirement. See `semmle.code.cpp.models.Models` for + * usage information. + */ + +import cpp +import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.Iterator + +/** + * An instantiation of the `std::iterator_traits` template. + */ +class IteratorTraits extends Class { + IteratorTraits() { + this.hasQualifiedName("std", "iterator_traits") and + not this instanceof TemplateClass and + exists(TypedefType t | + this.getAMember() = t and + t.getName() = "iterator_category" + ) + } + + Type getIteratorType() { result = this.getTemplateArgument(0) } +} + +/** + * A type which has the typedefs expected for an iterator. + */ +class IteratorByTypedefs extends Class { + IteratorByTypedefs() { + this.getAMember().(TypedefType).hasName("difference_type") and + this.getAMember().(TypedefType).hasName("value_type") and + this.getAMember().(TypedefType).hasName("pointer") and + this.getAMember().(TypedefType).hasName("reference") and + this.getAMember().(TypedefType).hasName("iterator_category") and + not this.hasQualifiedName("std", "iterator_traits") + } +} + +/** + * The `std::iterator` class. + */ +class StdIterator extends Class { + StdIterator() { this.hasQualifiedName("std", "iterator") } +} + +/** + * A type which can be used as an iterator + */ +class Iterator extends Type { + Iterator() { + this instanceof IteratorByTypedefs or + exists(IteratorTraits it | it.getIteratorType() = this) or + this instanceof StdIterator + } +} + +private FunctionInput getIteratorArgumentInput(Operator op, int index) { + exists(Type t | + t = + op + .getACallToThisFunction() + .getArgument(index) + .getExplicitlyConverted() + .getType() + .stripTopLevelSpecifiers() + | + ( + t instanceof Iterator or + t.(ReferenceType).getBaseType() instanceof Iterator + ) and + if op.getParameter(index).getUnspecifiedType() instanceof ReferenceType + then result.isParameterDeref(index) + else result.isParameter(index) + ) +} + +/** + * A non-member prefix `operator*` function for an iterator type. + */ +class IteratorPointerDereferenceOperator extends Operator, TaintFunction, IteratorReferenceFunction { + FunctionInput iteratorInput; + + IteratorPointerDereferenceOperator() { + this.hasName("operator*") and + iteratorInput = getIteratorArgumentInput(this, 0) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input = iteratorInput and + output.isReturnValue() + } +} + +/** + * A non-member `operator++` or `operator--` function for an iterator type. + */ +class IteratorCrementOperator extends Operator, DataFlowFunction { + FunctionInput iteratorInput; + + IteratorCrementOperator() { + this.hasName(["operator++", "operator--"]) and + iteratorInput = getIteratorArgumentInput(this, 0) + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + input = iteratorInput and + output.isReturnValue() + } +} + +/** + * A non-member `operator+` function for an iterator type. + */ +class IteratorAddOperator extends Operator, TaintFunction { + FunctionInput iteratorInput; + + IteratorAddOperator() { + this.hasName("operator+") and + iteratorInput = getIteratorArgumentInput(this, [0, 1]) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input = iteratorInput and + output.isReturnValue() + } +} + +/** + * A non-member `operator-` function that takes a pointer difference type as its second argument. + */ +class IteratorSubOperator extends Operator, TaintFunction { + FunctionInput iteratorInput; + + IteratorSubOperator() { + this.hasName("operator-") and + iteratorInput = getIteratorArgumentInput(this, 0) and + this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input = iteratorInput and + output.isReturnValue() + } +} + +/** + * A non-member `operator+=` or `operator-=` function for an iterator type. + */ +class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction { + IteratorAssignArithmeticOperator() { + this.hasName(["operator+=", "operator-="]) and + this.getDeclaringType() instanceof Iterator + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + input.isParameter(0) and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isParameterDeref(1) and + output.isParameterDeref(0) + } +} + +/** + * A prefix `operator*` member function for an iterator type. + */ +class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction, + IteratorReferenceFunction { + IteratorPointerDereferenceMemberOperator() { + this.hasName("operator*") and + this.getDeclaringType() instanceof Iterator + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * An `operator++` or `operator--` member function for an iterator type. + */ +class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction { + IteratorCrementMemberOperator() { + this.hasName(["operator++", "operator--"]) and + this.getDeclaringType() instanceof Iterator + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierAddress() and + output.isReturnValue() + or + input.isReturnValueDeref() and + output.isQualifierObject() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValueDeref() + } +} + +/** + * A member `operator->` function for an iterator type. + */ +class IteratorFieldMemberOperator extends Operator, TaintFunction { + IteratorFieldMemberOperator() { + this.hasName("operator->") and + this.getDeclaringType() instanceof Iterator + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * An `operator+` or `operator-` member function of an iterator class. + */ +class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction { + IteratorBinaryArithmeticMemberOperator() { + this.hasName(["operator+", "operator-"]) and + this.getDeclaringType() instanceof Iterator + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * An `operator+=` or `operator-=` member function of an iterator class. + */ +class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction { + IteratorAssignArithmeticMemberOperator() { + this.hasName(["operator+=", "operator-="]) and + this.getDeclaringType() instanceof Iterator + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierAddress() and + output.isReturnValue() + or + input.isReturnValueDeref() and + output.isQualifierObject() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValueDeref() + } +} + +/** + * An `operator[]` member function of an iterator class. + */ +class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, IteratorReferenceFunction { + IteratorArrayMemberOperator() { + this.hasName("operator[]") and + this.getDeclaringType() instanceof Iterator + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * A `begin` or `end` member function, or a related member function, that + * returns an iterator. + */ +class BeginOrEndFunction extends MemberFunction, TaintFunction { + BeginOrEndFunction() { + this + .hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", + "before_begin", "cbefore_begin"]) and + this.getType().getUnspecifiedType() instanceof Iterator + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll new file mode 100644 index 000000000000..0e4812cc25cc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll @@ -0,0 +1,92 @@ +/** + * Provides models for C++ constructors and user-defined operators. + */ + +import cpp +import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.Taint + +/** + * Model for C++ conversion constructors. As of C++11 this does not correspond + * perfectly with the language definition of a converting constructor, however, + * it does correspond with the constructors we are confident taint should flow + * through. + */ +class ConversionConstructorModel extends Constructor, TaintFunction { + ConversionConstructorModel() { + strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and + not hasSpecifier("explicit") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from the first constructor argument to the returned object + input.isParameter(0) and + ( + output.isReturnValue() + or + output.isQualifierObject() + ) + } +} + +/** + * Model for C++ copy constructors. + */ +class CopyConstructorModel extends CopyConstructor, DataFlowFunction { + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // data flow from the first constructor argument to the returned object + input.isParameter(0) and + ( + output.isReturnValue() + or + output.isQualifierObject() + ) + } +} + +/** + * Model for C++ move constructors. + */ +class MoveConstructorModel extends MoveConstructor, DataFlowFunction { + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // data flow from the first constructor argument to the returned object + input.isParameter(0) and + ( + output.isReturnValue() + or + output.isQualifierObject() + ) + } +} + +/** + * Model for C++ copy assignment operators. + */ +class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction { + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from argument to self + input.isParameterDeref(0) and + output.isQualifierObject() + or + // taint flow from argument to return value + input.isParameterDeref(0) and + output.isReturnValueDeref() + // TODO: it would be more accurate to model copy assignment as data flow + } +} + +/** + * Model for C++ move assignment operators. + */ +class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction { + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from argument to self + input.isParameterDeref(0) and + output.isQualifierObject() + or + // taint flow from argument to return value + input.isParameterDeref(0) and + output.isReturnValueDeref() + // TODO: it would be more accurate to model move assignment as data flow + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll index 0951daea11b7..ef4aa8b7290e 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modeling `memcpy` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.Function import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll index 7fc7fbfc9e5b..2c34369aee44 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modeling `memset` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.Function import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll index b5047c25e854..61dd3bc50b9d 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll @@ -1,3 +1,10 @@ +/** + * Provides implementation classes modeling various standard formatting + * functions (`printf`, `snprintf` etc). + * See `semmle.code.cpp.models.interfaces.FormattingFunction` for usage + * information. + */ + import semmle.code.cpp.models.interfaces.FormattingFunction import semmle.code.cpp.models.interfaces.Alias @@ -59,12 +66,25 @@ class Sprintf extends FormattingFunction { Sprintf() { this instanceof TopLevelFunction and ( - hasGlobalOrStdName("sprintf") or - hasGlobalName("_sprintf_l") or - hasGlobalName("__swprintf_l") or - hasGlobalOrStdName("wsprintf") or - hasGlobalName("g_strdup_printf") or - hasGlobalName("g_sprintf") or + // sprintf(dst, format, args...) + hasGlobalOrStdName("sprintf") + or + // _sprintf_l(dst, format, locale, args...) + hasGlobalName("_sprintf_l") + or + // __swprintf_l(dst, format, locale, args...) + hasGlobalName("__swprintf_l") + or + // wsprintf(dst, format, args...) + hasGlobalOrStdName("wsprintf") + or + // g_strdup_printf(format, ...) + hasGlobalName("g_strdup_printf") + or + // g_sprintf(dst, format, ...) + hasGlobalName("g_sprintf") + or + // __builtin___sprintf_chk(dst, flag, os, format, ...) hasGlobalName("__builtin___sprintf_chk") ) and not exists(getDefinition().getFile().getRelativePath()) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll new file mode 100644 index 000000000000..7dde921bfa1f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll @@ -0,0 +1,61 @@ +import semmle.code.cpp.models.interfaces.Taint + +/** + * The `std::shared_ptr` and `std::unique_ptr` template classes. + */ +class UniqueOrSharedPtr extends Class { + UniqueOrSharedPtr() { this.hasQualifiedName("std", ["shared_ptr", "unique_ptr"]) } +} + +/** + * The `std::make_shared` and `std::make_unique` template functions. + */ +class MakeUniqueOrShared extends TaintFunction { + MakeUniqueOrShared() { this.hasQualifiedName("std", ["make_shared", "make_unique"]) } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // Exclude the specializations of `std::make_shared` and `std::make_unique` that allocate arrays + // since these just take a size argument, which we don't want to propagate taint through. + not this.isArray() and + input.isParameter(_) and + output.isReturnValue() + } + + /** + * Holds if the function returns a `shared_ptr` (or `unique_ptr`) where `T` is an + * array type (i.e., `U[]` for some type `U`). + */ + predicate isArray() { + this.getTemplateArgument(0).(Type).getUnderlyingType() instanceof ArrayType + } +} + +/** + * A prefix `operator*` member function for a `shared_ptr` or `unique_ptr` type. + */ +class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction { + UniqueOrSharedDereferenceMemberOperator() { + this.hasName("operator*") and + this.getDeclaringType() instanceof UniqueOrSharedPtr + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValueDeref() + } +} + +/** + * The `std::shared_ptr` or `std::unique_ptr` function `get`. + */ +class UniqueOrSharedGet extends TaintFunction { + UniqueOrSharedGet() { + this.hasName("get") and + this.getDeclaringType() instanceof UniqueOrSharedPtr + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdContainer.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdContainer.qll new file mode 100644 index 000000000000..a339dadb860f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdContainer.qll @@ -0,0 +1,208 @@ +/** + * Provides models for C++ containers `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`. + */ + +import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.implementations.Iterator + +/** + * Additional model for standard container constructors that reference the + * value type of the container (that is, the `T` in `std::vector`). For + * example the fill constructor: + * ``` + * std::vector v(100, potentially_tainted_string); + * ``` + */ +class StdSequenceContainerConstructor extends Constructor, TaintFunction { + StdSequenceContainerConstructor() { + this.getDeclaringType().hasQualifiedName("std", ["vector", "deque", "list", "forward_list"]) + } + + /** + * Gets the index of a parameter to this function that is a reference to the + * value type of the container. + */ + int getAValueTypeParameterIndex() { + getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = + getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from any parameter of the value type to the returned object + ( + input.isParameterDeref(getAValueTypeParameterIndex()) or + input.isParameter(getAnIteratorParameterIndex()) + ) and + ( + output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object + or + output.isQualifierObject() + ) + } +} + +/** + * The standard container function `data`. + */ +class StdSequenceContainerData extends TaintFunction { + StdSequenceContainerData() { this.hasQualifiedName("std", ["array", "vector"], "data") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from container itself (qualifier) to return value + input.isQualifierObject() and + output.isReturnValueDeref() + or + // reverse flow from returned reference to the qualifier (for writes to + // `data`) + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The standard container functions `push_back` and `push_front`. + */ +class StdSequenceContainerPush extends TaintFunction { + StdSequenceContainerPush() { + this.hasQualifiedName("std", "vector", "push_back") or + this.hasQualifiedName("std", "deque", ["push_back", "push_front"]) or + this.hasQualifiedName("std", "list", ["push_back", "push_front"]) or + this.hasQualifiedName("std", "forward_list", "push_front") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from parameter to qualifier + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * The standard container functions `front` and `back`. + */ +class StdSequenceContainerFrontBack extends TaintFunction { + StdSequenceContainerFrontBack() { + this.hasQualifiedName("std", "array", ["front", "back"]) or + this.hasQualifiedName("std", "vector", ["front", "back"]) or + this.hasQualifiedName("std", "deque", ["front", "back"]) or + this.hasQualifiedName("std", "list", ["front", "back"]) or + this.hasQualifiedName("std", "forward_list", "front") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from object to returned reference + input.isQualifierObject() and + output.isReturnValueDeref() + } +} + +/** + * The standard container functions `insert` and `insert_after`. + */ +class StdSequenceContainerInsert extends TaintFunction { + StdSequenceContainerInsert() { + this.hasQualifiedName("std", ["vector", "deque", "list"], "insert") or + this.hasQualifiedName("std", ["forward_list"], "insert_after") + } + + /** + * Gets the index of a parameter to this function that is a reference to the + * value type of the container. + */ + int getAValueTypeParameterIndex() { + getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = + getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from parameter to container itself (qualifier) and return value + ( + input.isQualifierObject() or + input.isParameterDeref(getAValueTypeParameterIndex()) or + input.isParameter(getAnIteratorParameterIndex()) + ) and + ( + output.isQualifierObject() or + output.isReturnValueDeref() + ) + } +} + +/** + * The standard container function `assign`. + */ +class StdSequenceContainerAssign extends TaintFunction { + StdSequenceContainerAssign() { + this.hasQualifiedName("std", ["vector", "deque", "list", "forward_list"], "assign") + } + + /** + * Gets the index of a parameter to this function that is a reference to the + * value type of the container. + */ + int getAValueTypeParameterIndex() { + getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = + getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from parameter to container itself (qualifier) + ( + input.isParameterDeref(getAValueTypeParameterIndex()) or + input.isParameter(getAnIteratorParameterIndex()) + ) and + output.isQualifierObject() + } +} + +/** + * The standard container `swap` functions. + */ +class StdSequenceContainerSwap extends TaintFunction { + StdSequenceContainerSwap() { + this.hasQualifiedName("std", ["array", "vector", "deque", "list", "forward_list"], "swap") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // container1.swap(container2) + input.isQualifierObject() and + output.isParameterDeref(0) + or + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * The standard container functions `at` and `operator[]`. + */ +class StdSequenceContainerAt extends TaintFunction { + StdSequenceContainerAt() { + this.hasQualifiedName("std", ["vector", "array", "deque"], ["at", "operator[]"]) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to referenced return value + input.isQualifierObject() and + output.isReturnValueDeref() + or + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdMap.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdMap.qll new file mode 100644 index 000000000000..12e6624903e6 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdMap.qll @@ -0,0 +1,192 @@ +/** + * Provides models for C++ containers `std::map` and `std::unordered_map`. + */ + +import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.implementations.Iterator + +/** + * Additional model for map constructors using iterator inputs. + */ +class StdMapConstructor extends Constructor, TaintFunction { + StdMapConstructor() { + this.hasQualifiedName("std", "map", "map") or + this.hasQualifiedName("std", "unordered_map", "unordered_map") + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { + getParameter(result).getUnspecifiedType() instanceof Iterator + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from any parameter of an iterator type to the qualifier + input.isParameterDeref(getAnIteratorParameterIndex()) and + ( + output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object + or + output.isQualifierObject() + ) + } +} + +/** + * The standard map `insert` and `insert_or_assign` functions. + */ +class StdMapInsert extends TaintFunction { + StdMapInsert() { + this.hasQualifiedName("std", ["map", "unordered_map"], ["insert", "insert_or_assign"]) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from last parameter to qualifier and return value + // (where the return value is a pair, this should really flow just to the first part of it) + input.isParameterDeref(getNumberOfParameters() - 1) and + ( + output.isQualifierObject() or + output.isReturnValue() + ) + } +} + +/** + * The standard map `emplace` and `emplace_hint` functions. + */ +class StdMapEmplace extends TaintFunction { + StdMapEmplace() { + this.hasQualifiedName("std", ["map", "unordered_map"], ["emplace", "emplace_hint"]) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from the last parameter (which may be the value part used to + // construct a pair, or a pair to be copied / moved) to the qualifier and + // return value. + // (where the return value is a pair, this should really flow just to the first part of it) + input.isParameterDeref(getNumberOfParameters() - 1) and + ( + output.isQualifierObject() or + output.isReturnValue() + ) + or + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The standard map `try_emplace` function. + */ +class StdMapTryEmplace extends TaintFunction { + StdMapTryEmplace() { this.hasQualifiedName("std", ["map", "unordered_map"], "try_emplace") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from any parameter apart from the key to qualifier and return value + // (here we assume taint flow from any constructor parameter to the constructed object) + // (where the return value is a pair, this should really flow just to the first part of it) + exists(int arg | arg = [1 .. getNumberOfParameters() - 1] | + ( + not getUnspecifiedType() instanceof Iterator or + arg != 1 + ) and + input.isParameterDeref(arg) + ) and + ( + output.isQualifierObject() or + output.isReturnValue() + ) + or + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The standard map `swap` function. + */ +class StdMapSwap extends TaintFunction { + StdMapSwap() { this.hasQualifiedName("std", ["map", "unordered_map"], "swap") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // container1.swap(container2) + input.isQualifierObject() and + output.isParameterDeref(0) + or + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * The standard map `merge` function. + */ +class StdMapMerge extends TaintFunction { + StdMapMerge() { this.hasQualifiedName("std", ["map", "unordered_map"], "merge") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // container1.merge(container2) + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * The standard map functions `at` and `operator[]`. + */ +class StdMapAt extends TaintFunction { + StdMapAt() { this.hasQualifiedName("std", ["map", "unordered_map"], ["at", "operator[]"]) } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to referenced return value + input.isQualifierObject() and + output.isReturnValueDeref() + or + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The standard map `find` function. + */ +class StdMapFind extends TaintFunction { + StdMapFind() { this.hasQualifiedName("std", ["map", "unordered_map"], "find") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The standard map `erase` function. + */ +class StdMapErase extends TaintFunction { + StdMapErase() { this.hasQualifiedName("std", ["map", "unordered_map"], "erase") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to iterator return value + getType().getUnderlyingType() instanceof Iterator and + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The standard map `lower_bound`, `upper_bound` and `equal_range` functions. + */ +class StdMapEqualRange extends TaintFunction { + StdMapEqualRange() { + this + .hasQualifiedName("std", ["map", "unordered_map"], + ["lower_bound", "upper_bound", "equal_range"]) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to return value + input.isQualifierObject() and + output.isReturnValue() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdPair.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdPair.qll new file mode 100644 index 000000000000..b36f7ab3325d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdPair.qll @@ -0,0 +1,48 @@ +/** + * Provides models for the C++ `std::pair` class. + */ + +import semmle.code.cpp.models.interfaces.Taint + +/** + * Additional model for `std::pair` constructors. + */ +class StdPairConstructor extends Constructor, TaintFunction { + StdPairConstructor() { this.hasQualifiedName("std", "pair", "pair") } + + /** + * Gets the index of a parameter to this function that is a reference to + * either value type of the pair. + */ + int getAValueTypeParameterIndex() { + getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = + getDeclaringType().getTemplateArgument(_).(Type).getUnspecifiedType() // i.e. the `T1` or `T2` of this `std::pair` + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from second parameter of a value type to the qualifier + getAValueTypeParameterIndex() = 1 and + input.isParameterDeref(1) and + ( + output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object + or + output.isQualifierObject() + ) + } +} + +/** + * The standard pair `swap` function. + */ +class StdPairSwap extends TaintFunction { + StdPairSwap() { this.hasQualifiedName("std", "pair", "swap") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // container1.swap(container2) + input.isQualifierObject() and + output.isParameterDeref(0) + or + input.isParameterDeref(0) and + output.isQualifierObject() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdSet.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdSet.qll new file mode 100644 index 000000000000..06e8be4c4a41 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdSet.qll @@ -0,0 +1,145 @@ +/** + * Provides models for C++ containers `std::set` and `std::unordered_set`. + */ + +import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.implementations.Iterator + +/** + * Additional model for set constructors using iterator inputs. + */ +class StdSetConstructor extends Constructor, TaintFunction { + StdSetConstructor() { + this.hasQualifiedName("std", "set", "set") or + this.hasQualifiedName("std", "unordered_set", "unordered_set") + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { + getParameter(result).getUnspecifiedType() instanceof Iterator + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from any parameter of an iterator type to the qualifier + input.isParameterDeref(getAnIteratorParameterIndex()) and + ( + output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object + or + output.isQualifierObject() + ) + } +} + +/** + * The standard set `insert` and `insert_or_assign` functions. + */ +class StdSetInsert extends TaintFunction { + StdSetInsert() { this.hasQualifiedName("std", ["set", "unordered_set"], "insert") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from last parameter to qualifier and return value + // (where the return value is a pair, this should really flow just to the first part of it) + input.isParameterDeref(getNumberOfParameters() - 1) and + ( + output.isQualifierObject() or + output.isReturnValue() + ) + } +} + +/** + * The standard set `emplace` and `emplace_hint` functions. + */ +class StdSetEmplace extends TaintFunction { + StdSetEmplace() { + this.hasQualifiedName("std", ["set", "unordered_set"], ["emplace", "emplace_hint"]) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from any parameter to qualifier and return value + // (here we assume taint flow from any constructor parameter to the constructed object) + // (where the return value is a pair, this should really flow just to the first part of it) + input.isParameter([0 .. getNumberOfParameters() - 1]) and + ( + output.isQualifierObject() or + output.isReturnValue() + ) + or + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The standard set `swap` functions. + */ +class StdSetSwap extends TaintFunction { + StdSetSwap() { this.hasQualifiedName("std", ["set", "unordered_set"], "swap") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // container1.swap(container2) + input.isQualifierObject() and + output.isParameterDeref(0) + or + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * The standard set `merge` function. + */ +class StdSetMerge extends TaintFunction { + StdSetMerge() { this.hasQualifiedName("std", ["set", "unordered_set"], "merge") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // container1.merge(container2) + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * The standard set `find` function. + */ +class StdSetFind extends TaintFunction { + StdSetFind() { this.hasQualifiedName("std", ["set", "unordered_set"], "find") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The standard set `erase` function. + */ +class StdSetErase extends TaintFunction { + StdSetErase() { this.hasQualifiedName("std", ["set", "unordered_set"], "erase") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to iterator return value + getType().getUnderlyingType() instanceof Iterator and + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The standard set `lower_bound`, `upper_bound` and `equal_range` functions. + */ +class StdSetEqualRange extends TaintFunction { + StdSetEqualRange() { + this + .hasQualifiedName("std", ["set", "unordered_set"], + ["lower_bound", "upper_bound", "equal_range"]) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to return value + input.isQualifierObject() and + output.isReturnValue() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll index 9c6ebd1a8779..b8a000b963ea 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll @@ -1,21 +1,62 @@ +/** + * Provides implementation classes modeling `std::string` (and other + * instantiations of `std::basic_string`) and `std::ostream`. See + * `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.implementations.Iterator + +/** + * The `std::basic_string` template class. + */ +class StdBasicString extends TemplateClass { + StdBasicString() { this.hasQualifiedName("std", "basic_string") } +} /** - * The `std::basic_string` constructor(s). + * Additional model for `std::string` constructors that reference the character + * type of the container, or an iterator. For example construction from + * iterators: + * ``` + * std::string b(a.begin(), a.end()); + * ``` */ -class StdStringConstructor extends TaintFunction { - pragma[noinline] - StdStringConstructor() { this.hasQualifiedName("std", "basic_string", "basic_string") } +class StdStringConstructor extends Constructor, TaintFunction { + StdStringConstructor() { this.getDeclaringType().hasQualifiedName("std", "basic_string") } + + /** + * Gets the index of a parameter to this function that is a string (or + * character). + */ + int getAStringParameterIndex() { + getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` + getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` + getParameter(result).getUnspecifiedType() = + getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - // flow from any constructor argument to return value - input.isParameter(_) and - output.isReturnValue() + // taint flow from any parameter of the value type to the returned object + ( + input.isParameterDeref(getAStringParameterIndex()) or + input.isParameter(getAnIteratorParameterIndex()) + ) and + ( + output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object + or + output.isQualifierObject() + ) } } /** - * The standard function `std::string.c_str`. + * The `std::string` function `c_str`. */ class StdStringCStr extends TaintFunction { StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") } @@ -23,6 +64,547 @@ class StdStringCStr extends TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from string itself (qualifier) to return value input.isQualifierObject() and + output.isReturnValueDeref() + } +} + +/** + * The `std::string` function `data`. + */ +class StdStringData extends TaintFunction { + StdStringData() { this.hasQualifiedName("std", "basic_string", "data") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from string itself (qualifier) to return value + input.isQualifierObject() and + output.isReturnValueDeref() + or + // reverse flow from returned reference to the qualifier (for writes to + // `data`) + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The `std::string` function `push_back`. + */ +class StdStringPush extends TaintFunction { + StdStringPush() { this.hasQualifiedName("std", "basic_string", "push_back") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from parameter to qualifier + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * The `std::string` functions `front` and `back`. + */ +class StdStringFrontBack extends TaintFunction { + StdStringFrontBack() { this.hasQualifiedName("std", "basic_string", ["front", "back"]) } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from object to returned reference + input.isQualifierObject() and + output.isReturnValueDeref() + } +} + +/** + * The `std::string` function `operator+`. + */ +class StdStringPlus extends TaintFunction { + StdStringPlus() { + this.hasQualifiedName("std", "operator+") and + this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from parameters to return value + ( + input.isParameterDeref(0) or + input.isParameterDeref(1) + ) and + output.isReturnValue() + } +} + +/** + * The `std::string` functions `operator+=`, `append`, `insert` and + * `replace`. All of these functions combine the existing string + * with a new string (or character) from one of the arguments. + */ +class StdStringAppend extends TaintFunction { + StdStringAppend() { + this.hasQualifiedName("std", "basic_string", ["operator+=", "append", "insert", "replace"]) + } + + /** + * Gets the index of a parameter to this function that is a string (or + * character). + */ + int getAStringParameterIndex() { + getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` + getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` + getParameter(result).getUnspecifiedType() = + getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from string and parameter to string (qualifier) and return value + ( + input.isQualifierObject() or + input.isParameterDeref(getAStringParameterIndex()) or + input.isParameter(getAnIteratorParameterIndex()) + ) and + ( + output.isQualifierObject() or + output.isReturnValueDeref() + ) + or + // reverse flow from returned reference to the qualifier (for writes to + // the result) + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The standard function `std::string.assign`. + */ +class StdStringAssign extends TaintFunction { + StdStringAssign() { this.hasQualifiedName("std", "basic_string", "assign") } + + /** + * Gets the index of a parameter to this function that is a string (or + * character). + */ + int getAStringParameterIndex() { + getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` + getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` + getParameter(result).getUnspecifiedType() = + getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` + } + + /** + * Gets the index of a parameter to this function that is an iterator. + */ + int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from parameter to string itself (qualifier) and return value + ( + input.isParameterDeref(getAStringParameterIndex()) or + input.isParameter(getAnIteratorParameterIndex()) + ) and + ( + output.isQualifierObject() or + output.isReturnValueDeref() + ) + or + // reverse flow from returned reference to the qualifier (for writes to + // the result) + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The standard function `std::string.copy`. + */ +class StdStringCopy extends TaintFunction { + StdStringCopy() { this.hasQualifiedName("std", "basic_string", "copy") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // copy(dest, num, pos) + input.isQualifierObject() and + output.isParameterDeref(0) + } +} + +/** + * The standard function `std::string.substr`. + */ +class StdStringSubstr extends TaintFunction { + StdStringSubstr() { this.hasQualifiedName("std", "basic_string", "substr") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // substr(pos, num) + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The standard functions `std::string.swap` and `std::stringstream::swap`. + */ +class StdStringSwap extends TaintFunction { + StdStringSwap() { + this.hasQualifiedName("std", "basic_string", "swap") or + this.hasQualifiedName("std", "basic_stringstream", "swap") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // str1.swap(str2) + input.isQualifierObject() and + output.isParameterDeref(0) + or + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * The `std::string` functions `at` and `operator[]`. + */ +class StdStringAt extends TaintFunction { + StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to referenced return value + input.isQualifierObject() and + output.isReturnValueDeref() + or + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The `std::basic_istream` template class. + */ +class StdBasicIStream extends TemplateClass { + StdBasicIStream() { this.hasQualifiedName("std", "basic_istream") } +} + +/** + * The `std::istream` function `operator>>` (defined as a member function). + */ +class StdIStreamIn extends DataFlowFunction, TaintFunction { + StdIStreamIn() { this.hasQualifiedName("std", "basic_istream", "operator>>") } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // returns reference to `*this` + input.isQualifierAddress() and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to first parameter + input.isQualifierObject() and + output.isParameterDeref(0) + or + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The `std::istream` function `operator>>` (defined as a non-member function). + */ +class StdIStreamInNonMember extends DataFlowFunction, TaintFunction { + StdIStreamInNonMember() { + this.hasQualifiedName("std", "operator>>") and + this.getUnspecifiedType().(ReferenceType).getBaseType() = + any(StdBasicIStream s).getAnInstantiation() + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // flow from first parameter to return value + input.isParameter(0) and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from first parameter to second parameter + input.isParameterDeref(0) and + output.isParameterDeref(1) + or + // reverse flow from returned reference to the first parameter + input.isReturnValueDeref() and + output.isParameterDeref(0) + } +} + +/** + * The `std::istream` functions `get` (without parameters) and `peek`. + */ +class StdIStreamGet extends TaintFunction { + StdIStreamGet() { + this.hasQualifiedName("std", "basic_istream", ["get", "peek"]) and + this.getNumberOfParameters() = 0 + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to return value + input.isQualifierObject() and + output.isReturnValue() + } +} + +/** + * The `std::istream` functions `get` (with parameters) and `read`. + */ +class StdIStreamRead extends DataFlowFunction, TaintFunction { + StdIStreamRead() { + this.hasQualifiedName("std", "basic_istream", ["get", "read"]) and + this.getNumberOfParameters() > 0 + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // returns reference to `*this` + input.isQualifierAddress() and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to first parameter + input.isQualifierObject() and + output.isParameterDeref(0) + or + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The `std::istream` function `readsome`. + */ +class StdIStreamReadSome extends TaintFunction { + StdIStreamReadSome() { this.hasQualifiedName("std", "basic_istream", "readsome") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to first parameter + input.isQualifierObject() and + output.isParameterDeref(0) + } +} + +/** + * The `std::istream` function `putback`. + */ +class StdIStreamPutBack extends DataFlowFunction, TaintFunction { + StdIStreamPutBack() { this.hasQualifiedName("std", "basic_istream", "putback") } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // returns reference to `*this` + input.isQualifierAddress() and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from first parameter (value or pointer) to qualifier + input.isParameter(0) and + output.isQualifierObject() + or + input.isParameterDeref(0) and + output.isQualifierObject() + or + // flow from first parameter (value or pointer) to return value + input.isParameter(0) and + output.isReturnValueDeref() + or + input.isParameterDeref(0) and + output.isReturnValueDeref() + or + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The `std::istream` function `getline`. + */ +class StdIStreamGetLine extends DataFlowFunction, TaintFunction { + StdIStreamGetLine() { this.hasQualifiedName("std", "basic_istream", "getline") } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // returns reference to `*this` + input.isQualifierAddress() and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to first parameter + input.isQualifierObject() and + output.isParameterDeref(0) + or + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The (non-member) function `std::getline`. + */ +class StdGetLine extends DataFlowFunction, TaintFunction { + StdGetLine() { this.hasQualifiedName("std", "getline") } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // flow from first parameter to return value + input.isParameter(0) and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from first parameter to second parameter + input.isParameterDeref(0) and + output.isParameterDeref(1) + or + // reverse flow from returned reference to first parameter + input.isReturnValueDeref() and + output.isParameterDeref(0) + } +} + +/** + * The `std::basic_ostream` template class. + */ +class StdBasicOStream extends TemplateClass { + StdBasicOStream() { this.hasQualifiedName("std", "basic_ostream") } +} + +/** + * The `std::ostream` functions `operator<<` (defined as a member function), + * `put` and `write`. + */ +class StdOStreamOut extends DataFlowFunction, TaintFunction { + StdOStreamOut() { this.hasQualifiedName("std", "basic_ostream", ["operator<<", "put", "write"]) } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // returns reference to `*this` + input.isQualifierAddress() and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from first parameter (value or pointer) to qualifier + input.isParameter(0) and + output.isQualifierObject() + or + input.isParameterDeref(0) and + output.isQualifierObject() + or + // flow from first parameter (value or pointer) to return value + input.isParameter(0) and + output.isReturnValueDeref() + or + input.isParameterDeref(0) and + output.isReturnValueDeref() + or + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + +/** + * The `std::ostream` function `operator<<` (defined as a non-member function). + */ +class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction { + StdOStreamOutNonMember() { + this.hasQualifiedName("std", "operator<<") and + this.getUnspecifiedType().(ReferenceType).getBaseType() = + any(StdBasicOStream s).getAnInstantiation() + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // flow from first parameter to return value + input.isParameter(0) and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from second parameter to first parameter + input.isParameter(1) and + output.isParameterDeref(0) + or + // flow from second parameter to return value + input.isParameter(1) and + output.isReturnValueDeref() + or + // reverse flow from returned reference to the first parameter + input.isReturnValueDeref() and + output.isParameterDeref(0) + } +} + +/** + * Additional model for `std::stringstream` constructors that take a string + * input parameter. + */ +class StdStringStreamConstructor extends Constructor, TaintFunction { + StdStringStreamConstructor() { + this.getDeclaringType().hasQualifiedName("std", "basic_stringstream") + } + + /** + * Gets the index of a parameter to this function that is a string. + */ + int getAStringParameterIndex() { + getParameter(result).getType() instanceof ReferenceType // `const std::basic_string &` + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // taint flow from any parameter of string type to the returned object + input.isParameterDeref(getAStringParameterIndex()) and + ( + output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object + or + output.isQualifierObject() + ) + } +} + +/** + * The `std::stringstream` function `str`. + */ +class StdStringStreamStr extends TaintFunction { + StdStringStreamStr() { this.hasQualifiedName("std", "basic_stringstream", "str") } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from qualifier to return value (if any) + input.isQualifierObject() and output.isReturnValue() + or + // flow from first parameter (if any) to qualifier + input.isParameterDeref(0) and + output.isQualifierObject() + } +} + +/** + * A `std::` stream function that does not require a model, except that it + * returns a reference to `*this` and thus could be used in a chain. + */ +class StdStreamFunction extends DataFlowFunction, TaintFunction { + StdStreamFunction() { + this.hasQualifiedName("std", "basic_istream", ["ignore", "unget", "seekg"]) or + this.hasQualifiedName("std", "basic_ostream", ["seekp", "flush"]) or + this.hasQualifiedName("std", "basic_ios", "copyfmt") + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // returns reference to `*this` + input.isQualifierAddress() and + output.isReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // reverse flow from returned reference to the qualifier + input.isReturnValueDeref() and + output.isQualifierObject() } } diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll index d44b28f041da..9acd5b32d4f0 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modeling `strcat` and various similar functions. + * See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint @@ -19,6 +24,21 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid ) } + /** + * Gets the index of the parameter that is the size of the copy (in characters). + */ + int getParamSize() { exists(getParameter(2)) and result = 2 } + + /** + * Gets the index of the parameter that is the source of the copy. + */ + int getParamSrc() { result = 1 } + + /** + * Gets the index of the parameter that is the destination to be appended to. + */ + int getParamDest() { result = 0 } + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { input.isParameter(0) and output.isReturnValue() diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll index c7f0898f3582..d0b3f92fa0fd 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modeling `strcpy` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint @@ -8,70 +13,91 @@ import semmle.code.cpp.models.interfaces.SideEffect */ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction { StrcpyFunction() { - this.hasName("strcpy") or - this.hasName("_mbscpy") or - this.hasName("wcscpy") or - this.hasName("strncpy") or - this.hasName("_strncpy_l") or - this.hasName("_mbsncpy") or - this.hasName("_mbsncpy_l") or - this.hasName("wcsncpy") or - this.hasName("_wcsncpy_l") + getName() = + ["strcpy", // strcpy(dst, src) + "wcscpy", // wcscpy(dst, src) + "_mbscpy", // _mbscpy(dst, src) + "strncpy", // strncpy(dst, src, max_amount) + "_strncpy_l", // _strncpy_l(dst, src, max_amount, locale) + "wcsncpy", // wcsncpy(dst, src, max_amount) + "_wcsncpy_l", // _wcsncpy_l(dst, src, max_amount, locale) + "_mbsncpy", // _mbsncpy(dst, src, max_amount) + "_mbsncpy_l"] // _mbsncpy_l(dst, src, max_amount, locale) + or + getName() = + ["strcpy_s", // strcpy_s(dst, max_amount, src) + "wcscpy_s", // wcscpy_s(dst, max_amount, src) + "_mbscpy_s"] and // _mbscpy_s(dst, max_amount, src) + // exclude the 2-parameter template versions + // that find the size of a fixed size destination buffer. + getNumberOfParameters() = 3 } - override predicate hasArrayInput(int bufParam) { bufParam = 1 } + /** + * Holds if this is one of the `strcpy_s` variants. + */ + private predicate isSVariant() { + exists(string name | name = getName() | name.suffix(name.length() - 2) = "_s") + } + + /** + * Gets the index of the parameter that is the maximum size of the copy (in characters). + */ + int getParamSize() { + if isSVariant() + then result = 1 + else + if exists(getName().indexOf("ncpy")) + then result = 2 + else none() + } - override predicate hasArrayOutput(int bufParam) { bufParam = 0 } + /** + * Gets the index of the parameter that is the source of the copy. + */ + int getParamSrc() { if isSVariant() then result = 2 else result = 1 } - override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 1 } + /** + * Gets the index of the parameter that is the destination of the copy. + */ + int getParamDest() { result = 0 } + + override predicate hasArrayInput(int bufParam) { bufParam = getParamSrc() } + + override predicate hasArrayOutput(int bufParam) { bufParam = getParamDest() } + + override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = getParamSrc() } override predicate hasArrayWithVariableSize(int bufParam, int countParam) { - ( - this.hasName("strncpy") or - this.hasName("_strncpy_l") or - this.hasName("_mbsncpy") or - this.hasName("_mbsncpy_l") or - this.hasName("wcsncpy") or - this.hasName("_wcsncpy_l") - ) and - bufParam = 0 and - countParam = 2 + bufParam = getParamDest() and + countParam = getParamSize() } override predicate hasArrayWithUnknownSize(int bufParam) { - ( - this.hasName("strcpy") or - this.hasName("_mbscpy") or - this.hasName("wcscpy") - ) and - bufParam = 0 + not exists(getParamSize()) and + bufParam = getParamDest() } override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { - input.isParameterDeref(1) and - output.isParameterDeref(0) + not exists(getParamSize()) and + input.isParameterDeref(getParamSrc()) and + output.isParameterDeref(getParamDest()) or - input.isParameterDeref(1) and + not exists(getParamSize()) and + input.isParameterDeref(getParamSrc()) and output.isReturnValueDeref() or - input.isParameter(0) and + input.isParameter(getParamDest()) and output.isReturnValue() } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // these may do only a partial copy of the input buffer to the output + // buffer + exists(getParamSize()) and + input.isParameter(getParamSrc()) and ( - // these may do only a partial copy of the input buffer to the output - // buffer - this.hasName("strncpy") or - this.hasName("_strncpy_l") or - this.hasName("_mbsncpy") or - this.hasName("_mbsncpy_l") or - this.hasName("wcsncpy") or - this.hasName("_wcsncpy_l") - ) and - input.isParameter(2) and - ( - output.isParameterDeref(0) or + output.isParameterDeref(getParamDest()) or output.isReturnValueDeref() ) } @@ -81,17 +107,18 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { - i = 0 and + i = getParamDest() and buffer = true and mustWrite = false } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - i = 1 and + i = getParamSrc() and buffer = true } override ParameterIndex getParameterSizeIndex(ParameterIndex i) { - hasArrayWithVariableSize(i, result) + i = getParamDest() and + result = getParamSize() } } diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll index f7d122db259b..3497ab9a0650 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll @@ -1,3 +1,8 @@ +/** + * Provides implementation classes modeling `strdup` and various similar + * functions. See `semmle.code.cpp.models.Models` for usage information. + */ + import semmle.code.cpp.models.interfaces.Allocation import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll index 81a40cd349af..1ebf40b1f014 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll @@ -1,5 +1,5 @@ /** - * Provides an abstract class for modelling functions and expressions that + * Provides an abstract class for modeling functions and expressions that * allocate memory, such as the standard `malloc` function. To use this QL * library, create one or more QL classes extending a class here with a * characteristic predicate that selects the functions or expressions you are diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll index 872cfcd29975..c1b65d627069 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll @@ -19,5 +19,10 @@ import semmle.code.cpp.models.Models * to destinations; that is covered by `TaintModel.qll`. */ abstract class DataFlowFunction extends Function { + /** + * Holds if data can be copied from the argument, qualifier, or buffer + * represented by `input` to the return value or buffer represented by + * `output` + */ abstract predicate hasDataFlow(FunctionInput input, FunctionOutput output); } diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll index 9223592ef678..23eca5164188 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll @@ -1,5 +1,5 @@ /** - * Provides an abstract class for modelling functions and expressions that + * Provides an abstract class for modeling functions and expressions that * deallocate memory, such as the standard `free` function. To use this QL * library, create one or more QL classes extending a class here with a * characteristic predicate that selects the functions or expressions you are diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll index 7227e6e95133..f97646ca8330 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll @@ -1,6 +1,6 @@ /** * Provides a class for modeling `printf`-style formatting functions. To use - * this QL library, create a QL class extending `DataFlowFunction` with a + * this QL library, create a QL class extending `FormattingFunction` with a * characteristic predicate that selects the function or set of functions you * are modeling. Within that class, override the predicates provided by * `FormattingFunction` to match the flow within that function. @@ -44,7 +44,7 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction { /** Gets the position at which the format parameter occurs. */ abstract int getFormatParameterIndex(); - override string getCanonicalQLClass() { result = "FormattingFunction" } + override string getAPrimaryQlClass() { result = "FormattingFunction" } /** * Holds if this `FormattingFunction` is in a context that supports diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll index d0fbb50ebfa8..9804cd6aff75 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll @@ -10,7 +10,8 @@ private newtype TFunctionInput = TInParameter(ParameterIndex i) or TInParameterDeref(ParameterIndex i) or TInQualifierObject() or - TInQualifierAddress() + TInQualifierAddress() or + TInReturnValueDeref() /** * An input to a function. This can be: @@ -106,8 +107,47 @@ class FunctionInput extends TFunctionInput { * (with type `C const *`) on entry to the function. */ predicate isQualifierAddress() { none() } + + /** + * Holds if this is the input value pointed to by the return value of a + * function, if the function returns a pointer, or the input value referred + * to by the return value of a function, if the function returns a reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `isReturnValueDeref()` holds for the `FunctionInput` that represents the + * value of `*getPointer()` (with type `char`). + * - `isReturnValueDeref()` holds for the `FunctionInput` that represents the + * value of `getReference()` (with type `float`). + * - There is no `FunctionInput` of `getInt()` for which + * `isReturnValueDeref()` holds because the return type of `getInt()` is + * neither a pointer nor a reference. + * + * Note that data flows in through function return values are relatively + * rare, but they do occur when a function returns a reference to itself, + * part of itself, or one of its other inputs. + */ + predicate isReturnValueDeref() { none() } } +/** + * The input value of a parameter. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - There is an `InParameter` representing the value of `n` (with type `int`) on entry to the + * function. + * - There is an `InParameter` representing the value of `p` (with type `char*`) on entry to the + * function. + * - There is an `InParameter` representing the "value" of the reference `r` (with type `float&`) on + * entry to the function, _not_ the value of the referred-to `float`. + */ class InParameter extends FunctionInput, TInParameter { ParameterIndex index; @@ -121,6 +161,21 @@ class InParameter extends FunctionInput, TInParameter { override predicate isParameter(ParameterIndex i) { i = index } } +/** + * The input value pointed to by a pointer parameter to a function, or the input value referred to + * by a reference parameter to a function. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - There is an `InParameterDeref` with `getIndex() = 1` that represents the value of `*p` (with + * type `char`) on entry to the function. + * - There is an `InParameterDeref` with `getIndex() = 2` that represents the value of `r` (with + * type `float`) on entry to the function. + * - There is no `InParameterDeref` representing the value of `n`, because `n` is neither a pointer + * nor a reference. + */ class InParameterDeref extends FunctionInput, TInParameterDeref { ParameterIndex index; @@ -134,18 +189,70 @@ class InParameterDeref extends FunctionInput, TInParameterDeref { override predicate isParameterDeref(ParameterIndex i) { i = index } } +/** + * The input value pointed to by the `this` pointer of an instance member function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `InQualifierObject` represents the value of `*this` (with type `C const`) on entry to the + * function. + */ class InQualifierObject extends FunctionInput, TInQualifierObject { override string toString() { result = "InQualifierObject" } override predicate isQualifierObject() { any() } } +/** + * The input value of the `this` pointer of an instance member function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `InQualifierAddress` represents the value of `this` (with type `C const *`) on entry to the + * function. + */ class InQualifierAddress extends FunctionInput, TInQualifierAddress { override string toString() { result = "InQualifierAddress" } override predicate isQualifierAddress() { any() } } +/** + * The input value pointed to by the return value of a function, if the + * function returns a pointer, or the input value referred to by the return + * value of a function, if the function returns a reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `InReturnValueDeref` represents the value of `*getPointer()` (with type + * `char`). + * - `InReturnValueDeref` represents the value of `getReference()` (with type + * `float`). + * - `InReturnValueDeref` does not represent the return value of `getInt()` + * because the return type of `getInt()` is neither a pointer nor a reference. + * + * Note that data flows in through function return values are relatively + * rare, but they do occur when a function returns a reference to itself, + * part of itself, or one of its other inputs. + */ +class InReturnValueDeref extends FunctionInput, TInReturnValueDeref { + override string toString() { result = "InReturnValueDeref" } + + override predicate isReturnValueDeref() { any() } +} + private newtype TFunctionOutput = TOutParameterDeref(ParameterIndex i) or TOutQualifierObject() or @@ -265,6 +372,21 @@ class FunctionOutput extends TFunctionOutput { deprecated final predicate isOutReturnPointer() { isReturnValueDeref() } } +/** + * The output value pointed to by a pointer parameter to a function, or the output value referred to + * by a reference parameter to a function. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - There is an `OutParameterDeref` with `getIndex()=1` that represents the value of `*p` (with + * type `char`) on return from the function. + * - There is an `OutParameterDeref` with `getIndex()=2` that represents the value of `r` (with + * type `float`) on return from the function. + * - There is no `OutParameterDeref` representing the value of `n`, because `n` is neither a + * pointer nor a reference. + */ class OutParameterDeref extends FunctionOutput, TOutParameterDeref { ParameterIndex index; @@ -277,18 +399,62 @@ class OutParameterDeref extends FunctionOutput, TOutParameterDeref { override predicate isParameterDeref(ParameterIndex i) { i = index } } +/** + * The output value pointed to by the `this` pointer of an instance member function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r); + * }; + * ``` + * - The `OutQualifierObject` represents the value of `*this` (with type `C`) on return from the + * function. + */ class OutQualifierObject extends FunctionOutput, TOutQualifierObject { override string toString() { result = "OutQualifierObject" } override predicate isQualifierObject() { any() } } +/** + * The value returned by a function. + * + * Example: + * ``` + * int getInt(); + * char* getPointer(); + * float& getReference(); + * ``` + * - `OutReturnValue` represents the value returned by + * `getInt()` (with type `int`). + * - `OutReturnValue` represents the value returned by + * `getPointer()` (with type `char*`). + * - `OutReturnValue` represents the "value" of the reference returned by `getReference()` (with + * type `float&`), _not_ the value of the referred-to `float`. + */ class OutReturnValue extends FunctionOutput, TOutReturnValue { override string toString() { result = "OutReturnValue" } override predicate isReturnValue() { any() } } +/** + * The output value pointed to by the return value of a function, if the function returns a pointer, + * or the output value referred to by the return value of a function, if the function returns a + * reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `OutReturnValueDeref` represents the value of `*getPointer()` (with type `char`). + * - `OutReturnValueDeref` represents the value of `getReference()` (with type `float`). + * - `OutReturnValueDeref` does not represent the return value of `getInt()` because the return type + * of `getInt()` is neither a pointer nor a reference. + */ class OutReturnValueDeref extends FunctionOutput, TOutReturnValueDeref { override string toString() { result = "OutReturnValueDeref" } diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll new file mode 100644 index 000000000000..de6977765253 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll @@ -0,0 +1,17 @@ +/** + * Provides an abstract class for accurate modeling of flow through output + * iterators. To use this QL library, create a QL class extending + * `IteratorReferenceFunction` with a characteristic predicate that selects the + * function or set of functions you are modeling. Within that class, override + * the predicates provided by `AliasFunction` to match the flow within that + * function. + */ + +import cpp +import semmle.code.cpp.models.Models + +/** + * A function which takes an iterator argument and returns a reference that + * can be used to write to the iterator's underlying collection. + */ +abstract class IteratorReferenceFunction extends Function { } diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll index c619f2efaa5c..fe617533f59d 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll @@ -24,5 +24,9 @@ import semmle.code.cpp.models.Models * data flow. */ abstract class TaintFunction extends Function { + /** + * Holds if data passed into the argument, qualifier, or buffer represented by + * `input` influences the return value or buffer represented by `output` + */ abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output); } diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll deleted file mode 100644 index 9e56794233fe..000000000000 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll +++ /dev/null @@ -1,82 +0,0 @@ -import cpp -private import semmle.code.cpp.ir.IR -private import semmle.code.cpp.ir.ValueNumbering - -private newtype TBound = - TBoundZero() or - TBoundValueNumber(ValueNumber vn) { - exists(Instruction i | - vn.getAnInstruction() = i and - ( - i.getResultType() instanceof IntegralType or - i.getResultType() instanceof PointerType - ) and - not vn.getAnInstruction() instanceof ConstantInstruction - | - i instanceof PhiInstruction - or - i instanceof InitializeParameterInstruction - or - i instanceof CallInstruction - or - i instanceof VariableAddressInstruction - or - i instanceof FieldAddressInstruction - or - i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction - or - i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction - or - i.getAUse() instanceof ArgumentOperand - ) - } - -/** - * A bound that may be inferred for an expression plus/minus an integer delta. - */ -abstract class Bound extends TBound { - abstract string toString(); - - /** Gets an expression that equals this bound plus `delta`. */ - abstract Instruction getInstruction(int delta); - - /** Gets an expression that equals this bound. */ - Instruction getInstruction() { result = getInstruction(0) } - - abstract Location getLocation(); -} - -/** - * The bound that corresponds to the integer 0. This is used to represent all - * integer bounds as bounds are always accompanied by an added integer delta. - */ -class ZeroBound extends Bound, TBoundZero { - override string toString() { result = "0" } - - override Instruction getInstruction(int delta) { - result.(ConstantValueInstruction).getValue().toInt() = delta - } - - override Location getLocation() { result instanceof UnknownDefaultLocation } -} - -/** - * A bound corresponding to the value of an `Instruction`. - */ -class ValueNumberBound extends Bound, TBoundValueNumber { - ValueNumber vn; - - ValueNumberBound() { this = TBoundValueNumber(vn) } - - /** Gets an `Instruction` that equals this bound. */ - override Instruction getInstruction(int delta) { - this = TBoundValueNumber(valueNumber(result)) and delta = 0 - } - - override string toString() { result = vn.getExampleInstruction().toString() } - - override Location getLocation() { result = vn.getLocation() } - - /** Gets the value number that equals this bound. */ - ValueNumber getValueNumber() { result = vn } -} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll index ea12434ac5bd..e402b672cbc2 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll @@ -1,3 +1,7 @@ +/** + * Provides classes and predicates for recognizing floating point expressions which cannot be NaN. + */ + import cpp private import semmle.code.cpp.rangeanalysis.RangeSSA diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll index 3c9177ad9db5..47289c7552b9 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll @@ -11,8 +11,17 @@ private float lowerBoundFC(Expr expr) { result = lowerBound(expr.getFullyConvert /** Gets the upper bound of the fully converted expression. */ private float upperBoundFC(Expr expr) { result = upperBound(expr.getFullyConverted()) } +/** + * Describes which side of a pointless comparison is known to be smaller. + */ newtype SmallSide = + /** + * Represents that the left side of a pointless comparison is known to be smaller. + */ LeftIsSmaller() or + /** + * Represents that the right side of a pointless comparison is known to be smaller. + */ RightIsSmaller() /** diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll deleted file mode 100644 index a79df3c37d75..000000000000 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll +++ /dev/null @@ -1,624 +0,0 @@ -/** - * Provides classes and predicates for range analysis. - * - * An inferred bound can either be a specific integer or a `ValueNumber` - * representing the abstract value of a set of `Instruction`s. - * - * If an inferred bound relies directly on a condition, then this condition is - * reported as the reason for the bound. - */ - -/* - * This library tackles range analysis as a flow problem. Consider e.g.: - * ``` - * len = arr.length; - * if (x < len) { ... y = x-1; ... y ... } - * ``` - * In this case we would like to infer `y <= arr.length - 2`, and this is - * accomplished by tracking the bound through a sequence of steps: - * ``` - * arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y - * ``` - * - * In its simplest form the step relation `I1 --> I2` relates two `Instruction`s - * such that `I1 <= B` implies `I2 <= B` for any `B` (with a second separate - * step relation handling lower bounds). Examples of such steps include - * assignments `I2 = I1` and conditions `x <= I1` where `I2` is a use of `x` - * guarded by the condition. - * - * In order to handle subtractions and additions with constants, and strict - * comparisons, the step relation is augmented with an integer delta. With this - * generalization `I1 --(delta)--> I2` relates two `Instruction`s and an integer - * such that `I1 <= B` implies `I2 <= B + delta` for any `B`. This corresponds - * to the predicate `boundFlowStep`. - * - * The complete range analysis is then implemented as the transitive closure of - * the step relation summing the deltas along the way. If `I1` transitively - * steps to `I2`, `delta` is the sum of deltas along the path, and `B` is an - * interesting bound equal to the value of `I1` then `I2 <= B + delta`. This - * corresponds to the predicate `boundedInstruction`. - * - * Bounds come in two forms: either they are relative to zero (and thus provide - * a constant bound), or they are relative to some program value. This value is - * represented by the `ValueNumber` class, each instance of which represents a - * set of `Instructions` that must have the same value. - * - * Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`. - * There are essentially two cases: - * - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`. - * - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`. - * The first case is for whenever a bound can be proven without taking looping - * into account. The second case is relevant when `x2` comes from a back-edge - * where we can prove that the variable has been non-increasing through the - * loop-iteration as this means that any upper bound that holds prior to the - * loop also holds for the variable during the loop. - * This generalizes to a phi node with `n` inputs, so if - * `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we - * also have `x0 <= B + delta` if we can prove either: - * - `xj <= B + d` with `d <= delta` or - * - `xj <= x0 + d` with `d <= 0` - * for each input `xj`. - * - * As all inferred bounds can be related directly to a path in the source code - * the only source of non-termination is if successive redundant (and thereby - * increasingly worse) bounds are calculated along a loop in the source code. - * We prevent this by weakening the bound to a small finite set of bounds when - * a path follows a second back-edge (we postpone weakening till the second - * back-edge as a precise bound might require traversing a loop once). - */ - -import cpp -private import semmle.code.cpp.ir.IR -private import semmle.code.cpp.controlflow.IRGuards -private import semmle.code.cpp.ir.ValueNumbering -private import RangeUtils -private import SignAnalysis -import Bound - -cached -private module RangeAnalysisCache { - cached - module RangeAnalysisPublic { - /** - * Holds if `b + delta` is a valid bound for `i` and this is the best such delta. - * - `upper = true` : `i <= b + delta` - * - `upper = false` : `i >= b + delta` - * - * The reason for the bound is given by `reason` and may be either a condition - * or `NoReason` if the bound was proven directly without the use of a bounding - * condition. - */ - cached - predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) { - boundedInstruction(i, b, delta, upper, _, _, reason) and - bestInstructionBound(i, b, delta, upper) - } - - /** - * Holds if `b + delta` is a valid bound for `op` and this is the best such delta. - * - `upper = true` : `op <= b + delta` - * - `upper = false` : `op >= b + delta` - * - * The reason for the bound is given by `reason` and may be either a condition - * or `NoReason` if the bound was proven directly without the use of a bounding - * condition. - */ - cached - predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) { - boundedOperandCand(op, b, delta, upper, reason) and - bestOperandBound(op, b, delta, upper) - } - } - - /** - * Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`. - */ - cached - predicate possibleReason(IRGuardCondition guard) { - guard = boundFlowCond(_, _, _, _, _) - or - guard = eqFlowCond(_, _, _, _, _) - } -} - -private import RangeAnalysisCache -import RangeAnalysisPublic - -/** - * Holds if `b + delta` is a valid bound for `e` and this is the best such delta. - * - `upper = true` : `e <= b + delta` - * - `upper = false` : `e >= b + delta` - */ -private predicate bestInstructionBound(Instruction i, Bound b, int delta, boolean upper) { - delta = min(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = true - or - delta = max(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = false -} - -/** - * Holds if `b + delta` is a valid bound for `op`. - * - `upper = true` : `op <= b + delta` - * - `upper = false` : `op >= b + delta` - * - * The reason for the bound is given by `reason` and may be either a condition - * or `NoReason` if the bound was proven directly without the use of a bounding - * condition. - */ -private predicate boundedOperandCand(Operand op, Bound b, int delta, boolean upper, Reason reason) { - boundedNonPhiOperand(op, b, delta, upper, _, _, reason) - or - boundedPhiOperand(op, b, delta, upper, _, _, reason) -} - -/** - * Holds if `b + delta` is a valid bound for `op` and this is the best such delta. - * - `upper = true` : `op <= b + delta` - * - `upper = false` : `op >= b + delta` - */ -private predicate bestOperandBound(Operand op, Bound b, int delta, boolean upper) { - delta = min(int d | boundedOperandCand(op, b, d, upper, _)) and upper = true - or - delta = max(int d | boundedOperandCand(op, b, d, upper, _)) and upper = false -} - -/** - * Gets a condition that tests whether `vn` equals `bound + delta`. - * - * If the condition evaluates to `testIsTrue`: - * - `isEq = true` : `vn == bound + delta` - * - `isEq = false` : `vn != bound + delta` - */ -private IRGuardCondition eqFlowCond( - ValueNumber vn, Operand bound, int delta, boolean isEq, boolean testIsTrue -) { - result.comparesEq(vn.getAUse(), bound, delta, isEq, testIsTrue) -} - -/** - * Holds if `op1 + delta` is a valid bound for `op2`. - * - `upper = true` : `op2 <= op1 + delta` - * - `upper = false` : `op2 >= op1 + delta` - */ -private predicate boundFlowStepSsa( - NonPhiOperand op2, Operand op1, int delta, boolean upper, Reason reason -) { - exists(IRGuardCondition guard, boolean testIsTrue | - guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and - guard.controls(op2.getUse().getBlock(), testIsTrue) and - reason = TCondReason(guard) - ) - or - exists(IRGuardCondition guard, boolean testIsTrue, SafeCastInstruction cast | - valueNumberOfOperand(op2) = valueNumber(cast.getUnary()) and - guard = boundFlowCond(valueNumber(cast), op1, delta, upper, testIsTrue) and - guard.controls(op2.getUse().getBlock(), testIsTrue) and - reason = TCondReason(guard) - ) -} - -/** - * Gets a condition that tests whether `vn` is bounded by `bound + delta`. - * - * If the condition evaluates to `testIsTrue`: - * - `upper = true` : `vn <= bound + delta` - * - `upper = false` : `vn >= bound + delta` - */ -private IRGuardCondition boundFlowCond( - ValueNumber vn, NonPhiOperand bound, int delta, boolean upper, boolean testIsTrue -) { - exists(int d | - result.comparesLt(vn.getAUse(), bound, d, upper, testIsTrue) and - // `comparesLt` provides bounds of the form `x < y + k` or `x >= y + k`, but we need - // `x <= y + k` so we strengthen here. `testIsTrue` has the same semantics in `comparesLt` as - // it does here, so we don't need to account for it. - if upper = true then delta = d - 1 else delta = d - ) - or - result = eqFlowCond(vn, bound, delta, true, testIsTrue) and - (upper = true or upper = false) -} - -private newtype TReason = - TNoReason() or - TCondReason(IRGuardCondition guard) { possibleReason(guard) } - -/** - * A reason for an inferred bound. This can either be `CondReason` if the bound - * is due to a specific condition, or `NoReason` if the bound is inferred - * without going through a bounding condition. - */ -abstract class Reason extends TReason { - abstract string toString(); -} - -class NoReason extends Reason, TNoReason { - override string toString() { result = "NoReason" } -} - -class CondReason extends Reason, TCondReason { - IRGuardCondition getCond() { this = TCondReason(result) } - - override string toString() { result = getCond().toString() } -} - -/** - * Holds if `typ` is a small integral type with the given lower and upper bounds. - */ -private predicate typeBound(IntegralType typ, int lowerbound, int upperbound) { - typ.isSigned() and typ.getSize() = 1 and lowerbound = -128 and upperbound = 127 - or - typ.isUnsigned() and typ.getSize() = 1 and lowerbound = 0 and upperbound = 255 - or - typ.isSigned() and typ.getSize() = 2 and lowerbound = -32768 and upperbound = 32767 - or - typ.isUnsigned() and typ.getSize() = 2 and lowerbound = 0 and upperbound = 65535 -} - -/** - * A cast to a small integral type that may overflow or underflow. - */ -private class NarrowingCastInstruction extends ConvertInstruction { - NarrowingCastInstruction() { - not this instanceof SafeCastInstruction and - typeBound(getResultType(), _, _) - } - - /** Gets the lower bound of the resulting type. */ - int getLowerBound() { typeBound(getResultType(), result, _) } - - /** Gets the upper bound of the resulting type. */ - int getUpperBound() { typeBound(getResultType(), _, result) } -} - -/** - * Holds if `op + delta` is a valid bound for `i`. - * - `upper = true` : `i <= op + delta` - * - `upper = false` : `i >= op + delta` - */ -private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, boolean upper) { - valueFlowStep(i, op, delta) and - (upper = true or upper = false) - or - i.(SafeCastInstruction).getAnOperand() = op and - delta = 0 and - (upper = true or upper = false) - or - exists(Operand x | - i.(AddInstruction).getAnOperand() = op and - i.(AddInstruction).getAnOperand() = x and - op != x - | - not exists(getValue(getConstantValue(op.getUse()))) and - not exists(getValue(getConstantValue(x.getUse()))) and - if strictlyPositive(x) - then upper = false and delta = 1 - else - if positive(x) - then upper = false and delta = 0 - else - if strictlyNegative(x) - then upper = true and delta = -1 - else - if negative(x) - then upper = true and delta = 0 - else none() - ) - or - exists(Operand x | - exists(SubInstruction sub | - i = sub and - sub.getLeftOperand() = op and - sub.getRightOperand() = x - ) - | - // `x` with constant value is covered by valueFlowStep - not exists(getValue(getConstantValue(x.getUse()))) and - if strictlyPositive(x) - then upper = true and delta = -1 - else - if positive(x) - then upper = true and delta = 0 - else - if strictlyNegative(x) - then upper = false and delta = 1 - else - if negative(x) - then upper = false and delta = 0 - else none() - ) - or - i.(RemInstruction).getRightOperand() = op and positive(op) and delta = -1 and upper = true - or - i.(RemInstruction).getLeftOperand() = op and positive(op) and delta = 0 and upper = true - or - i.(BitAndInstruction).getAnOperand() = op and positive(op) and delta = 0 and upper = true - or - i.(BitOrInstruction).getAnOperand() = op and - positiveInstruction(i) and - delta = 0 and - upper = false - // TODO: min, max, rand -} - -private predicate boundFlowStepMul(Instruction i1, Operand op, int factor) { - exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | - i1.(MulInstruction).hasOperands(op, c.getAUse()) and factor = k - or - exists(ShiftLeftInstruction i | - i = i1 and i.getLeftOperand() = op and i.getRightOperand() = c.getAUse() and factor = 2.pow(k) - ) - ) -} - -private predicate boundFlowStepDiv(Instruction i1, Operand op, int factor) { - exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | - exists(DivInstruction i | - i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = k - ) - or - exists(ShiftRightInstruction i | - i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = 2.pow(k) - ) - ) -} - -/** - * Holds if `b` is a valid bound for `op` - */ -pragma[noinline] -private predicate boundedNonPhiOperand( - NonPhiOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, - Reason reason -) { - exists(NonPhiOperand op2, int d1, int d2 | - boundFlowStepSsa(op, op2, d1, upper, reason) and - boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, _) and - delta = d1 + d2 - ) - or - boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) - or - exists(int d, Reason r1, Reason r2 | - boundedNonPhiOperand(op, b, d, upper, fromBackEdge, origdelta, r2) - | - unequalOperand(op, b, d, r1) and - ( - upper = true and delta = d - 1 - or - upper = false and delta = d + 1 - ) and - ( - reason = r1 - or - reason = r2 and not r2 instanceof NoReason - ) - ) -} - -/** - * Holds if `op1 + delta` is a valid bound for `op2`. - * - `upper = true` : `op2 <= op1 + delta` - * - `upper = false` : `op2 >= op1 + delta` - */ -private predicate boundFlowStepPhi( - PhiInputOperand op2, Operand op1, int delta, boolean upper, Reason reason -) { - op2.getDef().(CopyInstruction).getSourceValueOperand() = op1 and - (upper = true or upper = false) and - reason = TNoReason() and - delta = 0 - or - exists(IRGuardCondition guard, boolean testIsTrue | - guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and - guard.controlsEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and - reason = TCondReason(guard) - ) -} - -private predicate boundedPhiOperand( - PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, - Reason reason -) { - exists(NonPhiOperand op2, int d1, int d2, Reason r1, Reason r2 | - boundFlowStepPhi(op, op2, d1, upper, r1) and - boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, r2) and - delta = d1 + d2 and - (if r1 instanceof NoReason then reason = r2 else reason = r1) - ) - or - boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) - or - exists(int d, Reason r1, Reason r2 | - boundedInstruction(op.getDef(), b, d, upper, fromBackEdge, origdelta, r2) - | - unequalOperand(op, b, d, r1) and - ( - upper = true and delta = d - 1 - or - upper = false and delta = d + 1 - ) and - ( - reason = r1 - or - reason = r2 and not r2 instanceof NoReason - ) - ) -} - -/** Holds if `op2 != op1 + delta` at `pos`. */ -private predicate unequalFlowStep(Operand op2, Operand op1, int delta, Reason reason) { - exists(IRGuardCondition guard, boolean testIsTrue | - guard = eqFlowCond(valueNumberOfOperand(op2), op1, delta, false, testIsTrue) and - guard.controls(op2.getUse().getBlock(), testIsTrue) and - reason = TCondReason(guard) - ) -} - -/** - * Holds if `op != b + delta` at `pos`. - */ -private predicate unequalOperand(Operand op, Bound b, int delta, Reason reason) { - exists(Operand op2, int d1, int d2 | - unequalFlowStep(op, op2, d1, reason) and - boundedNonPhiOperand(op2, b, d2, true, _, _, _) and - boundedNonPhiOperand(op2, b, d2, false, _, _, _) and - delta = d1 + d2 - ) -} - -private predicate boundedPhiCandValidForEdge( - PhiInstruction phi, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, - Reason reason, PhiInputOperand op -) { - boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and - ( - exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = true and d <= delta) - or - exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = false and d >= delta) - or - selfBoundedPhiInp(phi, op, upper) - ) -} - -/** Weakens a delta to lie in the range `[-1..1]`. */ -bindingset[delta, upper] -private int weakenDelta(boolean upper, int delta) { - delta in [-1 .. 1] and result = delta - or - upper = true and result = -1 and delta < -1 - or - upper = false and result = 1 and delta > 1 -} - -private predicate boundedPhiInp( - PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, - int origdelta, Reason reason -) { - phi.getAnOperand() = op and - exists(int d, boolean fromBackEdge0 | - boundedPhiOperand(op, b, d, upper, fromBackEdge0, origdelta, reason) - or - b.(ValueNumberBound).getInstruction() = op.getDef() and - d = 0 and - (upper = true or upper = false) and - fromBackEdge0 = false and - origdelta = 0 and - reason = TNoReason() - | - if backEdge(phi, op) - then - fromBackEdge = true and - ( - fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta - or - fromBackEdge0 = false and delta = d - ) - else ( - delta = d and fromBackEdge = fromBackEdge0 - ) - ) -} - -pragma[noinline] -private predicate boundedPhiInp1( - PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper -) { - boundedPhiInp(phi, op, b, delta, upper, _, _, _) -} - -private predicate selfBoundedPhiInp(PhiInstruction phi, PhiInputOperand op, boolean upper) { - exists(int d, ValueNumberBound phibound | - phibound.getInstruction() = phi and - boundedPhiInp(phi, op, phibound, d, upper, _, _, _) and - ( - upper = true and d <= 0 - or - upper = false and d >= 0 - ) - ) -} - -pragma[noinline] -private predicate boundedPhiCand( - PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta, - Reason reason -) { - exists(PhiInputOperand op | - boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason) - ) -} - -/** - * Holds if the value being cast has an upper (for `upper = true`) or lower - * (for `upper = false`) bound within the bounds of the resulting type. - * For `upper = true` this means that the cast will not overflow and for - * `upper = false` this means that the cast will not underflow. - */ -private predicate safeNarrowingCast(NarrowingCastInstruction cast, boolean upper) { - exists(int bound | - boundedNonPhiOperand(cast.getAnOperand(), any(ZeroBound zb), bound, upper, _, _, _) - | - upper = true and bound <= cast.getUpperBound() - or - upper = false and bound >= cast.getLowerBound() - ) -} - -pragma[noinline] -private predicate boundedCastExpr( - NarrowingCastInstruction cast, Bound b, int delta, boolean upper, boolean fromBackEdge, - int origdelta, Reason reason -) { - boundedNonPhiOperand(cast.getAnOperand(), b, delta, upper, fromBackEdge, origdelta, reason) -} - -/** - * Holds if `b + delta` is a valid bound for `i`. - * - `upper = true` : `i <= b + delta` - * - `upper = false` : `i >= b + delta` - */ -private predicate boundedInstruction( - Instruction i, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, - Reason reason -) { - i instanceof PhiInstruction and - forex(PhiInputOperand op | op = i.getAnOperand() | - boundedPhiCandValidForEdge(i, b, delta, upper, fromBackEdge, origdelta, reason, op) - ) - or - i = b.getInstruction(delta) and - (upper = true or upper = false) and - fromBackEdge = false and - origdelta = delta and - reason = TNoReason() - or - exists(Operand mid, int d1, int d2 | - boundFlowStep(i, mid, d1, upper) and - boundedNonPhiOperand(mid, b, d2, upper, fromBackEdge, origdelta, reason) and - delta = d1 + d2 and - not exists(getValue(getConstantValue(i))) - ) - or - exists(Operand mid, int factor, int d | - boundFlowStepMul(i, mid, factor) and - boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and - b instanceof ZeroBound and - delta = d * factor and - not exists(getValue(getConstantValue(i))) - ) - or - exists(Operand mid, int factor, int d | - boundFlowStepDiv(i, mid, factor) and - boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and - d >= 0 and - b instanceof ZeroBound and - delta = d / factor and - not exists(getValue(getConstantValue(i))) - ) - or - exists(NarrowingCastInstruction cast | - cast = i and - safeNarrowingCast(cast, upper.booleanNot()) and - boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason) - ) -} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll index 9e99fcb82041..410a39716dc3 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll @@ -5,7 +5,13 @@ import cpp * relation) or 'non-strict' (a `<=` or `>=` relation). */ newtype RelationStrictness = + /** + * Represents that a relation is 'strict' (that is, a `<` or `>` relation). + */ Strict() or + /** + * Represents that a relation is 'non-strict' (that is, a `<=` or `>=` relation) + */ Nonstrict() /** @@ -13,7 +19,13 @@ newtype RelationStrictness = * relation) or 'lesser' (a `<` or `<=` relation). */ newtype RelationDirection = + /** + * Represents that a relation is 'greater' (that is, a `>` or `>=` relation). + */ Greater() or + /** + * Represents that a relation is 'lesser' (that is, a `<` or `<=` relation). + */ Lesser() private RelationStrictness negateStrictness(RelationStrictness strict) { @@ -28,12 +40,18 @@ private RelationDirection negateDirection(RelationDirection dir) { dir = Lesser() and result = Greater() } +/** + * Holds if `dir` is `Greater` (that is, a `>` or `>=` relation) + */ boolean directionIsGreater(RelationDirection dir) { dir = Greater() and result = true or dir = Lesser() and result = false } +/** + * Holds if `dir` is `Lesser` (that is, a `<` or `<=` relation) + */ boolean directionIsLesser(RelationDirection dir) { dir = Greater() and result = false or @@ -153,6 +171,65 @@ predicate eqOpWithSwapAndNegate(EqualityOperation cmp, Expr a, Expr b, boolean i eqOpWithSwap(cmp, a, b, branch.booleanNot()) and isEQ = false } +/** + * Holds if `cmp` is an unconverted conversion of `a` to a Boolean that + * evalutes to `isEQ` iff `a` is 0. + * + * Note that `a` can be `cmp` itself or a conversion thereof. + */ +private predicate eqZero(Expr cmp, Expr a, boolean isEQ) { + // The `!a` expression tests `a` equal to zero when `a` is a number converted + // to a Boolean. + isEQ = true and + exists(Expr notOperand | notOperand = cmp.(NotExpr).getOperand().getFullyConverted() | + // In C++ code there will be a BoolConversion in `!myInt` + a = notOperand.(BoolConversion).getExpr() + or + // In C code there is no conversion since there was no bool type before C99 + a = notOperand and + not a instanceof BoolConversion // avoid overlap with the case above + ) + or + // The `(bool)a` expression tests `a` NOT equal to zero when `a` is a number + // converted to a Boolean. To avoid overlap with the case above, this case + // excludes conversions that are right below a `!`. + isEQ = false and + linearAccess(cmp, _, _, _) and + // This test for `isCondition` implies that `cmp` is unconverted and that the + // parent of `cfg` is not a `NotExpr` -- the CFG doesn't do branching from + // inside `NotExpr`. + cmp.isCondition() and + // The GNU two-operand conditional expression is not supported for the + // purpose of guards, but the value of the conditional expression itself is + // modeled in the range analysis. + not exists(ConditionalExpr cond | cmp = cond.getCondition() and cond.isTwoOperand()) and + ( + // In C++ code there will be a BoolConversion in `if (myInt)` + a = cmp.getFullyConverted().(BoolConversion).getExpr() + or + // In C code there is no conversion since there was no bool type before C99 + a = cmp.getFullyConverted() and + not a instanceof BoolConversion // avoid overlap with the case above + ) +} + +/** + * Holds if `branch` of `cmp` is taken when `a` compares `isEQ` to zero. + * + * Note that `a` can be `cmp` itself or a conversion thereof. + */ +predicate eqZeroWithNegate(Expr cmp, Expr a, boolean isEQ, boolean branch) { + // The comparison for _equality_ to zero is on the `true` branch when `cmp` + // compares equal to zero and on the `false` branch when `cmp` compares not + // equal to zero. + eqZero(cmp, a, branch) and isEQ = true + or + // The comparison for _inequality_ to zero is on the `false` branch when + // `cmp` compares equal to zero and on the `true` branch when `cmp` compares + // not equal to zero. + eqZero(cmp, a, branch.booleanNot()) and isEQ = false +} + /** * Holds if `expr` is equivalent to `p*v + q`, where `p` is a non-zero * number. This takes into account the associativity, commutativity and @@ -173,6 +250,8 @@ private predicate linearAccessImpl(Expr expr, VariableAccess v, float p, float q // Base case expr = v and p = 1.0 and q = 0.0 or + expr.(ReferenceDereferenceExpr).getExpr() = v and p = 1.0 and q = 0.0 + or // a+(p*v+b) == p*v + (a+b) exists(AddExpr addExpr, float a, float b | addExpr.getLeftOperand().isConstant() and @@ -331,13 +410,20 @@ private predicate typeBounds(ArithmeticType t, float lb, float ub) { t instanceof FloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0 } +private Type stripReference(Type t) { + if t instanceof ReferenceType then result = t.(ReferenceType).getBaseType() else result = t +} + +/** Gets the type used by range analysis for the given `StackVariable`. */ +Type getVariableRangeType(StackVariable v) { result = stripReference(v.getUnspecifiedType()) } + /** * Gets the lower bound for the unspecified type `t`. * * For example, if `t` is a signed 32-bit type then the result is * `-2^31`. */ -float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) } +float typeLowerBound(Type t) { typeBounds(stripReference(t), result, _) } /** * Gets the upper bound for the unspecified type `t`. @@ -345,7 +431,7 @@ float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) } * For example, if `t` is a signed 32-bit type then the result is * `2^31 - 1`. */ -float typeUpperBound(ArithmeticType t) { typeBounds(t, _, result) } +float typeUpperBound(Type t) { typeBounds(stripReference(t), _, result) } /** * Gets the minimum value that this expression could represent, based on diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll index 99e6539658d7..93dcf989590b 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll @@ -25,6 +25,10 @@ import semmle.code.cpp.controlflow.Dominance import semmle.code.cpp.controlflow.SSAUtils private import RangeAnalysisUtils +/** + * The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA. + * This class provides the range-analysis SSA logic. + */ library class RangeSSA extends SSAHelper { RangeSSA() { this = 1 } @@ -36,21 +40,20 @@ library class RangeSSA extends SSAHelper { } } -private predicate guard_defn( - VariableAccess v, ComparisonOperation guard, BasicBlock b, boolean branch -) { +private predicate guard_defn(VariableAccess v, Expr guard, BasicBlock b, boolean branch) { guardCondition(guard, v, branch) and guardSuccessor(guard, branch, b) } -private predicate guardCondition(ComparisonOperation guard, VariableAccess v, boolean branch) { +private predicate guardCondition(Expr guard, VariableAccess v, boolean branch) { exists(Expr lhs | linearAccess(lhs, v, _, _) | relOpWithSwapAndNegate(guard, lhs, _, _, _, branch) or - eqOpWithSwapAndNegate(guard, lhs, _, _, branch) + eqOpWithSwapAndNegate(guard, lhs, _, _, branch) or + eqZeroWithNegate(guard, lhs, _, branch) ) } -private predicate guardSuccessor(ComparisonOperation guard, boolean branch, BasicBlock succ) { +private predicate guardSuccessor(Expr guard, boolean branch, BasicBlock succ) { branch = true and succ = guard.getATrueSuccessor() or branch = false and succ = guard.getAFalseSuccessor() @@ -84,6 +87,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase { /** Gets the control flow node for this definition. */ ControlFlowNode getDefinition() { result = this } + /** Gets the basic block containing this definition. */ BasicBlock getBasicBlock() { result.contains(getDefinition()) } /** Whether this definition is a phi node for variable `v`. */ @@ -93,15 +97,17 @@ class RangeSsaDefinition extends ControlFlowNodeBase { * If this definition is a phi node corresponding to a guard, * then return the variable and the guard. */ - predicate isGuardPhi(VariableAccess v, ComparisonOperation guard, boolean branch) { + predicate isGuardPhi(VariableAccess v, Expr guard, boolean branch) { guard_defn(v, guard, this, branch) } + /** Gets the primary location of this definition. */ Location getLocation() { result = this.(ControlFlowNode).getLocation() } /** Whether this definition is from a parameter */ predicate definedByParameter(Parameter p) { this = p.getFunction().getEntryPoint() } + /** Gets a definition of `v` that is a phi input for this basic block. */ RangeSsaDefinition getAPhiInput(StackVariable v) { this.isPhiNode(v) and exists(BasicBlock pred | @@ -153,6 +159,9 @@ class RangeSsaDefinition extends ControlFlowNodeBase { ) } + /** + * Holds if this definition of the variable `v` reached the end of the basic block `b`. + */ predicate reachesEndOfBB(StackVariable v, BasicBlock b) { exists(RangeSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b)) } diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll deleted file mode 100644 index ee790404559e..000000000000 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll +++ /dev/null @@ -1,134 +0,0 @@ -import cpp -private import semmle.code.cpp.ir.IR -// TODO: move this dependency -import semmle.code.cpp.ir.internal.IntegerConstant - -// TODO: move this out of test code -language[monotonicAggregates] -IntValue getConstantValue(Instruction instr) { - result = instr.(IntegerConstantInstruction).getValue().toInt() - or - exists(BinaryInstruction binInstr, IntValue left, IntValue right | - binInstr = instr and - left = getConstantValue(binInstr.getLeft()) and - right = getConstantValue(binInstr.getRight()) and - ( - binInstr instanceof AddInstruction and result = add(left, right) - or - binInstr instanceof SubInstruction and result = sub(left, right) - or - binInstr instanceof MulInstruction and result = mul(left, right) - or - binInstr instanceof DivInstruction and result = div(left, right) - ) - ) - or - result = getConstantValue(instr.(CopyInstruction).getSourceValue()) - or - exists(PhiInstruction phi | - phi = instr and - result = - max(PhiInputOperand operand | - operand = phi.getAnOperand() - | - getConstantValue(operand.getDef()) - ) and - result = - min(PhiInputOperand operand | - operand = phi.getAnOperand() - | - getConstantValue(operand.getDef()) - ) - ) -} - -predicate valueFlowStep(Instruction i, Operand op, int delta) { - i.(CopyInstruction).getSourceValueOperand() = op and delta = 0 - or - exists(Operand x | - i.(AddInstruction).getAnOperand() = op and - i.(AddInstruction).getAnOperand() = x and - op != x - | - delta = getValue(getConstantValue(x.getDef())) - ) - or - exists(Operand x | - i.(SubInstruction).getLeftOperand() = op and - i.(SubInstruction).getRightOperand() = x - | - delta = -getValue(getConstantValue(x.getDef())) - ) - or - exists(Operand x | - i.(PointerAddInstruction).getAnOperand() = op and - i.(PointerAddInstruction).getAnOperand() = x and - op != x - | - delta = i.(PointerAddInstruction).getElementSize() * getValue(getConstantValue(x.getDef())) - ) - or - exists(Operand x | - i.(PointerSubInstruction).getLeftOperand() = op and - i.(PointerSubInstruction).getRightOperand() = x - | - delta = i.(PointerSubInstruction).getElementSize() * -getValue(getConstantValue(x.getDef())) - ) -} - -predicate backEdge(PhiInstruction phi, PhiInputOperand op) { - phi.getAnOperand() = op and - phi.getBlock() = op.getPredecessorBlock().getBackEdgeSuccessor(_) -} - -/** - * Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of - * range analysis. - */ -pragma[inline] -private predicate safeCast(IntegralType fromtyp, IntegralType totyp) { - fromtyp.getSize() < totyp.getSize() and - ( - fromtyp.isUnsigned() - or - totyp.isSigned() - ) - or - fromtyp.getSize() <= totyp.getSize() and - ( - fromtyp.isSigned() and - totyp.isSigned() - or - fromtyp.isUnsigned() and - totyp.isUnsigned() - ) -} - -/** - * A `ConvertInstruction` which casts from one pointer type to another. - */ -class PtrToPtrCastInstruction extends ConvertInstruction { - PtrToPtrCastInstruction() { - getResultType() instanceof PointerType and - getUnary().getResultType() instanceof PointerType - } -} - -/** - * A `ConvertInstruction` which casts from one integer type to another in a way - * that cannot overflow or underflow. - */ -class SafeIntCastInstruction extends ConvertInstruction { - SafeIntCastInstruction() { safeCast(getUnary().getResultType(), getResultType()) } -} - -/** - * A `ConvertInstruction` which does not invalidate bounds determined by - * range analysis. - */ -class SafeCastInstruction extends ConvertInstruction { - SafeCastInstruction() { - this instanceof PtrToPtrCastInstruction or - this instanceof SafeIntCastInstruction - } -} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll deleted file mode 100644 index 37e2ac463861..000000000000 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll +++ /dev/null @@ -1,579 +0,0 @@ -/** - * Provides sign analysis to determine whether expression are always positive - * or negative. - * - * The analysis is implemented as an abstract interpretation over the - * three-valued domain `{negative, zero, positive}`. - */ - -import cpp -private import semmle.code.cpp.ir.IR -private import semmle.code.cpp.controlflow.IRGuards -private import semmle.code.cpp.ir.ValueNumbering -private import SignAnalysisCached - -private newtype TSign = - TNeg() or - TZero() or - TPos() - -private class Sign extends TSign { - string toString() { - result = "-" and this = TNeg() - or - result = "0" and this = TZero() - or - result = "+" and this = TPos() - } - - Sign inc() { - this = TNeg() and result = TNeg() - or - this = TNeg() and result = TZero() - or - this = TZero() and result = TPos() - or - this = TPos() and result = TPos() - } - - Sign dec() { result.inc() = this } - - Sign neg() { - this = TNeg() and result = TPos() - or - this = TZero() and result = TZero() - or - this = TPos() and result = TNeg() - } - - Sign bitnot() { - this = TNeg() and result = TPos() - or - this = TNeg() and result = TZero() - or - this = TZero() and result = TNeg() - or - this = TPos() and result = TNeg() - } - - Sign add(Sign s) { - this = TZero() and result = s - or - s = TZero() and result = this - or - this = s and this = result - or - this = TPos() and s = TNeg() - or - this = TNeg() and s = TPos() - } - - Sign mul(Sign s) { - result = TZero() and this = TZero() - or - result = TZero() and s = TZero() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign div(Sign s) { - result = TZero() and s = TNeg() - or - result = TZero() and s = TPos() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign rem(Sign s) { - result = TZero() and s = TNeg() - or - result = TZero() and s = TPos() - or - result = this and s = TNeg() - or - result = this and s = TPos() - } - - Sign bitand(Sign s) { - result = TZero() and this = TZero() - or - result = TZero() and s = TZero() - or - result = TZero() and this = TPos() - or - result = TZero() and s = TPos() - or - result = TNeg() and this = TNeg() and s = TNeg() - or - result = TPos() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TNeg() - or - result = TPos() and this = TPos() and s = TPos() - } - - Sign bitor(Sign s) { - result = TZero() and this = TZero() and s = TZero() - or - result = TNeg() and this = TNeg() - or - result = TNeg() and s = TNeg() - or - result = TPos() and this = TPos() and s = TZero() - or - result = TPos() and this = TZero() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - } - - Sign bitxor(Sign s) { - result = TZero() and this = s - or - result = this and s = TZero() - or - result = s and this = TZero() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign lshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - this != TZero() and s != TZero() - } - - Sign rshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - result = TNeg() and this = TNeg() - or - result != TNeg() and this = TPos() and s != TZero() - } - - Sign urshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - result != TZero() and this = TNeg() and s != TZero() - or - result != TNeg() and this = TPos() and s != TZero() - } -} - -private Sign certainInstructionSign(Instruction inst) { - exists(int i | inst.(IntegerConstantInstruction).getValue().toInt() = i | - i < 0 and result = TNeg() - or - i = 0 and result = TZero() - or - i > 0 and result = TPos() - ) - or - exists(float f | f = inst.(FloatConstantInstruction).getValue().toFloat() | - f < 0 and result = TNeg() - or - f = 0 and result = TZero() - or - f > 0 and result = TPos() - ) -} - -private newtype CastKind = - TWiden() or - TSame() or - TNarrow() - -private CastKind getCastKind(ConvertInstruction ci) { - exists(int fromSize, int toSize | - toSize = ci.getResultSize() and - fromSize = ci.getUnary().getResultSize() - | - fromSize < toSize and - result = TWiden() - or - fromSize = toSize and - result = TSame() - or - fromSize > toSize and - result = TNarrow() - ) -} - -private predicate bindBool(boolean bool) { - bool = true or - bool = false -} - -private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck) { - result = TZero() and - ( - bindBool(fromSigned) and - bindBool(toSigned) and - s = TZero() - or - bindBool(fromSigned) and - bindBool(toSigned) and - ck = TNarrow() - ) - or - result = TPos() and - ( - bindBool(fromSigned) and - bindBool(toSigned) and - s = TPos() - or - bindBool(fromSigned) and - bindBool(toSigned) and - s = TNeg() and - ck = TNarrow() - or - fromSigned = true and - toSigned = false and - s = TNeg() - ) - or - result = TNeg() and - ( - fromSigned = true and - toSigned = true and - s = TNeg() - or - fromSigned = false and - toSigned = true and - s = TPos() and - ck != TWiden() - ) -} - -/** Holds if the sign of `e` is too complicated to determine. */ -private predicate unknownSign(Instruction i) { - // REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than - // the ones we don't understand. Currently, if we try to compute the sign of an instruction that - // we don't understand, and it isn't on this list, we incorrectly compute the sign as "none" - // instead of "+,0,-". - // Even better, we could track the state of each instruction as a power set of {non-negative, - // non-positive, non-zero}, which would mean that the representation of the sign of an unknown - // value would be the empty set. - ( - i instanceof UninitializedInstruction - or - i instanceof InitializeParameterInstruction - or - i instanceof BuiltInOperationInstruction - or - i instanceof CallInstruction - or - i instanceof ChiInstruction - ) -} - -/** - * Holds if `lowerbound` is a lower bound for `bounded`. This is restricted - * to only include bounds for which we might determine a sign. - */ -private predicate lowerBound( - IRGuardCondition comp, Operand lowerbound, Operand bounded, boolean isStrict -) { - exists(int adjustment, Operand compared | - valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and - ( - isStrict = true and - adjustment = 0 - or - isStrict = false and - adjustment = 1 - ) and - comp.ensuresLt(lowerbound, compared, adjustment, bounded.getUse().getBlock(), true) - ) -} - -/** - * Holds if `upperbound` is an upper bound for `bounded` at `pos`. This is restricted - * to only include bounds for which we might determine a sign. - */ -private predicate upperBound( - IRGuardCondition comp, Operand upperbound, Operand bounded, boolean isStrict -) { - exists(int adjustment, Operand compared | - valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and - ( - isStrict = true and - adjustment = 0 - or - isStrict = false and - adjustment = 1 - ) and - comp.ensuresLt(compared, upperbound, adjustment, bounded.getUse().getBlock(), true) - ) -} - -/** - * Holds if `eqbound` is an equality/inequality for `bounded` at `pos`. This is - * restricted to only include bounds for which we might determine a sign. The - * boolean `isEq` gives the polarity: - * - `isEq = true` : `bounded = eqbound` - * - `isEq = false` : `bounded != eqbound` - */ -private predicate eqBound(IRGuardCondition guard, Operand eqbound, Operand bounded, boolean isEq) { - exists(Operand compared | - valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and - guard.ensuresEq(compared, eqbound, 0, bounded.getUse().getBlock(), isEq) - ) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in - * order for `v` to be positive. - */ -private predicate posBound(IRGuardCondition comp, Operand bound, Operand op) { - upperBound(comp, bound, op, _) or - eqBound(comp, bound, op, true) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in - * order for `v` to be negative. - */ -private predicate negBound(IRGuardCondition comp, Operand bound, Operand op) { - lowerBound(comp, bound, op, _) or - eqBound(comp, bound, op, true) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` - * can be zero. - */ -private predicate zeroBound(IRGuardCondition comp, Operand bound, Operand op) { - lowerBound(comp, bound, op, _) or - upperBound(comp, bound, op, _) or - eqBound(comp, bound, op, _) -} - -/** Holds if `bound` allows `v` to be positive at `pos`. */ -private predicate posBoundOk(IRGuardCondition comp, Operand bound, Operand op) { - posBound(comp, bound, op) and TPos() = operandSign(bound) -} - -/** Holds if `bound` allows `v` to be negative at `pos`. */ -private predicate negBoundOk(IRGuardCondition comp, Operand bound, Operand op) { - negBound(comp, bound, op) and TNeg() = operandSign(bound) -} - -/** Holds if `bound` allows `v` to be zero at `pos`. */ -private predicate zeroBoundOk(IRGuardCondition comp, Operand bound, Operand op) { - lowerBound(comp, bound, op, _) and TNeg() = operandSign(bound) - or - lowerBound(comp, bound, op, false) and TZero() = operandSign(bound) - or - upperBound(comp, bound, op, _) and TPos() = operandSign(bound) - or - upperBound(comp, bound, op, false) and TZero() = operandSign(bound) - or - eqBound(comp, bound, op, true) and TZero() = operandSign(bound) - or - eqBound(comp, bound, op, false) and TZero() != operandSign(bound) -} - -private Sign binaryOpLhsSign(BinaryInstruction i) { result = operandSign(i.getLeftOperand()) } - -private Sign binaryOpRhsSign(BinaryInstruction i) { result = operandSign(i.getRightOperand()) } - -pragma[noinline] -private predicate binaryOpSigns(BinaryInstruction i, Sign lhs, Sign rhs) { - lhs = binaryOpLhsSign(i) and - rhs = binaryOpRhsSign(i) -} - -private Sign unguardedOperandSign(Operand operand) { - result = instructionSign(operand.getDef()) and - not hasGuard(operand, result) -} - -private Sign guardedOperandSign(Operand operand) { - result = instructionSign(operand.getDef()) and - hasGuard(operand, result) -} - -private Sign guardedOperandSignOk(Operand operand) { - result = TPos() and - forex(IRGuardCondition guard, Operand bound | posBound(guard, bound, operand) | - posBoundOk(guard, bound, operand) - ) - or - result = TNeg() and - forex(IRGuardCondition guard, Operand bound | negBound(guard, bound, operand) | - negBoundOk(guard, bound, operand) - ) - or - result = TZero() and - forex(IRGuardCondition guard, Operand bound | zeroBound(guard, bound, operand) | - zeroBoundOk(guard, bound, operand) - ) -} - -/** - * Holds if there is a bound that might restrict whether `v` has the sign `s` - * at `pos`. - */ -private predicate hasGuard(Operand op, Sign s) { - s = TPos() and posBound(_, _, op) - or - s = TNeg() and negBound(_, _, op) - or - s = TZero() and zeroBound(_, _, op) -} - -cached -module SignAnalysisCached { - /** - * Gets a sign that `operand` may have at `pos`, taking guards into account. - */ - cached - Sign operandSign(Operand operand) { - result = unguardedOperandSign(operand) - or - result = guardedOperandSign(operand) and - result = guardedOperandSignOk(operand) - or - // `result` is unconstrained if the definition is inexact. Then any sign is possible. - operand.isDefinitionInexact() - } - - cached - Sign instructionSign(Instruction i) { - result = certainInstructionSign(i) - or - not exists(certainInstructionSign(i)) and - not ( - result = TNeg() and - i.getResultType().(IntegralType).isUnsigned() - ) and - ( - unknownSign(i) - or - exists(ConvertInstruction ci, Instruction prior, boolean fromSigned, boolean toSigned | - i = ci and - prior = ci.getUnary() and - (if ci.getResultType().(IntegralType).isSigned() then toSigned = true else toSigned = false) and - ( - if prior.getResultType().(IntegralType).isSigned() - then fromSigned = true - else fromSigned = false - ) and - result = castSign(operandSign(ci.getAnOperand()), fromSigned, toSigned, getCastKind(ci)) - ) - or - result = operandSign(i.(CopyInstruction).getSourceValueOperand()) - or - result = operandSign(i.(BitComplementInstruction).getAnOperand()).bitnot() - or - result = operandSign(i.(NegateInstruction).getAnOperand()).neg() - or - exists(Sign s1, Sign s2 | binaryOpSigns(i, s1, s2) | - i instanceof AddInstruction and result = s1.add(s2) - or - i instanceof SubInstruction and result = s1.add(s2.neg()) - or - i instanceof MulInstruction and result = s1.mul(s2) - or - i instanceof DivInstruction and result = s1.div(s2) - or - i instanceof RemInstruction and result = s1.rem(s2) - or - i instanceof BitAndInstruction and result = s1.bitand(s2) - or - i instanceof BitOrInstruction and result = s1.bitor(s2) - or - i instanceof BitXorInstruction and result = s1.bitxor(s2) - or - i instanceof ShiftLeftInstruction and result = s1.lshift(s2) - or - i instanceof ShiftRightInstruction and - i.getResultType().(IntegralType).isSigned() and - result = s1.rshift(s2) - or - i instanceof ShiftRightInstruction and - not i.getResultType().(IntegralType).isSigned() and - result = s1.urshift(s2) - ) - or - // use hasGuard here? - result = operandSign(i.(PhiInstruction).getAnOperand()) - ) - } -} - -/** Holds if `i` can be positive and cannot be negative. */ -predicate positiveInstruction(Instruction i) { - instructionSign(i) = TPos() and - not instructionSign(i) = TNeg() -} - -/** Holds if `i` at `pos` can be positive at and cannot be negative. */ -predicate positive(Operand op) { - operandSign(op) = TPos() and - not operandSign(op) = TNeg() -} - -/** Holds if `i` can be negative and cannot be positive. */ -predicate negativeInstruction(Instruction i) { - instructionSign(i) = TNeg() and - not instructionSign(i) = TPos() -} - -/** Holds if `i` at `pos` can be negative and cannot be positive. */ -predicate negative(Operand op) { - operandSign(op) = TNeg() and - not operandSign(op) = TPos() -} - -/** Holds if `i` is strictly positive. */ -predicate strictlyPositiveInstruction(Instruction i) { - instructionSign(i) = TPos() and - not instructionSign(i) = TNeg() and - not instructionSign(i) = TZero() -} - -/** Holds if `i` is strictly positive at `pos`. */ -predicate strictlyPositive(Operand op) { - operandSign(op) = TPos() and - not operandSign(op) = TNeg() and - not operandSign(op) = TZero() -} - -/** Holds if `i` is strictly negative. */ -predicate strictlyNegativeInstruction(Instruction i) { - instructionSign(i) = TNeg() and - not instructionSign(i) = TPos() and - not instructionSign(i) = TZero() -} - -/** Holds if `i` is strictly negative at `pos`. */ -predicate strictlyNegative(Operand op) { - operandSign(op) = TNeg() and - not operandSign(op) = TPos() and - not operandSign(op) = TZero() -} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index 867d96b804c6..fcaec3d3e13b 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -44,6 +44,8 @@ import cpp private import RangeAnalysisUtils +private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr +private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition import RangeSSA import SimpleRangeAnalysisCached private import NanAnalysis @@ -126,7 +128,7 @@ private class UnsignedBitwiseAndExpr extends BitwiseAndExpr { UnsignedBitwiseAndExpr() { ( getLeftOperand().getFullyConverted().getType().getUnderlyingType().(IntegralType).isUnsigned() or - getLeftOperand().getFullyConverted().getValue().toInt() >= 0 + getValue(getLeftOperand().getFullyConverted()).toInt() >= 0 ) and ( getRightOperand() @@ -135,7 +137,7 @@ private class UnsignedBitwiseAndExpr extends BitwiseAndExpr { .getUnderlyingType() .(IntegralType) .isUnsigned() or - getRightOperand().getFullyConverted().getValue().toInt() >= 0 + getValue(getRightOperand().getFullyConverted()).toInt() >= 0 ) } } @@ -156,6 +158,92 @@ float safeFloor(float v) { result = v } +/** A `MulExpr` where exactly one operand is constant. */ +private class MulByConstantExpr extends MulExpr { + float constant; + Expr operand; + + MulByConstantExpr() { + exists(Expr constantExpr | + this.hasOperands(constantExpr, operand) and + constant = getValue(constantExpr.getFullyConverted()).toFloat() and + not exists(getValue(operand.getFullyConverted()).toFloat()) + ) + } + + /** Gets the value of the constant operand. */ + float getConstant() { result = constant } + + /** Gets the non-constant operand. */ + Expr getOperand() { result = operand } +} + +private class UnsignedMulExpr extends MulExpr { + UnsignedMulExpr() { + this.getType().(IntegralType).isUnsigned() and + // Avoid overlap. It should be slightly cheaper to analyze + // `MulByConstantExpr`. + not this instanceof MulByConstantExpr + } +} + +/** + * Holds if `expr` is effectively a multiplication of `operand` with the + * positive constant `positive`. + */ +private predicate effectivelyMultipliesByPositive(Expr expr, Expr operand, float positive) { + operand = expr.(MulByConstantExpr).getOperand() and + positive = expr.(MulByConstantExpr).getConstant() and + positive >= 0.0 // includes positive zero + or + operand = expr.(UnaryPlusExpr).getOperand() and + positive = 1.0 + or + operand = expr.(CommaExpr).getRightOperand() and + positive = 1.0 + or + operand = expr.(StmtExpr).getResultExpr() and + positive = 1.0 +} + +/** + * Holds if `expr` is effectively a multiplication of `operand` with the + * negative constant `negative`. + */ +private predicate effectivelyMultipliesByNegative(Expr expr, Expr operand, float negative) { + operand = expr.(MulByConstantExpr).getOperand() and + negative = expr.(MulByConstantExpr).getConstant() and + negative < 0.0 // includes negative zero + or + operand = expr.(UnaryMinusExpr).getOperand() and + negative = -1.0 +} + +private class AssignMulByConstantExpr extends AssignMulExpr { + float constant; + + AssignMulByConstantExpr() { constant = getValue(this.getRValue().getFullyConverted()).toFloat() } + + float getConstant() { result = constant } +} + +private class AssignMulByPositiveConstantExpr extends AssignMulByConstantExpr { + AssignMulByPositiveConstantExpr() { constant >= 0.0 } +} + +private class AssignMulByNegativeConstantExpr extends AssignMulByConstantExpr { + AssignMulByNegativeConstantExpr() { constant < 0.0 } +} + +private class UnsignedAssignMulExpr extends AssignMulExpr { + UnsignedAssignMulExpr() { + this.getType().(IntegralType).isUnsigned() and + // Avoid overlap. It should be slightly cheaper to analyze + // `AssignMulByConstantExpr`. + not this instanceof AssignMulByConstantExpr + } +} + /** Set of expressions which we know how to analyze. */ private predicate analyzableExpr(Expr e) { // The type of the expression must be arithmetic. We reuse the logic in @@ -164,9 +252,9 @@ private predicate analyzableExpr(Expr e) { ( exists(getValue(e).toFloat()) or - e instanceof UnaryPlusExpr + effectivelyMultipliesByPositive(e, _, _) or - e instanceof UnaryMinusExpr + effectivelyMultipliesByNegative(e, _, _) or e instanceof MinExpr or @@ -178,19 +266,21 @@ private predicate analyzableExpr(Expr e) { or e instanceof SubExpr or + e instanceof UnsignedMulExpr + or e instanceof AssignExpr or e instanceof AssignAddExpr or e instanceof AssignSubExpr or - e instanceof CrementOperation + e instanceof UnsignedAssignMulExpr or - e instanceof RemExpr + e instanceof AssignMulByConstantExpr or - e instanceof CommaExpr + e instanceof CrementOperation or - e instanceof StmtExpr + e instanceof RemExpr or // A conversion is analyzable, provided that its child has an arithmetic // type. (Sometimes the child is a reference type, and so does not get @@ -206,7 +296,10 @@ private predicate analyzableExpr(Expr e) { e instanceof UnsignedBitwiseAndExpr or // `>>` by a constant - exists(e.(RShiftExpr).getRightOperand().getValue()) + exists(getValue(e.(RShiftExpr).getRightOperand())) + or + // A modeled expression for range analysis + e instanceof SimpleRangeAnalysisExpr ) } @@ -227,30 +320,27 @@ private predicate defDependsOnDef( // Definitions with a defining value. exists(Expr expr | assignmentDef(def, v, expr) | exprDependsOnDef(expr, srcDef, srcVar)) or - exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef | - def = assignAdd and - assignAdd.getLValue() = nextDef.getAUse(v) - | - defDependsOnDef(nextDef, v, srcDef, srcVar) or - exprDependsOnDef(assignAdd.getRValue(), srcDef, srcVar) - ) - or - exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef | - def = assignSub and - assignSub.getLValue() = nextDef.getAUse(v) - | - defDependsOnDef(nextDef, v, srcDef, srcVar) or - exprDependsOnDef(assignSub.getRValue(), srcDef, srcVar) + // Assignment operations with a defining value + exists(AssignOperation assignOp | + analyzableExpr(assignOp) and + def = assignOp and + def.getAVariable() = v and + exprDependsOnDef(assignOp, srcDef, srcVar) ) or exists(CrementOperation crem | def = crem and - crem.getOperand() = v.getAnAccess() and + def.getAVariable() = v and exprDependsOnDef(crem.getOperand(), srcDef, srcVar) ) or // Phi nodes. phiDependsOnDef(def, v, srcDef, srcVar) + or + // Extensions + exists(Expr expr | def.(SimpleRangeAnalysisDefinition).dependsOnExpr(v, expr) | + exprDependsOnDef(expr, srcDef, srcVar) + ) } /** @@ -258,12 +348,14 @@ private predicate defDependsOnDef( * the structure of `getLowerBoundsImpl` and `getUpperBoundsImpl`. */ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVariable srcVar) { - exists(UnaryMinusExpr negateExpr | e = negateExpr | - exprDependsOnDef(negateExpr.getOperand(), srcDef, srcVar) + exists(Expr operand | + effectivelyMultipliesByNegative(e, operand, _) and + exprDependsOnDef(operand, srcDef, srcVar) ) or - exists(UnaryPlusExpr plusExpr | e = plusExpr | - exprDependsOnDef(plusExpr.getOperand(), srcDef, srcVar) + exists(Expr operand | + effectivelyMultipliesByPositive(e, operand, _) and + exprDependsOnDef(operand, srcDef, srcVar) ) or exists(MinExpr minExpr | e = minExpr | exprDependsOnDef(minExpr.getAnOperand(), srcDef, srcVar)) @@ -278,6 +370,10 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria or exists(SubExpr subExpr | e = subExpr | exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar)) or + exists(UnsignedMulExpr mulExpr | e = mulExpr | + exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar) + ) + or exists(AssignExpr addExpr | e = addExpr | exprDependsOnDef(addExpr.getRValue(), srcDef, srcVar)) or exists(AssignAddExpr addExpr | e = addExpr | @@ -288,20 +384,20 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar) ) or - exists(CrementOperation crementExpr | e = crementExpr | - exprDependsOnDef(crementExpr.getOperand(), srcDef, srcVar) + exists(UnsignedAssignMulExpr mulExpr | e = mulExpr | + exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar) ) or - exists(RemExpr remExpr | e = remExpr | exprDependsOnDef(remExpr.getAnOperand(), srcDef, srcVar)) - or - exists(CommaExpr commaExpr | e = commaExpr | - exprDependsOnDef(commaExpr.getRightOperand(), srcDef, srcVar) + exists(AssignMulByConstantExpr mulExpr | e = mulExpr | + exprDependsOnDef(mulExpr.getLValue(), srcDef, srcVar) ) or - exists(StmtExpr stmtExpr | e = stmtExpr | - exprDependsOnDef(stmtExpr.getResultExpr(), srcDef, srcVar) + exists(CrementOperation crementExpr | e = crementExpr | + exprDependsOnDef(crementExpr.getOperand(), srcDef, srcVar) ) or + exists(RemExpr remExpr | e = remExpr | exprDependsOnDef(remExpr.getAnOperand(), srcDef, srcVar)) + or exists(Conversion convExpr | e = convExpr | exprDependsOnDef(convExpr.getExpr(), srcDef, srcVar)) or // unsigned `&` @@ -313,11 +409,21 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria // `>>` by a constant exists(RShiftExpr rs | rs = e and - exists(rs.getRightOperand().getValue()) and + exists(getValue(rs.getRightOperand())) and exprDependsOnDef(rs.getLeftOperand(), srcDef, srcVar) ) or e = srcDef.getAUse(srcVar) + or + // A modeled expression for range analysis + exists(SimpleRangeAnalysisExpr rae | rae = e | + rae.dependsOnDef(srcDef, srcVar) + or + exists(Expr child | + rae.dependsOnChild(child) and + exprDependsOnDef(child, srcDef, srcVar) + ) + ) } /** @@ -327,11 +433,11 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria private predicate phiDependsOnDef( RangeSsaDefinition phi, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar ) { - exists(VariableAccess access, ComparisonOperation guard | + exists(VariableAccess access, Expr guard | access = v.getAnAccess() and phi.isGuardPhi(access, guard, _) | - exprDependsOnDef(guard.getAnOperand(), srcDef, srcVar) or + exprDependsOnDef(guard.(ComparisonOperation).getAnOperand(), srcDef, srcVar) or exprDependsOnDef(access, srcDef, srcVar) ) or @@ -354,6 +460,39 @@ private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) { defDependsOnDefTransitively(def, v, def, v) } +/** + * Holds if the bounds of `e` depend on a recursive definition, meaning that + * `e` is likely to have many candidate bounds during the main recursion. + */ +private predicate isRecursiveExpr(Expr e) { + exists(RangeSsaDefinition def, StackVariable v | exprDependsOnDef(e, def, v) | + isRecursiveDef(def, v) + ) +} + +/** + * Holds if `binop` is a binary operation that's likely to be assigned a + * quadratic (or more) number of candidate bounds during the analysis. This can + * happen when two conditions are satisfied: + * 1. It is likely there are many more candidate bounds for `binop` than for + * its operands. For example, the number of candidate bounds for `x + y`, + * denoted here nbounds(`x + y`), will be O(nbounds(`x`) * nbounds(`y`)). + * In contrast, nbounds(`b ? x : y`) is only O(nbounds(`x`) + nbounds(`y`)). + * 2. Both operands of `binop` are recursively determined and are therefore + * likely to have a large number of candidate bounds. + */ +private predicate isRecursiveBinary(BinaryOperation binop) { + ( + binop instanceof UnsignedMulExpr + or + binop instanceof AddExpr + or + binop instanceof SubExpr + ) and + isRecursiveExpr(binop.getLeftOperand()) and + isRecursiveExpr(binop.getRightOperand()) +} + /** * We distinguish 3 kinds of RangeSsaDefinition: * @@ -369,7 +508,7 @@ private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) { * This predicate finds all the definitions in the first set. */ private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr expr) { - v.getUnspecifiedType() instanceof ArithmeticType and + getVariableRangeType(v) instanceof ArithmeticType and ( def = v.getInitializer().getExpr() and def = expr or @@ -381,9 +520,20 @@ private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr ex ) } -/** See comment above sourceDef. */ +/** See comment above assignmentDef. */ private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) { - assignmentDef(def, v, _) or defDependsOnDef(def, v, _, _) + assignmentDef(def, v, _) + or + analyzableExpr(def.(AssignOperation)) and + v = def.getAVariable() + or + analyzableExpr(def.(CrementOperation)) and + v = def.getAVariable() + or + phiDependsOnDef(def, v, _, _) + or + // A modeled def for range analysis + def.(SimpleRangeAnalysisDefinition).hasRangeInformationFor(v) } /** @@ -435,13 +585,6 @@ private float addRoundingDownSmall(float x, float small) { if (x + small) - x > small then result = (x + small).nextDown() else result = (x + small) } -/** - * Gets the truncated lower bounds of the fully converted expression. - */ -private float getFullyConvertedLowerBounds(Expr expr) { - result = getTruncatedLowerBounds(expr.getFullyConverted()) -} - /** * Gets the lower bounds of the expression. * @@ -469,9 +612,18 @@ private float getTruncatedLowerBounds(Expr expr) { else ( // Some of the bounds computed by getLowerBoundsImpl might // overflow, so we replace invalid bounds with exprMinVal. - exists(float newLB | newLB = getLowerBoundsImpl(expr) | + exists(float newLB | newLB = normalizeFloatUp(getLowerBoundsImpl(expr)) | if exprMinVal(expr) <= newLB and newLB <= exprMaxVal(expr) - then result = newLB + then + // Apply widening where we might get a combinatorial explosion. + if isRecursiveBinary(expr) + then + result = + max(float widenLB | + widenLB = wideningLowerBounds(expr.getUnspecifiedType()) and + not widenLB > newLB + ) + else result = newLB else result = exprMinVal(expr) ) or @@ -488,13 +640,6 @@ private float getTruncatedLowerBounds(Expr expr) { result = exprMinVal(expr) } -/** - * Gets the truncated upper bounds of the fully converted expression. - */ -private float getFullyConvertedUpperBounds(Expr expr) { - result = getTruncatedUpperBounds(expr.getFullyConverted()) -} - /** * Gets the upper bounds of the expression. * @@ -523,9 +668,18 @@ private float getTruncatedUpperBounds(Expr expr) { // Some of the bounds computed by `getUpperBoundsImpl` // might overflow, so we replace invalid bounds with // `exprMaxVal`. - exists(float newUB | newUB = getUpperBoundsImpl(expr) | + exists(float newUB | newUB = normalizeFloatUp(getUpperBoundsImpl(expr)) | if exprMinVal(expr) <= newUB and newUB <= exprMaxVal(expr) - then result = newUB + then + // Apply widening where we might get a combinatorial explosion. + if isRecursiveBinary(expr) + then + result = + min(float widenUB | + widenUB = wideningUpperBounds(expr.getUnspecifiedType()) and + not widenUB < newUB + ) + else result = newUB else result = exprMaxVal(expr) ) or @@ -562,339 +716,393 @@ deprecated predicate positive_overflow(Expr expr) { exprMightOverflowPositively( /** Only to be called by `getTruncatedLowerBounds`. */ private float getLowerBoundsImpl(Expr expr) { - exists(UnaryPlusExpr plusExpr | - expr = plusExpr and - result = getFullyConvertedLowerBounds(plusExpr.getOperand()) - ) - or - exists(UnaryMinusExpr negateExpr, float xHigh | - expr = negateExpr and - xHigh = getFullyConvertedUpperBounds(negateExpr.getOperand()) and - result = -xHigh - ) - or - exists(MinExpr minExpr | - expr = minExpr and - // Return the union of the lower bounds from both children. - result = getFullyConvertedLowerBounds(minExpr.getAnOperand()) - ) - or - exists(MaxExpr maxExpr | - expr = maxExpr and - // Compute the cross product of the bounds from both children. We are - // using this mathematical property: - // - // max (minimum{X}, minimum{Y}) - // = minimum { max(x,y) | x in X, y in Y } - exists(float x, float y | - x = getFullyConvertedLowerBounds(maxExpr.getLeftOperand()) and - y = getFullyConvertedLowerBounds(maxExpr.getRightOperand()) and - if x >= y then result = x else result = y + ( + exists(Expr operand, float operandLow, float positive | + effectivelyMultipliesByPositive(expr, operand, positive) and + operandLow = getFullyConvertedLowerBounds(operand) and + result = positive * operandLow ) - ) - or - // ConditionalExpr (true branch) - exists(ConditionalExpr condExpr | - expr = condExpr and - // Use `boolConversionUpperBound` to determine whether the condition - // might evaluate to `true`. - boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and - result = getFullyConvertedLowerBounds(condExpr.getThen()) - ) - or - // ConditionalExpr (false branch) - exists(ConditionalExpr condExpr | - expr = condExpr and - // Use `boolConversionLowerBound` to determine whether the condition - // might evaluate to `false`. - boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and - result = getFullyConvertedLowerBounds(condExpr.getElse()) - ) - or - exists(AddExpr addExpr, float xLow, float yLow | - expr = addExpr and - xLow = getFullyConvertedLowerBounds(addExpr.getLeftOperand()) and - yLow = getFullyConvertedLowerBounds(addExpr.getRightOperand()) and - result = addRoundingDown(xLow, yLow) - ) - or - exists(SubExpr subExpr, float xLow, float yHigh | - expr = subExpr and - xLow = getFullyConvertedLowerBounds(subExpr.getLeftOperand()) and - yHigh = getFullyConvertedUpperBounds(subExpr.getRightOperand()) and - result = addRoundingDown(xLow, -yHigh) - ) - or - exists(AssignExpr assign | - expr = assign and - result = getFullyConvertedLowerBounds(assign.getRValue()) - ) - or - exists(AssignAddExpr addExpr, float xLow, float yLow | - expr = addExpr and - xLow = getFullyConvertedLowerBounds(addExpr.getLValue()) and - yLow = getFullyConvertedLowerBounds(addExpr.getRValue()) and - result = addRoundingDown(xLow, yLow) - ) - or - exists(AssignSubExpr subExpr, float xLow, float yHigh | - expr = subExpr and - xLow = getFullyConvertedLowerBounds(subExpr.getLValue()) and - yHigh = getFullyConvertedUpperBounds(subExpr.getRValue()) and - result = addRoundingDown(xLow, -yHigh) - ) - or - exists(PrefixIncrExpr incrExpr, float xLow | - expr = incrExpr and - xLow = getFullyConvertedLowerBounds(incrExpr.getOperand()) and - result = xLow + 1 - ) - or - exists(PrefixDecrExpr decrExpr, float xLow | - expr = decrExpr and - xLow = getFullyConvertedLowerBounds(decrExpr.getOperand()) and - result = addRoundingDownSmall(xLow, -1) - ) - or - // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their - // operand. The incrementing/decrementing behavior is handled in - // `getDefLowerBoundsImpl`. - exists(PostfixIncrExpr incrExpr | - expr = incrExpr and - result = getFullyConvertedLowerBounds(incrExpr.getOperand()) - ) - or - exists(PostfixDecrExpr decrExpr | - expr = decrExpr and - result = getFullyConvertedLowerBounds(decrExpr.getOperand()) - ) - or - exists(RemExpr remExpr | expr = remExpr | - // If both inputs are positive then the lower bound is zero. - result = 0 or - // If either input could be negative then the output could be - // negative. If so, the lower bound of `x%y` is `-abs(y)`, which is - // equal to `min(-y,y)`. - exists(float childLB | - childLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and - not childLB >= 0 - | - result = getFullyConvertedLowerBounds(remExpr.getRightOperand()) + exists(Expr operand, float operandHigh, float negative | + effectivelyMultipliesByNegative(expr, operand, negative) and + operandHigh = getFullyConvertedUpperBounds(operand) and + result = negative * operandHigh + ) + or + exists(MinExpr minExpr | + expr = minExpr and + // Return the union of the lower bounds from both children. + result = getFullyConvertedLowerBounds(minExpr.getAnOperand()) + ) + or + exists(MaxExpr maxExpr | + expr = maxExpr and + // Compute the cross product of the bounds from both children. We are + // using this mathematical property: + // + // max (minimum{X}, minimum{Y}) + // = minimum { max(x,y) | x in X, y in Y } + exists(float x, float y | + x = getFullyConvertedLowerBounds(maxExpr.getLeftOperand()) and + y = getFullyConvertedLowerBounds(maxExpr.getRightOperand()) and + if x >= y then result = x else result = y + ) + ) + or + // ConditionalExpr (true branch) + exists(ConditionalExpr condExpr | + expr = condExpr and + // Use `boolConversionUpperBound` to determine whether the condition + // might evaluate to `true`. + boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and + result = getFullyConvertedLowerBounds(condExpr.getThen()) + ) + or + // ConditionalExpr (false branch) + exists(ConditionalExpr condExpr | + expr = condExpr and + // Use `boolConversionLowerBound` to determine whether the condition + // might evaluate to `false`. + boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and + result = getFullyConvertedLowerBounds(condExpr.getElse()) + ) + or + exists(AddExpr addExpr, float xLow, float yLow | + expr = addExpr and + xLow = getFullyConvertedLowerBounds(addExpr.getLeftOperand()) and + yLow = getFullyConvertedLowerBounds(addExpr.getRightOperand()) and + result = addRoundingDown(xLow, yLow) + ) + or + exists(SubExpr subExpr, float xLow, float yHigh | + expr = subExpr and + xLow = getFullyConvertedLowerBounds(subExpr.getLeftOperand()) and + yHigh = getFullyConvertedUpperBounds(subExpr.getRightOperand()) and + result = addRoundingDown(xLow, -yHigh) + ) + or + exists(UnsignedMulExpr mulExpr, float xLow, float yLow | + expr = mulExpr and + xLow = getFullyConvertedLowerBounds(mulExpr.getLeftOperand()) and + yLow = getFullyConvertedLowerBounds(mulExpr.getRightOperand()) and + result = xLow * yLow + ) + or + exists(AssignExpr assign | + expr = assign and + result = getFullyConvertedLowerBounds(assign.getRValue()) + ) + or + exists(AssignAddExpr addExpr, float xLow, float yLow | + expr = addExpr and + xLow = getFullyConvertedLowerBounds(addExpr.getLValue()) and + yLow = getFullyConvertedLowerBounds(addExpr.getRValue()) and + result = addRoundingDown(xLow, yLow) + ) + or + exists(AssignSubExpr subExpr, float xLow, float yHigh | + expr = subExpr and + xLow = getFullyConvertedLowerBounds(subExpr.getLValue()) and + yHigh = getFullyConvertedUpperBounds(subExpr.getRValue()) and + result = addRoundingDown(xLow, -yHigh) + ) + or + exists(UnsignedAssignMulExpr mulExpr, float xLow, float yLow | + expr = mulExpr and + xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and + yLow = getFullyConvertedLowerBounds(mulExpr.getRValue()) and + result = xLow * yLow + ) + or + exists(AssignMulByPositiveConstantExpr mulExpr, float xLow | + expr = mulExpr and + xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and + result = xLow * mulExpr.getConstant() + ) + or + exists(AssignMulByNegativeConstantExpr mulExpr, float xHigh | + expr = mulExpr and + xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and + result = xHigh * mulExpr.getConstant() + ) + or + exists(PrefixIncrExpr incrExpr, float xLow | + expr = incrExpr and + xLow = getFullyConvertedLowerBounds(incrExpr.getOperand()) and + result = xLow + 1 + ) + or + exists(PrefixDecrExpr decrExpr, float xLow | + expr = decrExpr and + xLow = getFullyConvertedLowerBounds(decrExpr.getOperand()) and + result = addRoundingDownSmall(xLow, -1) + ) + or + // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their + // operand. The incrementing/decrementing behavior is handled in + // `getDefLowerBoundsImpl`. + exists(PostfixIncrExpr incrExpr | + expr = incrExpr and + result = getFullyConvertedLowerBounds(incrExpr.getOperand()) + ) + or + exists(PostfixDecrExpr decrExpr | + expr = decrExpr and + result = getFullyConvertedLowerBounds(decrExpr.getOperand()) + ) + or + exists(RemExpr remExpr | expr = remExpr | + // If both inputs are positive then the lower bound is zero. + result = 0 or - exists(float rhsUB | rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) | - result = -rhsUB + // If either input could be negative then the output could be + // negative. If so, the lower bound of `x%y` is `-abs(y)`, which is + // equal to `min(-y,y)`. + exists(float childLB | + childLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and + not childLB >= 0 + | + result = getFullyConvertedLowerBounds(remExpr.getRightOperand()) + or + exists(float rhsUB | rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) | + result = -rhsUB + ) ) ) - ) - or - exists(CommaExpr commaExpr | - expr = commaExpr and - result = getFullyConvertedLowerBounds(commaExpr.getRightOperand()) - ) - or - exists(StmtExpr stmtExpr | - expr = stmtExpr and - result = getFullyConvertedLowerBounds(stmtExpr.getResultExpr()) - ) - or - // If the conversion is to an arithmetic type then we just return the - // lower bound of the child. We do not need to handle truncation and - // overflow here, because that is done in `getTruncatedLowerBounds`. - // Conversions to `bool` need to be handled specially because they test - // whether the value of the expression is equal to 0. - exists(Conversion convExpr | expr = convExpr | - if convExpr.getUnspecifiedType() instanceof BoolType - then result = boolConversionLowerBound(convExpr.getExpr()) - else result = getTruncatedLowerBounds(convExpr.getExpr()) - ) - or - // Use SSA to get the lower bounds for a variable use. - exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | - result = getDefLowerBounds(def, v) - ) - or - // unsigned `&` (tighter bounds may exist) - exists(UnsignedBitwiseAndExpr andExpr | - andExpr = expr and - result = 0.0 - ) + or + // If the conversion is to an arithmetic type then we just return the + // lower bound of the child. We do not need to handle truncation and + // overflow here, because that is done in `getTruncatedLowerBounds`. + // Conversions to `bool` need to be handled specially because they test + // whether the value of the expression is equal to 0. + exists(Conversion convExpr | expr = convExpr | + if convExpr.getUnspecifiedType() instanceof BoolType + then result = boolConversionLowerBound(convExpr.getExpr()) + else result = getTruncatedLowerBounds(convExpr.getExpr()) + ) + or + // Use SSA to get the lower bounds for a variable use. + exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | + result = getDefLowerBounds(def, v) + ) + or + // unsigned `&` (tighter bounds may exist) + exists(UnsignedBitwiseAndExpr andExpr | + andExpr = expr and + result = 0.0 + ) + or + // `>>` by a constant + exists(RShiftExpr rsExpr, float left, int right | + rsExpr = expr and + left = getFullyConvertedLowerBounds(rsExpr.getLeftOperand()) and + right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and + result = safeFloor(left / 2.pow(right)) + ) + // Not explicitly modeled by a SimpleRangeAnalysisExpr + ) and + not expr instanceof SimpleRangeAnalysisExpr or - // `>>` by a constant - exists(RShiftExpr rsExpr, float left, int right | - rsExpr = expr and - left = getFullyConvertedLowerBounds(rsExpr.getLeftOperand()) and - right = rsExpr.getRightOperand().getValue().toInt() and - result = safeFloor(left / 2.pow(right)) + // A modeled expression for range analysis + exists(SimpleRangeAnalysisExpr rangeAnalysisExpr | + rangeAnalysisExpr = expr and + result = rangeAnalysisExpr.getLowerBounds() ) } /** Only to be called by `getTruncatedUpperBounds`. */ private float getUpperBoundsImpl(Expr expr) { - exists(UnaryPlusExpr plusExpr | - expr = plusExpr and - result = getFullyConvertedUpperBounds(plusExpr.getOperand()) - ) - or - exists(UnaryMinusExpr negateExpr, float xLow | - expr = negateExpr and - xLow = getFullyConvertedLowerBounds(negateExpr.getOperand()) and - result = -xLow - ) - or - exists(MaxExpr maxExpr | - expr = maxExpr and - // Return the union of the upper bounds from both children. - result = getFullyConvertedUpperBounds(maxExpr.getAnOperand()) - ) - or - exists(MinExpr minExpr | - expr = minExpr and - // Compute the cross product of the bounds from both children. We are - // using this mathematical property: - // - // min (maximum{X}, maximum{Y}) - // = maximum { min(x,y) | x in X, y in Y } - exists(float x, float y | - x = getFullyConvertedUpperBounds(minExpr.getLeftOperand()) and - y = getFullyConvertedUpperBounds(minExpr.getRightOperand()) and - if x <= y then result = x else result = y + ( + exists(Expr operand, float operandHigh, float positive | + effectivelyMultipliesByPositive(expr, operand, positive) and + operandHigh = getFullyConvertedUpperBounds(operand) and + result = positive * operandHigh ) - ) - or - // ConditionalExpr (true branch) - exists(ConditionalExpr condExpr | - expr = condExpr and - // Use `boolConversionUpperBound` to determine whether the condition - // might evaluate to `true`. - boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and - result = getFullyConvertedUpperBounds(condExpr.getThen()) - ) - or - // ConditionalExpr (false branch) - exists(ConditionalExpr condExpr | - expr = condExpr and - // Use `boolConversionLowerBound` to determine whether the condition - // might evaluate to `false`. - boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and - result = getFullyConvertedUpperBounds(condExpr.getElse()) - ) - or - exists(AddExpr addExpr, float xHigh, float yHigh | - expr = addExpr and - xHigh = getFullyConvertedUpperBounds(addExpr.getLeftOperand()) and - yHigh = getFullyConvertedUpperBounds(addExpr.getRightOperand()) and - result = addRoundingUp(xHigh, yHigh) - ) - or - exists(SubExpr subExpr, float xHigh, float yLow | - expr = subExpr and - xHigh = getFullyConvertedUpperBounds(subExpr.getLeftOperand()) and - yLow = getFullyConvertedLowerBounds(subExpr.getRightOperand()) and - result = addRoundingUp(xHigh, -yLow) - ) - or - exists(AssignExpr assign | - expr = assign and - result = getFullyConvertedUpperBounds(assign.getRValue()) - ) - or - exists(AssignAddExpr addExpr, float xHigh, float yHigh | - expr = addExpr and - xHigh = getFullyConvertedUpperBounds(addExpr.getLValue()) and - yHigh = getFullyConvertedUpperBounds(addExpr.getRValue()) and - result = addRoundingUp(xHigh, yHigh) - ) - or - exists(AssignSubExpr subExpr, float xHigh, float yLow | - expr = subExpr and - xHigh = getFullyConvertedUpperBounds(subExpr.getLValue()) and - yLow = getFullyConvertedLowerBounds(subExpr.getRValue()) and - result = addRoundingUp(xHigh, -yLow) - ) - or - exists(PrefixIncrExpr incrExpr, float xHigh | - expr = incrExpr and - xHigh = getFullyConvertedUpperBounds(incrExpr.getOperand()) and - result = addRoundingUpSmall(xHigh, 1) - ) - or - exists(PrefixDecrExpr decrExpr, float xHigh | - expr = decrExpr and - xHigh = getFullyConvertedUpperBounds(decrExpr.getOperand()) and - result = xHigh - 1 - ) - or - // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their operand. - // The incrementing/decrementing behavior is handled in - // `getDefUpperBoundsImpl`. - exists(PostfixIncrExpr incrExpr | - expr = incrExpr and - result = getFullyConvertedUpperBounds(incrExpr.getOperand()) - ) - or - exists(PostfixDecrExpr decrExpr | - expr = decrExpr and - result = getFullyConvertedUpperBounds(decrExpr.getOperand()) - ) - or - exists(RemExpr remExpr, float rhsUB | - expr = remExpr and - rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) - | - result = rhsUB - or - // If the right hand side could be negative then we need to take its - // absolute value. Since `abs(x) = max(-x,x)` this is equivalent to - // adding `-rhsLB` to the set of upper bounds. - exists(float rhsLB | - rhsLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and - not rhsLB >= 0 + or + exists(Expr operand, float operandLow, float negative | + effectivelyMultipliesByNegative(expr, operand, negative) and + operandLow = getFullyConvertedLowerBounds(operand) and + result = negative * operandLow + ) + or + exists(MaxExpr maxExpr | + expr = maxExpr and + // Return the union of the upper bounds from both children. + result = getFullyConvertedUpperBounds(maxExpr.getAnOperand()) + ) + or + exists(MinExpr minExpr | + expr = minExpr and + // Compute the cross product of the bounds from both children. We are + // using this mathematical property: + // + // min (maximum{X}, maximum{Y}) + // = maximum { min(x,y) | x in X, y in Y } + exists(float x, float y | + x = getFullyConvertedUpperBounds(minExpr.getLeftOperand()) and + y = getFullyConvertedUpperBounds(minExpr.getRightOperand()) and + if x <= y then result = x else result = y + ) + ) + or + // ConditionalExpr (true branch) + exists(ConditionalExpr condExpr | + expr = condExpr and + // Use `boolConversionUpperBound` to determine whether the condition + // might evaluate to `true`. + boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and + result = getFullyConvertedUpperBounds(condExpr.getThen()) + ) + or + // ConditionalExpr (false branch) + exists(ConditionalExpr condExpr | + expr = condExpr and + // Use `boolConversionLowerBound` to determine whether the condition + // might evaluate to `false`. + boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and + result = getFullyConvertedUpperBounds(condExpr.getElse()) + ) + or + exists(AddExpr addExpr, float xHigh, float yHigh | + expr = addExpr and + xHigh = getFullyConvertedUpperBounds(addExpr.getLeftOperand()) and + yHigh = getFullyConvertedUpperBounds(addExpr.getRightOperand()) and + result = addRoundingUp(xHigh, yHigh) + ) + or + exists(SubExpr subExpr, float xHigh, float yLow | + expr = subExpr and + xHigh = getFullyConvertedUpperBounds(subExpr.getLeftOperand()) and + yLow = getFullyConvertedLowerBounds(subExpr.getRightOperand()) and + result = addRoundingUp(xHigh, -yLow) + ) + or + exists(UnsignedMulExpr mulExpr, float xHigh, float yHigh | + expr = mulExpr and + xHigh = getFullyConvertedUpperBounds(mulExpr.getLeftOperand()) and + yHigh = getFullyConvertedUpperBounds(mulExpr.getRightOperand()) and + result = xHigh * yHigh + ) + or + exists(AssignExpr assign | + expr = assign and + result = getFullyConvertedUpperBounds(assign.getRValue()) + ) + or + exists(AssignAddExpr addExpr, float xHigh, float yHigh | + expr = addExpr and + xHigh = getFullyConvertedUpperBounds(addExpr.getLValue()) and + yHigh = getFullyConvertedUpperBounds(addExpr.getRValue()) and + result = addRoundingUp(xHigh, yHigh) + ) + or + exists(AssignSubExpr subExpr, float xHigh, float yLow | + expr = subExpr and + xHigh = getFullyConvertedUpperBounds(subExpr.getLValue()) and + yLow = getFullyConvertedLowerBounds(subExpr.getRValue()) and + result = addRoundingUp(xHigh, -yLow) + ) + or + exists(UnsignedAssignMulExpr mulExpr, float xHigh, float yHigh | + expr = mulExpr and + xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and + yHigh = getFullyConvertedUpperBounds(mulExpr.getRValue()) and + result = xHigh * yHigh + ) + or + exists(AssignMulByPositiveConstantExpr mulExpr, float xHigh | + expr = mulExpr and + xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and + result = xHigh * mulExpr.getConstant() + ) + or + exists(AssignMulByNegativeConstantExpr mulExpr, float xLow | + expr = mulExpr and + xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and + result = xLow * mulExpr.getConstant() + ) + or + exists(PrefixIncrExpr incrExpr, float xHigh | + expr = incrExpr and + xHigh = getFullyConvertedUpperBounds(incrExpr.getOperand()) and + result = addRoundingUpSmall(xHigh, 1) + ) + or + exists(PrefixDecrExpr decrExpr, float xHigh | + expr = decrExpr and + xHigh = getFullyConvertedUpperBounds(decrExpr.getOperand()) and + result = xHigh - 1 + ) + or + // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their operand. + // The incrementing/decrementing behavior is handled in + // `getDefUpperBoundsImpl`. + exists(PostfixIncrExpr incrExpr | + expr = incrExpr and + result = getFullyConvertedUpperBounds(incrExpr.getOperand()) + ) + or + exists(PostfixDecrExpr decrExpr | + expr = decrExpr and + result = getFullyConvertedUpperBounds(decrExpr.getOperand()) + ) + or + exists(RemExpr remExpr, float rhsUB | + expr = remExpr and + rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) | - result = -rhsLB + result = rhsUB + or + // If the right hand side could be negative then we need to take its + // absolute value. Since `abs(x) = max(-x,x)` this is equivalent to + // adding `-rhsLB` to the set of upper bounds. + exists(float rhsLB | + rhsLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and + not rhsLB >= 0 + | + result = -rhsLB + ) ) - ) - or - exists(CommaExpr commaExpr | - expr = commaExpr and - result = getFullyConvertedUpperBounds(commaExpr.getRightOperand()) - ) - or - exists(StmtExpr stmtExpr | - expr = stmtExpr and - result = getFullyConvertedUpperBounds(stmtExpr.getResultExpr()) - ) - or - // If the conversion is to an arithmetic type then we just return the - // upper bound of the child. We do not need to handle truncation and - // overflow here, because that is done in `getTruncatedUpperBounds`. - // Conversions to `bool` need to be handled specially because they test - // whether the value of the expression is equal to 0. - exists(Conversion convExpr | expr = convExpr | - if convExpr.getUnspecifiedType() instanceof BoolType - then result = boolConversionUpperBound(convExpr.getExpr()) - else result = getTruncatedUpperBounds(convExpr.getExpr()) - ) - or - // Use SSA to get the upper bounds for a variable use. - exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | - result = getDefUpperBounds(def, v) - ) - or - // unsigned `&` (tighter bounds may exist) - exists(UnsignedBitwiseAndExpr andExpr, float left, float right | - andExpr = expr and - left = getFullyConvertedUpperBounds(andExpr.getLeftOperand()) and - right = getFullyConvertedUpperBounds(andExpr.getRightOperand()) and - result = left.minimum(right) - ) + or + // If the conversion is to an arithmetic type then we just return the + // upper bound of the child. We do not need to handle truncation and + // overflow here, because that is done in `getTruncatedUpperBounds`. + // Conversions to `bool` need to be handled specially because they test + // whether the value of the expression is equal to 0. + exists(Conversion convExpr | expr = convExpr | + if convExpr.getUnspecifiedType() instanceof BoolType + then result = boolConversionUpperBound(convExpr.getExpr()) + else result = getTruncatedUpperBounds(convExpr.getExpr()) + ) + or + // Use SSA to get the upper bounds for a variable use. + exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) | + result = getDefUpperBounds(def, v) + ) + or + // unsigned `&` (tighter bounds may exist) + exists(UnsignedBitwiseAndExpr andExpr, float left, float right | + andExpr = expr and + left = getFullyConvertedUpperBounds(andExpr.getLeftOperand()) and + right = getFullyConvertedUpperBounds(andExpr.getRightOperand()) and + result = left.minimum(right) + ) + or + // `>>` by a constant + exists(RShiftExpr rsExpr, float left, int right | + rsExpr = expr and + left = getFullyConvertedUpperBounds(rsExpr.getLeftOperand()) and + right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and + result = safeFloor(left / 2.pow(right)) + ) + // Not explicitly modeled by a SimpleRangeAnalysisExpr + ) and + not expr instanceof SimpleRangeAnalysisExpr or - // `>>` by a constant - exists(RShiftExpr rsExpr, float left, int right | - rsExpr = expr and - left = getFullyConvertedUpperBounds(rsExpr.getLeftOperand()) and - right = rsExpr.getRightOperand().getValue().toInt() and - result = safeFloor(left / 2.pow(right)) + // A modeled expression for range analysis + exists(SimpleRangeAnalysisExpr rangeAnalysisExpr | + rangeAnalysisExpr = expr and + result = rangeAnalysisExpr.getUpperBounds() ) } @@ -988,9 +1196,7 @@ private float boolConversionUpperBound(Expr expr) { * use the guard to deduce that the lower bound is 2 inside the block. */ private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) { - exists( - VariableAccess access, ComparisonOperation guard, boolean branch, float defLB, float guardLB - | + exists(VariableAccess access, Expr guard, boolean branch, float defLB, float guardLB | access = v.getAnAccess() and phi.isGuardPhi(access, guard, branch) and lowerBoundFromGuard(guard, access, guardLB, branch) and @@ -1000,14 +1206,23 @@ private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) { if guardLB > defLB then result = guardLB else result = defLB ) or + exists(VariableAccess access, float neConstant, float lower | + isNEPhi(v, phi, access, neConstant) and + lower = getTruncatedLowerBounds(access) and + if lower = neConstant then result = lower + 1 else result = lower + ) + or + exists(VariableAccess access | + isUnsupportedGuardPhi(v, phi, access) and + result = getTruncatedLowerBounds(access) + ) + or result = getDefLowerBounds(phi.getAPhiInput(v), v) } /** See comment for `getPhiLowerBounds`, above. */ private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) { - exists( - VariableAccess access, ComparisonOperation guard, boolean branch, float defUB, float guardUB - | + exists(VariableAccess access, Expr guard, boolean branch, float defUB, float guardUB | access = v.getAnAccess() and phi.isGuardPhi(access, guard, branch) and upperBoundFromGuard(guard, access, guardUB, branch) and @@ -1017,6 +1232,17 @@ private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) { if guardUB < defUB then result = guardUB else result = defUB ) or + exists(VariableAccess access, float neConstant, float upper | + isNEPhi(v, phi, access, neConstant) and + upper = getTruncatedUpperBounds(access) and + if upper = neConstant then result = upper - 1 else result = upper + ) + or + exists(VariableAccess access | + isUnsupportedGuardPhi(v, phi, access) and + result = getTruncatedUpperBounds(access) + ) + or result = getDefUpperBounds(phi.getAPhiInput(v), v) } @@ -1025,20 +1251,11 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) { // Definitions with a defining value. exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedLowerBounds(expr)) or - exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef, float lhsLB, float rhsLB | - def = assignAdd and - assignAdd.getLValue() = nextDef.getAUse(v) and - lhsLB = getDefLowerBounds(nextDef, v) and - rhsLB = getFullyConvertedLowerBounds(assignAdd.getRValue()) and - result = addRoundingDown(lhsLB, rhsLB) - ) - or - exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef, float lhsLB, float rhsUB | - def = assignSub and - assignSub.getLValue() = nextDef.getAUse(v) and - lhsLB = getDefLowerBounds(nextDef, v) and - rhsUB = getFullyConvertedUpperBounds(assignSub.getRValue()) and - result = addRoundingDown(lhsLB, -rhsUB) + // Assignment operations with a defining value + exists(AssignOperation assignOp | + def = assignOp and + assignOp.getLValue() = v.getAnAccess() and + result = getTruncatedLowerBounds(assignOp) ) or exists(IncrementOperation incr, float newLB | @@ -1058,6 +1275,9 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) { // Phi nodes. result = getPhiLowerBounds(v, def) or + // A modeled def for range analysis + result = def.(SimpleRangeAnalysisDefinition).getLowerBounds(v) + or // Unanalyzable definitions. unanalyzableDefBounds(def, v, result, _) } @@ -1067,20 +1287,11 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) { // Definitions with a defining value. exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedUpperBounds(expr)) or - exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef, float lhsUB, float rhsUB | - def = assignAdd and - assignAdd.getLValue() = nextDef.getAUse(v) and - lhsUB = getDefUpperBounds(nextDef, v) and - rhsUB = getFullyConvertedUpperBounds(assignAdd.getRValue()) and - result = addRoundingUp(lhsUB, rhsUB) - ) - or - exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef, float lhsUB, float rhsLB | - def = assignSub and - assignSub.getLValue() = nextDef.getAUse(v) and - lhsUB = getDefUpperBounds(nextDef, v) and - rhsLB = getFullyConvertedLowerBounds(assignSub.getRValue()) and - result = addRoundingUp(lhsUB, -rhsLB) + // Assignment operations with a defining value + exists(AssignOperation assignOp | + def = assignOp and + assignOp.getLValue() = v.getAnAccess() and + result = getTruncatedUpperBounds(assignOp) ) or exists(IncrementOperation incr, float newUB | @@ -1100,74 +1311,13 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) { // Phi nodes. result = getPhiUpperBounds(v, def) or + // A modeled def for range analysis + result = def.(SimpleRangeAnalysisDefinition).getUpperBounds(v) + or // Unanalyzable definitions. unanalyzableDefBounds(def, v, _, result) } -/** - * Get the lower bounds for a `RangeSsaDefinition`. Most of the work is - * done by `getDefLowerBoundsImpl`, but this is where widening is applied - * to prevent the analysis from exploding due to a recursive definition. - */ -private float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) { - exists(float newLB, float truncatedLB | - newLB = getDefLowerBoundsImpl(def, v) and - if varMinVal(v) <= newLB and newLB <= varMaxVal(v) - then truncatedLB = newLB - else truncatedLB = varMinVal(v) - | - // Widening: check whether the new lower bound is from a source which - // depends recursively on the current definition. - if isRecursiveDef(def, v) - then - // The new lower bound is from a recursive source, so we round - // down to one of a limited set of values to prevent the - // recursion from exploding. - result = - max(float widenLB | - widenLB = wideningLowerBounds(v.getUnspecifiedType()) and - not widenLB > truncatedLB - | - widenLB - ) - else result = truncatedLB - ) - or - // The definition might overflow positively and wrap. If so, the lower - // bound is `typeLowerBound`. - defMightOverflowPositively(def, v) and result = varMinVal(v) -} - -/** See comment for `getDefLowerBounds`, above. */ -private float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) { - exists(float newUB, float truncatedUB | - newUB = getDefUpperBoundsImpl(def, v) and - if varMinVal(v) <= newUB and newUB <= varMaxVal(v) - then truncatedUB = newUB - else truncatedUB = varMaxVal(v) - | - // Widening: check whether the new upper bound is from a source which - // depends recursively on the current definition. - if isRecursiveDef(def, v) - then - // The new upper bound is from a recursive source, so we round - // up to one of a fixed set of values to prevent the recursion - // from exploding. - result = - min(float widenUB | - widenUB = wideningUpperBounds(v.getUnspecifiedType()) and - not widenUB < truncatedUB - | - widenUB - ) - else result = truncatedUB - ) - or - // The definition might overflow negatively and wrap. If so, the upper - // bound is `typeUpperBound`. - defMightOverflowNegatively(def, v) and result = varMaxVal(v) -} - /** * Helper for `getDefLowerBounds` and `getDefUpperBounds`. Find the set of * unanalyzable definitions (such as function parameters) and make their @@ -1186,10 +1336,11 @@ private predicate unanalyzableDefBounds(RangeSsaDefinition def, StackVariable v, * inferences about `v`. */ bindingset[guard, v, branch] -predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boolean branch) { - v.getUnspecifiedType() instanceof IntegralType +predicate nonNanGuardedVariable(Expr guard, VariableAccess v, boolean branch) { + getVariableRangeType(v.getTarget()) instanceof IntegralType or - v.getUnspecifiedType() instanceof FloatingPointType and v instanceof NonNanVariableAccess + getVariableRangeType(v.getTarget()) instanceof FloatingPointType and + v instanceof NonNanVariableAccess or // The reason the following case is here is to ensure that when we say // `if (x > 5) { ...then... } else { ...else... }` @@ -1204,9 +1355,7 @@ predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boo * predicate uses the bounds information for `r` to compute a lower bound * for `v`. */ -private predicate lowerBoundFromGuard( - ComparisonOperation guard, VariableAccess v, float lb, boolean branch -) { +private predicate lowerBoundFromGuard(Expr guard, VariableAccess v, float lb, boolean branch) { exists(float childLB, RelationStrictness strictness | boundFromGuard(guard, v, childLB, true, strictness, branch) | @@ -1214,7 +1363,7 @@ private predicate lowerBoundFromGuard( then if strictness = Nonstrict() or - not v.getUnspecifiedType() instanceof IntegralType + not getVariableRangeType(v.getTarget()) instanceof IntegralType then lb = childLB else lb = childLB + 1 else lb = varMinVal(v.getTarget()) @@ -1226,9 +1375,7 @@ private predicate lowerBoundFromGuard( * predicate uses the bounds information for `r` to compute a upper bound * for `v`. */ -private predicate upperBoundFromGuard( - ComparisonOperation guard, VariableAccess v, float ub, boolean branch -) { +private predicate upperBoundFromGuard(Expr guard, VariableAccess v, float ub, boolean branch) { exists(float childUB, RelationStrictness strictness | boundFromGuard(guard, v, childUB, false, strictness, branch) | @@ -1236,7 +1383,7 @@ private predicate upperBoundFromGuard( then if strictness = Nonstrict() or - not v.getUnspecifiedType() instanceof IntegralType + not getVariableRangeType(v.getTarget()) instanceof IntegralType then ub = childUB else ub = childUB - 1 else ub = varMaxVal(v.getTarget()) @@ -1248,7 +1395,7 @@ private predicate upperBoundFromGuard( * `linearBoundFromGuard`. */ private predicate boundFromGuard( - ComparisonOperation guard, VariableAccess v, float boundValue, boolean isLowerBound, + Expr guard, VariableAccess v, float boundValue, boolean isLowerBound, RelationStrictness strictness, boolean branch ) { exists(float p, float q, float r, boolean isLB | @@ -1261,6 +1408,15 @@ private predicate boundFromGuard( or p < 0 and isLowerBound = isLB.booleanNot() ) + or + // When `!e` is true, we know that `0 <= e <= 0` + exists(float p, float q, Expr e | + linearAccess(e, v, p, q) and + eqZeroWithNegate(guard, e, true, branch) and + boundValue = (0.0 - q) / p and + isLowerBound = [false, true] and + strictness = Nonstrict() + ) } /** @@ -1296,22 +1452,13 @@ private predicate linearBoundFromGuard( // 1. x <= upperbound(RHS) // 2. x >= lowerbound(RHS) // - // For x != RHS, we create trivial bounds: - // - // 1. x <= typeUpperBound(RHS.getUnspecifiedType()) - // 2. x >= typeLowerBound(RHS.getUnspecifiedType()) - // - exists(Expr lhs, Expr rhs, boolean isEQ | + exists(Expr lhs, Expr rhs | linearAccess(lhs, v, p, q) and - eqOpWithSwapAndNegate(guard, lhs, rhs, isEQ, branch) and + eqOpWithSwapAndNegate(guard, lhs, rhs, true, branch) and + getBounds(rhs, boundValue, isLowerBound) and strictness = Nonstrict() - | - // True branch - isEQ = true and getBounds(rhs, boundValue, isLowerBound) - or - // False branch: set the bounds to the min/max for the type. - isEQ = false and exprTypeBounds(rhs, boundValue, isLowerBound) ) + // x != RHS and !x are handled elsewhere } /** Utility for `linearBoundFromGuard`. */ @@ -1328,6 +1475,54 @@ private predicate exprTypeBounds(Expr expr, float boundValue, boolean isLowerBou isLowerBound = false and boundValue = exprMaxVal(expr.getFullyConverted()) } +/** + * Holds if `(v, phi)` ensures that `access` is not equal to `neConstant`. For + * example, the condition `if (x + 1 != 3)` ensures that `x` is not equal to 2. + * Only integral types are supported. + */ +private predicate isNEPhi( + Variable v, RangeSsaDefinition phi, VariableAccess access, float neConstant +) { + exists( + ComparisonOperation cmp, boolean branch, Expr linearExpr, Expr rExpr, float p, float q, float r + | + access.getTarget() = v and + phi.isGuardPhi(access, cmp, branch) and + eqOpWithSwapAndNegate(cmp, linearExpr, rExpr, false, branch) and + v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!=` is too imprecise + r = getValue(rExpr).toFloat() and + linearAccess(linearExpr, access, p, q) and + neConstant = (r - q) / p + ) + or + exists(Expr op, boolean branch, Expr linearExpr, float p, float q | + access.getTarget() = v and + phi.isGuardPhi(access, op, branch) and + eqZeroWithNegate(op, linearExpr, false, branch) and + v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!` is too imprecise + linearAccess(linearExpr, access, p, q) and + neConstant = (0.0 - q) / p + ) +} + +/** + * Holds if `(v, phi)` constrains the value of `access` but in a way that + * doesn't allow this library to constrain the upper or lower bounds of + * `access`. An example is `if (x != y)` if neither `x` nor `y` is a + * compile-time constant. + */ +private predicate isUnsupportedGuardPhi(Variable v, RangeSsaDefinition phi, VariableAccess access) { + exists(Expr cmp, boolean branch | + eqOpWithSwapAndNegate(cmp, _, _, false, branch) + or + eqZeroWithNegate(cmp, _, false, branch) + | + access.getTarget() = v and + phi.isGuardPhi(access, cmp, branch) and + not isNEPhi(v, phi, access, _) + ) +} + cached private module SimpleRangeAnalysisCached { /** @@ -1480,3 +1675,89 @@ private module SimpleRangeAnalysisCached { convertedExprMightOverflowPositively(expr) } } + +/** + * INTERNAL: do not use. This module contains utilities for use in the + * experimental `SimpleRangeAnalysisExpr` module. + */ +module SimpleRangeAnalysisInternal { + /** + * Gets the truncated lower bounds of the fully converted expression. + */ + float getFullyConvertedLowerBounds(Expr expr) { + result = getTruncatedLowerBounds(expr.getFullyConverted()) + } + + /** + * Gets the truncated upper bounds of the fully converted expression. + */ + float getFullyConvertedUpperBounds(Expr expr) { + result = getTruncatedUpperBounds(expr.getFullyConverted()) + } + + /** + * Get the lower bounds for a `RangeSsaDefinition`. Most of the work is + * done by `getDefLowerBoundsImpl`, but this is where widening is applied + * to prevent the analysis from exploding due to a recursive definition. + */ + float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) { + exists(float newLB, float truncatedLB | + newLB = getDefLowerBoundsImpl(def, v) and + if varMinVal(v) <= newLB and newLB <= varMaxVal(v) + then truncatedLB = newLB + else truncatedLB = varMinVal(v) + | + // Widening: check whether the new lower bound is from a source which + // depends recursively on the current definition. + if isRecursiveDef(def, v) + then + // The new lower bound is from a recursive source, so we round + // down to one of a limited set of values to prevent the + // recursion from exploding. + result = + max(float widenLB | + widenLB = wideningLowerBounds(getVariableRangeType(v)) and + not widenLB > truncatedLB + | + widenLB + ) + else result = truncatedLB + ) + or + // The definition might overflow positively and wrap. If so, the lower + // bound is `typeLowerBound`. + defMightOverflowPositively(def, v) and result = varMinVal(v) + } + + /** See comment for `getDefLowerBounds`, above. */ + float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) { + exists(float newUB, float truncatedUB | + newUB = getDefUpperBoundsImpl(def, v) and + if varMinVal(v) <= newUB and newUB <= varMaxVal(v) + then truncatedUB = newUB + else truncatedUB = varMaxVal(v) + | + // Widening: check whether the new upper bound is from a source which + // depends recursively on the current definition. + if isRecursiveDef(def, v) + then + // The new upper bound is from a recursive source, so we round + // up to one of a fixed set of values to prevent the recursion + // from exploding. + result = + min(float widenUB | + widenUB = wideningUpperBounds(getVariableRangeType(v)) and + not widenUB < truncatedUB + | + widenUB + ) + else result = truncatedUB + ) + or + // The definition might overflow negatively and wrap. If so, the upper + // bound is `typeUpperBound`. + defMightOverflowNegatively(def, v) and result = varMaxVal(v) + } +} + +private import SimpleRangeAnalysisInternal diff --git a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll index 4c53f34c9367..f06b0180c231 100644 --- a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll @@ -10,6 +10,7 @@ import semmle.code.cpp.commons.Alloc import semmle.code.cpp.commons.Buffer import semmle.code.cpp.commons.Scanf import semmle.code.cpp.models.implementations.Strcat +import semmle.code.cpp.models.implementations.Strcpy /* * --- BufferWrite framework --- @@ -106,68 +107,19 @@ abstract class BufferWriteCall extends BufferWrite, FunctionCall { } * A call to a variant of `strcpy`. */ class StrCopyBW extends BufferWriteCall { - StrCopyBW() { - exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() | - // strcpy(dst, src) - name = "strcpy" - or - // wcscpy(dst, src) - name = "wcscpy" - or - // _mbscpy(dst, src) - name = "_mbscpy" - or - ( - name = "strcpy_s" or // strcpy_s(dst, max_amount, src) - name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src) - name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src) - ) and - // exclude the 2-parameter template versions - // that find the size of a fixed size destination buffer. - fn.getNumberOfParameters() = 3 - or - // strncpy(dst, src, max_amount) - name = "strncpy" - or - // strncpy_l(dst, src, max_amount, locale) - name = "strncpy_l" - or - // wcsncpy(dst, src, max_amount) - name = "wcsncpy" - or - // _wcsncpy_l(dst, src, max_amount, locale) - name = "_wcsncpy_l" - or - // _mbsncpy(dst, src, max_amount) - name = "_mbsncpy" - or - // _mbsncpy_l(dst, src, max_amount, locale) - name = "_mbsncpy_l" - ) - } + StrcpyFunction f; - int getParamSize() { - exists(TopLevelFunction fn, string name | - fn = getTarget() and - name = fn.getName() and - ( - if name.suffix(name.length() - 2) = "_s" - then result = 1 - else - if exists(name.indexOf("ncpy")) - then result = 2 - else none() - ) - ) - } + StrCopyBW() { getTarget() = f.(TopLevelFunction) } - int getParamSrc() { - exists(TopLevelFunction fn, string name | - fn = getTarget() and - name = fn.getName() and - (if name.suffix(name.length() - 2) = "_s" then result = 2 else result = 1) - ) - } + /** + * Gets the index of the parameter that is the maximum size of the copy (in characters). + */ + int getParamSize() { result = f.getParamSize() } + + /** + * Gets the index of the parameter that is the source of the copy. + */ + int getParamSrc() { result = f.getParamSrc() } override Type getBufferType() { result = this.getTarget().getParameter(getParamSrc()).getUnspecifiedType() @@ -175,7 +127,7 @@ class StrCopyBW extends BufferWriteCall { override Expr getASource() { result = getArgument(getParamSrc()) } - override Expr getDest() { result = getArgument(0) } + override Expr getDest() { result = getArgument(f.getParamDest()) } override predicate hasExplicitLimit() { exists(getParamSize()) } @@ -192,11 +144,19 @@ class StrCopyBW extends BufferWriteCall { * A call to a variant of `strcat`. */ class StrCatBW extends BufferWriteCall { - StrCatBW() { exists(TopLevelFunction fn | fn = getTarget() and fn instanceof StrcatFunction) } + StrcatFunction f; + + StrCatBW() { getTarget() = f.(TopLevelFunction) } - int getParamSize() { if exists(getArgument(2)) then result = 2 else none() } + /** + * Gets the index of the parameter that is the maximum size of the copy (in characters). + */ + int getParamSize() { result = f.getParamSize() } - int getParamSrc() { result = 1 } + /** + * Gets the index of the parameter that is the source of the copy. + */ + int getParamSrc() { result = f.getParamSrc() } override Type getBufferType() { result = this.getTarget().getParameter(getParamSrc()).getUnspecifiedType() @@ -204,7 +164,7 @@ class StrCatBW extends BufferWriteCall { override Expr getASource() { result = getArgument(getParamSrc()) } - override Expr getDest() { result = getArgument(0) } + override Expr getDest() { result = getArgument(f.getParamDest()) } override predicate hasExplicitLimit() { exists(getParamSize()) } @@ -221,8 +181,10 @@ class StrCatBW extends BufferWriteCall { * A call to a variant of `sprintf`. */ class SprintfBW extends BufferWriteCall { + FormattingFunction f; + SprintfBW() { - exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() | + exists(string name | f = getTarget().(TopLevelFunction) and name = f.getName() | /* * C sprintf variants: */ @@ -258,10 +220,7 @@ class SprintfBW extends BufferWriteCall { } override Type getBufferType() { - exists(FormattingFunction f | - f = this.getTarget() and - result = f.getParameter(f.getFormatParameterIndex()).getUnspecifiedType() - ) + result = f.getParameter(f.getFormatParameterIndex()).getUnspecifiedType() } override Expr getASource() { @@ -270,7 +229,7 @@ class SprintfBW extends BufferWriteCall { result = this.(FormattingFunctionCall).getFormatArgument(_) } - override Expr getDest() { result = getArgument(0) } + override Expr getDest() { result = getArgument(f.getOutputParameterIndex()) } override int getMaxData() { exists(FormatLiteral fl | @@ -349,6 +308,9 @@ class SnprintfBW extends BufferWriteCall { ) } + /** + * Gets the index of the parameter that is the size of the destination (in characters). + */ int getParamSize() { result = 1 } override Type getBufferType() { @@ -392,13 +354,15 @@ class SnprintfBW extends BufferWriteCall { */ class GetsBW extends BufferWriteCall { GetsBW() { - exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() | - name = "gets" or // gets(dst) - name = "fgets" or // fgets(dst, max_amount, src_stream) - name = "fgetws" // fgetws(dst, max_amount, src_stream) - ) + getTarget().(TopLevelFunction).getName() = + ["gets", // gets(dst) + "fgets", // fgets(dst, max_amount, src_stream) + "fgetws"] // fgetws(dst, max_amount, src_stream) } + /** + * Gets the index of the parameter that is the maximum number of characters to be read. + */ int getParamSize() { if exists(getArgument(1)) then result = 1 else none() } override Type getBufferType() { result = this.getTarget().getParameter(0).getUnspecifiedType() } @@ -434,6 +398,9 @@ class ScanfBW extends BufferWrite { ) } + /** + * Gets the index of the parameter that is the first format argument. + */ int getParamArgs() { exists(FunctionCall fc | this = fc.getArgument(_) and diff --git a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll index 48fb60442c15..5a24184e1a22 100644 --- a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll +++ b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll @@ -2,21 +2,44 @@ import cpp import semmle.code.cpp.security.FunctionWithWrappers +import semmle.code.cpp.models.interfaces.SideEffect /** * A function for running a command using a command interpreter. */ -class SystemFunction extends FunctionWithWrappers { +class SystemFunction extends FunctionWithWrappers, ArrayFunction, AliasFunction, SideEffectFunction { SystemFunction() { - hasGlobalOrStdName("system") or - hasGlobalName("popen") or + hasGlobalOrStdName("system") or // system(command) + hasGlobalName("popen") or // popen(command, mode) // Windows variants - hasGlobalName("_popen") or - hasGlobalName("_wpopen") or - hasGlobalName("_wsystem") + hasGlobalName("_popen") or // _popen(command, mode) + hasGlobalName("_wpopen") or // _wpopen(command, mode) + hasGlobalName("_wsystem") // _wsystem(command) } override predicate interestingArg(int arg) { arg = 0 } + + override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 } + + override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 } + + override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 } + + override predicate parameterEscapesOnlyViaReturn(int index) { none() } + + override predicate parameterIsAlwaysReturned(int index) { none() } + + override predicate hasOnlySpecificReadSideEffects() { any() } + + override predicate hasOnlySpecificWriteSideEffects() { + hasGlobalOrStdName("system") or + hasGlobalName("_wsystem") + } + + override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { + (i = 0 or i = 1) and + buffer = true + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 61d646733148..d8b2d44c923b 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -1,8 +1,13 @@ -// Common predicates relating to encryption in C and C++ +/** + * Provides predicates relating to encryption in C and C++. + */ + import cpp -/** A blacklist of algorithms that are known to be insecure */ -string algorithmBlacklist() { +/** + * Gets the name of an algorithm that is known to be insecure. + */ +string getAnInsecureAlgorithmName() { result = "DES" or result = "RC2" or result = "RC4" or @@ -10,29 +15,36 @@ string algorithmBlacklist() { result = "ARCFOUR" // a variant of RC4 } -// these are only bad if they're being used for encryption, and it's -// hard to know when that's happening -string hashAlgorithmBlacklist() { +/** + * Gets the name of a hash algorithm that is insecure if it is being used for + * encryption (but it is hard to know when that is happening). + */ +string getAnInsecureHashAlgorithmName() { result = "SHA1" or result = "MD5" } -/** A regex for matching strings that look like they contain a blacklisted algorithm */ -string algorithmBlacklistRegex() { +/** + * Gets the regular expression used for matching strings that look like they + * contain an algorithm that is known to be insecure. + */ +string getInsecureAlgorithmRegex() { result = // algorithms usually appear in names surrounded by characters that are not // alphabetical characters in the same case. This handles the upper and lower // case cases - "(^|.*[^A-Z])(" + strictconcat(algorithmBlacklist(), "|") + ")([^A-Z].*|$)" + "|" + + "(^|.*[^A-Z])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" + // for lowercase, we want to be careful to avoid being confused by camelCase // hence we require two preceding uppercase letters to be sure of a case switch, // or a preceding non-alphabetic character - "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(algorithmBlacklist().toLowerCase(), "|") + + "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") + ")([^a-z].*|$)" } -/** A whitelist of algorithms that are known to be secure */ -string algorithmWhitelist() { +/** + * Gets the name of an algorithm that is known to be secure. + */ +string getASecureAlgorithmName() { result = "RSA" or result = "SHA256" or result = "CCM" or @@ -42,17 +54,47 @@ string algorithmWhitelist() { result = "ECIES" } -/** A regex for matching strings that look like they contain a whitelisted algorithm */ -string algorithmWhitelistRegex() { - // The implementation of this is a duplicate of algorithmBlacklistRegex, as it isn't - // possible to have string -> string functions at the moment - // algorithms usually appear in names surrounded by characters that are not - // alphabetical characters in the same case. This handles the upper and lower - // case cases - result = "(^|.*[^A-Z])" + algorithmWhitelist() + "([^A-Z].*|$)" - or - // for lowercase, we want to be careful to avoid being confused by camelCase - // hence we require two preceding uppercase letters to be sure of a case switch, - // or a preceding non-alphabetic character - result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + algorithmWhitelist().toLowerCase() + "([^a-z].*|$)" +/** + * Gets a regular expression for matching strings that look like they + * contain an algorithm that is known to be secure. + */ +string getSecureAlgorithmRegex() { + result = + // algorithms usually appear in names surrounded by characters that are not + // alphabetical characters in the same case. This handles the upper and lower + // case cases + "(^|.*[^A-Z])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" + + // for lowercase, we want to be careful to avoid being confused by camelCase + // hence we require two preceding uppercase letters to be sure of a case + // switch, or a preceding non-alphabetic character + "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") + + ")([^a-z].*|$)" } + +/** + * DEPRECATED: Terminology has been updated. Use `getAnInsecureAlgorithmName()` + * instead. + */ +deprecated string algorithmBlacklist() { result = getAnInsecureAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use + * `getAnInsecureHashAlgorithmName()` instead. + */ +deprecated string hashAlgorithmBlacklist() { result = getAnInsecureHashAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use `getInsecureAlgorithmRegex()` instead. + */ +deprecated string algorithmBlacklistRegex() { result = getInsecureAlgorithmRegex() } + +/** + * DEPRECATED: Terminology has been updated. Use `getASecureAlgorithmName()` + * instead. + */ +deprecated string algorithmWhitelist() { result = getASecureAlgorithmName() } + +/** + * DEPRECATED: Terminology has been updated. Use `getSecureAlgorithmRegex()` instead. + */ +deprecated string algorithmWhitelistRegex() { result = getSecureAlgorithmRegex() } diff --git a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll index 219f3d0a75b7..9f63ce99c0b8 100644 --- a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll @@ -1,13 +1,23 @@ +/** + * Provides classes for modeling writing of data to files through various standard mechanisms such as `fprintf`, `fwrite` and `operator<<`. + */ + import cpp /** - * A function call that writes to a file + * A function call that writes to a file. */ class FileWrite extends Expr { FileWrite() { fileWrite(this, _, _) } + /** + * Gets a source expression of this write. + */ Expr getASource() { fileWrite(this, result, _) } + /** + * Gets the expression for the object being written to. + */ Expr getDest() { fileWrite(this, _, result) } } @@ -44,17 +54,17 @@ class BasicOStreamCall extends FunctionCall { */ abstract class ChainedOutputCall extends BasicOStreamCall { /** - * The source expression of this output. + * Gets the source expression of this output. */ abstract Expr getSource(); /** - * The immediate destination expression of this output. + * Gets the immediate destination expression of this output. */ abstract Expr getDest(); /** - * The destination at the far left-hand end of the output chain. + * Gets the destination at the far left-hand end of the output chain. */ Expr getEndDest() { // recurse into the destination @@ -108,14 +118,12 @@ class WriteFunctionCall extends ChainedOutputCall { } /** - * Whether the function call is a call to << that eventually starts at the given file stream. + * Whether the function call is a call to `operator<<` or a similar function, that eventually starts at the given file stream. */ private predicate fileStreamChain(ChainedOutputCall out, Expr source, Expr dest) { source = out.getSource() and dest = out.getEndDest() and - exists(string nme | nme = "basic_ofstream" or nme = "basic_fstream" | - dest.getUnderlyingType().(Class).getSimpleName() = nme - ) + dest.getUnderlyingType().(Class).getSimpleName() = ["basic_ofstream", "basic_fstream"] } /** @@ -129,15 +137,7 @@ private predicate fileWrite(Call write, Expr source, Expr dest) { // named functions name = "fwrite" and s = 0 and d = 3 or - ( - name = "fputs" or - name = "fputws" or - name = "fputc" or - name = "fputwc" or - name = "putc" or - name = "putwc" or - name = "putw" - ) and + name = ["fputs", "fputws", "fputc", "fputwc", "putc", "putwc", "putw"] and s = 0 and d = 1 ) diff --git a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll index 23dda0ddb1e9..5451011b3511 100644 --- a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll +++ b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll @@ -1,3 +1,20 @@ +/** + * Provides predicates for identifying functions that wrap other functions, + * passing the same arguments from the outer call into the inner call. In the + * following example `MyMalloc` wraps a call to `malloc`, passing in the `size` + * parameter: + * ``` + * void *MyMalloc(size_t size) + * { + * void *ptr = malloc(size); + * + * // ... additional logic? + * + * return ptr; + * } + * ``` + */ + import cpp import PrintfLike private import TaintTracking @@ -152,6 +169,9 @@ abstract class FunctionWithWrappers extends Function { } } +/** + * A `printf`-like formatting function. + */ class PrintfLikeFunction extends FunctionWithWrappers { PrintfLikeFunction() { printfLikeFunction(this, _) } diff --git a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll index 06abfdb454da..97635d92dbd3 100644 --- a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll +++ b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll @@ -1,12 +1,19 @@ +/** + * Provides classes for modeling output to standard output / standard error through various mechanisms such as `printf`, `puts` and `operator<<`. + */ + import cpp import FileWrite /** - * A function call that writes to standard output or standard error + * A function call that writes to standard output or standard error. */ class OutputWrite extends Expr { OutputWrite() { outputWrite(this, _) } + /** + * Gets a source expression for this output. + */ Expr getASource() { outputWrite(this, result) } } @@ -41,15 +48,12 @@ private predicate outputFile(Expr e) { name = e.(VariableAccess).getTarget().(GlobalVariable).toString() or name = e.findRootCause().(Macro).getName() ) and - ( - name = "stdout" or - name = "stderr" - ) + name = ["stdout", "stderr"] ) } /** - * is the function call a write to standard output or standard error from 'source' + * Holds if the function call is a write to standard output or standard error from 'source'. */ private predicate outputWrite(Expr write, Expr source) { exists(Function f, int arg | diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll index 59993954cc8d..e7ad1c559e63 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll @@ -1,11 +1,14 @@ +/** + * Provides predicates for reasoning about when the value of an expression is + * guarded by an operation such as `<`, which confines its range. + */ + import cpp import semmle.code.cpp.controlflow.Dominance -/* - * Guarding +/** + * Holds if the value of `use` is guarded using `abs`. */ - -/** is the size of this use guarded using 'abs'? */ predicate guardedAbs(Operation e, Expr use) { exists(FunctionCall fc | fc.getTarget().getName() = "abs" | fc.getArgument(0).getAChild*() = use and @@ -13,7 +16,10 @@ predicate guardedAbs(Operation e, Expr use) { ) } -/** This is `BasicBlock.getNode`, restricted to `Stmt` for performance. */ +/** + * Gets the position of `stmt` in basic block `block` (this is a thin layer + * over `BasicBlock.getNode`, intended to improve performance). + */ pragma[noinline] private int getStmtIndexInBlock(BasicBlock block, Stmt stmt) { block.getNode(result) = stmt } @@ -30,7 +36,9 @@ private predicate stmtDominates(Stmt dominator, Stmt dominated) { bbStrictlyDominates(dominator.getBasicBlock(), dominated.getBasicBlock()) } -/** is the size of this use guarded to be less than something? */ +/** + * Holds if the value of `use` is guarded to be less than something. + */ pragma[nomagic] predicate guardedLesser(Operation e, Expr use) { exists(IfStmt c, RelationalOperation guard | @@ -54,7 +62,9 @@ predicate guardedLesser(Operation e, Expr use) { guardedAbs(e, use) } -/** is the size of this use guarded to be greater than something? */ +/** + * Holds if the value of `use` is guarded to be greater than something. + */ pragma[nomagic] predicate guardedGreater(Operation e, Expr use) { exists(IfStmt c, RelationalOperation guard | @@ -78,10 +88,14 @@ predicate guardedGreater(Operation e, Expr use) { guardedAbs(e, use) } -/** a use of a given variable */ +/** + * Gets a use of a given variable `v`. + */ VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() } -/** is e not guarded against overflow by use? */ +/** + * Holds if `e` is not guarded against overflow by `use`. + */ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) { use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | @@ -100,7 +114,9 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) { ) } -/** is e not guarded against underflow by use? */ +/** + * Holds if `e` is not guarded against underflow by `use`. + */ predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) { use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | diff --git a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll index 47d0ca3aa930..197a332aa1a7 100644 --- a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll +++ b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll @@ -1,6 +1,18 @@ +/** + * Provides a predicate for identifying formatting functions like `printf`. + * + * Consider using the newer model in + * `semmle.code.cpp.models.interfaces.FormattingFunction` directly instead of + * this library. + */ + import semmle.code.cpp.commons.Printf import external.ExternalArtifact +/** + * Holds if `func` is a `printf`-like formatting function and `formatArg` is + * the index of the format string argument. + */ predicate printfLikeFunction(Function func, int formatArg) { formatArg = func.(FormattingFunction).getFormatParameterIndex() and not func instanceof UserDefinedFormattingFunction diff --git a/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll b/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll index 8d35d80e613a..64babe419c36 100644 --- a/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll +++ b/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll @@ -1,4 +1,4 @@ -/* +/** * Security pack options. * * see https://semmle.com/wiki/display/SD/_Configuring+SecurityOptions+for+your+code+base @@ -9,6 +9,10 @@ import semmle.code.cpp.security.Security +/** + * This class overrides `SecurityOptions` and can be used to add project + * specific customization. + */ class CustomSecurityOptions extends SecurityOptions { override predicate sqlArgument(string function, int arg) { SecurityOptions.super.sqlArgument(function, arg) diff --git a/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll b/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll index 25d68fde7162..553cc98351cb 100644 --- a/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll +++ b/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll @@ -1,5 +1,14 @@ +/** + * Provides classes for heuristically identifying variables and functions that + * might contain or return a password or other sensitive information. + */ + import cpp +/** + * Holds if the name `s` suggests something might contain or return a password + * or other sensitive information. + */ bindingset[s] private predicate suspicious(string s) { ( @@ -16,14 +25,23 @@ private predicate suspicious(string s) { ) } +/** + * A variable that might contain a password or other sensitive information. + */ class SensitiveVariable extends Variable { SensitiveVariable() { suspicious(getName().toLowerCase()) } } +/** + * A function that might return a password or other sensitive information. + */ class SensitiveFunction extends Function { SensitiveFunction() { suspicious(getName().toLowerCase()) } } +/** + * An expression whose value might be a password or other sensitive information. + */ class SensitiveExpr extends Expr { SensitiveExpr() { this.(VariableAccess).getTarget() instanceof SensitiveVariable or diff --git a/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll index 06cf4c456ce1..4b206d984dc4 100644 --- a/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll @@ -252,11 +252,10 @@ private predicate insideFunctionValueMoveTo(Element src, Element dest) { copyValueBetweenArguments(c.getTarget(), sourceArg, destArg) and // Only consider copies from `printf`-like functions if the format is a string ( - exists(FormattingFunctionCall ffc, FormatLiteral format, string argFormat | + exists(FormattingFunctionCall ffc, FormatLiteral format | ffc = c and format = ffc.getFormat() and - format.getConversionChar(sourceArg - ffc.getTarget().getNumberOfParameters()) = argFormat and - (argFormat = "s" or argFormat = "S") + format.getConversionChar(sourceArg - ffc.getTarget().getNumberOfParameters()) = ["s", "S"] ) or not exists(FormatLiteral fl | fl = c.(FormattingFunctionCall).getFormat()) @@ -273,12 +272,12 @@ private predicate insideFunctionValueMoveTo(Element src, Element dest) { dest = c ) or - exists(FormattingFunctionCall formattingSend, int arg, FormatLiteral format, string argFormat | + exists(FormattingFunctionCall formattingSend, int arg, FormatLiteral format | dest = formattingSend and formattingSend.getArgument(arg) = src and format = formattingSend.getFormat() and - format.getConversionChar(arg - formattingSend.getTarget().getNumberOfParameters()) = argFormat and - (argFormat = "s" or argFormat = "S" or argFormat = "@") + format.getConversionChar(arg - formattingSend.getTarget().getNumberOfParameters()) = + ["s", "S", "@"] ) or // Expressions computed from tainted data are also tainted diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll index 4f69f52caf40..3bebc660456f 100644 --- a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll +++ b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll @@ -1,3 +1,7 @@ +/** + * Provides a class to model C/C++ block statements, enclosed by `{` and `}`. + */ + import semmle.code.cpp.Element import semmle.code.cpp.stmts.Stmt @@ -13,8 +17,8 @@ import semmle.code.cpp.stmts.Stmt * } * ``` */ -class Block extends Stmt, @stmt_block { - override string getCanonicalQLClass() { result = "Block" } +class BlockStmt extends Stmt, @stmt_block { + override string getAPrimaryQlClass() { result = "BlockStmt" } /** * Gets a child declaration of this block. @@ -72,8 +76,8 @@ class Block extends Stmt, @stmt_block { * the result is the expression statement `a = b`. */ Stmt getLastStmtIn() { - if getLastStmt() instanceof Block - then result = getLastStmt().(Block).getLastStmtIn() + if getLastStmt() instanceof BlockStmt + then result = getLastStmt().(BlockStmt).getLastStmtIn() else result = getLastStmt() } @@ -122,3 +126,9 @@ class Block extends Stmt, @stmt_block { override predicate mayBeGloballyImpure() { this.getAStmt().mayBeGloballyImpure() } } + +/** + * DEPRECATED: This is now called `BlockStmt` to avoid confusion with + * `BasicBlock`. + */ +deprecated class Block = BlockStmt; diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll index 1640bee0f35d..08840f2c5054 100644 --- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll @@ -1,3 +1,7 @@ +/** + * Provides a hierarchy of classes for modeling C/C++ statements. + */ + import semmle.code.cpp.Element private import semmle.code.cpp.Enclosing private import semmle.code.cpp.internal.ResolveClass @@ -21,10 +25,10 @@ class Stmt extends StmtParent, @stmt { /** * Gets the nearest enclosing block of this statement in the source, if any. */ - Block getEnclosingBlock() { + BlockStmt getEnclosingBlock() { if - getParentStmt() instanceof Block and - not getParentStmt().(Block).getLocation() instanceof UnknownLocation + getParentStmt() instanceof BlockStmt and + not getParentStmt().(BlockStmt).getLocation() instanceof UnknownLocation then result = getParentStmt() else result = getParentStmt().getEnclosingBlock() } @@ -49,7 +53,7 @@ class Stmt extends StmtParent, @stmt { * to trace the flow of control instead. */ Stmt getFollowingStmt() { - exists(Block b, int i | + exists(BlockStmt b, int i | this = b.getStmt(i) and result = b.getStmt(i + 1) ) @@ -133,12 +137,14 @@ class Stmt extends StmtParent, @stmt { predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) } } +private class TStmtParent = @stmt or @expr; + /** * An element that is the parent of a statement in the C/C++ AST. * * This is normally a statement, but may be a `StmtExpr`. */ -abstract class StmtParent extends ControlFlowNode { } +class StmtParent extends ControlFlowNode, TStmtParent { } /** * A C/C++ 'expression' statement. @@ -150,7 +156,7 @@ abstract class StmtParent extends ControlFlowNode { } * is an assignment expression inside an 'expression' statement. */ class ExprStmt extends Stmt, @stmt_expr { - override string getCanonicalQLClass() { result = "ExprStmt" } + override string getAPrimaryQlClass() { result = "ExprStmt" } /** * Gets the expression of this 'expression' statement. @@ -175,28 +181,32 @@ class ExprStmt extends Stmt, @stmt_expr { } } +private class TControlStructure = TConditionalStmt or TLoop; + /** * A C/C++ control structure, that is, either a conditional statement or * a loop. */ -abstract class ControlStructure extends Stmt { +class ControlStructure extends Stmt, TControlStructure { /** * Gets the controlling expression of this control structure. * * This is the condition of 'if' statements and loops, and the * switched expression for 'switch' statements. */ - abstract Expr getControllingExpr(); + Expr getControllingExpr() { none() } // overridden by subclasses /** Gets a child declaration of this scope. */ Declaration getADeclaration() { none() } } +private class TConditionalStmt = @stmt_if or @stmt_constexpr_if or @stmt_switch; + /** * A C/C++ conditional statement, that is, either an 'if' statement or a * 'switch' statement. */ -abstract class ConditionalStmt extends ControlStructure { } +class ConditionalStmt extends ControlStructure, TConditionalStmt { } /** * A C/C++ 'if' statement. For example, the `if` statement in the following @@ -208,7 +218,7 @@ abstract class ConditionalStmt extends ControlStructure { } * ``` */ class IfStmt extends ConditionalStmt, @stmt_if { - override string getCanonicalQLClass() { result = "IfStmt" } + override string getAPrimaryQlClass() { result = "IfStmt" } /** * Gets the condition expression of this 'if' statement. @@ -230,7 +240,7 @@ class IfStmt extends ConditionalStmt, @stmt_if { * ``` * if (b) { x = 1; } * ``` - * the result is the `Block` `{ x = 1; }`. + * the result is the `BlockStmt` `{ x = 1; }`. */ Stmt getThen() { if_then(underlyingElement(this), unresolveElement(result)) } @@ -241,7 +251,7 @@ class IfStmt extends ConditionalStmt, @stmt_if { * ``` * if (b) { x = 1; } else { x = 2; } * ``` - * the result is the `Block` `{ x = 2; }`, and for + * the result is the `BlockStmt` `{ x = 2; }`, and for * ``` * if (b) { x = 1; } * ``` @@ -294,7 +304,7 @@ class IfStmt extends ConditionalStmt, @stmt_if { * ``` */ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if { - override string getCanonicalQLClass() { result = "ConstexprIfStmt" } + override string getAPrimaryQlClass() { result = "ConstexprIfStmt" } /** * Gets the condition expression of this 'constexpr if' statement. @@ -316,7 +326,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if { * ``` * if constexpr (b) { x = 1; } * ``` - * the result is the `Block` `{ x = 1; }`. + * the result is the `BlockStmt` `{ x = 1; }`. */ Stmt getThen() { constexpr_if_then(underlyingElement(this), unresolveElement(result)) } @@ -327,7 +337,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if { * ``` * if constexpr (b) { x = 1; } else { x = 2; } * ``` - * the result is the `Block` `{ x = 2; }`, and for + * the result is the `BlockStmt` `{ x = 2; }`, and for * ``` * if constexpr (b) { x = 1; } * ``` @@ -370,16 +380,18 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if { } } +private class TLoop = @stmt_while or @stmt_end_test_while or @stmt_range_based_for or @stmt_for; + /** * A C/C++ loop, that is, either a 'while' loop, a 'for' loop, or a * 'do' loop. */ -abstract class Loop extends ControlStructure { +class Loop extends ControlStructure, TLoop { /** Gets the condition expression of this loop. */ - abstract Expr getCondition(); + Expr getCondition() { none() } // overridden in subclasses /** Gets the body statement of this loop. */ - abstract Stmt getStmt(); + Stmt getStmt() { none() } // overridden in subclasses } /** @@ -393,7 +405,7 @@ abstract class Loop extends ControlStructure { * ``` */ class WhileStmt extends Loop, @stmt_while { - override string getCanonicalQLClass() { result = "WhileStmt" } + override string getAPrimaryQlClass() { result = "WhileStmt" } override Expr getCondition() { result = this.getChild(0) } @@ -457,8 +469,8 @@ class WhileStmt extends Loop, @stmt_while { /** * A C/C++ jump statement. */ -abstract class JumpStmt extends Stmt, @jump { - override string getCanonicalQLClass() { result = "JumpStmt" } +class JumpStmt extends Stmt, @jump { + override string getAPrimaryQlClass() { result = "JumpStmt" } /** Gets the target of this jump statement. */ Stmt getTarget() { jumpinfo(underlyingElement(this), _, unresolveElement(result)) } @@ -475,7 +487,7 @@ abstract class JumpStmt extends Stmt, @jump { * ``` */ class GotoStmt extends JumpStmt, @stmt_goto { - override string getCanonicalQLClass() { result = "GotoStmt" } + override string getAPrimaryQlClass() { result = "GotoStmt" } /** * Gets the name of the label this 'goto' statement refers to. @@ -570,7 +582,7 @@ class ComputedGotoStmt extends Stmt, @stmt_assigned_goto { * ``` */ class ContinueStmt extends JumpStmt, @stmt_continue { - override string getCanonicalQLClass() { result = "ContinueStmt" } + override string getAPrimaryQlClass() { result = "ContinueStmt" } override string toString() { result = "continue;" } @@ -602,7 +614,7 @@ private Stmt getEnclosingContinuable(Stmt s) { * ``` */ class BreakStmt extends JumpStmt, @stmt_break { - override string getCanonicalQLClass() { result = "BreakStmt" } + override string getAPrimaryQlClass() { result = "BreakStmt" } override string toString() { result = "break;" } @@ -635,7 +647,7 @@ private Stmt getEnclosingBreakable(Stmt s) { * ``` */ class LabelStmt extends Stmt, @stmt_label { - override string getCanonicalQLClass() { result = "LabelStmt" } + override string getAPrimaryQlClass() { result = "LabelStmt" } /** Gets the name of this 'label' statement. */ string getName() { jumpinfo(underlyingElement(this), result, _) and result != "" } @@ -650,6 +662,67 @@ class LabelStmt extends Stmt, @stmt_label { override predicate mayBeGloballyImpure() { none() } } +/** + * A C/C++ `co_return` statement. + * + * For example: + * ``` + * co_return 1+2; + * ``` + * or + * ``` + * co_return; + * ``` + */ +class CoReturnStmt extends Stmt, @stmt_co_return { + override string getAPrimaryQlClass() { result = "CoReturnStmt" } + + /** + * Gets the operand of this 'co_return' statement. + * + * For example, for + * ``` + * co_return 1+2; + * ``` + * the operand is a function call `return_value(1+2)`, and for + * ``` + * co_return; + * ``` + * the operand is a function call `return_void()`. + */ + FunctionCall getOperand() { result = this.getChild(0) } + + /** + * Gets the expression of this 'co_return' statement, if any. + * + * For example, for + * ``` + * co_return 1+2; + * ``` + * the result is `1+2`, and there is no result for + * ``` + * co_return; + * ``` + */ + Expr getExpr() { result = this.getOperand().getArgument(0) } + + /** + * Holds if this 'co_return' statement has an expression. + * + * For example, this holds for + * ``` + * co_return 1+2; + * ``` + * but not for + * ``` + * co_return; + * ``` + */ + predicate hasExpr() { exists(this.getExpr()) } + + override string toString() { result = "co_return ..." } +} + /** * A C/C++ 'return' statement. * @@ -663,7 +736,7 @@ class LabelStmt extends Stmt, @stmt_label { * ``` */ class ReturnStmt extends Stmt, @stmt_return { - override string getCanonicalQLClass() { result = "ReturnStmt" } + override string getAPrimaryQlClass() { result = "ReturnStmt" } /** * Gets the expression of this 'return' statement. @@ -711,7 +784,7 @@ class ReturnStmt extends Stmt, @stmt_return { * ``` */ class DoStmt extends Loop, @stmt_end_test_while { - override string getCanonicalQLClass() { result = "DoStmt" } + override string getAPrimaryQlClass() { result = "DoStmt" } override Expr getCondition() { result = this.getChild(0) } @@ -760,7 +833,7 @@ class DoStmt extends Loop, @stmt_end_test_while { * where `begin_expr` and `end_expr` depend on the type of `xs`. */ class RangeBasedForStmt extends Loop, @stmt_range_based_for { - override string getCanonicalQLClass() { result = "RangeBasedForStmt" } + override string getAPrimaryQlClass() { result = "RangeBasedForStmt" } /** * Gets the 'body' statement of this range-based 'for' statement. @@ -769,7 +842,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for { * ``` * for (int x : xs) { y += x; } * ``` - * the result is the `Block` `{ y += x; }`. + * the result is the `BlockStmt` `{ y += x; }`. */ override Stmt getStmt() { result = this.getChild(5) } @@ -847,7 +920,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for { * ``` */ class ForStmt extends Loop, @stmt_for { - override string getCanonicalQLClass() { result = "ForStmt" } + override string getAPrimaryQlClass() { result = "ForStmt" } /** * Gets the initialization statement of this 'for' statement. @@ -1078,7 +1151,7 @@ private predicate inForUpdate(Expr forUpdate, Expr child) { * ``` */ class SwitchCase extends Stmt, @stmt_switch_case { - override string getCanonicalQLClass() { result = "SwitchCase" } + override string getAPrimaryQlClass() { result = "SwitchCase" } /** * Gets the expression of this 'switch case' statement (or the start of @@ -1156,7 +1229,7 @@ class SwitchCase extends Stmt, @stmt_switch_case { * DEPRECATED: use `SwitchCase.getAStmt` or `ControlFlowNode.getASuccessor` * rather than this predicate. * - * Gets the `Block` statement immediately following this 'switch case' + * Gets the `BlockStmt` statement immediately following this 'switch case' * statement, if any. * * For example, for @@ -1177,7 +1250,7 @@ class SwitchCase extends Stmt, @stmt_switch_case { * the `case 7:` has result `{ x = 2; break; }`, `default:` has result * `{ x = 3; }`, and the others have no result. */ - deprecated Block getLabelledStmt() { + deprecated BlockStmt getLabelledStmt() { exists(int i, Stmt parent | this = parent.getChild(i) and result = parent.getChild(i + 1) @@ -1258,7 +1331,7 @@ class SwitchCase extends Stmt, @stmt_switch_case { * `default:` has results `{ x = 3; }, `x = 4;` and `break;`. */ Stmt getAStmt() { - exists(Block b, int i, int j | + exists(BlockStmt b, int i, int j | b.getStmt(i) = this and b.getStmt(j) = result and i < j and @@ -1297,8 +1370,8 @@ class SwitchCase extends Stmt, @stmt_switch_case { exists(Stmt lastStmt | lastStmt = this.getAStmt() and not lastStmt.getFollowingStmt() = this.getAStmt() and - if lastStmt instanceof Block - then result = lastStmt.(Block).getLastStmtIn() + if lastStmt instanceof BlockStmt + then result = lastStmt.(BlockStmt).getLastStmtIn() else result = lastStmt ) } @@ -1431,7 +1504,7 @@ class DefaultCase extends SwitchCase { * ``` */ class SwitchStmt extends ConditionalStmt, @stmt_switch { - override string getCanonicalQLClass() { result = "SwitchStmt" } + override string getAPrimaryQlClass() { result = "SwitchStmt" } /** * Gets the expression that this 'switch' statement switches on. @@ -1642,7 +1715,7 @@ class EnumSwitch extends SwitchStmt { class Handler extends Stmt, @stmt_handler { override string toString() { result = "" } - override string getCanonicalQLClass() { result = "Handler" } + override string getAPrimaryQlClass() { result = "Handler" } /** * Gets the block containing the implementation of this handler. @@ -1695,7 +1768,7 @@ deprecated class FinallyEnd extends Stmt { * ``` */ class TryStmt extends Stmt, @stmt_try_block { - override string getCanonicalQLClass() { result = "TryStmt" } + override string getAPrimaryQlClass() { result = "TryStmt" } override string toString() { result = "try { ... }" } @@ -1770,7 +1843,7 @@ class TryStmt extends Stmt, @stmt_try_block { class FunctionTryStmt extends TryStmt { FunctionTryStmt() { not exists(this.getEnclosingBlock()) } - override string getCanonicalQLClass() { result = "FunctionTryStmt" } + override string getAPrimaryQlClass() { result = "FunctionTryStmt" } } /** @@ -1786,8 +1859,8 @@ class FunctionTryStmt extends TryStmt { * } * ``` */ -class CatchBlock extends Block { - override string getCanonicalQLClass() { result = "CatchBlock" } +class CatchBlock extends BlockStmt { + override string getAPrimaryQlClass() { result = "CatchBlock" } CatchBlock() { ishandler(underlyingElement(this)) } @@ -1818,7 +1891,7 @@ class CatchBlock extends Block { class CatchAnyBlock extends CatchBlock { CatchAnyBlock() { not exists(this.getParameter()) } - override string getCanonicalQLClass() { result = "CatchAnyBlock" } + override string getAPrimaryQlClass() { result = "CatchAnyBlock" } } /** @@ -1852,10 +1925,10 @@ class MicrosoftTryExceptStmt extends MicrosoftTryStmt { /** Gets the expression guarding the `__except` statement. */ Expr getCondition() { result = getChild(1) } - /** Gets the `__except` statement (usually a `Block`). */ + /** Gets the `__except` statement (usually a `BlockStmt`). */ Stmt getExcept() { result = getChild(2) } - override string getCanonicalQLClass() { result = "MicrosoftTryExceptStmt" } + override string getAPrimaryQlClass() { result = "MicrosoftTryExceptStmt" } } /** @@ -1876,10 +1949,10 @@ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt { override string toString() { result = "__try { ... } __finally { ... }" } - /** Gets the `__finally` statement (usually a `Block`). */ + /** Gets the `__finally` statement (usually a `BlockStmt`). */ Stmt getFinally() { result = getChild(1) } - override string getCanonicalQLClass() { result = "MicrosoftTryFinallyStmt" } + override string getAPrimaryQlClass() { result = "MicrosoftTryFinallyStmt" } } /** @@ -1891,7 +1964,7 @@ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt { * ``` */ class DeclStmt extends Stmt, @stmt_decl { - override string getCanonicalQLClass() { result = "DeclStmt" } + override string getAPrimaryQlClass() { result = "DeclStmt" } /** * Gets the `i`th declaration entry declared by this 'declaration' statement. @@ -1972,7 +2045,7 @@ class DeclStmt extends Stmt, @stmt_decl { * ``` */ class EmptyStmt extends Stmt, @stmt_empty { - override string getCanonicalQLClass() { result = "EmptyStmt" } + override string getAPrimaryQlClass() { result = "EmptyStmt" } override string toString() { result = ";" } @@ -1992,7 +2065,7 @@ class EmptyStmt extends Stmt, @stmt_empty { class AsmStmt extends Stmt, @stmt_asm { override string toString() { result = "asm statement" } - override string getCanonicalQLClass() { result = "AsmStmt" } + override string getAPrimaryQlClass() { result = "AsmStmt" } } /** @@ -2009,7 +2082,7 @@ class AsmStmt extends Stmt, @stmt_asm { class VlaDimensionStmt extends Stmt, @stmt_set_vla_size { override string toString() { result = "VLA dimension size" } - override string getCanonicalQLClass() { result = "VlaDimensionStmt" } + override string getAPrimaryQlClass() { result = "VlaDimensionStmt" } /** Gets the expression which gives the size. */ Expr getDimensionExpr() { result = this.getChild(0) } @@ -2028,14 +2101,14 @@ class VlaDimensionStmt extends Stmt, @stmt_set_vla_size { class VlaDeclStmt extends Stmt, @stmt_vla_decl { override string toString() { result = "VLA declaration" } - override string getCanonicalQLClass() { result = "VlaDeclStmt" } + override string getAPrimaryQlClass() { result = "VlaDeclStmt" } /** * Gets the number of VLA dimension statements in this VLA * declaration statement. */ int getNumberOfVlaDimensionStmts() { - exists(Block b, int j | + exists(BlockStmt b, int j | this = b.getStmt(j) and result = j - 1 - @@ -2052,7 +2125,7 @@ class VlaDeclStmt extends Stmt, @stmt_vla_decl { */ VlaDimensionStmt getVlaDimensionStmt(int i) { i in [0 .. this.getNumberOfVlaDimensionStmts() - 1] and - exists(Block b, int j | + exists(BlockStmt b, int j | this = b.getStmt(j) and result = b.getStmt(j - this.getNumberOfVlaDimensionStmts() + i) ) diff --git a/cpp/ql/src/semmle/uml/MagicDraw.qll b/cpp/ql/src/semmle/uml/MagicDraw.qll index 3cd4701a05dd..cb8fb761c1f4 100644 --- a/cpp/ql/src/semmle/uml/MagicDraw.qll +++ b/cpp/ql/src/semmle/uml/MagicDraw.qll @@ -65,15 +65,8 @@ class UMLElement extends XMLElement { */ class UMLType extends UMLElement { UMLType() { - exists(string type | - this.getName() = "packagedElement" and - this.getAttribute("type").getValue() = type and - ( - type = "uml:Class" or - type = "uml:Interface" or - type = "uml:PrimitiveType" - ) - ) + this.getName() = "packagedElement" and + this.getAttribute("type").getValue() = ["uml:Class", "uml:Interface", "uml:PrimitiveType"] } /** diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme b/cpp/ql/src/semmlecode.cpp.dbscheme index 282c13bfdbcb..ef73d8cf906d 100644 --- a/cpp/ql/src/semmlecode.cpp.dbscheme +++ b/cpp/ql/src/semmlecode.cpp.dbscheme @@ -414,13 +414,35 @@ function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); function_return_type(int id: @function ref, int return_type: @type ref); +/** If `function` is a coroutine, then this gives the + std::experimental::resumable_traits instance associated with it, + and the variables representing the `handle` and `promise` for it. */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + purefunctions(unique int id: @function ref); function_deleted(unique int id: @function ref); function_defaulted(unique int id: @function ref); - +member_function_this_type(unique int id: @function ref, int this_type: @type ref); #keyset[id, type_id] fun_decls( @@ -508,7 +530,8 @@ static_asserts( unique int id: @static_assert, int condition : @expr ref, string message : string ref, - int location: @location_default ref + int location: @location_default ref, + int enclosing : @element ref ); // each function has an ordered list of parameters @@ -1227,6 +1250,8 @@ funbind( | @builtinaddressof | @vec_fill | @un_log_op_expr + | @co_await + | @co_yield ; @bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; @@ -1646,6 +1671,8 @@ case @expr.kind of | 324 = @builtinconvertvector | 325 = @builtincomplex | 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield ; @var_args_expr = @vastartexpr @@ -1850,6 +1877,7 @@ case @stmt.kind of | 33 = @stmt_handler // ... 34 @stmt_finally_end deprecated | 35 = @stmt_constexpr_if +| 37 = @stmt_co_return ; type_vla( @@ -1934,20 +1962,6 @@ stmtparents( ishandler(unique int block: @stmt_block ref); @cfgnode = @stmt | @expr | @function | @initialiser ; -successors( - int from: @cfgnode ref, - int to: @cfgnode ref -); - -truecond( - unique int from: @cfgnode ref, - int to: @cfgnode ref -); - -falsecond( - unique int from: @cfgnode ref, - int to: @cfgnode ref -); stmt_decl_bind( int stmt: @stmt_decl ref, diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme.stats b/cpp/ql/src/semmlecode.cpp.dbscheme.stats index 49f84494fd76..eb699cba62e0 100644 --- a/cpp/ql/src/semmlecode.cpp.dbscheme.stats +++ b/cpp/ql/src/semmlecode.cpp.dbscheme.stats @@ -1,7 +1,7 @@ @compilation -9550 +9552 @externalDataElement @@ -9,7 +9,7 @@ @duplication -185065 +185064 @similarity @@ -25,11 +25,11 @@ @location_default -8811984 +8813832 @location_stmt -2176425 +2176409 @location_expr @@ -37,67 +37,67 @@ @diagnostic -68684 +68699 @file -60011 +60023 @folder -10943 +10945 @macroinvocation -35573465 +35580051 @function -3467587 +3468314 @fun_decl -3539879 +3540621 @var_decl -5359112 +5360115 @type_decl -1331883 +1332162 @namespace_decl -136842 +136871 @using -291383 +291444 @static_assert -130659 +130658 @parameter -4627013 +4627983 @membervariable -305626 +305691 @globalvariable -301146 +301143 @localvariable -521220 +521215 @enumconstant -93840 +93839 @builtintype @@ -105,31 +105,31 @@ @derivedtype -4416924 +4463123 @decltype -46995 +47005 @usertype -4193074 +4193942 @mangledname -483588 +483689 @type_mention -1677549 +1677532 @routinetype -430397 +430487 @ptrtomember -12631 +12634 @specifier @@ -137,7 +137,7 @@ @gnuattribute -413730 +413727 @stdattribute @@ -145,7 +145,7 @@ @declspec -57828 +57827 @msattribute @@ -165,7 +165,7 @@ @attribute_arg_constant -146276 +146275 @attribute_arg_type @@ -173,19 +173,19 @@ @derivation -390188 +390270 @frienddecl -240297 +240347 @comment -1580009 +1580341 @namespace -7686 +7688 @specialnamequalifyingelement @@ -193,7 +193,7 @@ @namequalifier -1114421 +1114410 @value @@ -201,11 +201,11 @@ @initialiser -1664822 +1664806 @errorexpr -48706 +48716 @address_of @@ -213,15 +213,15 @@ @reference_to -1058087 +1057914 @indirect -294273 +294271 @ref_indirect -1253933 +1253867 @array_to_pointer @@ -229,7 +229,7 @@ @vacuous_destructor_call -5120 +5121 @assume @@ -237,7 +237,7 @@ @parexpr -2996467 +2996445 @arithnegexpr @@ -253,7 +253,7 @@ @notexpr -337996 +337994 @conjugation @@ -277,31 +277,31 @@ @preincrexpr -62511 +62524 @predecrexpr -24791 +24797 @conditionalexpr -154262 +154261 @addexpr -246479 +246477 @subexpr -134812 +134811 @mulexpr -140470 +140469 @divexpr -63655 +63654 @remexpr @@ -333,7 +333,7 @@ @paddexpr -87144 +87143 @psubexpr @@ -341,11 +341,11 @@ @pdiffexpr -25076 +25082 @lshiftexpr -350150 +350147 @rshiftexpr @@ -353,11 +353,11 @@ @andexpr -257685 +257683 @orexpr -143373 +143371 @xorexpr @@ -365,15 +365,15 @@ @eqexpr -212358 +212356 @neexpr -88347 +88346 @gtexpr -43882 +43881 @ltexpr @@ -385,7 +385,7 @@ @leexpr -213634 +213633 @minexpr @@ -397,7 +397,7 @@ @assignexpr -550955 +550951 @assignaddexpr @@ -409,7 +409,7 @@ @assignmulexpr -6907 +6909 @assigndivexpr @@ -449,19 +449,19 @@ @andlogicalexpr -130796 +130795 @orlogicalexpr -74562 +74561 @commaexpr -10734 +10726 @subscriptexpr -165978 +165977 @virtfunptrexpr @@ -469,7 +469,7 @@ @callexpr -226788 +226781 @vastartexpr @@ -477,7 +477,7 @@ @vaargexpr -986 +987 @vaendexpr @@ -489,27 +489,27 @@ @varaccess -5318255 +5318202 @thisaccess -1167122 +1167110 @new_expr -32127 +32134 @delete_expr -5964 +5966 @throw_expr -22313 +22318 @condition_decl -7028 +7008 @braced_init_list @@ -517,11 +517,11 @@ @type_id -4418 +4419 @runtime_sizeof -278886 +278884 @runtime_alignof @@ -533,11 +533,11 @@ @expr_stmt -156384 +156383 @routineexpr -2265935 +2265785 @type_operand @@ -661,15 +661,15 @@ @ctordirectinit -90175 +90194 @ctorvirtualinit -6228 +6229 @ctorfieldinit -196009 +196051 @ctordelegatinginit @@ -677,7 +677,7 @@ @dtordirectdestruct -29133 +29140 @dtorvirtualdestruct @@ -685,11 +685,11 @@ @dtorfielddestruct -29901 +29907 @static_cast -214716 +214761 @reinterpret_cast @@ -697,11 +697,11 @@ @const_cast -5307 +5308 @dynamic_cast -986 +987 @c_style_cast @@ -713,7 +713,7 @@ @param_ref -85877 +85895 @noopexpr @@ -817,7 +817,7 @@ @noexceptexpr -17522 +17525 @builtinshufflevector @@ -829,7 +829,7 @@ @builtinaddressof -3969 +3970 @vec_fill @@ -848,20 +848,28 @@ 1 +@co_await +6 + + +@co_yield +1 + + @lambdacapture -21653 +21652 @stmt_expr -1269739 +1269727 @stmt_if -523914 +523910 @stmt_while -30997 +30993 @stmt_goto @@ -873,23 +881,23 @@ @stmt_return -1130006 +1130211 @stmt_block -1324997 +1325121 @stmt_end_test_while -149714 +149713 @stmt_for -32154 +32153 @stmt_switch_case -271391 +271389 @stmt_switch @@ -897,11 +905,11 @@ @stmt_asm -241255 +241253 @stmt_try_block -17960 +17964 @stmt_microsoft_try @@ -909,7 +917,7 @@ @stmt_decl -613063 +613092 @stmt_set_vla_size @@ -925,7 +933,7 @@ @stmt_empty -102357 +102356 @stmt_continue @@ -933,7 +941,7 @@ @stmt_break -223843 +223842 @stmt_range_based_for @@ -948,40 +956,44 @@ 3 +@stmt_co_return +2 + + @ppd_if -156064 +156097 @ppd_ifdef -61074 +61087 @ppd_ifndef -83311 +83329 @ppd_elif -20625 +20629 @ppd_else -57653 +57665 @ppd_endif -300451 +300514 @ppd_plain_include -290703 +290764 @ppd_define -318006 +318073 @ppd_undef -19243 +19247 @ppd_line @@ -1038,11 +1050,11 @@ compilations -9550 +9552 id -9550 +9552 cwd @@ -1060,7 +1072,7 @@ 1 2 -9550 +9552 @@ -1086,7 +1098,7 @@ compilation_args -375671 +375669 id @@ -1098,7 +1110,7 @@ arg -18603 +18602 @@ -1382,11 +1394,11 @@ compilation_compiling_files -9550 +9552 id -9550 +9552 num @@ -1394,7 +1406,7 @@ file -4846 +4847 @@ -1408,7 +1420,7 @@ 1 2 -9550 +9552 @@ -1424,7 +1436,7 @@ 1 2 -9550 +9552 @@ -1477,7 +1489,7 @@ 2 3 -4550 +4551 3 @@ -1498,7 +1510,7 @@ 1 2 -4846 +4847 @@ -1508,11 +1520,11 @@ compilation_time -38114 +38122 id -9528 +9530 num @@ -1524,7 +1536,7 @@ seconds -12094 +12700 @@ -1538,7 +1550,7 @@ 1 2 -9528 +9530 @@ -1554,7 +1566,7 @@ 4 5 -9528 +9530 @@ -1570,17 +1582,17 @@ 2 3 -10 +21 3 4 -2675 +2599 4 5 -6842 +6909 @@ -1626,8 +1638,8 @@ 12 -1103 -1104 +1158 +1159 10 @@ -1684,13 +1696,13 @@ 10 -579 -580 +590 +591 10 -670 -671 +694 +695 10 @@ -1707,22 +1719,22 @@ 1 2 -7949 +8653 2 3 -2401 +2588 3 -4 -932 +5 +1074 -4 -627 -811 +5 +629 +383 @@ -1738,7 +1750,7 @@ 1 2 -12094 +12700 @@ -1754,17 +1766,17 @@ 1 2 -10285 +11131 2 3 -1798 +1535 3 4 -10 +32 @@ -1774,15 +1786,15 @@ diagnostic_for -846935 +847112 diagnostic -68684 +68699 compilation -9221 +9223 file_number @@ -1790,7 +1802,7 @@ file_number_diagnostic_number -6502 +6503 @@ -1804,12 +1816,12 @@ 1 2 -9254 +9256 2 3 -56732 +56744 254 @@ -1830,7 +1842,7 @@ 1 2 -68684 +68699 @@ -1846,7 +1858,7 @@ 1 2 -68684 +68699 @@ -1867,7 +1879,7 @@ 7 8 -5811 +5812 8 @@ -1903,7 +1915,7 @@ 1 2 -9221 +9223 @@ -1924,7 +1936,7 @@ 7 8 -5811 +5812 8 @@ -2008,7 +2020,7 @@ 1 2 -2675 +2676 2 @@ -2018,7 +2030,7 @@ 5 6 -953 +954 7 @@ -2069,7 +2081,7 @@ 10 11 -953 +954 14 @@ -2104,7 +2116,7 @@ 254 255 -2620 +2621 309 @@ -2125,7 +2137,7 @@ 1 2 -6502 +6503 @@ -2135,19 +2147,19 @@ compilation_finished -9550 +9552 id -9550 +9552 cpu_seconds -8157 +8093 elapsed_seconds -186 +219 @@ -2161,7 +2173,7 @@ 1 2 -9550 +9552 @@ -2177,7 +2189,7 @@ 1 2 -9550 +9552 @@ -2193,17 +2205,17 @@ 1 2 -7160 +6931 2 3 -756 +954 3 -6 -241 +7 +208 @@ -2219,12 +2231,12 @@ 1 2 -7675 +7764 2 3 -482 +329 @@ -2240,17 +2252,22 @@ 1 2 -32 +54 2 3 -10 +21 3 4 -32 +10 + + +5 +6 +10 7 @@ -2258,48 +2275,53 @@ 10 -8 -9 +9 +10 10 -21 -22 +11 +12 10 -26 -27 +17 +18 10 -31 -32 +41 +42 10 -104 -105 +46 +47 10 -137 -138 +47 +48 10 -144 -145 +86 +87 10 -173 -174 +157 +158 10 -206 -207 +183 +184 +10 + + +250 +251 10 @@ -2316,17 +2338,22 @@ 1 2 -32 +54 2 3 -10 +21 3 4 -32 +10 + + +5 +6 +10 7 @@ -2334,38 +2361,38 @@ 10 -8 -9 +9 +10 10 -21 -22 +11 +12 10 -25 -26 +17 +18 10 -29 -30 +40 +41 10 -84 -85 +44 +45 10 -122 -123 +46 +47 10 -132 -133 +73 +74 10 @@ -2374,8 +2401,13 @@ 10 -196 -197 +164 +165 +10 + + +190 +191 10 @@ -2624,11 +2656,11 @@ duplicateCode -185065 +185064 id -185065 +185064 relativePath @@ -2650,7 +2682,7 @@ 1 2 -185065 +185064 @@ -2666,7 +2698,7 @@ 1 2 -185065 +185064 @@ -3146,11 +3178,11 @@ tokens -39551856 +39551564 id -278635 +278633 offset @@ -3158,7 +3190,7 @@ beginLine -784463 +784457 beginColumn @@ -3166,7 +3198,7 @@ endLine -784463 +784457 endColumn @@ -3270,7 +3302,7 @@ 5 6 -109619 +109618 6 @@ -3285,7 +3317,7 @@ 8 12 -23598 +23597 12 @@ -3310,7 +3342,7 @@ 28 151 -14527 +14526 @@ -3341,7 +3373,7 @@ 32 33 -163370 +163368 33 @@ -3361,7 +3393,7 @@ 80 132 -2860 +2859 @@ -3382,7 +3414,7 @@ 5 6 -109619 +109618 6 @@ -3397,7 +3429,7 @@ 8 12 -23598 +23597 12 @@ -3422,7 +3454,7 @@ 28 151 -14527 +14526 @@ -3453,7 +3485,7 @@ 32 33 -163658 +163657 33 @@ -3819,12 +3851,12 @@ 1 2 -403450 +403447 2 3 -103003 +103002 3 @@ -3834,7 +3866,7 @@ 4 6 -70444 +70443 6 @@ -3844,7 +3876,7 @@ 8 13 -65884 +65883 13 @@ -3870,7 +3902,7 @@ 7 12 -64233 +64232 12 @@ -3885,17 +3917,17 @@ 32 33 -266937 +266935 33 41 -62613 +62612 41 55 -61281 +61280 55 @@ -3931,7 +3963,7 @@ 5 9 -62576 +62575 9 @@ -3951,7 +3983,7 @@ 32 33 -348865 +348862 33 @@ -3961,12 +3993,12 @@ 37 42 -61459 +61458 42 122 -53542 +53541 @@ -3982,7 +4014,7 @@ 1 2 -784463 +784457 @@ -4023,7 +4055,7 @@ 32 33 -349865 +349862 33 @@ -4038,7 +4070,7 @@ 43 123 -47190 +47189 @@ -4454,12 +4486,12 @@ 1 2 -403450 +403447 2 3 -103003 +103002 3 @@ -4469,7 +4501,7 @@ 4 6 -70444 +70443 6 @@ -4479,7 +4511,7 @@ 8 13 -65884 +65883 13 @@ -4505,7 +4537,7 @@ 7 12 -64233 +64232 12 @@ -4520,17 +4552,17 @@ 32 33 -266937 +266935 33 41 -62613 +62612 41 55 -61281 +61280 55 @@ -4561,7 +4593,7 @@ 1 2 -784463 +784457 @@ -4582,7 +4614,7 @@ 5 9 -62576 +62575 9 @@ -4602,7 +4634,7 @@ 32 33 -348865 +348862 33 @@ -4612,12 +4644,12 @@ 37 42 -61459 +61458 42 122 -53542 +53541 @@ -4658,7 +4690,7 @@ 32 33 -349865 +349862 33 @@ -4673,7 +4705,7 @@ 43 123 -47190 +47189 @@ -5279,11 +5311,11 @@ header_to_external_package -8530 +8532 fileid -8530 +8532 package @@ -5301,7 +5333,7 @@ 1 2 -8530 +8532 @@ -6570,31 +6602,31 @@ locations_default -8811984 +8813832 id -8811984 +8813832 container -70954 +70969 startLine -150604 +150635 startColumn -5460 +5461 endLine -150428 +150460 endColumn -10625 +10627 @@ -6608,7 +6640,7 @@ 1 2 -8811984 +8813832 @@ -6624,7 +6656,7 @@ 1 2 -8811984 +8813832 @@ -6640,7 +6672,7 @@ 1 2 -8811984 +8813832 @@ -6656,7 +6688,7 @@ 1 2 -8811984 +8813832 @@ -6672,7 +6704,7 @@ 1 2 -8811984 +8813832 @@ -6688,62 +6720,62 @@ 1 2 -11524 +11526 2 19 -6096 +6097 19 25 -5482 +5483 25 31 -5515 +5516 31 41 -5822 +5823 41 54 -5526 +5527 54 72 -5603 +5604 72 99 -5416 +5417 99 137 -5383 +5384 137 220 -5328 +5330 220 430 -5328 +5330 430 20913 -3925 +3926 @@ -6759,57 +6791,57 @@ 1 2 -11524 +11526 2 15 -6008 +6010 15 20 -6140 +6141 20 25 -5548 +5549 25 32 -6096 +6097 32 41 -5679 +5681 41 53 -5723 +5724 53 71 -5592 +5593 71 99 -5449 +5450 99 158 -5328 +5330 158 351 -5361 +5363 351 @@ -6830,57 +6862,57 @@ 1 2 -11524 +11526 2 4 -6008 +6010 4 8 -6546 +6547 8 11 -5394 +5395 11 14 -5778 +5779 14 18 -6250 +6251 18 23 -5756 +5757 23 29 -5833 +5834 29 37 -5690 +5692 37 50 -5668 +5670 50 78 -5383 +5384 78 @@ -6901,62 +6933,62 @@ 1 2 -11524 +11526 2 15 -5986 +5988 15 20 -6162 +6163 20 25 -5493 +5494 25 32 -6107 +6108 32 41 -5668 +5670 41 53 -5723 +5724 53 70 -5383 +5384 70 96 -5339 +5341 96 153 -5394 +5395 153 333 -5350 +5352 333 9356 -2817 +2818 @@ -6972,57 +7004,57 @@ 1 2 -11524 +11526 2 14 -5734 +5735 14 19 -6118 +6119 19 23 -5712 +5713 23 28 -6414 +6415 28 33 -5515 +5516 33 40 -5953 +5955 40 47 -5350 +5352 47 57 -5603 +5604 57 69 -5635 +5637 69 91 -5328 +5330 91 @@ -7043,52 +7075,52 @@ 1 2 -30844 +30850 2 3 -18026 +18030 3 4 -17949 +17953 4 5 -9714 +9717 5 7 -13706 +13709 7 9 -13410 +13412 9 13 -13201 +13204 13 32 -11568 +11570 32 127 -11304 +11307 127 6472 -10877 +10879 @@ -7104,42 +7136,42 @@ 1 2 -55647 +55658 2 3 -33432 +33439 3 4 -9901 +9903 4 5 -8432 +8433 5 8 -12905 +12908 8 27 -11491 +11493 27 123 -11337 +11340 123 6472 -7456 +7457 @@ -7155,52 +7187,52 @@ 1 2 -31951 +31958 2 3 -17861 +17865 3 4 -19791 +19795 4 5 -9583 +9585 5 7 -13914 +13917 7 9 -13826 +13829 9 13 -12708 +12711 13 27 -11480 +11482 27 62 -11348 +11351 62 153 -8136 +8137 @@ -7216,22 +7248,22 @@ 1 2 -112950 +112973 2 3 -17412 +17416 3 7 -12532 +12535 7 184 -7708 +7709 @@ -7247,52 +7279,52 @@ 1 2 -31798 +31805 2 3 -17774 +17777 3 4 -18640 +18644 4 5 -9868 +9870 5 7 -13728 +13731 7 9 -13772 +13774 9 13 -12730 +12733 13 29 -11655 +11658 29 74 -11348 +11351 74 258 -9287 +9289 @@ -7435,7 +7467,7 @@ 2 3 -997 +998 3 @@ -7501,7 +7533,7 @@ 2 3 -997 +998 3 @@ -7613,52 +7645,52 @@ 1 2 -30559 +30565 2 3 -18059 +18063 3 4 -17807 +17810 4 5 -9868 +9870 5 7 -13772 +13774 7 9 -13454 +13456 9 13 -13048 +13051 13 31 -11458 +11460 31 124 -11293 +11296 124 6472 -11107 +11109 @@ -7674,42 +7706,42 @@ 1 2 -55362 +55373 2 3 -33333 +33340 3 4 -9868 +9870 4 5 -8541 +8543 5 8 -13103 +13105 8 27 -11326 +11329 27 121 -11293 +11296 121 6472 -7598 +7600 @@ -7725,22 +7757,22 @@ 1 2 -112160 +112184 2 3 -17247 +17251 3 7 -12072 +12074 7 46 -8947 +8949 @@ -7756,57 +7788,57 @@ 1 2 -31644 +31651 2 3 -17938 +17942 3 4 -19736 +19741 4 5 -9605 +9607 5 6 -7861 +7863 6 7 -6129 +6130 7 9 -13837 +13840 9 13 -12708 +12711 13 27 -11491 +11493 27 62 -11348 +11351 62 153 -8125 +8126 @@ -7822,52 +7854,52 @@ 1 2 -31546 +31552 2 3 -17741 +17745 3 4 -18629 +18633 4 5 -9934 +9936 5 7 -13815 +13818 7 9 -13772 +13774 9 13 -12785 +12787 13 29 -11513 +11515 29 74 -11359 +11362 74 258 -9331 +9333 @@ -7883,7 +7915,7 @@ 1 2 -4210 +4211 2 @@ -7898,7 +7930,7 @@ 4 5 -646 +647 5 @@ -7939,17 +7971,17 @@ 1 2 -4835 +4836 2 3 -1282 +1283 3 4 -953 +954 4 @@ -7990,7 +8022,7 @@ 1 2 -4243 +4244 2 @@ -8005,7 +8037,7 @@ 4 5 -635 +636 5 @@ -8046,17 +8078,17 @@ 1 2 -4868 +4869 2 3 -1326 +1327 3 4 -942 +943 4 @@ -8097,7 +8129,7 @@ 1 2 -4243 +4244 2 @@ -8112,7 +8144,7 @@ 4 5 -635 +636 5 @@ -8147,19 +8179,19 @@ locations_stmt -2176425 +2176409 id -2176425 +2176409 container -3173 +3172 startLine -296354 +296351 startColumn @@ -8167,7 +8199,7 @@ endLine -294555 +294553 endColumn @@ -8185,7 +8217,7 @@ 1 2 -2176425 +2176409 @@ -8201,7 +8233,7 @@ 1 2 -2176425 +2176409 @@ -8217,7 +8249,7 @@ 1 2 -2176425 +2176409 @@ -8233,7 +8265,7 @@ 1 2 -2176425 +2176409 @@ -8249,7 +8281,7 @@ 1 2 -2176425 +2176409 @@ -8650,7 +8682,7 @@ 1 2 -113657 +113656 2 @@ -8660,7 +8692,7 @@ 3 4 -31282 +31281 4 @@ -8696,12 +8728,12 @@ 1 2 -175337 +175336 2 3 -51977 +51976 3 @@ -8737,7 +8769,7 @@ 1 2 -130651 +130650 2 @@ -8778,7 +8810,7 @@ 1 2 -191442 +191440 2 @@ -8814,7 +8846,7 @@ 1 2 -155827 +155826 2 @@ -8829,7 +8861,7 @@ 4 9 -24672 +24671 9 @@ -9255,7 +9287,7 @@ 6 16 -23107 +23106 16 @@ -9281,7 +9313,7 @@ 1 2 -175049 +175048 2 @@ -9317,7 +9349,7 @@ 1 2 -193725 +193723 2 @@ -9337,7 +9369,7 @@ 8 32 -14613 +14612 @@ -9353,22 +9385,22 @@ 1 2 -132812 +132811 2 3 -67971 +67970 3 4 -32442 +32441 4 7 -26685 +26684 7 @@ -9378,7 +9410,7 @@ 16 46 -12514 +12513 @@ -9394,7 +9426,7 @@ 1 2 -155722 +155721 2 @@ -11616,23 +11648,23 @@ numlines -499158 +499263 element_id -492196 +492299 num_lines -9342 +9344 num_code -7291 +7293 num_comment -3936 +3937 @@ -11646,12 +11678,12 @@ 1 2 -485310 +485411 2 7 -6886 +6887 @@ -11667,12 +11699,12 @@ 1 2 -485364 +485466 2 7 -6831 +6832 @@ -11688,7 +11720,7 @@ 1 2 -492119 +492222 2 @@ -11709,7 +11741,7 @@ 1 2 -4254 +4255 2 @@ -11760,12 +11792,12 @@ 1 2 -4320 +4321 2 3 -1260 +1261 3 @@ -11811,12 +11843,12 @@ 1 2 -4309 +4310 2 3 -1260 +1261 3 @@ -11857,7 +11889,7 @@ 1 2 -3179 +3180 2 @@ -11892,7 +11924,7 @@ 101 7978 -328 +329 @@ -11908,7 +11940,7 @@ 1 2 -3201 +3202 2 @@ -11918,7 +11950,7 @@ 3 4 -635 +636 4 @@ -11928,7 +11960,7 @@ 6 10 -635 +636 10 @@ -11959,7 +11991,7 @@ 1 2 -3190 +3191 2 @@ -11969,7 +12001,7 @@ 3 4 -635 +636 4 @@ -11994,7 +12026,7 @@ 27 34 -317 +318 @@ -12010,7 +12042,7 @@ 1 2 -1885 +1886 2 @@ -12025,12 +12057,12 @@ 4 7 -328 +329 7 13 -328 +329 14 @@ -12061,7 +12093,7 @@ 1 2 -1896 +1897 2 @@ -12081,7 +12113,7 @@ 7 13 -328 +329 13 @@ -12112,7 +12144,7 @@ 1 2 -1896 +1897 2 @@ -12127,7 +12159,7 @@ 4 7 -328 +329 7 @@ -12157,11 +12189,11 @@ diagnostics -68684 +68699 id -68684 +68699 severity @@ -12177,7 +12209,7 @@ full_error_message -59474 +59486 location @@ -12195,7 +12227,7 @@ 1 2 -68684 +68699 @@ -12211,7 +12243,7 @@ 1 2 -68684 +68699 @@ -12227,7 +12259,7 @@ 1 2 -68684 +68699 @@ -12243,7 +12275,7 @@ 1 2 -68684 +68699 @@ -12259,7 +12291,7 @@ 1 2 -68684 +68699 @@ -12675,7 +12707,7 @@ 1 2 -59463 +59475 841 @@ -12696,7 +12728,7 @@ 1 2 -59474 +59486 @@ -12712,7 +12744,7 @@ 1 2 -59474 +59486 @@ -12728,7 +12760,7 @@ 1 2 -59474 +59486 @@ -12744,7 +12776,7 @@ 1 2 -59474 +59486 @@ -12854,19 +12886,19 @@ files -60011 +60023 id -60011 +60023 name -60011 +60023 simple -41052 +41061 ext @@ -12888,7 +12920,7 @@ 1 2 -60011 +60023 @@ -12904,7 +12936,7 @@ 1 2 -60011 +60023 @@ -12920,7 +12952,7 @@ 1 2 -60011 +60023 @@ -12936,7 +12968,7 @@ 1 2 -60011 +60023 @@ -12952,7 +12984,7 @@ 1 2 -60011 +60023 @@ -12968,7 +13000,7 @@ 1 2 -60011 +60023 @@ -12984,7 +13016,7 @@ 1 2 -60011 +60023 @@ -13000,7 +13032,7 @@ 1 2 -60011 +60023 @@ -13016,17 +13048,17 @@ 1 2 -31173 +31179 2 3 -6195 +6196 3 7 -3234 +3235 7 @@ -13047,17 +13079,17 @@ 1 2 -31173 +31179 2 3 -6195 +6196 3 7 -3234 +3235 7 @@ -13078,12 +13110,12 @@ 1 2 -36699 +36707 2 3 -3782 +3783 3 @@ -13104,7 +13136,7 @@ 1 2 -41052 +41061 @@ -13362,19 +13394,19 @@ folders -10943 +10945 id -10943 +10945 name -10943 +10945 simple -3135 +3136 @@ -13388,7 +13420,7 @@ 1 2 -10943 +10945 @@ -13404,7 +13436,7 @@ 1 2 -10943 +10945 @@ -13420,7 +13452,7 @@ 1 2 -10943 +10945 @@ -13436,7 +13468,7 @@ 1 2 -10943 +10945 @@ -13457,7 +13489,7 @@ 2 3 -668 +669 3 @@ -13493,7 +13525,7 @@ 2 3 -668 +669 3 @@ -13518,15 +13550,15 @@ containerparent -70932 +70947 parent -10943 +10945 child -70932 +70947 @@ -13540,7 +13572,7 @@ 1 2 -4989 +4990 2 @@ -13591,7 +13623,7 @@ 1 2 -70932 +70947 @@ -13601,11 +13633,11 @@ fileannotations -5149615 +5150695 id -4824 +4825 kind @@ -13613,11 +13645,11 @@ name -54616 +54628 value -44638 +44647 @@ -13852,42 +13884,42 @@ 1 2 -8925 +8927 2 3 -6030 +6032 3 6 -4517 +4518 6 8 -4451 +4452 8 14 -4407 +4408 14 18 -3914 +3915 18 21 -4243 +4244 21 34 -4396 +4397 34 @@ -13897,12 +13929,12 @@ 129 236 -4155 +4156 236 395 -4111 +4112 395 @@ -13923,7 +13955,7 @@ 1 2 -54616 +54628 @@ -13939,12 +13971,12 @@ 1 2 -9978 +9980 2 3 -8048 +8049 3 @@ -13954,42 +13986,42 @@ 4 6 -3958 +3959 6 10 -4879 +4880 10 14 -3486 +3487 14 18 -4495 +4496 18 23 -4199 +4200 23 44 -4396 +4397 44 97 -4155 +4156 97 405 -4100 +4101 421 @@ -14010,7 +14042,7 @@ 1 2 -6896 +6898 2 @@ -14020,7 +14052,7 @@ 5 8 -3234 +3235 8 @@ -14040,22 +14072,22 @@ 25 40 -3234 +3235 40 195 -3519 +3520 195 207 -3508 +3509 207 273 -3662 +3663 273 @@ -14065,12 +14097,12 @@ 328 407 -3837 +3838 407 441 -1282 +1283 @@ -14086,7 +14118,7 @@ 1 2 -44627 +44636 2 @@ -14107,12 +14139,12 @@ 1 2 -6918 +6920 2 5 -2510 +2511 5 @@ -14122,12 +14154,12 @@ 8 16 -3497 +3498 16 18 -3015 +3016 18 @@ -14137,17 +14169,17 @@ 21 31 -3881 +3882 31 41 -3530 +3531 41 54 -3530 +3531 54 @@ -14177,15 +14209,15 @@ inmacroexpansion -60859049 +60858600 id -15688248 +15688133 inv -2531860 +2531842 @@ -14199,47 +14231,47 @@ 1 2 -3618172 +3618145 2 3 -2444422 +2444404 3 4 -1897977 +1897963 4 5 -1930674 +1930660 5 6 -1866536 +1866522 6 7 -968687 +968680 7 8 -1531676 +1531664 8 11 -1254555 +1254546 11 6193 -175545 +175544 @@ -14255,52 +14287,52 @@ 1 2 -616471 +616466 2 3 -369752 +369750 3 4 -160128 +160127 4 5 -256555 +256553 5 7 -183476 +183475 7 9 -150758 +150757 9 12 -221730 +221729 12 22 -202906 +202905 22 45 -191200 +191199 45 153127 -178879 +178878 @@ -14310,15 +14342,15 @@ affectedbymacroexpansion -35484840 +35484578 id -4079168 +4079138 inv -3279104 +3279080 @@ -14332,37 +14364,37 @@ 1 2 -1340494 +1340484 2 3 -718492 +718487 3 4 -682067 +682062 4 6 -320608 +320606 6 10 -313182 +313180 10 24 -307677 +307675 24 64 -309561 +309559 64 @@ -14383,72 +14415,72 @@ 1 2 -252913 +252912 2 3 -209805 +209803 3 4 -202323 +202322 4 5 -257768 +257766 5 6 -301208 +301206 6 7 -247568 +247566 7 8 -230831 +230830 8 9 -227720 +227718 9 10 -182291 +182290 10 12 -285325 +285323 12 16 -297059 +297057 16 23 -275039 +275036 23 60 -248654 +248652 60 526 -60594 +60593 @@ -14458,19 +14490,19 @@ macroinvocations -35573465 +35580051 id -35573465 +35580051 macro_id -81217 +81234 location -761079 +761238 kind @@ -14488,7 +14520,7 @@ 1 2 -35573465 +35580051 @@ -14504,7 +14536,7 @@ 1 2 -35573465 +35580051 @@ -14520,7 +14552,7 @@ 1 2 -35573465 +35580051 @@ -14536,52 +14568,52 @@ 1 2 -17017 +17021 2 3 -16557 +16560 3 4 -3574 +3575 4 6 -7193 +7194 6 11 -6896 +6898 11 19 -6173 +6174 19 40 -6173 +6174 40 105 -6195 +6196 105 487 -6107 +6108 488 196960 -5328 +5330 @@ -14597,32 +14629,32 @@ 1 2 -43212 +43222 2 3 -10745 +10747 3 4 -5339 +5341 4 6 -6973 +6975 6 13 -6721 +6722 13 66 -6107 +6108 66 @@ -14643,12 +14675,12 @@ 1 2 -75077 +75092 2 3 -6140 +6141 @@ -14664,42 +14696,42 @@ 1 2 -284332 +284425 2 3 -177479 +177483 3 4 -43443 +43452 4 5 -58585 +58598 5 8 -63563 +63577 8 17 -61151 +61164 17 80 -57149 +57161 80 258137 -15372 +15376 @@ -14715,12 +14747,12 @@ 1 2 -712383 +712533 2 354 -48695 +48705 @@ -14736,7 +14768,7 @@ 1 2 -761079 +761238 @@ -14755,8 +14787,8 @@ 10 -3216594 -3216595 +3216514 +3216515 10 @@ -14809,15 +14841,15 @@ macroparent -31697444 +31703249 id -31697444 +31703249 parent_id -24675437 +24680109 @@ -14831,7 +14863,7 @@ 1 2 -31697444 +31703249 @@ -14847,17 +14879,17 @@ 1 2 -18985994 +18989648 2 3 -4852266 +4853131 3 88 -837176 +837329 @@ -14867,15 +14899,15 @@ macrolocationbind -4013345 +4013316 id -2798451 +2798431 location -2003426 +2003411 @@ -14889,17 +14921,17 @@ 1 2 -2198274 +2198258 2 3 -338898 +338895 3 7 -231525 +231523 7 @@ -14920,17 +14952,17 @@ 1 2 -1601810 +1601798 2 3 -170839 +170837 3 8 -155317 +155316 8 @@ -14945,11 +14977,11 @@ macro_argument_unexpanded -92630091 +92648459 invocation -27414544 +27419703 argument_index @@ -14957,7 +14989,7 @@ text -312611 +312677 @@ -14971,22 +15003,22 @@ 1 2 -7655047 +7656521 2 3 -11464654 +11466609 3 4 -6260126 +6261428 4 67 -2034716 +2035143 @@ -15002,22 +15034,22 @@ 1 2 -7722986 +7724474 2 3 -11618525 +11620513 3 4 -6088743 +6090010 4 67 -1984288 +1984704 @@ -15033,7 +15065,7 @@ 50787 50788 -635 +636 50989 @@ -15041,8 +15073,8 @@ 54 -756485 -2500192 +756484 +2500138 32 @@ -15059,7 +15091,7 @@ 2 3 -635 +636 13 @@ -15085,57 +15117,57 @@ 1 2 -37894 +37902 2 3 -61118 +61164 3 4 -14089 +14092 4 5 -42138 +42136 5 8 -25296 +25290 8 12 -15120 +15112 12 16 -21765 +21781 16 21 -23936 +23941 21 41 -24967 +24972 41 121 -23794 +23788 121 567376 -22489 +22493 @@ -15151,17 +15183,17 @@ 1 2 -226021 +226068 2 3 -76294 +76310 3 9 -10296 +10298 @@ -15171,11 +15203,11 @@ macro_argument_expanded -92630091 +92648459 invocation -27414544 +27419703 argument_index @@ -15183,7 +15215,7 @@ text -189409 +189448 @@ -15197,22 +15229,22 @@ 1 2 -7655047 +7656521 2 3 -11464654 +11466609 3 4 -6260126 +6261428 4 67 -2034716 +2035143 @@ -15228,22 +15260,22 @@ 1 2 -11162952 +11165031 2 3 -9892725 +9894482 3 4 -5268749 +5269844 4 9 -1090116 +1090345 @@ -15259,7 +15291,7 @@ 50787 50788 -635 +636 50989 @@ -15267,8 +15299,8 @@ 54 -756485 -2500192 +756484 +2500138 32 @@ -15311,22 +15343,22 @@ 1 2 -22861 +22866 2 3 -38180 +38220 3 4 -6239 +6240 4 5 -15186 +15178 5 @@ -15336,37 +15368,37 @@ 6 7 -22039 +22033 7 9 -15471 +15463 9 15 -16480 +16494 15 28 -14254 +14257 28 77 -14331 +14323 77 337 -14320 +14323 338 -1133308 -7291 +1133296 +7293 @@ -15382,17 +15414,17 @@ 1 2 -95614 +95634 2 3 -79474 +79490 3 6 -14243 +14246 6 @@ -15407,15 +15439,15 @@ functions -3467587 +3468314 id -3467587 +3468314 name -288335 +288395 kind @@ -15433,7 +15465,7 @@ 1 2 -3467587 +3468314 @@ -15449,7 +15481,7 @@ 1 2 -3467587 +3468314 @@ -15465,27 +15497,27 @@ 1 2 -195363 +195404 2 3 -28311 +28317 3 5 -26074 +26080 5 13 -22160 +22164 13 123517 -16425 +16428 @@ -15501,7 +15533,7 @@ 1 2 -286876 +286936 2 @@ -15608,15 +15640,15 @@ function_entry_point -1008372 +1008551 id -1005444 +1005633 entry_point -1008372 +1008551 @@ -15630,12 +15662,12 @@ 1 2 -1002791 +1002990 2 9 -2653 +2643 @@ -15651,7 +15683,7 @@ 1 2 -1008372 +1008551 @@ -15661,15 +15693,15 @@ function_return_type -3477751 +3478481 id -3467115 +3467843 return_type -1023372 +1023587 @@ -15683,12 +15715,12 @@ 1 2 -3456973 +3457698 2 6 -10142 +10144 @@ -15704,17 +15736,329 @@ 1 2 -299102 +299165 2 3 -662569 +662708 3 84263 -61699 +61712 + + + + + + + + +coroutine +2 + + +function +2 + + +traits +2 + + +handle +2 + + +promise +2 + + + + +function +traits + + +12 + + +1 +2 +2 + + + + + + +function +handle + + +12 + + +1 +2 +2 + + + + + + +function +promise + + +12 + + +1 +2 +2 + + + + + + +traits +function + + +12 + + +1 +2 +2 + + + + + + +traits +handle + + +12 + + +1 +2 +2 + + + + + + +traits +promise + + +12 + + +1 +2 +2 + + + + + + +handle +function + + +12 + + +1 +2 +2 + + + + + + +handle +traits + + +12 + + +1 +2 +2 + + + + + + +handle +promise + + +12 + + +1 +2 +2 + + + + + + +promise +function + + +12 + + +1 +2 +2 + + + + + + +promise +traits + + +12 + + +1 +2 +2 + + + + + + +promise +handle + + +12 + + +1 +2 +2 + + + + + + + + +coroutine_new +2 + + +function +2 + + +new +1 + + + + +function +new + + +12 + + +1 +2 +2 + + + + + + +new +function + + +12 + + +2 +3 +1 + + + + + + + + +coroutine_delete +2 + + +function +2 + + +delete +1 + + + + +function +delete + + +12 + + +1 +2 +2 + + + + + + +delete +function + + +12 + + +2 +3 +1 @@ -15735,49 +16079,127 @@ function_deleted -56173 +56185 id -56173 +56185 function_defaulted -12927 +12930 id -12927 +12930 +member_function_this_type +523358 + + +id +523358 + + +this_type +171374 + + + + +id +this_type + + +12 + + +1 +2 +523358 + + + + + + +this_type +id + + +12 + + +1 +2 +62546 + + +2 +3 +44560 + + +3 +4 +22296 + + +4 +5 +15014 + + +5 +7 +13687 + + +7 +36 +12875 + + +40 +87 +394 + + + + + + + + fun_decls -3543146 +3543890 id -3539879 +3540621 function -3374154 +3374862 type_id -1010302 +1010514 name -256788 +256842 location -795290 +795456 @@ -15791,7 +16213,7 @@ 1 2 -3539879 +3540621 @@ -15807,12 +16229,12 @@ 1 2 -3536896 +3537638 2 4 -2982 +2983 @@ -15828,7 +16250,7 @@ 1 2 -3539879 +3540621 @@ -15844,7 +16266,7 @@ 1 2 -3539879 +3540621 @@ -15860,12 +16282,12 @@ 1 2 -3238156 +3238835 2 9 -135998 +136027 @@ -15881,12 +16303,12 @@ 1 2 -3357498 +3358203 2 6 -16655 +16659 @@ -15902,7 +16324,7 @@ 1 2 -3374154 +3374862 @@ -15918,12 +16340,12 @@ 1 2 -3286533 +3287222 2 9 -87621 +87639 @@ -15939,17 +16361,17 @@ 1 2 -280275 +280334 2 3 -660947 +661085 3 89606 -69079 +69093 @@ -15965,17 +16387,17 @@ 1 2 -292107 +292168 2 3 -657295 +657433 3 83378 -60899 +60912 @@ -15991,12 +16413,12 @@ 1 2 -943777 +943975 2 7512 -66524 +66538 @@ -16012,17 +16434,17 @@ 1 2 -912922 +913113 2 6 -80132 +80148 6 22467 -17247 +17251 @@ -16038,32 +16460,32 @@ 1 2 -153520 +153552 2 3 -29298 +29304 3 4 -15932 +15935 4 6 -19517 +19521 6 13 -20197 +20201 13 123766 -18322 +18326 @@ -16079,32 +16501,32 @@ 1 2 -164145 +164180 2 3 -28476 +28482 3 4 -14451 +14454 4 7 -21633 +21638 7 25 -19638 +19642 25 123501 -8443 +8444 @@ -16120,17 +16542,17 @@ 1 2 -224288 +224335 2 5 -20866 +20870 5 63265 -11633 +11636 @@ -16146,27 +16568,27 @@ 1 2 -164639 +164673 2 3 -43454 +43463 3 4 -16436 +16439 4 8 -20570 +20574 8 8921 -11688 +11691 @@ -16182,27 +16604,27 @@ 1 2 -522656 +522766 2 3 -143860 +143890 3 5 -64846 +64860 5 125 -59682 +59694 125 3043 -4243 +4244 @@ -16218,22 +16640,22 @@ 1 2 -537580 +537692 2 3 -156821 +156854 3 9 -64331 +64345 9 3043 -36557 +36564 @@ -16249,17 +16671,17 @@ 1 2 -701660 +701807 2 4 -61776 +61789 4 1522 -31853 +31859 @@ -16275,12 +16697,12 @@ 1 2 -770289 +770451 2 134 -25000 +25005 @@ -16290,11 +16712,11 @@ fun_def -1230544 +1230803 id -1230544 +1230803 @@ -16323,11 +16745,11 @@ fun_decl_specifiers -485338 +485334 id -260688 +260686 name @@ -16345,12 +16767,12 @@ 1 2 -69263 +69262 2 3 -158200 +158199 3 @@ -16517,26 +16939,26 @@ fun_decl_empty_throws -1420688 +1420986 fun_decl -1420688 +1420986 fun_decl_noexcept -32905 +32912 fun_decl -32116 +32123 constant -32785 +32792 @@ -16550,7 +16972,7 @@ 1 2 -31326 +31333 2 @@ -16571,7 +16993,7 @@ 1 2 -32664 +32671 2 @@ -16586,11 +17008,11 @@ fun_decl_empty_noexcept -391877 +391959 fun_decl -391877 +391959 @@ -16645,11 +17067,11 @@ param_decl_bind -4650555 +4651530 id -4650555 +4651530 index @@ -16657,7 +17079,7 @@ fun_decl -3071115 +3071759 @@ -16671,7 +17093,7 @@ 1 2 -4650555 +4651530 @@ -16687,7 +17109,7 @@ 1 2 -4650555 +4651530 @@ -16785,22 +17207,22 @@ 1 2 -2194827 +2195287 2 3 -478478 +478579 3 4 -242665 +242716 4 65 -155143 +155176 @@ -16816,22 +17238,22 @@ 1 2 -2194827 +2195287 2 3 -478478 +478579 3 4 -242665 +242716 4 65 -155143 +155176 @@ -16841,27 +17263,27 @@ var_decls -5367960 +5368966 id -5359112 +5360115 variable -5131248 +5132204 type_id -2007358 +2007769 name -126283 +126310 location -1232189 +1232448 @@ -16875,7 +17297,7 @@ 1 2 -5359112 +5360115 @@ -16891,12 +17313,12 @@ 1 2 -5350449 +5351451 2 4 -8662 +8664 @@ -16912,7 +17334,7 @@ 1 2 -5359112 +5360115 @@ -16928,7 +17350,7 @@ 1 2 -5359068 +5360071 2 @@ -16949,12 +17371,12 @@ 1 2 -4943758 +4944675 2 9 -187490 +187529 @@ -16970,12 +17392,12 @@ 1 2 -5095886 +5096835 2 7 -35362 +35369 @@ -16991,12 +17413,12 @@ 1 2 -5113682 +5114635 2 3 -17565 +17569 @@ -17012,12 +17434,12 @@ 1 2 -5023057 +5023990 2 9 -108191 +108214 @@ -17033,22 +17455,22 @@ 1 2 -1580525 +1580878 2 3 -228861 +228876 3 11 -156941 +156974 11 5924 -41030 +41039 @@ -17064,22 +17486,22 @@ 1 2 -1604713 +1605072 2 3 -219628 +219641 3 13 -151108 +151129 13 5424 -31908 +31925 @@ -17095,17 +17517,17 @@ 1 2 -1832533 +1832906 2 5 -151218 +151238 5 772 -23607 +23623 @@ -17121,17 +17543,17 @@ 1 2 -1758157 +1758515 2 4 -154441 +154496 4 3608 -94759 +94757 @@ -17147,42 +17569,42 @@ 1 2 -52083 +52160 2 3 -19276 +19236 3 4 -10910 +10890 4 5 -7686 +7688 5 8 -10515 +10517 8 15 -9506 +9508 15 47 -9528 +9530 47 165630 -6776 +6777 @@ -17198,37 +17620,37 @@ 1 2 -54901 +54978 2 3 -18826 +18786 3 4 -11809 +11789 4 6 -11195 +11197 6 11 -10734 +10736 11 27 -9484 +9486 27 164602 -9331 +9333 @@ -17244,32 +17666,32 @@ 1 2 -76294 +76321 2 3 -16886 +16878 3 4 -8848 +8861 4 7 -10482 +10473 7 27 -9517 +9519 27 125807 -4254 +4255 @@ -17285,32 +17707,32 @@ 1 2 -72807 +72822 2 3 -19024 +19028 3 4 -6973 +6975 4 7 -11184 +11186 7 21 -9747 +9749 21 10073 -6546 +6547 @@ -17326,22 +17748,22 @@ 1 2 -892231 +892506 2 3 -149123 +149067 3 6 -113388 +113423 6 128450 -77445 +77450 @@ -17357,22 +17779,22 @@ 1 2 -940981 +941266 2 3 -114551 +114487 3 6 -102632 +102664 6 128224 -74024 +74029 @@ -17388,17 +17810,17 @@ 1 2 -1055160 +1055403 2 3 -85099 +85095 3 118388 -91930 +91949 @@ -17414,12 +17836,12 @@ 1 2 -1223220 +1223476 2 52 -8969 +8971 @@ -17429,11 +17851,11 @@ var_def -2437164 +2437554 id -2437164 +2437554 @@ -17503,19 +17925,19 @@ type_decls -1331883 +1332162 id -1331883 +1332162 type_id -1300139 +1300412 location -1086618 +1086846 @@ -17529,7 +17951,7 @@ 1 2 -1331883 +1332162 @@ -17545,7 +17967,7 @@ 1 2 -1331883 +1332162 @@ -17561,12 +17983,12 @@ 1 2 -1277047 +1277315 2 24 -23092 +23097 @@ -17582,12 +18004,12 @@ 1 2 -1278341 +1278609 2 24 -21798 +21802 @@ -17603,12 +18025,12 @@ 1 2 -1031168 +1031384 2 506 -55449 +55461 @@ -17624,12 +18046,12 @@ 1 2 -1032648 +1032865 2 506 -53969 +53980 @@ -17639,45 +18061,45 @@ type_def -937549 +937746 id -937549 +937746 type_decl_top -268642 +268698 type_decl -268642 +268698 namespace_decls -136842 +136871 id -136842 +136871 namespace_id -7675 +7677 location -122226 +122252 bodylocation -122555 +122581 @@ -17691,7 +18113,7 @@ 1 2 -136842 +136871 @@ -17707,7 +18129,7 @@ 1 2 -136842 +136871 @@ -17723,7 +18145,7 @@ 1 2 -136842 +136871 @@ -17739,7 +18161,7 @@ 1 2 -3618 +3619 2 @@ -17754,7 +18176,7 @@ 4 7 -646 +647 7 @@ -17790,7 +18212,7 @@ 1 2 -3618 +3619 2 @@ -17805,7 +18227,7 @@ 4 7 -646 +647 7 @@ -17841,7 +18263,7 @@ 1 2 -3618 +3619 2 @@ -17856,7 +18278,7 @@ 4 7 -646 +647 7 @@ -17892,12 +18314,12 @@ 1 2 -113849 +113873 2 8 -8377 +8379 @@ -17913,12 +18335,12 @@ 1 2 -113849 +113873 2 8 -8377 +8379 @@ -17934,7 +18356,7 @@ 1 2 -121481 +121506 2 @@ -17955,12 +18377,12 @@ 1 2 -114540 +114564 2 11 -8015 +8017 @@ -17976,12 +18398,12 @@ 1 2 -114540 +114564 2 9 -8015 +8017 @@ -17997,7 +18419,7 @@ 1 2 -122160 +122186 2 @@ -18012,19 +18434,19 @@ usings -291383 +291444 id -291383 +291444 element_id -46392 +46402 location -23859 +23864 @@ -18038,7 +18460,7 @@ 1 2 -291383 +291444 @@ -18054,7 +18476,7 @@ 1 2 -291383 +291444 @@ -18070,7 +18492,7 @@ 1 2 -39386 +39394 2 @@ -18080,7 +18502,7 @@ 4 127 -3256 +3257 @@ -18096,7 +18518,7 @@ 1 2 -39386 +39394 2 @@ -18106,7 +18528,7 @@ 4 127 -3256 +3257 @@ -18122,12 +18544,12 @@ 1 2 -18059 +18063 2 3 -2236 +2237 3 @@ -18153,12 +18575,12 @@ 1 2 -18059 +18063 2 3 -2236 +2237 3 @@ -18178,15 +18600,15 @@ using_container -458007 +458103 parent -11228 +11230 child -291383 +291444 @@ -18200,12 +18622,12 @@ 1 2 -3212 +3213 2 4 -942 +943 4 @@ -18230,7 +18652,7 @@ 178 179 -1260 +1261 179 @@ -18256,17 +18678,17 @@ 1 2 -215812 +215858 2 3 -50362 +50372 3 11 -23136 +23140 13 @@ -18281,15 +18703,15 @@ static_asserts -130659 +130658 id -130659 +130658 condition -130659 +130658 message @@ -18299,6 +18721,10 @@ location 16721 + +enclosing +1724 + @@ -18311,7 +18737,7 @@ 1 2 -130659 +130658 @@ -18327,7 +18753,7 @@ 1 2 -130659 +130658 @@ -18343,7 +18769,23 @@ 1 2 -130659 +130658 + + + + + + +id +enclosing + + +12 + + +1 +2 +130658 @@ -18359,7 +18801,7 @@ 1 2 -130659 +130658 @@ -18375,7 +18817,7 @@ 1 2 -130659 +130658 @@ -18391,7 +18833,23 @@ 1 2 -130659 +130658 + + + + + + +condition +enclosing + + +12 + + +1 +2 +130658 @@ -18489,7 +18947,7 @@ 1 2 -27554 +27553 2 @@ -18501,6 +18959,42 @@ +message +enclosing + + +12 + + +1 +2 +23513 + + +2 +3 +182 + + +3 +4 +2654 + + +4 +11 +1301 + + +12 +21 +2055 + + + + + + location id @@ -18643,19 +19137,209 @@ + +location +enclosing + + +12 + + +1 +2 +3441 + + +2 +3 +6193 + + +3 +4 +1125 + + +4 +5 +3715 + + +5 +6 +188 + + +13 +14 +2036 + + +16 +21 +19 + + + + + + +enclosing +id + + +12 + + +1 +2 +1171 + + +2 +3 +130 + + +3 +7 +136 + + +9 +108 +136 + + +170 +347 +130 + + +348 +10697 +19 + + + + + + +enclosing +condition + + +12 + + +1 +2 +1171 + + +2 +3 +130 + + +3 +7 +136 + + +9 +108 +136 + + +170 +347 +130 + + +348 +10697 +19 + + + + + + +enclosing +message + + +12 + + +1 +2 +1327 + + +2 +5 +136 + + +5 +180 +130 + + +211 +2870 +130 + + + + + + +enclosing +location + + +12 + + +1 +2 +1314 + + +2 +5 +149 + + +5 +180 +130 + + +211 +1886 +130 + + + + + params -4644074 +4645049 id -4627013 +4627983 function -3043297 +3043935 index @@ -18663,7 +19347,7 @@ type_id -1856458 +1856848 @@ -18677,12 +19361,12 @@ 1 2 -4626344 +4627314 2 69 -668 +669 @@ -18698,7 +19382,7 @@ 1 2 -4627013 +4627983 @@ -18714,12 +19398,12 @@ 1 2 -4611969 +4612936 2 4 -15043 +15047 @@ -18735,22 +19419,22 @@ 1 2 -2166219 +2166673 2 3 -475024 +475124 3 4 -244409 +244460 4 65 -157643 +157676 @@ -18766,22 +19450,22 @@ 1 2 -2166219 +2166673 2 3 -475024 +475124 3 4 -244409 +244460 4 65 -157643 +157676 @@ -18797,22 +19481,22 @@ 1 2 -2282777 +2283256 2 3 -470880 +470978 3 5 -254277 +254331 5 20 -35362 +35369 @@ -18951,22 +19635,22 @@ 1 2 -1504450 +1504765 2 3 -186678 +186717 3 14 -139463 +139492 14 5175 -25866 +25871 @@ -18982,22 +19666,22 @@ 1 2 -1524307 +1524627 2 3 -179814 +179852 3 23 -139693 +139723 23 4690 -12642 +12645 @@ -19013,12 +19697,12 @@ 1 2 -1744791 +1745157 2 65 -111667 +111690 @@ -19028,11 +19712,11 @@ overrides -159059 +159058 new -124550 +124549 old @@ -19050,7 +19734,7 @@ 1 2 -90047 +90046 2 @@ -19116,19 +19800,19 @@ membervariables -310243 +310308 id -305626 +305691 type_id -132895 +132923 name -53213 +53224 @@ -19142,12 +19826,12 @@ 1 2 -301164 +301227 2 7 -4462 +4463 @@ -19163,7 +19847,7 @@ 1 2 -305626 +305691 @@ -19179,22 +19863,22 @@ 1 2 -108048 +108071 2 3 -12269 +12272 3 9 -10054 +10056 9 1712 -2521 +2522 @@ -19210,17 +19894,17 @@ 1 2 -115099 +115123 2 3 -9089 +9091 3 266 -8706 +8708 @@ -19236,32 +19920,32 @@ 1 2 -28092 +28098 2 3 -8168 +8170 3 4 -5372 +5373 4 6 -4013 +4014 6 15 -4089 +4090 15 1967 -3475 +3476 @@ -19277,12 +19961,12 @@ 1 2 -34287 +34294 2 3 -6853 +6854 3 @@ -19292,12 +19976,12 @@ 4 7 -4298 +4299 7 242 -3991 +3992 262 @@ -19312,11 +19996,11 @@ globalvariables -301154 +301151 id -301146 +301143 type_id @@ -19338,7 +20022,7 @@ 1 2 -301138 +301135 2 @@ -19359,7 +20043,7 @@ 1 2 -301146 +301143 @@ -19447,12 +20131,12 @@ 1 2 -291380 +291383 2 33 -3766 +3763 @@ -19468,12 +20152,12 @@ 1 2 -294542 +294545 2 12 -604 +601 @@ -19483,11 +20167,11 @@ localvariables -521298 +521293 id -521220 +521215 type_id @@ -19495,7 +20179,7 @@ name -75239 +75238 @@ -19509,7 +20193,7 @@ 1 2 -521142 +521137 2 @@ -19530,7 +20214,7 @@ 1 2 -521220 +521215 @@ -19546,12 +20230,12 @@ 1 2 -26474 +26473 2 3 -6760 +6759 3 @@ -19592,7 +20276,7 @@ 1 2 -35700 +35699 2 @@ -19623,12 +20307,12 @@ 1 2 -42961 +42960 2 3 -12915 +12914 3 @@ -19638,12 +20322,12 @@ 4 7 -6721 +6720 7 31 -5706 +5705 31 @@ -19752,11 +20436,11 @@ enumconstants -93840 +93839 id -93840 +93839 parent @@ -19772,11 +20456,11 @@ name -72903 +72902 location -75870 +75869 @@ -19790,7 +20474,7 @@ 1 2 -93840 +93839 @@ -19806,7 +20490,7 @@ 1 2 -93840 +93839 @@ -19822,7 +20506,7 @@ 1 2 -93840 +93839 @@ -19838,7 +20522,7 @@ 1 2 -93840 +93839 @@ -19854,7 +20538,7 @@ 1 2 -93840 +93839 @@ -20620,7 +21304,7 @@ 1 2 -65544 +65543 2 @@ -20651,7 +21335,7 @@ 2 3 -6194 +6193 3 @@ -20672,7 +21356,7 @@ 1 2 -67880 +67879 2 @@ -20693,7 +21377,7 @@ 1 2 -61146 +61145 2 @@ -20740,7 +21424,7 @@ 1 2 -70580 +70579 2 @@ -20761,12 +21445,12 @@ 1 2 -70691 +70690 2 229 -5179 +5178 @@ -20782,7 +21466,7 @@ 1 2 -72272 +72271 2 @@ -21556,15 +22240,15 @@ derivedtypes -4416924 +4463123 id -4416924 +4463123 name -2172217 +2202887 kind @@ -21572,7 +22256,7 @@ type_id -2605685 +2610563 @@ -21586,7 +22270,7 @@ 1 2 -4416924 +4463123 @@ -21602,7 +22286,7 @@ 1 2 -4416924 +4463123 @@ -21618,7 +22302,7 @@ 1 2 -4416924 +4463123 @@ -21634,17 +22318,17 @@ 1 2 -1570229 +1593501 2 3 -487437 +493889 3 45177 -114551 +115496 @@ -21660,7 +22344,7 @@ 1 2 -2172184 +2202855 2 @@ -21681,17 +22365,17 @@ 1 2 -1570470 +1593743 2 3 -487206 +493659 3 45159 -114540 +115485 @@ -21720,8 +22404,8 @@ 10 -27116 -27117 +31133 +31134 10 @@ -21735,8 +22419,8 @@ 10 -96890 -96891 +97001 +97002 10 @@ -21776,8 +22460,8 @@ 10 -14760 -14761 +17432 +17433 10 @@ -21786,8 +22470,8 @@ 10 -48258 -48259 +48341 +48342 10 @@ -21822,8 +22506,8 @@ 10 -27116 -27117 +31133 +31134 10 @@ -21837,8 +22521,8 @@ 10 -96559 -96560 +96670 +96671 10 @@ -21860,22 +22544,22 @@ 1 2 -1545963 +1541056 2 3 -372633 +369487 3 4 -633753 +628085 4 202 -53333 +71934 @@ -21891,22 +22575,22 @@ 1 2 -1547180 +1542273 2 3 -372480 +369334 3 4 -632690 +627021 4 198 -53333 +71934 @@ -21922,22 +22606,22 @@ 1 2 -1547498 +1542591 2 3 -373763 +370617 3 4 -632646 +626977 4 7 -51776 +70377 @@ -21947,11 +22631,11 @@ pointerishsize -3331139 +3375893 id -3331139 +3375893 size @@ -21973,7 +22657,7 @@ 1 2 -3331139 +3375893 @@ -21989,7 +22673,7 @@ 1 2 -3331139 +3375893 @@ -22008,8 +22692,8 @@ 10 -303777 -303778 +307794 +307795 10 @@ -22040,8 +22724,8 @@ 12 -303798 -303799 +307815 +307816 10 @@ -22068,11 +22752,11 @@ arraysizes -17193 +17196 id -17193 +17196 num_elements @@ -22080,7 +22764,7 @@ bytesize -2510 +2511 alignment @@ -22098,7 +22782,7 @@ 1 2 -17193 +17196 @@ -22114,7 +22798,7 @@ 1 2 -17193 +17196 @@ -22130,7 +22814,7 @@ 1 2 -17193 +17196 @@ -22151,7 +22835,7 @@ 2 3 -1271 +1272 3 @@ -22192,7 +22876,7 @@ 1 2 -1589 +1590 2 @@ -22228,7 +22912,7 @@ 1 2 -1589 +1590 2 @@ -22315,7 +22999,7 @@ 1 2 -1907 +1908 2 @@ -22346,7 +23030,7 @@ 1 2 -1951 +1952 2 @@ -22504,15 +23188,15 @@ typedefbase -1819320 +1819702 id -1819320 +1819702 type_id -847209 +847375 @@ -22526,7 +23210,7 @@ 1 2 -1819320 +1819702 @@ -22542,22 +23226,22 @@ 1 2 -656517 +656643 2 3 -87895 +87913 3 6 -69583 +69598 6 5503 -33212 +33219 @@ -22567,19 +23251,19 @@ decltypes -46995 +47005 id -46995 +47005 expr -43432 +43441 base_type -8629 +8631 parentheses_would_change_meaning @@ -22597,7 +23281,7 @@ 1 2 -46995 +47005 @@ -22613,7 +23297,7 @@ 1 2 -46995 +47005 @@ -22629,7 +23313,7 @@ 1 2 -46995 +47005 @@ -22645,12 +23329,12 @@ 1 2 -40153 +40162 2 4 -3278 +3279 @@ -22666,12 +23350,12 @@ 1 2 -40153 +40162 2 4 -3278 +3279 @@ -22687,7 +23371,7 @@ 1 2 -43432 +43441 @@ -22703,12 +23387,12 @@ 1 2 -5822 +5823 2 3 -2521 +2522 3 @@ -22729,12 +23413,12 @@ 1 2 -2313 +2314 2 3 -5701 +5702 3 @@ -22755,7 +23439,7 @@ 1 2 -8629 +8631 @@ -22828,15 +23512,15 @@ usertypes -4193074 +4193942 id -4193074 +4193942 name -879226 +879411 kind @@ -22854,7 +23538,7 @@ 1 2 -4193074 +4193942 @@ -22870,7 +23554,7 @@ 1 2 -4193074 +4193942 @@ -22886,22 +23570,22 @@ 1 2 -577350 +577471 2 3 -194759 +194800 3 7 -69298 +69313 7 32744 -37818 +37826 @@ -22917,12 +23601,12 @@ 1 2 -826595 +826768 2 10 -52631 +52642 @@ -22981,8 +23665,8 @@ 10 -95210 -95211 +95209 +95210 10 @@ -23059,11 +23743,11 @@ usertypesize -1386324 +1386604 id -1386324 +1386604 size @@ -23085,7 +23769,7 @@ 1 2 -1386324 +1386604 @@ -23101,7 +23785,7 @@ 1 2 -1386324 +1386604 @@ -23161,7 +23845,7 @@ 284 -99381 +99380 109 @@ -23237,8 +23921,8 @@ 10 -113882 -113883 +113881 +113882 10 @@ -23354,15 +24038,15 @@ mangled_name -4189795 +4190663 id -4189795 +4190663 mangled_name -483588 +483689 @@ -23376,7 +24060,7 @@ 1 2 -4189795 +4190663 @@ -23392,32 +24076,32 @@ 1 2 -292030 +292091 2 3 -62281 +62294 3 4 -33333 +33340 4 7 -36962 +36970 7 24 -37061 +37069 24 8580 -21918 +21923 @@ -23427,59 +24111,59 @@ is_pod_class -589104 +589228 id -589104 +589228 is_standard_layout_class -1158976 +1159208 id -1158976 +1159208 is_complete -1364822 +1365097 id -1364822 +1365097 is_class_template -225308 +225355 id -225308 +225355 class_instantiation -1157583 +1157815 to -1156015 +1156247 from -68015 +68030 @@ -23493,7 +24177,7 @@ 1 2 -1154535 +1154766 2 @@ -23514,47 +24198,47 @@ 1 2 -19956 +19960 2 3 -11995 +11998 3 4 -6853 +6854 4 5 -4616 +4617 5 7 -5635 +5637 7 11 -6052 +6053 11 20 -5197 +5198 20 84 -5109 +5110 84 4845 -2598 +2599 @@ -23564,11 +24248,11 @@ class_template_argument -3035457 +3036083 type_id -1392563 +1392844 index @@ -23576,7 +24260,7 @@ arg_type -860750 +860931 @@ -23590,27 +24274,27 @@ 1 2 -567010 +567118 2 3 -433654 +433744 3 4 -244935 +244987 4 7 -122785 +122811 7 113 -24177 +24182 @@ -23626,22 +24310,22 @@ 1 2 -593238 +593351 2 3 -445879 +445973 3 4 -258389 +258443 4 113 -95055 +95075 @@ -23686,7 +24370,7 @@ 13712 -123926 +123925 43 @@ -23749,27 +24433,27 @@ 1 2 -522185 +522294 2 3 -187402 +187441 3 4 -56699 +56711 4 11 -67379 +67393 11 -11852 -27083 +11851 +27089 @@ -23785,17 +24469,17 @@ 1 2 -746320 +746476 2 3 -95526 +95546 3 22 -18903 +18907 @@ -23805,11 +24489,11 @@ class_template_argument_value -345736 +345798 type_id -223806 +223842 index @@ -23817,7 +24501,7 @@ arg_value -328181 +328239 @@ -23831,17 +24515,17 @@ 1 2 -201448 +201479 2 3 -13706 +13709 3 14 -8651 +8653 @@ -23857,17 +24541,17 @@ 1 2 -189825 +189854 2 3 -16918 +16922 3 37 -16787 +16790 44 @@ -23931,8 +24615,8 @@ 10 -9813 -9814 +9812 +9813 10 @@ -23992,8 +24676,8 @@ 10 -12053 -12054 +12052 +12053 10 @@ -24010,12 +24694,12 @@ 1 2 -310944 +310999 2 4 -17236 +17240 @@ -24031,7 +24715,7 @@ 1 2 -328181 +328239 @@ -24041,15 +24725,15 @@ is_proxy_class_for -46370 +46380 id -46370 +46380 templ_param_id -46370 +46380 @@ -24063,7 +24747,7 @@ 1 2 -46370 +46380 @@ -24079,7 +24763,7 @@ 1 2 -46370 +46380 @@ -24089,19 +24773,19 @@ type_mentions -1677549 +1677532 id -1677549 +1677532 type_id -67210 +67209 location -1647041 +1647024 kind @@ -24119,7 +24803,7 @@ 1 2 -1677549 +1677532 @@ -24135,7 +24819,7 @@ 1 2 -1677549 +1677532 @@ -24151,7 +24835,7 @@ 1 2 -1677549 +1677532 @@ -24167,7 +24851,7 @@ 1 2 -30072 +30071 2 @@ -24177,7 +24861,7 @@ 3 4 -3637 +3636 4 @@ -24192,7 +24876,7 @@ 13 35 -5192 +5191 35 @@ -24213,7 +24897,7 @@ 1 2 -30072 +30071 2 @@ -24223,7 +24907,7 @@ 3 4 -3637 +3636 4 @@ -24238,7 +24922,7 @@ 13 35 -5192 +5191 35 @@ -24259,7 +24943,7 @@ 1 2 -65967 +65966 2 @@ -24280,7 +24964,7 @@ 1 2 -1616871 +1616855 2 @@ -24301,7 +24985,7 @@ 1 2 -1616871 +1616855 2 @@ -24322,7 +25006,7 @@ 1 2 -1647041 +1647024 @@ -24395,26 +25079,26 @@ is_function_template -983580 +983786 id -983580 +983786 function_instantiation -708184 +708332 to -708184 +708332 from -129310 +129337 @@ -24428,7 +25112,7 @@ 1 2 -708184 +708332 @@ -24444,37 +25128,37 @@ 1 2 -60998 +61010 2 3 -30833 +30839 3 4 -7346 +7348 4 5 -8815 +8817 5 10 -10000 +10002 10 71 -9704 +9706 71 653 -1611 +1612 @@ -24484,11 +25168,11 @@ function_template_argument -1910187 +1910587 function_id -1054173 +1054394 index @@ -24496,7 +25180,7 @@ arg_type -338390 +338461 @@ -24510,22 +25194,22 @@ 1 2 -583479 +583601 2 3 -290955 +291016 3 4 -127742 +127768 4 21 -51995 +52006 @@ -24541,22 +25225,22 @@ 1 2 -597898 +598023 2 3 -288642 +288702 3 4 -110921 +110945 4 21 -56710 +56722 @@ -24794,27 +25478,27 @@ 1 2 -226086 +226134 2 3 -45109 +45119 3 6 -27664 +27670 6 19 -25603 +25608 19 2030 -13925 +13928 @@ -24830,12 +25514,12 @@ 1 2 -314771 +314837 2 12 -23618 +23623 @@ -24845,11 +25529,11 @@ function_template_argument_value -198027 +198069 function_id -107062 +107084 index @@ -24857,7 +25541,7 @@ arg_value -170571 +170607 @@ -24871,12 +25555,12 @@ 1 2 -101546 +101567 2 14 -5515 +5516 @@ -24892,17 +25576,17 @@ 1 2 -84945 +84963 2 3 -16173 +16176 3 113 -5943 +5944 @@ -25030,12 +25714,12 @@ 1 2 -143619 +143649 2 3 -26447 +26453 3 @@ -25056,7 +25740,7 @@ 1 2 -170571 +170607 @@ -25066,26 +25750,26 @@ is_variable_template -17807 +17810 id -17807 +17810 variable_instantiation -35800 +35808 to -35800 +35808 from -6469 +6470 @@ -25099,7 +25783,7 @@ 1 2 -35800 +35808 @@ -25115,12 +25799,12 @@ 1 2 -2236 +2237 2 3 -1907 +1908 3 @@ -25507,15 +26191,15 @@ routinetypes -430397 +430487 id -430397 +430487 return_type -177533 +177571 @@ -25529,7 +26213,7 @@ 1 2 -430397 +430487 @@ -25545,22 +26229,22 @@ 1 2 -143082 +143112 2 3 -18125 +18128 3 9 -13322 +13325 9 8267 -3004 +3005 @@ -25570,11 +26254,11 @@ routinetypeargs -719708 +719859 routine -351778 +351852 index @@ -25582,7 +26266,7 @@ type_id -205527 +205570 @@ -25596,27 +26280,27 @@ 1 2 -161897 +161931 2 3 -95932 +95952 3 4 -53925 +53937 4 6 -32324 +32331 6 33 -7697 +7699 @@ -25632,22 +26316,22 @@ 1 2 -186711 +186750 2 3 -96689 +96709 3 4 -47653 +47663 4 22 -20723 +20728 @@ -25805,27 +26489,27 @@ 1 2 -122391 +122416 2 3 -40833 +40842 3 4 -13212 +13215 4 7 -16633 +16637 7 1349 -12456 +12458 @@ -25841,17 +26525,17 @@ 1 2 -154079 +154112 2 3 -39430 +39438 3 33 -12017 +12020 @@ -25861,19 +26545,19 @@ ptrtomembers -12631 +12634 id -12631 +12634 type_id -9396 +9398 class_id -6359 +6361 @@ -25887,7 +26571,7 @@ 1 2 -12631 +12634 @@ -25903,7 +26587,7 @@ 1 2 -12631 +12634 @@ -25919,7 +26603,7 @@ 1 2 -9035 +9037 2 @@ -25940,7 +26624,7 @@ 1 2 -9035 +9037 2 @@ -25961,7 +26645,7 @@ 1 2 -5339 +5341 2 @@ -25987,7 +26671,7 @@ 1 2 -5339 +5341 2 @@ -26055,11 +26739,11 @@ typespecifiers -1331696 +1333215 type_id -1324832 +1326328 spec_id @@ -26077,12 +26761,12 @@ 1 2 -1317968 +1319440 2 3 -6864 +6887 @@ -26111,8 +26795,8 @@ 10 -955 -956 +957 +958 10 @@ -26126,8 +26810,8 @@ 10 -96425 -96426 +96536 +96537 10 @@ -26138,11 +26822,11 @@ funspecifiers -11101011 +11122577 func_id -3417148 +3417865 spec_id @@ -26160,27 +26844,27 @@ 1 2 -342249 +342321 2 3 -436910 +421977 3 4 -842560 +853879 4 5 -1677016 +1680922 5 8 -118410 +118764 @@ -26254,8 +26938,8 @@ 10 -137685 -137686 +139439 +139440 10 @@ -26281,11 +26965,11 @@ varspecifiers -1112020 +1112009 var_id -924807 +924798 spec_id @@ -26303,7 +26987,7 @@ 1 2 -786197 +786189 2 @@ -26384,11 +27068,11 @@ attributes -413730 +413727 id -413730 +413727 kind @@ -26404,7 +27088,7 @@ location -90510 +90509 @@ -26418,7 +27102,7 @@ 1 2 -413730 +413727 @@ -26434,7 +27118,7 @@ 1 2 -413730 +413727 @@ -26450,7 +27134,7 @@ 1 2 -413730 +413727 @@ -26466,7 +27150,7 @@ 1 2 -413730 +413727 @@ -26825,7 +27509,7 @@ 1 2 -90510 +90509 @@ -26877,7 +27561,7 @@ 1 2 -90510 +90509 @@ -26887,11 +27571,11 @@ attribute_args -152615 +152614 id -152615 +152614 kind @@ -26899,7 +27583,7 @@ attribute -151316 +151315 index @@ -26907,7 +27591,7 @@ location -57362 +57361 @@ -26921,7 +27605,7 @@ 1 2 -152615 +152614 @@ -26937,7 +27621,7 @@ 1 2 -152615 +152614 @@ -26953,7 +27637,7 @@ 1 2 -152615 +152614 @@ -26969,7 +27653,7 @@ 1 2 -152615 +152614 @@ -27069,7 +27753,7 @@ 1 2 -150538 +150537 2 @@ -27090,7 +27774,7 @@ 1 2 -150826 +150825 2 @@ -27111,7 +27795,7 @@ 1 2 -150538 +150537 2 @@ -27132,7 +27816,7 @@ 1 2 -150543 +150542 2 @@ -27272,7 +27956,7 @@ 2 3 -8429 +8428 3 @@ -27303,7 +27987,7 @@ 1 2 -51567 +51566 2 @@ -27324,7 +28008,7 @@ 1 2 -27281 +27280 2 @@ -27360,7 +28044,7 @@ 1 2 -57357 +57356 3 @@ -27375,11 +28059,11 @@ attribute_arg_value -152591 +152590 arg -152591 +152590 value @@ -27397,7 +28081,7 @@ 1 2 -152591 +152590 @@ -27544,15 +28228,15 @@ typeattributes -19320 +19324 type_id -17938 +17942 spec_id -19320 +19324 @@ -27566,7 +28250,7 @@ 1 2 -17236 +17240 2 @@ -27587,7 +28271,7 @@ 1 2 -19320 +19324 @@ -27597,15 +28281,15 @@ funcattributes -304333 +304396 func_id -164211 +164245 spec_id -304333 +304396 @@ -27619,22 +28303,22 @@ 1 2 -89671 +89690 2 3 -12576 +12579 3 4 -59956 +59969 4 14 -2006 +2007 @@ -27650,7 +28334,7 @@ 1 2 -304333 +304396 @@ -27660,7 +28344,7 @@ varattributes -371224 +371223 var_id @@ -27668,7 +28352,7 @@ spec_id -371224 +371223 @@ -27682,12 +28366,12 @@ 1 2 -273654 +273655 2 3 -48764 +48763 14 @@ -27708,7 +28392,7 @@ 1 2 -371224 +371223 @@ -27766,15 +28450,15 @@ unspecifiedtype -9068477 +9115641 type_id -9068477 +9115641 unspecified_type_id -4975940 +5012847 @@ -27788,7 +28472,7 @@ 1 2 -9068477 +9115641 @@ -27804,17 +28488,17 @@ 1 2 -2708920 +2736095 2 3 -1952501 +1962057 3 7950 -314519 +314695 @@ -27824,19 +28508,19 @@ member -4920765 +4921797 parent -814336 +814507 index -2675 +2676 child -4905205 +4906234 @@ -27850,47 +28534,47 @@ 1 2 -42313 +42322 2 3 -223740 +223787 3 4 -204584 +204627 4 5 -86908 +86926 5 7 -65888 +65902 7 9 -61601 +61614 9 15 -62039 +62052 15 47 -61173 +61186 47 245 -6085 +6086 @@ -27906,47 +28590,47 @@ 1 2 -41655 +41664 2 3 -223564 +223611 3 4 -199343 +199385 4 5 -89715 +89734 5 7 -66360 +66373 7 9 -61678 +61690 9 15 -62960 +62974 15 42 -61261 +61274 42 281 -7796 +7797 @@ -28104,7 +28788,7 @@ 1 2 -4905205 +4906234 @@ -28120,12 +28804,12 @@ 1 2 -4889854 +4890880 2 7 -15350 +15354 @@ -28135,15 +28819,15 @@ enclosingfunction -125055 +125081 child -125055 +125081 parent -71392 +71407 @@ -28157,7 +28841,7 @@ 1 2 -125055 +125081 @@ -28173,22 +28857,22 @@ 1 2 -38333 +38341 2 3 -21074 +21079 3 4 -6535 +6536 4 7 -5372 +5373 7 @@ -28203,15 +28887,15 @@ derivations -390188 +390270 derivation -390188 +390270 sub -364377 +364453 index @@ -28219,11 +28903,11 @@ super -235659 +235708 location -86963 +86981 @@ -28237,7 +28921,7 @@ 1 2 -390188 +390270 @@ -28253,7 +28937,7 @@ 1 2 -390188 +390270 @@ -28269,7 +28953,7 @@ 1 2 -390188 +390270 @@ -28285,7 +28969,7 @@ 1 2 -390188 +390270 @@ -28301,12 +28985,12 @@ 1 2 -341811 +341883 2 7 -22565 +22570 @@ -28322,12 +29006,12 @@ 1 2 -351427 +351501 2 7 -12949 +12952 @@ -28343,12 +29027,12 @@ 1 2 -341822 +341893 2 7 -22554 +22559 @@ -28364,12 +29048,12 @@ 1 2 -351416 +351490 2 7 -12960 +12963 @@ -28549,12 +29233,12 @@ 1 2 -220878 +220924 2 1142 -14780 +14783 @@ -28570,12 +29254,12 @@ 1 2 -220889 +220935 2 1142 -14769 +14772 @@ -28591,7 +29275,7 @@ 1 2 -235209 +235259 2 @@ -28612,12 +29296,12 @@ 1 2 -228301 +228349 2 439 -7357 +7359 @@ -28633,22 +29317,22 @@ 1 2 -66338 +66352 2 3 -8388 +8389 3 7 -6611 +6613 7 795 -5625 +5626 @@ -28664,22 +29348,22 @@ 1 2 -68574 +68589 2 3 -6370 +6371 3 8 -7039 +7040 8 795 -4978 +4979 @@ -28695,7 +29379,7 @@ 1 2 -86941 +86959 2 @@ -28716,22 +29400,22 @@ 1 2 -69276 +69291 2 3 -8201 +8203 3 9 -6524 +6525 9 795 -2960 +2961 @@ -28741,11 +29425,11 @@ derspecifiers -392568 +392650 der_id -390155 +390237 spec_id @@ -28763,7 +29447,7 @@ 1 2 -387743 +387824 2 @@ -28809,11 +29493,11 @@ direct_base_offsets -310495 +310560 der_id -310495 +310560 offset @@ -28831,7 +29515,7 @@ 1 2 -310495 +310560 @@ -28917,11 +29601,11 @@ virtual_base_offsets -6337 +6339 sub -3508 +3509 super @@ -28974,7 +29658,7 @@ 1 2 -2960 +2961 2 @@ -29208,23 +29892,23 @@ frienddecls -240297 +240347 id -240297 +240347 type_id -27280 +27286 decl_id -49035 +49045 location -7302 +7304 @@ -29238,7 +29922,7 @@ 1 2 -240297 +240347 @@ -29254,7 +29938,7 @@ 1 2 -240297 +240347 @@ -29270,7 +29954,7 @@ 1 2 -240297 +240347 @@ -29286,17 +29970,17 @@ 1 2 -6217 +6218 2 3 -10230 +10232 3 5 -1984 +1985 5 @@ -29306,7 +29990,7 @@ 6 8 -2313 +2314 8 @@ -29337,17 +30021,17 @@ 1 2 -6217 +6218 2 3 -10230 +10232 3 5 -1984 +1985 5 @@ -29357,7 +30041,7 @@ 6 8 -2313 +2314 8 @@ -29388,12 +30072,12 @@ 1 2 -25701 +25707 2 31 -1578 +1579 @@ -29409,12 +30093,12 @@ 1 2 -33684 +33691 2 3 -4989 +4990 3 @@ -29424,12 +30108,12 @@ 7 23 -3804 +3805 23 394 -2532 +2533 @@ -29445,12 +30129,12 @@ 1 2 -33684 +33691 2 3 -4989 +4990 3 @@ -29460,12 +30144,12 @@ 7 23 -3804 +3805 23 394 -2532 +2533 @@ -29481,7 +30165,7 @@ 1 2 -48498 +48508 2 @@ -29502,12 +30186,12 @@ 1 2 -6239 +6240 2 3 -942 +943 3 @@ -29528,7 +30212,7 @@ 1 2 -6864 +6865 2 @@ -29549,7 +30233,7 @@ 1 2 -6250 +6251 2 @@ -29569,19 +30253,19 @@ comments -1580009 +1580341 id -1580009 +1580341 contents -784018 +784182 location -1580009 +1580341 @@ -29595,7 +30279,7 @@ 1 2 -1580009 +1580341 @@ -29611,7 +30295,7 @@ 1 2 -1580009 +1580341 @@ -29627,17 +30311,17 @@ 1 2 -663819 +663959 2 3 -75088 +75103 3 10738 -45109 +45119 @@ -29653,17 +30337,17 @@ 1 2 -663819 +663959 2 3 -75088 +75103 3 10738 -45109 +45119 @@ -29679,7 +30363,7 @@ 1 2 -1580009 +1580341 @@ -29695,7 +30379,7 @@ 1 2 -1580009 +1580341 @@ -29705,15 +30389,15 @@ commentbinding -713206 +713311 id -618260 +618433 element -684434 +684533 @@ -29727,17 +30411,17 @@ 1 2 -556867 +557093 2 3 -49079 +49045 3 97 -12313 +12294 @@ -29753,12 +30437,12 @@ 1 2 -655661 +655755 2 3 -28772 +28778 @@ -29768,15 +30452,15 @@ exprconv -6443933 +6443797 converted -6443639 +6443503 conversion -6443933 +6443797 @@ -29790,7 +30474,7 @@ 1 2 -6443346 +6443210 2 @@ -29811,7 +30495,7 @@ 1 2 -6443933 +6443797 @@ -29821,22 +30505,22 @@ compgenerated -6708045 +6707808 id -6708045 +6707808 synthetic_destructor_call -59605 +59596 element -46392 +46380 i @@ -29844,7 +30528,7 @@ destructor_call -49463 +49451 @@ -29858,17 +30542,17 @@ 1 2 -36951 +36937 2 3 -6765 +6766 3 29 -2675 +2676 @@ -29884,17 +30568,17 @@ 1 2 -36951 +36937 2 3 -6765 +6766 3 29 -2675 +2676 @@ -29928,8 +30612,8 @@ 21 -4231 -4232 +4229 +4230 10 @@ -29964,8 +30648,8 @@ 21 -3565 -3566 +3563 +3564 10 @@ -29982,17 +30666,17 @@ 1 2 -43618 +43605 2 3 -3618 +3619 3 26 -2225 +2226 @@ -30008,7 +30692,7 @@ 1 2 -49463 +49451 @@ -30018,15 +30702,15 @@ namespaces -7686 +7688 id -7686 +7688 name -4133 +4134 @@ -30040,7 +30724,7 @@ 1 2 -7686 +7688 @@ -30056,7 +30740,7 @@ 1 2 -3475 +3476 2 @@ -30087,15 +30771,15 @@ namespacembrs -1603036 +1603361 parentid -7160 +7161 memberid -1603036 +1603361 @@ -30169,7 +30853,7 @@ 778 39485 -317 +318 @@ -30185,7 +30869,7 @@ 1 2 -1603036 +1603361 @@ -30195,11 +30879,11 @@ exprparents -13453568 +13453475 expr_id -13453379 +13453286 child_index @@ -30207,7 +30891,7 @@ parent_id -9530495 +9530420 @@ -30221,7 +30905,7 @@ 1 2 -13453373 +13453279 2 @@ -30242,7 +30926,7 @@ 1 2 -13453197 +13453104 2 @@ -30292,7 +30976,7 @@ 6420 -1188854 +1188857 45 @@ -30338,7 +31022,7 @@ 6405 -1188867 +1188870 45 @@ -30355,17 +31039,17 @@ 1 2 -6759182 +6759115 2 3 -2187728 +2187726 3 1681 -583583 +583578 @@ -30381,17 +31065,17 @@ 1 2 -6759202 +6759135 2 3 -2187709 +2187707 3 480 -583583 +583578 @@ -30401,11 +31085,11 @@ expr_isload -5007422 +5007373 expr_id -5007422 +5007373 @@ -30458,8 +31142,8 @@ 1 -13857 -13858 +13714 +13715 1 @@ -30473,8 +31157,8 @@ 1 -4155321 -4155322 +4155464 +4155465 1 @@ -30485,11 +31169,11 @@ iscall -2320354 +2320215 caller -2320354 +2320215 kind @@ -30507,7 +31191,7 @@ 1 2 -2320354 +2320215 @@ -30526,13 +31210,13 @@ 10 -6429 -6430 +6428 +6429 10 -203800 -203801 +203744 +203745 10 @@ -30543,11 +31227,11 @@ numtemplatearguments -164694 +164695 expr_id -164694 +164695 num @@ -30565,7 +31249,7 @@ 1 2 -164694 +164695 @@ -30594,8 +31278,8 @@ 10 -14307 -14308 +14304 +14305 10 @@ -30654,23 +31338,23 @@ namequalifiers -1114421 +1114410 id -1114421 +1114410 qualifiableelement -1114421 +1114410 qualifyingelement -38862 +38861 location -504707 +504702 @@ -30684,7 +31368,7 @@ 1 2 -1114421 +1114410 @@ -30700,7 +31384,7 @@ 1 2 -1114421 +1114410 @@ -30716,7 +31400,7 @@ 1 2 -1114421 +1114410 @@ -30732,7 +31416,7 @@ 1 2 -1114421 +1114410 @@ -30748,7 +31432,7 @@ 1 2 -1114421 +1114410 @@ -30764,7 +31448,7 @@ 1 2 -1114421 +1114410 @@ -30862,7 +31546,7 @@ 1 2 -24789 +24788 2 @@ -30882,7 +31566,7 @@ 11 16728 -2596 +2595 @@ -30898,17 +31582,17 @@ 1 2 -383625 +383621 2 3 -56715 +56714 3 7 -38758 +38757 7 @@ -30929,17 +31613,17 @@ 1 2 -383625 +383621 2 3 -56715 +56714 3 7 -38758 +38757 7 @@ -30960,7 +31644,7 @@ 1 2 -447849 +447844 2 @@ -30980,15 +31664,15 @@ varbind -5434639 +5434586 expr -5434516 +5434462 var -1532809 +1532794 @@ -31002,7 +31686,7 @@ 1 2 -5434392 +5434339 2 @@ -31023,32 +31707,32 @@ 1 2 -679968 +679961 2 3 -308900 +308897 3 4 -232743 +232741 4 5 -92819 +92818 5 9 -133060 +133059 9 6150 -85317 +85316 @@ -31058,15 +31742,15 @@ funbind -2416575 +2416551 expr -2119711 +2119690 fun -434036 +434031 @@ -31080,12 +31764,12 @@ 1 2 -1823088 +1823070 2 3 -296447 +296444 3 @@ -31106,7 +31790,7 @@ 1 2 -252054 +252052 2 @@ -31141,11 +31825,11 @@ expr_allocator -30329 +30335 expr -30329 +30335 func @@ -31167,7 +31851,7 @@ 1 2 -30329 +30335 @@ -31183,7 +31867,7 @@ 1 2 -30329 +30335 @@ -31292,11 +31976,11 @@ expr_deallocator -33300 +33307 expr -33300 +33307 func @@ -31318,7 +32002,7 @@ 1 2 -33300 +33307 @@ -31334,7 +32018,7 @@ 1 2 -33300 +33307 @@ -31464,15 +32148,15 @@ expr_cond_guard -154262 +154261 cond -154262 +154261 guard -154262 +154261 @@ -31486,7 +32170,7 @@ 1 2 -154262 +154261 @@ -31502,7 +32186,7 @@ 1 2 -154262 +154261 @@ -31512,15 +32196,15 @@ expr_cond_true -154262 +154261 cond -154262 +154261 true -154262 +154261 @@ -31534,7 +32218,7 @@ 1 2 -154262 +154261 @@ -31550,7 +32234,7 @@ 1 2 -154262 +154261 @@ -31560,15 +32244,15 @@ expr_cond_false -154262 +154261 cond -154262 +154261 false -154262 +154261 @@ -31582,7 +32266,7 @@ 1 2 -154262 +154261 @@ -31598,7 +32282,7 @@ 1 2 -154262 +154261 @@ -31787,11 +32471,11 @@ fieldoffsets -251150 +251147 id -251150 +251147 byteoffset @@ -31813,7 +32497,7 @@ 1 2 -251150 +251147 @@ -31829,7 +32513,7 @@ 1 2 -251150 +251147 @@ -32214,23 +32898,23 @@ initialisers -1664822 +1664806 init -1664822 +1664806 var -642778 +642772 expr -1664822 +1664806 location -318685 +318682 @@ -32244,7 +32928,7 @@ 1 2 -1664822 +1664806 @@ -32260,7 +32944,7 @@ 1 2 -1664822 +1664806 @@ -32276,7 +32960,7 @@ 1 2 -1664822 +1664806 @@ -32292,7 +32976,7 @@ 1 2 -556218 +556212 2 @@ -32302,12 +32986,12 @@ 16 17 -49051 +49050 17 53 -8803 +8802 @@ -32323,7 +33007,7 @@ 1 2 -556218 +556212 2 @@ -32333,12 +33017,12 @@ 16 17 -49051 +49050 17 53 -8803 +8802 @@ -32354,7 +33038,7 @@ 1 2 -642765 +642758 2 @@ -32375,7 +33059,7 @@ 1 2 -1664822 +1664806 @@ -32391,7 +33075,7 @@ 1 2 -1664822 +1664806 @@ -32407,7 +33091,7 @@ 1 2 -1664822 +1664806 @@ -32423,7 +33107,7 @@ 1 2 -246094 +246092 2 @@ -32433,7 +33117,7 @@ 3 7 -24249 +24248 7 @@ -32459,12 +33143,12 @@ 1 2 -268678 +268675 2 3 -24971 +24970 3 @@ -32490,7 +33174,7 @@ 1 2 -246094 +246092 2 @@ -32500,7 +33184,7 @@ 3 7 -24249 +24248 7 @@ -32520,15 +33204,15 @@ expr_ancestor -66085 +66077 exp -65384 +65375 ancestor -47105 +47093 @@ -32542,12 +33226,12 @@ 1 2 -64748 +64739 2 4 -635 +636 @@ -32563,12 +33247,12 @@ 1 2 -34945 +34930 2 3 -9704 +9706 3 @@ -32583,11 +33267,11 @@ exprs -18434237 +18434101 id -18434237 +18434101 kind @@ -32595,7 +33279,7 @@ location -3622478 +3623249 @@ -32609,7 +33293,7 @@ 1 2 -18434237 +18434101 @@ -32625,7 +33309,7 @@ 1 2 -18434237 +18434101 @@ -32660,7 +33344,7 @@ 306 -472 +471 87 @@ -32684,23 +33368,23 @@ 87 -4718 +4717 6425 87 -6723 -13441 +6722 +13439 87 17876 -114359 +114329 87 -192896 -428379 +192871 +428312 43 @@ -32775,7 +33459,7 @@ 87 -72726 +72772 118610 21 @@ -32793,37 +33477,37 @@ 1 2 -1679637 +1680483 2 3 -738677 +738624 3 4 -319727 +319926 4 5 -277074 +276890 5 9 -301811 +301720 9 53 -272074 +272120 53 -144742 -33476 +144478 +33483 @@ -32839,17 +33523,17 @@ 1 2 -2586627 +2586676 2 3 -806737 +807411 3 30 -229113 +229161 @@ -32859,15 +33543,15 @@ expr_types -18573393 +18573385 id -18430421 +18430284 typeid -1322332 +1322456 value_category @@ -32885,12 +33569,12 @@ 1 2 -18288907 +18288641 2 4 -141514 +141642 @@ -32906,7 +33590,7 @@ 1 2 -18430421 +18430284 @@ -32922,42 +33606,42 @@ 1 2 -513731 +513839 2 3 -252041 +251951 3 4 -108355 +108499 4 5 -86009 +86038 5 8 -114200 +114147 8 14 -106042 +106075 14 45 -99759 +99747 45 -126323 -42193 +126297 +42158 @@ -32973,17 +33657,17 @@ 1 2 -1170807 +1170932 2 3 -143125 +143123 3 4 -8399 +8400 @@ -33002,13 +33686,13 @@ 10 -370541 -370542 +370443 +370444 10 -1304856 -1304857 +1304589 +1304590 10 @@ -33028,13 +33712,13 @@ 10 -30957 -30958 +30955 +30956 10 -102771 -102772 +102756 +102757 10 @@ -33045,15 +33729,15 @@ new_allocated_type -32127 +32134 expr -32127 +32134 type_id -16513 +16516 @@ -33067,7 +33751,7 @@ 1 2 -32127 +32134 @@ -33083,7 +33767,7 @@ 1 2 -10339 +10342 2 @@ -33708,15 +34392,15 @@ condition_decl_bind -7028 +7008 expr -7028 +7008 decl -7028 +7008 @@ -33730,7 +34414,7 @@ 1 2 -7028 +7008 @@ -33746,7 +34430,7 @@ 1 2 -7028 +7008 @@ -33756,11 +34440,11 @@ typeid_bind -4418 +4419 expr -4418 +4419 type_id @@ -33778,7 +34462,7 @@ 1 2 -4418 +4419 @@ -33877,11 +34561,11 @@ sizeof_bind -156888 +156887 expr -156888 +156887 type_id @@ -33899,7 +34583,7 @@ 1 2 -156888 +156887 @@ -34144,11 +34828,11 @@ lambda_capture -21653 +21652 id -21653 +21652 lambda @@ -34160,7 +34844,7 @@ field -21653 +21652 captured_by_reference @@ -34186,7 +34870,7 @@ 1 2 -21653 +21652 @@ -34202,7 +34886,7 @@ 1 2 -21653 +21652 @@ -34218,7 +34902,7 @@ 1 2 -21653 +21652 @@ -34234,7 +34918,7 @@ 1 2 -21653 +21652 @@ -34250,7 +34934,7 @@ 1 2 -21653 +21652 @@ -34266,7 +34950,7 @@ 1 2 -21653 +21652 @@ -34894,7 +35578,7 @@ 1 2 -21653 +21652 @@ -34910,7 +35594,7 @@ 1 2 -21653 +21652 @@ -34926,7 +35610,7 @@ 1 2 -21653 +21652 @@ -34942,7 +35626,7 @@ 1 2 -21653 +21652 @@ -34958,7 +35642,7 @@ 1 2 -21653 +21652 @@ -34974,7 +35658,7 @@ 1 2 -21653 +21652 @@ -35473,11 +36157,11 @@ stmts -4688614 +4688994 id -4688614 +4688994 kind @@ -35485,7 +36169,7 @@ location -1193856 +1194106 @@ -35499,7 +36183,7 @@ 1 2 -4688614 +4688994 @@ -35515,7 +36199,7 @@ 1 2 -4688614 +4688994 @@ -35549,8 +36233,8 @@ 10 -736 -737 +735 +736 10 @@ -35564,13 +36248,13 @@ 10 -2237 -2238 +2235 +2236 10 -2267 -2268 +2266 +2267 10 @@ -35579,13 +36263,13 @@ 10 -2827 -2828 +2826 +2827 10 -3121 -3122 +3119 +3120 10 @@ -35594,33 +36278,33 @@ 10 -4775 -4776 +4772 +4773 10 -30484 -30485 +30477 +30478 10 -55911 -55912 +55902 +55903 10 -90778 -90779 +90766 +90767 10 -103056 -103057 +103053 +103054 10 -120839 -120840 +120825 +120826 10 @@ -35743,22 +36427,22 @@ 1 2 -677438 +678008 2 3 -181952 +181585 3 4 -107851 +107907 4 6 -102116 +102105 6 @@ -35768,7 +36452,7 @@ 22 5041 -22960 +22965 @@ -35784,12 +36468,12 @@ 1 2 -1170270 +1170515 2 9 -23585 +23590 @@ -35895,15 +36579,15 @@ if_then -523914 +523910 if_stmt -523914 +523910 then_id -523914 +523910 @@ -35917,7 +36601,7 @@ 1 2 -523914 +523910 @@ -35933,7 +36617,7 @@ 1 2 -523914 +523910 @@ -35943,15 +36627,15 @@ if_else -148100 +148099 if_stmt -148100 +148099 else_id -148100 +148099 @@ -35965,7 +36649,7 @@ 1 2 -148100 +148099 @@ -35981,7 +36665,7 @@ 1 2 -148100 +148099 @@ -36087,15 +36771,15 @@ while_body -30997 +30993 while_stmt -30997 +30993 body_id -30997 +30993 @@ -36109,7 +36793,7 @@ 1 2 -30997 +30993 @@ -36125,7 +36809,7 @@ 1 2 -30997 +30993 @@ -36135,15 +36819,15 @@ do_body -149714 +149713 do_stmt -149714 +149713 body_id -149714 +149713 @@ -36157,7 +36841,7 @@ 1 2 -149714 +149713 @@ -36173,7 +36857,7 @@ 1 2 -149714 +149713 @@ -36183,7 +36867,7 @@ switch_case -271391 +271389 switch_stmt @@ -36195,7 +36879,7 @@ case_id -271391 +271389 @@ -36214,7 +36898,7 @@ 5 6 -47131 +47130 6 @@ -36240,7 +36924,7 @@ 5 6 -47131 +47130 6 @@ -36353,7 +37037,7 @@ 1 2 -271391 +271389 @@ -36369,7 +37053,7 @@ 1 2 -271391 +271389 @@ -36523,15 +37207,15 @@ for_update -29454 +29453 for_stmt -29454 +29453 update_id -29454 +29453 @@ -36545,7 +37229,7 @@ 1 2 -29454 +29453 @@ -36561,7 +37245,7 @@ 1 2 -29454 +29453 @@ -36571,15 +37255,15 @@ for_body -32154 +32153 for_stmt -32154 +32153 body_id -32154 +32153 @@ -36593,7 +37277,7 @@ 1 2 -32154 +32153 @@ -36609,7 +37293,7 @@ 1 2 -32154 +32153 @@ -36619,11 +37303,11 @@ stmtparents -4119908 +4119868 id -4119908 +4119868 index @@ -36631,7 +37315,7 @@ parent -1741441 +1741423 @@ -36645,7 +37329,7 @@ 1 2 -4119908 +4119868 @@ -36661,7 +37345,7 @@ 1 2 -4119908 +4119868 @@ -36799,27 +37483,27 @@ 1 2 -991907 +991897 2 3 -382688 +382684 3 4 -108382 +108381 4 6 -114023 +114022 6 17 -131785 +131784 17 @@ -36840,27 +37524,27 @@ 1 2 -991907 +991897 2 3 -382688 +382684 3 4 -108382 +108381 4 6 -114023 +114022 6 17 -131785 +131784 17 @@ -36885,181 +37569,12 @@ -successors -16889416 - - -from -16062711 - - -to -16041364 - - - - -from -to - - -12 - - -1 -2 -15355208 - - -2 -647 -707503 - - - - - - -to -from - - -12 - - -1 -2 -15497963 - - -2 -647 -543400 - - - - - - - - -truecond -965686 - - -from -965686 - - -to -936031 - - - - -from -to - - -12 - - -1 -2 -965686 - - - - - - -to -from - - -12 - - -1 -2 -912488 - - -2 -21 -23542 - - - - - - - - -falsecond -965686 - - -from -965686 - - -to -811117 - - - - -from -to - - -12 - - -1 -2 -965686 - - - - - - -to -from - - -12 - - -1 -2 -697840 - - -2 -3 -86960 - - -3 -25 -26316 - - - - - - - - stmt_decl_bind -532229 +532224 stmt -525358 +525353 num @@ -37067,7 +37582,7 @@ decl -532229 +532224 @@ -37081,7 +37596,7 @@ 1 2 -519919 +519914 2 @@ -37102,7 +37617,7 @@ 1 2 -519900 +519894 2 @@ -37225,7 +37740,7 @@ 1 2 -532229 +532224 @@ -37241,7 +37756,7 @@ 1 2 -532229 +532224 @@ -37251,11 +37766,11 @@ stmt_decl_entry_bind -497400 +497397 stmt -453689 +453686 num @@ -37263,7 +37778,7 @@ decl_entry -473433 +473430 @@ -37277,7 +37792,7 @@ 1 2 -422159 +422156 2 @@ -37298,7 +37813,7 @@ 1 2 -422159 +422156 2 @@ -37386,7 +37901,7 @@ 1 2 -461768 +461765 2 @@ -37407,7 +37922,7 @@ 1 2 -473354 +473350 2 @@ -37422,15 +37937,15 @@ blockscope -1324975 +1325099 block -1324975 +1325099 enclosing -1186772 +1186944 @@ -37444,7 +37959,7 @@ 1 2 -1324975 +1325099 @@ -37460,12 +37975,12 @@ 1 2 -1106388 +1106598 2 509 -80384 +80346 @@ -37475,11 +37990,11 @@ jumpinfo -350611 +350608 id -350611 +350608 str @@ -37501,7 +38016,7 @@ 1 2 -350611 +350608 @@ -37517,7 +38032,7 @@ 1 2 -350611 +350608 @@ -37661,11 +38176,11 @@ preprocdirects -1323352 +1323630 id -1323352 +1323630 kind @@ -37673,7 +38188,7 @@ location -1317036 +1317312 @@ -37687,7 +38202,7 @@ 1 2 -1323352 +1323630 @@ -37703,7 +38218,7 @@ 1 2 -1323352 +1323630 @@ -37871,12 +38386,12 @@ 1 2 -1316707 +1316983 2 235 -328 +329 @@ -37892,7 +38407,7 @@ 1 2 -1317036 +1317312 @@ -37902,15 +38417,15 @@ preprocpair -378730 +378809 begin -300451 +300514 elseelifend -378730 +378809 @@ -37924,17 +38439,17 @@ 1 2 -238576 +238626 2 3 -54539 +54551 3 53 -7335 +7337 @@ -37950,7 +38465,7 @@ 1 2 -378730 +378809 @@ -37960,41 +38475,41 @@ preproctrue -166536 +166571 branch -166536 +166571 preprocfalse -119101 +119126 branch -119101 +119126 preproctext -965236 +965438 id -965236 +965438 head -463478 +463575 body -175549 +175586 @@ -38008,7 +38523,7 @@ 1 2 -965236 +965438 @@ -38024,7 +38539,7 @@ 1 2 -965236 +965438 @@ -38040,22 +38555,22 @@ 1 2 -345901 +345973 2 3 -78158 +78174 3 19 -34769 +34777 19 752 -4649 +4650 @@ -38071,12 +38586,12 @@ 1 2 -441800 +441893 2 38 -21677 +21682 @@ -38092,12 +38607,12 @@ 1 2 -164979 +165013 2 64816 -10570 +10572 @@ -38113,12 +38628,12 @@ 1 2 -166459 +166494 2 21810 -9089 +9091 @@ -38128,15 +38643,15 @@ includes -290791 +290852 id -290791 +290852 included -54594 +54606 @@ -38150,7 +38665,7 @@ 1 2 -290791 +290852 @@ -38166,37 +38681,37 @@ 1 2 -26886 +26891 2 3 -8958 +8960 3 4 -4616 +4617 4 6 -4846 +4847 6 11 -4188 +4189 11 41 -4111 +4112 41 763 -986 +987 @@ -38254,11 +38769,11 @@ link_parent -18149936 +18153711 element -4991686 +4992722 link_target @@ -38276,32 +38791,32 @@ 1 2 -1493156 +1493469 2 3 -1882851 +1883246 3 4 -718710 +718850 4 6 -400791 +400876 6 29 -398072 +398156 29 45 -98103 +98124 @@ -38351,7 +38866,7 @@ 27154 -32621 +32620 43 @@ -38366,7 +38881,7 @@ 46593 -56499 +56498 43 @@ -38375,8 +38890,8 @@ 43 -370737 -370738 +370736 +370737 10 diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll index bbd9090c8e61..9e30de327f83 100644 --- a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll +++ b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -2,6 +2,11 @@ * Provides a library for writing QL tests whose success or failure is based on expected results * embedded in the test source code as comments, rather than a `.expected` file. * + * To add this framework to a new language: + * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class + * must support a `getContents` method that returns the contents of the given comment, _excluding_ + * the comment indicator itself. It should also define `toString` and `getLocation` as usual. + * * To create a new inline expectations test: * - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the * new class, bind `this` to a unique string (usually the name of the test). @@ -13,7 +18,7 @@ * `hasActualResult()`. Often this is just a single tag. * * Example: - * ``` + * ```ql * class ConstantValueTest extends InlineExpectationsTest { * ConstantValueTest() { this = "ConstantValueTest" } * @@ -23,11 +28,11 @@ * } * * override predicate hasActualResult( - * Location location, string element, string tag, string valuesasas + * Location location, string element, string tag, string value * ) { * exists(Expr e | * tag = "const" and // The tag for this test. - * valuesasas = e.getValue() and // The expected value. Will only hold for constant expressions. + * value = e.getValue() and // The expected value. Will only hold for constant expressions. * location = e.getLocation() and // The location of the result to be reported. * element = e.toString() // The display text for the result. * ) @@ -38,10 +43,10 @@ * There is no need to write a `select` clause or query predicate. All of the differences between * expected results and actual results will be reported in the `failures()` query predicate. * - * To annotate the test source code with an expected result, place a C++-style (`//`) comment on the + * To annotate the test source code with an expected result, place a comment on the * same line as the expected result, with text of the following format as the body of the comment: * - * `// $tag=expected-value` + * `$tag=expected-value` * * Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is * the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be @@ -53,7 +58,7 @@ * "Missing result: tag=expected-value". * * Example: - * ``` + * ```cpp * int i = x + 5; // $const=5 * int j = y + (7 - 3) // $const=7 $const=3 $const=4 // The result of the subtraction is a constant. * ``` @@ -62,8 +67,8 @@ * annotate that a particular expected result is known to be a false positive, or that a particular * missing result is known to be a false negative: * - * `// $f+:tag=expected-value` // False positive - * `// $f-:tag=expected-value` // False negative + * `$f+:tag=expected-value` // False positive + * `$f-:tag=expected-value` // False negative * * A false positive expectation is treated as any other expected result, except that if there is no * matching actual result, the message will be of the form "Fixed false positive: tag=value". A @@ -74,14 +79,14 @@ * If the same result value is expected for two or more tags on the same line, there is a shorthand * notation available: * - * `// $tag1,tag2=expected-value` + * `$tag1,tag2=expected-value` * * is equivalent to: * - * `// $tag1=expected-value $tag2=expected-value` + * `$tag1=expected-value $tag2=expected-value` */ -import cpp +private import InlineExpectationsTestPrivate /** * Base class for tests with inline expectations. The test extends this class to provide the actual @@ -150,12 +155,12 @@ abstract class InlineExpectationsTest extends string { } /** - * RegEx pattern to match a comment containing one or more expected results. The comment must be a - * C++-style (`//`) comment with `$` as its first non-whitespace character. Any subsequent character + * RegEx pattern to match a comment containing one or more expected results. The comment must have + * `$` as its first non-whitespace character. Any subsequent character * is treated as part of the expected results, except that the comment may contain a `//` sequence * to treat the remainder of the line as a regular (non-interpreted) comment. */ -private string expectationCommentPattern() { result = "//\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" } +private string expectationCommentPattern() { result = "\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" } /** * RegEx pattern to match a single expected result, not including the leading `$`. It starts with an @@ -166,7 +171,7 @@ private string expectationPattern() { result = "(?:(f(?:\\+|-)):)?((?:[A-Za-z-_]+)(?:\\s*,\\s*[A-Za-z-_]+)*)(?:=(.*))?" } -private string getAnExpectation(CppStyleComment comment) { +private string getAnExpectation(LineComment comment) { result = comment.getContents().regexpCapture(expectationCommentPattern(), 1).splitAt("$").trim() and result != "" } @@ -177,7 +182,7 @@ private newtype TFailureLocatable = ) { test.hasActualResult(location, element, tag, value) } or - TValidExpectation(CppStyleComment comment, string tag, string value, string knownFailure) { + TValidExpectation(LineComment comment, string tag, string value, string knownFailure) { exists(string expectation | expectation = getAnExpectation(comment) and expectation.regexpMatch(expectationPattern()) and @@ -194,7 +199,7 @@ private newtype TFailureLocatable = ) ) } or - TInvalidExpectation(CppStyleComment comment, string expectation) { + TInvalidExpectation(LineComment comment, string expectation) { expectation = getAnExpectation(comment) and not expectation.regexpMatch(expectationPattern()) } @@ -232,7 +237,7 @@ class ActualResult extends FailureLocatable, TActualResult { } abstract private class Expectation extends FailureLocatable { - CppStyleComment comment; + LineComment comment; override string toString() { result = comment.toString() } diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll new file mode 100644 index 000000000000..f3aae029eb4a --- /dev/null +++ b/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -0,0 +1,23 @@ +import cpp + +private newtype TLineComment = MkLineComment(CppStyleComment c) + +/** + * Represents a line comment in the CPP style. + * Unlike the `CppStyleComment` class, however, the string returned by `getContents` does _not_ + * include the preceding comment marker (`//`). + */ +class LineComment extends TLineComment { + CppStyleComment comment; + + LineComment() { this = MkLineComment(comment) } + + /** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */ + string getContents() { result = comment.getContents().suffix(2) } + + /** Gets a textual representation of this element. */ + string toString() { result = comment.toString() } + + /** Gets the location of this comment. */ + Location getLocation() { result = comment.getLocation() } +} diff --git a/cpp/ql/test/examples/expressions/PrintAST.expected b/cpp/ql/test/examples/expressions/PrintAST.expected index 585ebae6ff4f..61c20b14921c 100644 --- a/cpp/ql/test/examples/expressions/PrintAST.expected +++ b/cpp/ql/test/examples/expressions/PrintAST.expected @@ -19,7 +19,7 @@ AddressOf.c: # 1| params: # 1| 0: [Parameter] i # 1| Type = [IntType] int -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [DeclStmt] declaration # 2| 0: [VariableDeclarationEntry] definition of j # 2| Type = [IntPointerType] int * @@ -34,7 +34,7 @@ AddressOf.c: ArrayToPointer.c: # 5| [TopLevelFunction] void ArrayToPointer() # 5| params: -# 6| body: [Block] { ... } +# 6| body: [BlockStmt] { ... } # 7| 0: [DeclStmt] declaration # 7| 0: [VariableDeclarationEntry] definition of c # 7| Type = [ArrayType] char[] @@ -70,7 +70,7 @@ Cast.c: # 1| Type = [CharPointerType] char * # 1| 1: [Parameter] v # 1| Type = [VoidPointerType] void * -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [ExprStmt] ExprStmt # 2| 0: [AssignExpr] ... = ... # 2| Type = [CharPointerType] char * @@ -89,7 +89,7 @@ Cast.c: ConditionDecl.cpp: # 1| [TopLevelFunction] void ConditionDecl() # 1| params: -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [DeclStmt] declaration # 2| 0: [VariableDeclarationEntry] definition of j # 2| Type = [IntType] int @@ -109,7 +109,7 @@ ConditionDecl.cpp: # 3| expr: [VariableAccess] k # 3| Type = [IntType] int # 3| ValueCategory = prvalue(load) -# 3| 1: [Block] { ... } +# 3| 1: [BlockStmt] { ... } # 5| 2: [ReturnStmt] return ... ConstructorCall.cpp: # 1| [CopyAssignmentOperator] C& C::operator=(C const&) @@ -133,7 +133,7 @@ ConstructorCall.cpp: # 3| 0: [Parameter] i # 3| Type = [IntType] int # 3| initializations: -# 3| body: [Block] { ... } +# 3| body: [BlockStmt] { ... } # 4| 0: [ReturnStmt] return ... # 7| [CopyAssignmentOperator] D& D::operator=(D const&) # 7| params: @@ -154,7 +154,7 @@ ConstructorCall.cpp: # 9| [Constructor] void D::D() # 9| params: # 9| initializations: -# 9| body: [Block] { ... } +# 9| body: [BlockStmt] { ... } # 10| 0: [ReturnStmt] return ... # 13| [CopyAssignmentOperator] E& E::operator=(E const&) # 13| params: @@ -172,7 +172,7 @@ ConstructorCall.cpp: # 17| Type = [PointerType] D * # 17| 2: [Parameter] e # 17| Type = [PointerType] E * -# 17| body: [Block] { ... } +# 17| body: [BlockStmt] { ... } # 18| 0: [ExprStmt] ExprStmt # 18| 0: [AssignExpr] ... = ... # 18| Type = [PointerType] C * @@ -221,7 +221,7 @@ ConstructorCall.cpp: Conversion1.c: # 1| [TopLevelFunction] void Conversion1() # 1| params: -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [DeclStmt] declaration # 2| 0: [VariableDeclarationEntry] definition of i # 2| Type = [IntType] int @@ -241,7 +241,7 @@ Conversion2.c: # 1| params: # 1| 0: [Parameter] x # 1| Type = [IntType] int -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [ExprStmt] ExprStmt # 2| 0: [AssignExpr] ... = ... # 2| Type = [IntType] int @@ -277,7 +277,7 @@ Conversion3.cpp: # 1| params: # 1| 0: [Parameter] x # 1| Type = [IntType] int -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [ExprStmt] ExprStmt # 2| 0: [AssignExpr] ... = ... # 2| Type = [IntType] int @@ -327,7 +327,7 @@ Conversion4.c: # 1| params: # 1| 0: [Parameter] x # 1| Type = [IntType] int -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [ExprStmt] ExprStmt # 2| 0: [AssignExpr] ... = ... # 2| Type = [IntType] int @@ -350,9 +350,11 @@ Conversion4.c: # 2| ValueCategory = prvalue # 3| 1: [ReturnStmt] return ... DestructorCall.cpp: +# 1| [Constructor] void C::C() +# 1| params: # 3| [Destructor] void C::~C() # 3| params: -# 3| body: [Block] { ... } +# 3| body: [BlockStmt] { ... } # 4| 0: [ReturnStmt] return ... # 3| destructions: # 11| [TopLevelFunction] void DestructorCall(C*, D*) @@ -361,7 +363,7 @@ DestructorCall.cpp: # 11| Type = [PointerType] C * # 11| 1: [Parameter] d # 11| Type = [PointerType] D * -# 11| body: [Block] { ... } +# 11| body: [BlockStmt] { ... } # 12| 0: [ExprStmt] ExprStmt # 12| 0: [DeleteExpr] delete # 12| Type = [VoidType] void @@ -385,7 +387,7 @@ DynamicCast.cpp: # 1| params: #-----| 0: [Parameter] p#0 #-----| Type = [LValueReferenceType] const Base & -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ReturnStmt] return ... #-----| 0: [ReferenceToExpr] (reference to) #-----| Type = [LValueReferenceType] Base & @@ -412,13 +414,13 @@ DynamicCast.cpp: #-----| Type = [RValueReferenceType] Base && # 2| [VirtualFunction] void Base::f() # 2| params: -# 2| body: [Block] { ... } +# 2| body: [BlockStmt] { ... } # 2| 0: [ReturnStmt] return ... # 4| [CopyAssignmentOperator] Derived& Derived::operator=(Derived const&) # 4| params: #-----| 0: [Parameter] p#0 #-----| Type = [LValueReferenceType] const Derived & -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ExprStmt] ExprStmt #-----| 0: [ReferenceDereferenceExpr] (reference dereference) #-----| Type = [Class] Base @@ -478,7 +480,7 @@ DynamicCast.cpp: #-----| Type = [RValueReferenceType] Derived && # 5| [VirtualFunction] void Derived::f() # 5| params: -# 5| body: [Block] { ... } +# 5| body: [BlockStmt] { ... } # 5| 0: [ReturnStmt] return ... # 8| [TopLevelFunction] void DynamicCast(Base*, Derived*) # 8| params: @@ -486,7 +488,7 @@ DynamicCast.cpp: # 8| Type = [PointerType] Base * # 8| 1: [Parameter] d # 8| Type = [PointerType] Derived * -# 8| body: [Block] { ... } +# 8| body: [BlockStmt] { ... } # 9| 0: [ExprStmt] ExprStmt # 9| 0: [AssignExpr] ... = ... # 9| Type = [PointerType] Derived * @@ -508,7 +510,7 @@ DynamicCast.cpp: # 12| Type = [LValueReferenceType] Base & # 12| 1: [Parameter] d # 12| Type = [LValueReferenceType] Derived & -# 12| body: [Block] { ... } +# 12| body: [BlockStmt] { ... } # 13| 0: [ExprStmt] ExprStmt # 13| 0: [ReferenceDereferenceExpr] (reference dereference) # 13| Type = [Class] Derived @@ -545,7 +547,7 @@ Parenthesis.c: # 1| params: # 1| 0: [Parameter] i # 1| Type = [IntType] int -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [ExprStmt] ExprStmt # 2| 0: [AssignExpr] ... = ... # 2| Type = [IntType] int @@ -581,7 +583,7 @@ PointerDereference.c: # 1| Type = [IntPointerType] int * # 1| 1: [Parameter] j # 1| Type = [IntType] int -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [ExprStmt] ExprStmt # 2| 0: [AssignExpr] ... = ... # 2| Type = [IntType] int @@ -603,7 +605,7 @@ ReferenceDereference.cpp: # 4| Type = [LValueReferenceType] int & # 4| 1: [Parameter] j # 4| Type = [IntType] int -# 4| body: [Block] { ... } +# 4| body: [BlockStmt] { ... } # 5| 0: [ExprStmt] ExprStmt # 5| 0: [AssignExpr] ... = ... # 5| Type = [IntType] int @@ -623,7 +625,7 @@ ReferenceTo.cpp: # 1| params: # 1| 0: [Parameter] i # 1| Type = [IntPointerType] int * -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [ReturnStmt] return ... # 2| 0: [ReferenceToExpr] (reference to) # 2| Type = [LValueReferenceType] int & @@ -639,7 +641,7 @@ Sizeof.c: # 1| params: # 1| 0: [Parameter] array # 1| Type = [ArrayType] int[] -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [DeclStmt] declaration # 2| 0: [VariableDeclarationEntry] definition of i # 2| Type = [IntType] int @@ -676,7 +678,7 @@ Sizeof.c: StatementExpr.c: # 1| [TopLevelFunction] void StatementExpr() # 1| params: -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [DeclStmt] declaration # 2| 0: [VariableDeclarationEntry] definition of j # 2| Type = [IntType] int @@ -700,7 +702,7 @@ StaticMemberAccess.cpp: # 5| Type = [IntType] int # 5| 1: [Parameter] xref # 5| Type = [LValueReferenceType] X & -# 5| body: [Block] { ... } +# 5| body: [BlockStmt] { ... } # 7| 0: [ExprStmt] ExprStmt # 7| 0: [AssignExpr] ... = ... # 7| Type = [IntType] int @@ -725,7 +727,7 @@ Subscript.c: # 1| Type = [ArrayType] int[] # 1| 1: [Parameter] j # 1| Type = [IntType] int -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [ExprStmt] ExprStmt # 2| 0: [AssignExpr] ... = ... # 2| Type = [IntType] int @@ -762,20 +764,20 @@ Throw.cpp: #-----| 0: [Parameter] p#0 #-----| Type = [RValueReferenceType] F && # 2| initializations: -# 2| body: [Block] { ... } +# 2| body: [BlockStmt] { ... } # 2| 0: [ReturnStmt] return ... # 4| [Constructor] void F::F() # 4| params: # 4| initializations: -# 4| body: [Block] { ... } +# 4| body: [BlockStmt] { ... } # 4| 0: [ReturnStmt] return ... # 6| [TopLevelFunction] void Throw(int) # 6| params: # 6| 0: [Parameter] i # 6| Type = [IntType] int -# 6| body: [Block] { ... } +# 6| body: [BlockStmt] { ... } # 7| 0: [TryStmt] try { ... } -# 7| 0: [Block] { ... } +# 7| 0: [BlockStmt] { ... } # 8| 0: [IfStmt] if (...) ... # 8| 0: [CStyleCast] (bool)... # 8| Conversion = [BoolConversion] conversion to bool @@ -818,13 +820,13 @@ Typeid.cpp: # 7| params: # 13| [VirtualFunction] void Base::v() # 13| params: -# 13| body: [Block] { ... } +# 13| body: [BlockStmt] { ... } # 13| 0: [ReturnStmt] return ... # 18| [TopLevelFunction] void TypeId(Base*) # 18| params: # 18| 0: [Parameter] bp # 18| Type = [PointerType] Base * -# 18| body: [Block] { ... } +# 18| body: [BlockStmt] { ... } # 19| 0: [DeclStmt] declaration # 19| 0: [VariableDeclarationEntry] definition of name # 19| Type = [PointerType] const char * @@ -846,7 +848,7 @@ VacuousDestructorCall.cpp: # 2| Type = [TemplateParameter] T # 2| 1: [Parameter] y # 2| Type = [PointerType] T * -# 2| body: [Block] { ... } +# 2| body: [BlockStmt] { ... } # 3| 0: [ExprStmt] ExprStmt # 3| 0: [ExprCall] call to expression # 3| Type = [UnknownType] unknown @@ -874,7 +876,7 @@ VacuousDestructorCall.cpp: # 2| Type = [IntType] int # 2| 1: [Parameter] y # 2| Type = [IntPointerType] int * -# 2| body: [Block] { ... } +# 2| body: [BlockStmt] { ... } # 3| 0: [ExprStmt] ExprStmt # 3| 0: [VacuousDestructorCall] (vacuous destructor call) # 3| Type = [VoidType] void @@ -894,7 +896,7 @@ VacuousDestructorCall.cpp: # 7| params: # 7| 0: [Parameter] i # 7| Type = [IntType] int -# 7| body: [Block] { ... } +# 7| body: [BlockStmt] { ... } # 10| 0: [ExprStmt] ExprStmt # 10| 0: [FunctionCall] call to CallDestructor # 10| Type = [VoidType] void @@ -914,7 +916,7 @@ Varargs.c: # 8| params: # 8| 0: [Parameter] text # 8| Type = [PointerType] const char * -# 8| body: [Block] { ... } +# 8| body: [BlockStmt] { ... } # 9| 0: [DeclStmt] declaration # 9| 0: [VariableDeclarationEntry] definition of args # 9| Type = [CTypedefType] va_list @@ -947,7 +949,7 @@ macro_etc.c: # 3| params: # 3| 0: [Parameter] i # 3| Type = [IntType] int -# 3| body: [Block] { ... } +# 3| body: [BlockStmt] { ... } # 4| 0: [DeclStmt] declaration # 4| 0: [TypeDeclarationEntry] definition of u # 4| Type = [LocalUnion] u @@ -997,7 +999,7 @@ macro_etc.c: # 10| ValueCategory = prvalue # 22| [TopLevelFunction] int foo() # 22| params: -# 22| body: [Block] { ... } +# 22| body: [BlockStmt] { ... } # 23| 0: [DeclStmt] declaration # 23| 0: [VariableDeclarationEntry] definition of t # 23| Type = [IntType] int @@ -1059,7 +1061,7 @@ macro_etc.c: # 27| 0: [VariableAccess] i # 27| Type = [PlainCharType] char # 27| ValueCategory = lvalue -# 27| 3: [Block] { ... } +# 27| 3: [BlockStmt] { ... } # 27| 0: [ExprStmt] ExprStmt # 27| 0: [AssignAddExpr] ... += ... # 27| Type = [IntType] int @@ -1111,7 +1113,7 @@ macro_etc.c: # 28| 0: [VariableAccess] i # 28| Type = [PlainCharType] char # 28| ValueCategory = lvalue -# 28| 3: [Block] { ... } +# 28| 3: [BlockStmt] { ... } # 28| 0: [ExprStmt] ExprStmt # 28| 0: [AssignAddExpr] ... += ... # 28| Type = [IntType] int @@ -1210,7 +1212,7 @@ union_etc.cpp: # 2| [Constructor] void S::S() # 2| params: # 2| initializations: -# 2| body: [Block] { ... } +# 2| body: [BlockStmt] { ... } # 2| 0: [ReturnStmt] return ... # 2| [CopyConstructor] void S::S(S const&) # 2| params: @@ -1240,7 +1242,7 @@ union_etc.cpp: # 6| params: # 6| 0: [Parameter] val # 6| Type = [IntType] int -# 6| body: [Block] { ... } +# 6| body: [BlockStmt] { ... } # 6| 0: [ExprStmt] ExprStmt # 6| 0: [AssignExpr] ... = ... # 6| Type = [IntType] int @@ -1305,7 +1307,7 @@ union_etc.cpp: #-----| Type = [RValueReferenceType] C && # 22| [TopLevelFunction] int foo() # 22| params: -# 22| body: [Block] { ... } +# 22| body: [BlockStmt] { ... } # 23| 0: [DeclStmt] declaration # 23| 0: [VariableDeclarationEntry] definition of s # 23| Type = [Struct] S @@ -1423,7 +1425,7 @@ union_etc.cpp: # 33| params: # 33| 0: [Parameter] val # 33| Type = [IntType] int -# 33| body: [Block] { ... } +# 33| body: [BlockStmt] { ... } # 33| 0: [ExprStmt] ExprStmt # 33| 0: [AssignExpr] ... = ... # 33| Type = [IntType] int @@ -1440,7 +1442,7 @@ union_etc.cpp: # 33| 1: [ReturnStmt] return ... # 36| [TopLevelFunction] int bar() # 36| params: -# 36| body: [Block] { ... } +# 36| body: [BlockStmt] { ... } # 37| 0: [DeclStmt] declaration # 37| 0: [VariableDeclarationEntry] definition of s # 37| Type = [PointerType] const T * diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.cpp new file mode 100644 index 000000000000..d45b0062d5d4 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.cpp @@ -0,0 +1,59 @@ +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned uint32_t; +typedef signed long long int64_t; + +void test_assign_operator(uint8_t x) { + x &= 7; // [0 .. 7] +} + +void test_non_negative_const(uint8_t x) { + uint8_t unsigned_const = 7; + + // Non-negative range operand and non-negative constant. The operands are promoted + // to signed ints. + x & 0; // [0 .. 0] + x & 7; // [0 .. 7] + x & unsigned_const; // [0 .. 7] + + // This tests what happens when both arguments are promoted to `unsigned int` instead + // of `int`, and when the constant is larger than the max bound + x & 0xFFFFFFFF; // [0 .. 255] +} + +void test_non_const(uint8_t a, uint8_t b, uint32_t c, uint32_t d) { + if (b <= 100) { + // `a` and `b` are promoted to signed ints, meaning neither the range analysis library + // nor this extension handle it + a & b; // [-2147483648 .. 2147483647] + } + if (d <= 100) { + // Handled by the range analysis library + c & d; // [0 .. 100] + } +} + +void test_negative_operand(uint8_t x, int8_t y) { + uint8_t unsigned_const = 7; + int8_t signed_const = -7; + + // The right operand can be negative + x & -7; // [-2147483648 .. 2147483647] + x & signed_const; // [-2147483648 .. 2147483647] + x & y; // [-2147483648 .. 2147483647] + + // The left operand can be negative + y & 7; // [-2147483648 .. 2147483647] + y & unsigned_const; // [-2147483648 .. 2147483647] + y & 0xFFFFFFFF; // [0 .. 4294967295] + (int64_t)y & 0xFFFFFFFF; // [-9223372036854776000 .. 9223372036854776000] + y & x; // [-2147483648 .. 2147483647] + + // Both can be negative + y & -7; // [-2147483648 .. 2147483647] + y & signed_const; // [-2147483648 .. 2147483647] + signed_const & -7; // [-2147483648 .. 2147483647] + signed_const & y; // [-2147483648 .. 2147483647] + -7 & y; // [-2147483648 .. 2147483647] + -7 & signed_const; // [-2147483648 .. 2147483647] +} diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.expected new file mode 100644 index 000000000000..22ad956469e4 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.expected @@ -0,0 +1,21 @@ +| bitwiseand.cpp:7:3:7:8 | ... &= ... | 0.0 | 7.0 | +| bitwiseand.cpp:15:3:15:7 | ... & ... | 0.0 | 0.0 | +| bitwiseand.cpp:16:3:16:7 | ... & ... | 0.0 | 7.0 | +| bitwiseand.cpp:17:3:17:20 | ... & ... | 0.0 | 7.0 | +| bitwiseand.cpp:21:3:21:16 | ... & ... | 0.0 | 255.0 | +| bitwiseand.cpp:28:5:28:9 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:32:5:32:9 | ... & ... | 0.0 | 100.0 | +| bitwiseand.cpp:41:3:41:8 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:42:3:42:18 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:43:3:43:7 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:46:3:46:7 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:47:3:47:20 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:48:3:48:16 | ... & ... | 0.0 | 4.294967295E9 | +| bitwiseand.cpp:49:3:49:25 | ... & ... | -9.223372036854776E18 | 9.223372036854776E18 | +| bitwiseand.cpp:50:3:50:7 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:53:3:53:8 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:54:3:54:18 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:55:3:55:19 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:56:3:56:18 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:57:3:57:8 | ... & ... | -2.147483648E9 | 2.147483647E9 | +| bitwiseand.cpp:58:3:58:19 | ... & ... | -2.147483648E9 | 2.147483647E9 | diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.ql new file mode 100644 index 000000000000..6521e8f0f610 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.ql @@ -0,0 +1,8 @@ +import experimental.semmle.code.cpp.rangeanalysis.ExtendedRangeAnalysis + +from Operation expr, float lower, float upper +where + (expr instanceof BitwiseAndExpr or expr instanceof AssignAndExpr) and + lower = lowerBound(expr) and + upper = upperBound(expr) +select expr, lower, upper diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp new file mode 100644 index 000000000000..0fed35bc9aff --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp @@ -0,0 +1,9 @@ + + +void test_overridability_sub(int x) { + int zero = x - x; + zero; // 0 + + int nonzero = x - (unsigned char)x; + nonzero; // full range +} diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected new file mode 100644 index 000000000000..b43601c80886 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected @@ -0,0 +1,6 @@ +| extended.cpp:4:14:4:14 | x | -2.147483648E9 | 2.147483647E9 | +| extended.cpp:4:18:4:18 | x | -2.147483648E9 | 2.147483647E9 | +| extended.cpp:5:3:5:6 | zero | 0.0 | 0.0 | +| extended.cpp:7:17:7:17 | x | -2.147483648E9 | 2.147483647E9 | +| extended.cpp:7:36:7:36 | x | -2.147483648E9 | 2.147483647E9 | +| extended.cpp:8:3:8:9 | nonzero | -2.147483648E9 | 2.147483647E9 | diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql new file mode 100644 index 000000000000..d6344e5d0629 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql @@ -0,0 +1,7 @@ +import experimental.semmle.code.cpp.rangeanalysis.ExtendedRangeAnalysis + +from VariableAccess expr, float lower, float upper +where + lower = lowerBound(expr) and + upper = upperBound(expr) +select expr, lower, upper diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c new file mode 100644 index 000000000000..33cf54ee9ebf --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c @@ -0,0 +1,19 @@ +/// Adds its arguments (has custom modeling in QL) +int custom_add_function(int a, int b); + +int test_extensibility_add(int x) { + if (x >= -10 && x <= 10) { + int result = custom_add_function(x, 100); + return result; // 90 .. 110 + } +} + +int test_overridability_sub(int x) { + int result = x - (unsigned char)x; // Returns 0 due to custom modeling for this test being deliberately wrong + return result; // 0 +} + +void test_parameter_override(int magic_name_at_most_10, int magic_name_at_most_20) { + magic_name_at_most_10; + magic_name_at_most_20; +} diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected new file mode 100644 index 000000000000..e9133e191044 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected @@ -0,0 +1,9 @@ +| extensibility.c:5:7:5:7 | x | -2.147483648E9 | 2.147483647E9 | +| extensibility.c:5:19:5:19 | x | -10.0 | 2.147483647E9 | +| extensibility.c:6:38:6:38 | x | -10.0 | 10.0 | +| extensibility.c:7:12:7:17 | result | 90.0 | 110.0 | +| extensibility.c:12:16:12:16 | x | -2.147483648E9 | 2.147483647E9 | +| extensibility.c:12:35:12:35 | x | -2.147483648E9 | 2.147483647E9 | +| extensibility.c:13:10:13:15 | result | 0.0 | 0.0 | +| extensibility.c:17:3:17:23 | magic_name_at_most_10 | -2.147483648E9 | 10.0 | +| extensibility.c:18:3:18:23 | magic_name_at_most_20 | -2.147483648E9 | 20.0 | diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql new file mode 100644 index 000000000000..4fbfed1706db --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql @@ -0,0 +1,80 @@ +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils +import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr +import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition + +class CustomAddFunctionCall extends SimpleRangeAnalysisExpr, FunctionCall { + CustomAddFunctionCall() { this.getTarget().hasGlobalName("custom_add_function") } + + override float getLowerBounds() { + exists(float lower0, float lower1 | + lower0 = getFullyConvertedLowerBounds(this.getArgument(0)) and + lower1 = getFullyConvertedLowerBounds(this.getArgument(1)) and + // Note: this rounds toward 0, not -Inf as it should + result = lower0 + lower1 + ) + } + + override float getUpperBounds() { + exists(float upper0, float upper1 | + upper0 = getFullyConvertedUpperBounds(this.getArgument(0)) and + upper1 = getFullyConvertedUpperBounds(this.getArgument(1)) and + // Note: this rounds toward 0, not Inf as it should + result = upper0 + upper1 + ) + } + + override predicate dependsOnChild(Expr child) { child = this.getAnArgument() } +} + +class SelfSub extends SimpleRangeAnalysisExpr, SubExpr { + SelfSub() { + getLeftOperand().(VariableAccess).getTarget() = getRightOperand().(VariableAccess).getTarget() + } + + override float getLowerBounds() { result = 0 } + + override float getUpperBounds() { result = 0 } + + override predicate dependsOnChild(Expr child) { child = this.getAnOperand() } +} + +/** + * A definition for test purposes of a parameter `p` that starts with a + * special prefix. This class is written to exploit how QL behaves when class + * fields are not functionally determined by `this`. When multiple parameters + * of the same function have the special prefix, there is still only one + * instance of this class. + */ +class MagicParameterName extends SimpleRangeAnalysisDefinition { + Parameter p; + float value; + + MagicParameterName() { + this.definedByParameter(p) and + value = p.getName().regexpCapture("magic_name_at_most_(\\d+)", 1).toFloat() + } + + override predicate hasRangeInformationFor(StackVariable v) { v = p } + + override predicate dependsOnExpr(StackVariable v, Expr e) { + // No dependencies. This sample class yields constant values. + none() + } + + override float getLowerBounds(StackVariable var) { + var = p and + result = typeLowerBound(p.getUnspecifiedType()) + } + + override float getUpperBounds(StackVariable var) { + var = p and + result = value + } +} + +from VariableAccess expr, float lower, float upper +where + lower = lowerBound(expr) and + upper = upperBound(expr) +select expr, lower, upper diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected rename to cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql new file mode 100644 index 000000000000..1b77763682ad --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql @@ -0,0 +1,19 @@ +import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis +import semmle.code.cpp.ir.IR +import semmle.code.cpp.controlflow.IRGuards +import semmle.code.cpp.ir.ValueNumbering + +query predicate instructionBounds( + Instruction i, Bound b, int delta, boolean upper, Reason reason, Location reasonLoc +) { + ( + i.getAUse() instanceof ArgumentOperand + or + exists(ReturnValueInstruction retInstr | retInstr.getReturnValueOperand() = i.getAUse()) + ) and + boundedInstruction(i, b, delta, upper, reason) and + not valueNumber(b.getInstruction()) = valueNumber(i) and + if reason instanceof CondReason + then reasonLoc = reason.(CondReason).getCond().getLocation() + else reasonLoc instanceof UnknownDefaultLocation +} diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/test.cpp similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp rename to cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/test.cpp diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql new file mode 100644 index 000000000000..fadd86f8a855 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql @@ -0,0 +1,19 @@ +import experimental.semmle.code.cpp.rangeanalysis.SignAnalysis +import semmle.code.cpp.ir.IR + +string getASignString(Instruction i) { + positiveInstruction(i) and + result = "positive" + or + negativeInstruction(i) and + result = "negative" + or + strictlyPositiveInstruction(i) and + result = "strictlyPositive" + or + strictlyNegativeInstruction(i) and + result = "strictlyNegative" +} + +from Instruction i +select i, strictconcat(string s | s = getASignString(i) | s, " ") diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/bounded_bounds.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/bounded_bounds.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/bounded_bounds.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/bounded_bounds.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/inline_assembly.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/inline_assembly.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/inline_assembly.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/inline_assembly.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/minmax.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/minmax.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/minmax.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/minmax.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.c similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.c rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.c diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.cpp similarity index 100% rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.cpp rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.cpp diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected new file mode 100644 index 000000000000..c263d7e6dfd7 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected @@ -0,0 +1,21 @@ +edges +| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp | +| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 | +| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func | +nodes +| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode | +| test.cpp:74:24:74:30 | medical | semmle.label | medical | +| test.cpp:77:16:77:22 | medical | semmle.label | medical | +| test.cpp:78:24:78:27 | temp | semmle.label | temp | +| test.cpp:81:17:81:20 | call to func | semmle.label | call to func | +| test.cpp:81:22:81:28 | medical | semmle.label | medical | +| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 | +| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode | +| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode | +#select +| test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:57:9:57:18 | theZipcode | this source. | +| test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@ | test.cpp:74:24:74:30 | medical | this source. | +| test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@ | test.cpp:77:16:77:22 | medical | this source. | +| test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@ | test.cpp:81:22:81:28 | medical | this source. | +| test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:96:37:96:46 | theZipcode | this source. | +| test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:99:42:99:51 | theZipcode | this source. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.qlref new file mode 100644 index 000000000000..65c8c9c2dd4c --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql \ No newline at end of file diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/test.cpp new file mode 100644 index 000000000000..4d69ee5b2b72 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/test.cpp @@ -0,0 +1,105 @@ +#define FILE int +#define wchar char +#define size_t int +typedef int streamsize; + +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); +int fputs(const char *s, FILE *stream); +int fputc(int c, FILE *stream); +int fprintf(FILE *stream, const char *format, ...); +int sprintf(char *s, const char *format, ...); +size_t strlen(const char *s); + +namespace std +{ + template + struct char_traits; + + template > + class basic_ostream /*: virtual public basic_ios - not needed for this test */ + { + public: + typedef charT char_type; + basic_ostream &write(const char_type *s, streamsize n); + }; + + template > + class basic_ofstream : public basic_ostream + { + public: + }; + + template + basic_ostream &operator<<(basic_ostream &, const charT *); + + typedef basic_ostream ostream; + typedef basic_ofstream ofstream; +}; // namespace std + +using namespace std; + +char *encrypt(char *buffer) +{ + return buffer; +} +char *func(char *buffer) +{ + return buffer; +} + +// test for CleartextFileWrite +void file() +{ + char *theZipcode = "cleartext zipcode!"; + FILE *file; + + // BAD: write zipcode to file in cleartext + fputs(theZipcode, file); + + // GOOD: encrypt first + char *encrypted = encrypt(theZipcode); + fwrite(encrypted, sizeof(encrypted), 1, file); +} + +// test for CleartextBufferWrite +int main(int argc, char **argv) +{ + char *medical = "medical"; + char *buff1; + char *buff2; + char *buff3; + char *buff4; + + // BAD: write medical to buffer in cleartext + sprintf(buff1, "%s", medical); + + // BAD: write medical to buffer in cleartext + char *temp = medical; + sprintf(buff2, "%s", temp); + + // BAD: write medical to buffer in cleartext + char *buff5 = func(medical); + sprintf(buff3, "%s", buff5); + + char *buff6 = encrypt(medical); + // GOOD: encrypt first + sprintf(buff4, "%s", buff6); +} + +// test for CleartextFileWrite +void stream() +{ + char *theZipcode = "cleartext zipcode!"; + ofstream mystream; + + // BAD: write zipcode to file in cleartext + mystream << "the zipcode is: " << theZipcode; + + // BAD: write zipcode to file in cleartext + (mystream << "the zipcode is: ").write(theZipcode, strlen(theZipcode)); + + // GOOD: encrypt first + char *encrypted = encrypt(theZipcode); + mystream << "the zipcode is: " << encrypted; + (mystream << "the zipcode is: ").write(encrypted, strlen(encrypted)); +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.cpp new file mode 100644 index 000000000000..f9ffc5e8687e --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.cpp @@ -0,0 +1,25 @@ +///// Library routines ///// + +int scanf(const char *format, ...); +int sscanf(const char *str, const char *format, ...); +int fscanf(const char *str, const char *format, ...); + +///// Test code ///// + +int main(int argc, char **argv) +{ + + // BAD, do not use scanf without specifying a length first + char buf1[10]; + scanf("%s", buf1); + + // GOOD, length is specified + char buf2[10]; + sscanf(buf2, "%9s"); + + // BAD, do not use scanf without specifying a length first + char file[10]; + fscanf(file, "%s", buf2); + + return 0; +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.expected new file mode 100644 index 000000000000..701fd4320b0f --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.expected @@ -0,0 +1,2 @@ +| MemoryUnsafeFunctionScan.cpp:14:5:14:9 | call to scanf | Dangerous use of one of the scanf functions | +| MemoryUnsafeFunctionScan.cpp:22:5:22:10 | call to fscanf | Dangerous use of one of the scanf functions | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.qlref new file mode 100644 index 000000000000..428d988a161d --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.ql \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/deduplication/functions.expected b/cpp/ql/test/header-variant-tests/deduplication/functions.expected index a5ffe6852907..b55bfa8856fb 100644 --- a/cpp/ql/test/header-variant-tests/deduplication/functions.expected +++ b/cpp/ql/test/header-variant-tests/deduplication/functions.expected @@ -7,6 +7,8 @@ | foo.h:1:13:1:15 | foo | Function | 1 | | | foo.h:1:13:1:15 | foo | MemberFunction | 1 | C | | foo.h:1:13:1:15 | foo | MemberFunction | 1 | C | +| main2.cpp:7:7:7:7 | C | MemberFunction | 0 | C | +| main2.cpp:7:7:7:7 | C | MemberFunction | 0 | C | | main2.cpp:7:7:7:7 | operator= | MemberFunction | 0 | C | | main2.cpp:7:7:7:7 | operator= | MemberFunction | 0 | C | | main2.cpp:7:7:7:7 | operator= | MemberFunction | 0 | C | diff --git a/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected index 9238c6182984..72beb5a1913e 100644 --- a/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected +++ b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected @@ -8,89 +8,143 @@ | direct_friend::D::f | Can access D::f | | direct_friend::D::f | Can access D::operator= | | direct_friend::D::f | Can access D::x | +| field_and_base::P::f | Can access B::B | | field_and_base::P::f | Can access B::operator= | +| field_and_base::P::f | Can access P::B | +| field_and_base::P::f | Can access P::P | | field_and_base::P::f | Can access P::f | | field_and_base::P::f | Can access P::fieldB | | field_and_base::P::f | Can access P::m | | field_and_base::P::f | Can access P::m_static | | field_and_base::P::f | Can access P::operator= | +| friend_class::B::fun | Can access B::B | | friend_class::B::fun | Can access B::a | | friend_class::B::fun | Can access B::b | | friend_class::B::fun | Can access B::fun | | friend_class::B::fun | Can access B::operator= | +| friend_class::B::fun | Can access C::C | | friend_class::B::fun | Can access C::operator= | +| friend_class::B::fun | Can access D1::D1 | | friend_class::B::fun | Can access D1::operator= | +| friend_class::B::fun | Can access D2::D2 | | friend_class::B::fun | Can access D2::operator= | +| friend_class::C::fun | Can access B::B | | friend_class::C::fun | Can access B::operator= | +| friend_class::C::fun | Can access C::B | +| friend_class::C::fun | Can access C::C | | friend_class::C::fun | Can access C::fun | | friend_class::C::fun | Can access C::operator= | | friend_class::C::fun | Can access C::x | | friend_class::C::fun | Can access C::y | +| friend_class::C::fun | Can access D1::D1 | | friend_class::C::fun | Can access D1::operator= | +| friend_class::C::fun | Can access D2::D2 | | friend_class::C::fun | Can access D2::operator= | +| friend_class::D1::fun | Can access B::B | | friend_class::D1::fun | Can access B::a | | friend_class::D1::fun | Can access B::b | | friend_class::D1::fun | Can access B::fun | | friend_class::D1::fun | Can access B::operator= | +| friend_class::D1::fun | Can access C::C | | friend_class::D1::fun | Can access C::operator= | +| friend_class::D1::fun | Can access D1::C | +| friend_class::D1::fun | Can access D1::D1 | | friend_class::D1::fun | Can access D1::fun | | friend_class::D1::fun | Can access D1::operator= | +| friend_class::D1::fun | Can access D2::D2 | | friend_class::D1::fun | Can access D2::operator= | +| friend_class::D2::fun | Can access B::B | | friend_class::D2::fun | Can access B::a | | friend_class::D2::fun | Can access B::b | | friend_class::D2::fun | Can access B::fun | | friend_class::D2::fun | Can access B::operator= | +| friend_class::D2::fun | Can access C::C | | friend_class::D2::fun | Can access C::operator= | +| friend_class::D2::fun | Can access D1::D1 | | friend_class::D2::fun | Can access D1::operator= | +| friend_class::D2::fun | Can access D2::B | +| friend_class::D2::fun | Can access D2::D2 | | friend_class::D2::fun | Can access D2::a | | friend_class::D2::fun | Can access D2::b | | friend_class::D2::fun | Can access D2::fun | | friend_class::D2::fun | Can access D2::operator= | | friend_fun::fun1 | Can access A::operator= | +| friend_fun::fun1 | Can access B::B | | friend_fun::fun1 | Can access B::operator= | | friend_fun::fun2 | Can access A::operator= | +| friend_fun::fun2 | Can access B::B | | friend_fun::fun2 | Can access B::operator= | | friend_fun::fun2 | Can access B::x | | friend_fun::fun2 | Can access B::y | +| mixed::B::fun | Can access A::A | | mixed::B::fun | Can access A::operator= | +| mixed::B::fun | Can access B::A | +| mixed::B::fun | Can access B::B | | mixed::B::fun | Can access B::fun | | mixed::B::fun | Can access B::operator= | +| mixed::B::fun | Can access C::C | | mixed::B::fun | Can access C::operator= | +| mixed::B::fun | Can access D::C | +| mixed::B::fun | Can access D::D | | mixed::B::fun | Can access D::operator= | +| mixed::C::fun | Can access A::A | | mixed::C::fun | Can access A::operator= | +| mixed::C::fun | Can access B::B | | mixed::C::fun | Can access B::operator= | +| mixed::C::fun | Can access C::B | +| mixed::C::fun | Can access C::C | | mixed::C::fun | Can access C::fun | | mixed::C::fun | Can access C::operator= | +| mixed::C::fun | Can access D::B | +| mixed::C::fun | Can access D::C | +| mixed::C::fun | Can access D::D | | mixed::C::fun | Can access D::fun | | mixed::C::fun | Can access D::operator= | +| mixed::D::fun | Can access A::A | | mixed::D::fun | Can access A::operator= | +| mixed::D::fun | Can access B::B | | mixed::D::fun | Can access B::operator= | +| mixed::D::fun | Can access C::B | +| mixed::D::fun | Can access C::C | | mixed::D::fun | Can access C::operator= | +| mixed::D::fun | Can access D::B | +| mixed::D::fun | Can access D::C | +| mixed::D::fun | Can access D::D | | mixed::D::fun | Can access D::fun | | mixed::D::fun | Can access D::operator= | | protected_derived::BP::f | Can access B::m | | protected_derived::BP::f | Can access B::operator= | +| protected_derived::BP::f | Can access BN::BN | | protected_derived::BP::f | Can access BN::operator= | +| protected_derived::BP::f | Can access BP::BP | | protected_derived::BP::f | Can access BP::f | | protected_derived::BP::f | Can access BP::m | | protected_derived::BP::f | Can access BP::operator= | +| protected_derived::BP::f | Can access BPNprot::BPNprot | | protected_derived::BP::f | Can access BPNprot::operator= | +| protected_derived::BP::f | Can access BPNpub::BP | +| protected_derived::BP::f | Can access BPNpub::BPNpub | | protected_derived::BP::f | Can access BPNpub::f | | protected_derived::BP::f | Can access BPNpub::m | | protected_derived::BP::f | Can access BPNpub::operator= | +| protected_virtual::P::f | Can access B::B | | protected_virtual::P::f | Can access B::operator= | | protected_virtual::P::f | Can access Nprot::Nprot | | protected_virtual::P::f | Can access Nprot::operator= | +| protected_virtual::P::f | Can access Npub::B | | protected_virtual::P::f | Can access Npub::Npub | | protected_virtual::P::f | Can access Npub::m | | protected_virtual::P::f | Can access Npub::operator= | +| protected_virtual::P::f | Can access P::B | | protected_virtual::P::f | Can access P::P | | protected_virtual::P::f | Can access P::f | | protected_virtual::P::f | Can access P::m | | protected_virtual::P::f | Can access P::operator= | | simple::Derived::castme | Can access Base::operator= | +| simple::Derived::castme | Can access Derived::Derived | | simple::Derived::castme | Can access Derived::castme | | simple::Derived::castme | Can access Derived::operator= | | simple::top | Can access Base::operator= | +| simple::top | Can access Derived::Derived | | simple::top | Can access Derived::castme | | simple::top | Can access Derived::operator= | diff --git a/cpp/ql/test/library-tests/complex_numbers/expr.ql b/cpp/ql/test/library-tests/complex_numbers/expr.ql index 0f2e6f14d4e7..83c6dca9c64d 100644 --- a/cpp/ql/test/library-tests/complex_numbers/expr.ql +++ b/cpp/ql/test/library-tests/complex_numbers/expr.ql @@ -1,4 +1,4 @@ import cpp from Expr e -select e, e.getCanonicalQLClass() +select e, e.getAPrimaryQlClass() diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp index 9836f7fb20df..cd8b0daf6f30 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp @@ -149,3 +149,63 @@ void test_conflated_fields2() { taint_x(&p); y_to_sink(&p); } + +void sink(Point*); +void sink(Point); + +void test_field_to_obj_taint_object(Point p) { + p.x = getenv("VAR")[0]; + sink(p); // not tainted + sink(p.x); // tainted +} + +void test_field_to_obj_taint_object_addrof(Point p) { + taint_x(&p); + sink(p); // tainted [field -> object] + sink(&p); // tainted [field -> object] + sink(p.x); // tainted +} + +void test_field_to_obj_taint_pointer(Point* pp) { + pp->x = getenv("VAR")[0]; + sink(pp); // tainted [field -> object] + sink(*pp); // not tainted +} + +void call_sink_on_object(Point* pp) { + sink(pp); // tainted [field -> object] + sink(*pp); // tainted [field -> object] +} + +void test_field_to_obj_taint_call_sink(Point* pp) { + pp->x = getenv("VAR")[0]; + call_sink_on_object(pp); +} + +void test_field_to_obj_taint_through_setter(Point* pp) { + taint_x(pp); + sink(pp); // tainted [field -> object] + sink(*pp); // not tainted +} + +Point* getPoint(); + +void test_field_to_obj_local_variable() { + Point* pp = getPoint(); + pp->x = getenv("VAR")[0]; + sink(pp); // not tainted + sink(*pp); // not tainted +} + +void test_field_to_obj_taint_array(Point* pp, int i) { + pp[0].x = getenv("VAR")[0]; + sink(pp[i]); // not tainted + sink(pp); // tainted [field -> object] + sink(*pp); // not tainted +} + +void test_field_to_obj_test_pointer_arith(Point* pp) { + (pp + sizeof(*pp))->x = getenv("VAR")[0]; + sink(pp); // tainted [field -> object] + sink(pp + sizeof(*pp)); // tainted [field -> object] +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp similarity index 79% rename from cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp rename to cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp index d92bb39d1584..3454e6ac9472 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp @@ -1,4 +1,6 @@ +#include "shared.h" + typedef unsigned long size_t; namespace std @@ -57,8 +59,7 @@ namespace std using stringstream = basic_stringstream; } -char *source(); -void sink(const char *s) {}; +char *source() { return getenv("USERDATA"); } void sink(const std::string &s) {}; void sink(const std::stringstream &s) {}; @@ -70,9 +71,9 @@ void test_string() sink(a); // tainted sink(b); - sink(c); // tainted + sink(c); // tainted [NOT DETECTED] sink(b.c_str()); - sink(c.c_str()); // tainted + sink(c.c_str()); // tainted [NOT DETECTED] } void test_stringstream() @@ -87,14 +88,14 @@ void test_stringstream() ss5 << t; sink(ss1); - sink(ss2); // tainted [NOT DETECTED] + sink(ss2); // tainted sink(ss3); // tainted [NOT DETECTED] - sink(ss4); // tainted [NOT DETECTED] + sink(ss4); // tainted sink(ss5); // tainted [NOT DETECTED] sink(ss1.str()); - sink(ss2.str()); // tainted [NOT DETECTED] + sink(ss2.str()); // tainted sink(ss3.str()); // tainted [NOT DETECTED] - sink(ss4.str()); // tainted [NOT DETECTED] + sink(ss4.str()); // tainted sink(ss5.str()); // tainted [NOT DETECTED] } @@ -122,12 +123,37 @@ void sink(const char *filename, const char *mode); void test_strings2() { string path1 = user_input(); - sink(path1.c_str(), "r"); // tainted + sink(path1.c_str(), "r"); // tainted [NOT DETECTED] string path2; path2 = user_input(); sink(path2.c_str(), "r"); // tainted string path3(user_input()); - sink(path3.c_str(), "r"); // tainted + sink(path3.c_str(), "r"); // tainted [NOT DETECTED] +} + +void test_string3() +{ + const char *cs = source(); + + // convert char * -> std::string + std::string ss(cs); + + sink(cs); // tainted + sink(ss); // tainted [NOT DETECTED] +} + +void test_string4() +{ + const char *cs = source(); + + // convert char * -> std::string + std::string ss(cs); + + // convert back std::string -> char * + cs = ss.c_str(); + + sink(cs); // tainted [NOT DETECTED] + sink(ss); // tainted [NOT DETECTED] } diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected index 4a87761da6fa..83f50d7fe157 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected @@ -112,9 +112,28 @@ | defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:16 | call to getenv | | defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:26 | (int)... | | defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:26 | access to array | -| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:143:23:143:24 | pp | -| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:144:8:144:9 | pp | -| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:150:13:150:14 | & ... | +| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x | +| defaulttainttracking.cpp:140:11:140:16 | call to getenv | shared.h:6:15:6:23 | sinkparam | +| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:14 | call to getenv | +| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | (int)... | +| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | access to array | +| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x | +| defaulttainttracking.cpp:157:9:157:14 | call to getenv | shared.h:6:15:6:23 | sinkparam | +| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:16 | call to getenv | +| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | (int)... | +| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | access to array | +| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:16 | call to getenv | +| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:26 | (int)... | +| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:26 | access to array | +| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:16 | call to getenv | +| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:26 | (int)... | +| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:26 | access to array | +| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:18 | call to getenv | +| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:28 | (int)... | +| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:28 | access to array | +| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:32 | call to getenv | +| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | (int)... | +| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | access to array | | dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:24:28:27 | call to atoi | | dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:34 | call to getenv | | dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:45 | (const char *)... | @@ -153,6 +172,71 @@ | globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:15:13:20 | call to getenv | | globals.cpp:23:15:23:20 | call to getenv | globals.cpp:16:15:16:21 | global2 | | globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:15:23:20 | call to getenv | +| stl.cpp:62:25:62:30 | call to getenv | shared.h:5:23:5:31 | sinkparam | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:21:29:21:29 | s | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:78:43:104 | p#0 | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:114:43:118 | p#1 | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:25:62:30 | call to getenv | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:64:36:64:36 | s | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:8:68:8 | a | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:12:68:17 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:21 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:23 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | a | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:21 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:23 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:14 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:16 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:15 | call to operator<< | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:26 | (reference dereference) | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:23 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:25 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:6 | call to operator<< | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference dereference) | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference to) | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:14 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (const stringstream)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (reference to) | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (const stringstream)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (reference to) | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | (const basic_stringstream, allocator>)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | (const basic_stringstream, allocator>)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:118:10:118:15 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:26 | call to user_input | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:28 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:19 | call to user_input | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string, allocator>)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:24 | call to user_input | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:26 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:14:138:15 | cs | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:24 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:26 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:18 | cs | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:143:7:143:8 | cs | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:14:149:15 | cs | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:24 | call to source | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:26 | (const char *)... | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:18 | cs | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string | | test_diff.cpp:92:10:92:13 | argv | shared.h:5:23:5:31 | sinkparam | | test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv | | test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:16 | (const char *)... | diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected index 79b02cd9d608..414ee9623e59 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected @@ -26,11 +26,53 @@ | defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:134:10:134:10 | x | IR only | | defaulttainttracking.cpp:133:9:133:14 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only | | defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:7:140:7 | x | AST only | -| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:143:23:143:24 | pp | IR only | -| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:144:8:144:9 | pp | IR only | -| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:150:13:150:14 | & ... | IR only | +| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x | IR only | +| defaulttainttracking.cpp:140:11:140:16 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only | +| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:5:157:5 | x | AST only | +| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x | IR only | +| defaulttainttracking.cpp:157:9:157:14 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only | +| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:7:170:7 | x | AST only | +| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:7:181:7 | x | AST only | +| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:7:195:7 | x | AST only | +| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:9:201:9 | x | AST only | +| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:23:208:23 | x | AST only | | globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only | | globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:78:43:104 | p#0 | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:7:62:12 | source | AST only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:64:36:64:36 | s | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:15 | call to operator<< | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:26 | (reference dereference) | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:6 | call to operator<< | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference dereference) | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference to) | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (const stringstream)... | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (reference to) | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (const stringstream)... | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (reference to) | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | (const basic_stringstream, allocator>)... | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | (const basic_stringstream, allocator>)... | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:117:7:117:16 | user_input | AST only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string, allocator>)... | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string | IR only | +| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:157:7:157:8 | cs | AST only | | test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only | | test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:36:24:36:24 | p | AST only | | test_diff.cpp:111:10:111:13 | argv | shared.h:5:23:5:31 | sinkparam | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll index 58a1dd2672b1..7ea87aeb583b 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll @@ -10,7 +10,7 @@ import semmle.code.cpp.ir.IR class TestBarrierGuard extends DataFlow::BarrierGuard { TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" } - override predicate checks(Instruction checked, boolean isTrue) { + override predicate checksInstr(Instruction checked, boolean isTrue) { checked = this.(CallInstruction).getPositionalArgument(0) and isTrue = true } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp index 0ad17fb47afb..033bdfd1899e 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp @@ -48,6 +48,6 @@ void following_pointers( int stackArray[2] = { source(), source() }; stackArray[0] = source(); - sink(stackArray); // no flow + sink(stackArray); // flow } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected index d01f0daa6a24..901339c4f386 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected @@ -1,6 +1,5 @@ uniqueEnclosingCallable -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation missingLocation uniqueNodeToString @@ -15,7 +14,6 @@ postHasUniquePre uniquePostUpdate postIsInSameCallable reverseRead -storeIsPostUpdate argHasPostUpdate | lambdas.cpp:18:7:18:7 | a | ArgumentNode is missing PostUpdateNode. | | lambdas.cpp:25:2:25:2 | b | ArgumentNode is missing PostUpdateNode. | @@ -23,3 +21,69 @@ argHasPostUpdate | lambdas.cpp:38:2:38:2 | d | ArgumentNode is missing PostUpdateNode. | | lambdas.cpp:45:2:45:2 | e | ArgumentNode is missing PostUpdateNode. | | test.cpp:67:29:67:35 | source1 | ArgumentNode is missing PostUpdateNode. | +postWithInFlow +| BarrierGuard.cpp:49:6:49:6 | x [post update] | PostUpdateNode should not be the target of local flow. | +| BarrierGuard.cpp:60:7:60:7 | x [post update] | PostUpdateNode should not be the target of local flow. | +| clang.cpp:22:9:22:20 | sourceArray1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| clang.cpp:28:22:28:23 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| clang.cpp:50:3:50:12 | stackArray [inner post update] | PostUpdateNode should not be the target of local flow. | +| clang.cpp:50:3:50:15 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| dispatch.cpp:60:3:60:14 | globalBottom [post update] | PostUpdateNode should not be the target of local flow. | +| dispatch.cpp:61:3:61:14 | globalMiddle [post update] | PostUpdateNode should not be the target of local flow. | +| dispatch.cpp:78:24:78:37 | call to allocateBottom [inner post update] | PostUpdateNode should not be the target of local flow. | +| dispatch.cpp:148:5:148:5 | f [post update] | PostUpdateNode should not be the target of local flow. | +| dispatch.cpp:168:8:168:8 | f [post update] | PostUpdateNode should not be the target of local flow. | +| example.c:24:9:24:9 | x [post update] | PostUpdateNode should not be the target of local flow. | +| example.c:24:20:24:20 | y [post update] | PostUpdateNode should not be the target of local flow. | +| example.c:26:9:26:9 | x [post update] | PostUpdateNode should not be the target of local flow. | +| example.c:26:19:26:24 | coords [inner post update] | PostUpdateNode should not be the target of local flow. | +| example.c:28:23:28:25 | pos [inner post update] | PostUpdateNode should not be the target of local flow. | +| globals.cpp:13:5:13:19 | flowTestGlobal1 [post update] | PostUpdateNode should not be the target of local flow. | +| globals.cpp:23:5:23:19 | flowTestGlobal2 [post update] | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:43:3:43:3 | c [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:11:5:11:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:11:5:11:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:20:5:20:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:22:7:22:9 | lhs [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:24:7:24:9 | lhs [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:29:5:29:7 | out [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:31:7:31:9 | out [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:39:7:39:9 | out [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:44:5:44:7 | out [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:46:7:46:9 | out [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:48:7:48:9 | out [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:75:9:75:11 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:83:9:83:11 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:87:11:87:13 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:89:11:89:13 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:94:9:94:11 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:96:11:96:13 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:104:11:104:13 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:109:9:109:11 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:113:11:113:13 | val [post update] | PostUpdateNode should not be the target of local flow. | +| ref.cpp:115:11:115:13 | val [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:91:3:91:9 | source1 [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:115:3:115:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:115:4:115:6 | out [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:120:3:120:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:120:4:120:6 | out [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:125:3:125:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:125:4:125:6 | out [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:333:5:333:13 | globalVar [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:347:5:347:13 | globalVar [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:359:5:359:9 | field [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:373:5:373:9 | field [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:384:10:384:13 | ref arg & ... | PostUpdateNode should not be the target of local flow. | +| test.cpp:384:11:384:13 | tmp [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:391:10:391:13 | ref arg & ... | PostUpdateNode should not be the target of local flow. | +| test.cpp:391:11:391:13 | tmp [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:400:10:400:13 | ref arg & ... | PostUpdateNode should not be the target of local flow. | +| test.cpp:400:11:400:13 | tmp [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:407:10:407:13 | ref arg & ... | PostUpdateNode should not be the target of local flow. | +| test.cpp:407:11:407:13 | tmp [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:423:21:423:25 | local [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:436:19:436:23 | local [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:465:3:465:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:465:4:465:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:470:22:470:22 | x [inner post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index 0bb9343dcaf8..038a138b2c0b 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -1,6 +1,5 @@ uniqueEnclosingCallable -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation | BarrierGuard.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. | | acrossLinkTargets.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. | @@ -28,9 +27,59 @@ localCallNodes postIsNotPre postHasUniquePre uniquePostUpdate -| ref.cpp:83:5:83:17 | Chi | Node has multiple PostUpdateNodes. | -| ref.cpp:109:5:109:22 | Chi | Node has multiple PostUpdateNodes. | postIsInSameCallable reverseRead -storeIsPostUpdate argHasPostUpdate +postWithInFlow +| BarrierGuard.cpp:49:3:49:17 | Chi | PostUpdateNode should not be the target of local flow. | +| BarrierGuard.cpp:60:3:60:18 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:28:3:28:34 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:34:22:34:27 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:34:32:34:37 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:39:32:39:37 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:39:42:39:47 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:43:35:43:40 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:43:51:43:51 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:49:25:49:30 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:49:35:49:40 | Chi | PostUpdateNode should not be the target of local flow. | +| clang.cpp:50:3:50:26 | Chi | PostUpdateNode should not be the target of local flow. | +| example.c:17:19:17:22 | Chi | PostUpdateNode should not be the target of local flow. | +| example.c:17:21:17:21 | Chi | PostUpdateNode should not be the target of local flow. | +| example.c:24:2:24:30 | Chi | PostUpdateNode should not be the target of local flow. | +| example.c:24:13:24:30 | Chi | PostUpdateNode should not be the target of local flow. | +| example.c:26:2:26:25 | Chi | PostUpdateNode should not be the target of local flow. | +| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | +| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | +| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:13:12:13:12 | Chi | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:13:15:13:15 | Chi | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:28:10:31:2 | Chi | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:28:10:31:2 | Chi | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:43:3:43:14 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:11:5:11:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:20:5:20:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:22:7:22:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:24:7:24:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:29:5:29:18 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:31:7:31:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:39:7:39:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:44:5:44:18 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:46:7:46:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:48:7:48:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:75:5:75:17 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:83:5:83:17 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:87:7:87:17 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:89:7:89:17 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:94:5:94:22 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:96:7:96:17 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:104:7:104:17 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:109:5:109:22 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:113:7:113:17 | Chi | PostUpdateNode should not be the target of local flow. | +| ref.cpp:115:7:115:17 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:91:3:91:18 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:115:3:115:17 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:120:3:120:10 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:125:3:125:11 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:359:5:359:20 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:373:5:373:20 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:465:3:465:15 | Chi | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected index 78d20cbf7ae0..75cdaee69ac4 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected @@ -7,11 +7,15 @@ | example.c:17:19:17:22 | {...} | example.c:26:19:26:24 | coords | | example.c:24:2:24:7 | coords [post update] | example.c:26:2:26:7 | coords | | example.c:24:2:24:7 | coords [post update] | example.c:26:19:26:24 | coords | +| example.c:24:2:24:30 | ... = ... | example.c:24:9:24:9 | x [post update] | | example.c:24:13:24:18 | coords [post update] | example.c:24:2:24:7 | coords | | example.c:24:13:24:18 | coords [post update] | example.c:26:2:26:7 | coords | | example.c:24:13:24:18 | coords [post update] | example.c:26:19:26:24 | coords | | example.c:24:13:24:30 | ... = ... | example.c:24:2:24:30 | ... = ... | +| example.c:24:13:24:30 | ... = ... | example.c:24:20:24:20 | y [post update] | +| example.c:24:20:24:20 | y | example.c:24:13:24:30 | ... = ... | | example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... | +| example.c:26:2:26:25 | ... = ... | example.c:26:9:26:9 | x [post update] | | example.c:26:13:26:16 | call to getX | example.c:26:2:26:25 | ... = ... | | example.c:26:18:26:24 | ref arg & ... | example.c:26:2:26:7 | coords | | example.c:26:18:26:24 | ref arg & ... | example.c:26:19:26:24 | coords [inner post update] | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp index 8e1ac7657ea4..52849a557843 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp @@ -56,13 +56,13 @@ namespace withoutFields { sink(x1); // flow [FALSE POSITIVE from uninitialized] notAssign(x2, source()); - sink(x2); // no flow [FALSE POSITIVE from uninitialized] + sink(x2); // no flow [FALSE POSITIVE from uninitialized, FALSE POSITIVE by IR] sourceToParamWrapper(x3); sink(x3); // flow [FALSE POSITIVE from uninitialized] notSource(x4); - sink(x4); // no flow [FALSE POSITIVE from uninitialized] + sink(x4); // no flow [FALSE POSITIVE from uninitialized, FALSE POSITIVE by IR] } } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp index bce01956dc73..25ab760cbcd9 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -428,7 +428,7 @@ void intPointerSourceCaller2() { int local[1]; intPointerSource(local); sink(local); // tainted - sink(*local); // clean + sink(*local); // tainted } void intArraySourceCaller() { @@ -441,7 +441,7 @@ void intArraySourceCaller2() { int local[2]; intArraySource(local, 2); sink(local); // tainted - sink(*local); // clean + sink(*local); // tainted } /////////////////////////////////////////////////////////////////////////////// @@ -468,5 +468,5 @@ void intOutparamSource(int *p) { void viaOutparam() { int x = 0; intOutparamSource(&x); - sink(x); // tainted [FALSE NEGATIVE] + sink(x); // tainted } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected index 6ff83eb74b5f..fbe5a51298ed 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected @@ -16,6 +16,7 @@ | clang.cpp:30:27:30:34 | call to getFirst | clang.cpp:28:27:28:32 | call to source | | clang.cpp:37:10:37:11 | m2 | clang.cpp:34:32:34:37 | call to source | | clang.cpp:45:17:45:18 | m2 | clang.cpp:43:35:43:40 | call to source | +| clang.cpp:51:8:51:17 | stackArray | clang.cpp:50:19:50:24 | call to source | | dispatch.cpp:11:38:11:38 | x | dispatch.cpp:37:19:37:24 | call to source | | dispatch.cpp:11:38:11:38 | x | dispatch.cpp:45:18:45:23 | call to source | | dispatch.cpp:35:16:35:25 | call to notSource1 | dispatch.cpp:9:37:9:42 | call to source | @@ -79,12 +80,17 @@ | test.cpp:424:8:424:12 | local | test.cpp:423:20:423:25 | ref arg & ... | | test.cpp:430:8:430:12 | local | test.cpp:428:7:428:11 | local | | test.cpp:430:8:430:12 | local | test.cpp:429:20:429:24 | ref arg local | +| test.cpp:431:8:431:13 | * ... | test.cpp:428:7:428:11 | local | +| test.cpp:431:8:431:13 | * ... | test.cpp:429:20:429:24 | ref arg local | | test.cpp:437:8:437:12 | local | test.cpp:435:7:435:11 | local | | test.cpp:437:8:437:12 | local | test.cpp:436:18:436:23 | ref arg & ... | | test.cpp:443:8:443:12 | local | test.cpp:441:7:441:11 | local | | test.cpp:443:8:443:12 | local | test.cpp:442:18:442:22 | ref arg local | +| test.cpp:444:8:444:13 | * ... | test.cpp:441:7:441:11 | local | +| test.cpp:444:8:444:13 | * ... | test.cpp:442:18:442:22 | ref arg local | | test.cpp:450:9:450:22 | (statement expression) | test.cpp:449:26:449:32 | source1 | | test.cpp:461:8:461:12 | local | test.cpp:449:26:449:32 | source1 | +| test.cpp:471:8:471:8 | x | test.cpp:465:8:465:13 | call to source | | true_upon_entry.cpp:21:8:21:8 | x | true_upon_entry.cpp:17:11:17:16 | call to source | | true_upon_entry.cpp:29:8:29:8 | x | true_upon_entry.cpp:27:9:27:14 | call to source | | true_upon_entry.cpp:39:8:39:8 | x | true_upon_entry.cpp:33:11:33:16 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected index d663862361cf..7d4bcf36adb2 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected @@ -2,6 +2,7 @@ | BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only | | clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only | | clang.cpp:39:42:39:47 | clang.cpp:41:18:41:19 | IR only | +| clang.cpp:50:19:50:24 | clang.cpp:51:8:51:17 | AST only | | dispatch.cpp:16:37:16:42 | dispatch.cpp:32:16:32:24 | IR only | | dispatch.cpp:16:37:16:42 | dispatch.cpp:40:15:40:23 | IR only | | dispatch.cpp:22:37:22:42 | dispatch.cpp:31:16:31:24 | IR only | @@ -19,13 +20,12 @@ | globals.cpp:13:23:13:28 | globals.cpp:12:10:12:24 | IR only | | globals.cpp:23:23:23:28 | globals.cpp:19:10:19:24 | IR only | | lambdas.cpp:8:10:8:15 | lambdas.cpp:21:3:21:6 | AST only | -| lambdas.cpp:43:7:43:12 | lambdas.cpp:46:7:46:7 | AST only | -| ref.cpp:29:11:29:16 | ref.cpp:62:10:62:11 | AST only | +| ref.cpp:44:11:44:16 | ref.cpp:65:10:65:11 | IR only | | ref.cpp:53:9:53:10 | ref.cpp:56:10:56:11 | AST only | | ref.cpp:53:13:53:14 | ref.cpp:59:10:59:11 | AST only | | ref.cpp:53:17:53:18 | ref.cpp:62:10:62:11 | AST only | | ref.cpp:53:21:53:22 | ref.cpp:65:10:65:11 | AST only | -| ref.cpp:55:23:55:28 | ref.cpp:56:10:56:11 | AST only | +| ref.cpp:58:19:58:24 | ref.cpp:59:10:59:11 | IR only | | test.cpp:75:7:75:8 | test.cpp:76:8:76:9 | AST only | | test.cpp:83:7:83:8 | test.cpp:84:8:84:18 | AST only | | test.cpp:83:7:83:8 | test.cpp:86:8:86:9 | AST only | @@ -41,11 +41,15 @@ | test.cpp:422:7:422:11 | test.cpp:424:8:424:12 | AST only | | test.cpp:423:20:423:25 | test.cpp:424:8:424:12 | AST only | | test.cpp:428:7:428:11 | test.cpp:430:8:430:12 | AST only | +| test.cpp:428:7:428:11 | test.cpp:431:8:431:13 | AST only | | test.cpp:429:20:429:24 | test.cpp:430:8:430:12 | AST only | +| test.cpp:429:20:429:24 | test.cpp:431:8:431:13 | AST only | | test.cpp:435:7:435:11 | test.cpp:437:8:437:12 | AST only | | test.cpp:436:18:436:23 | test.cpp:437:8:437:12 | AST only | | test.cpp:441:7:441:11 | test.cpp:443:8:443:12 | AST only | +| test.cpp:441:7:441:11 | test.cpp:444:8:444:13 | AST only | | test.cpp:442:18:442:22 | test.cpp:443:8:443:12 | AST only | +| test.cpp:442:18:442:22 | test.cpp:444:8:444:13 | AST only | | true_upon_entry.cpp:9:11:9:16 | true_upon_entry.cpp:13:8:13:8 | IR only | | true_upon_entry.cpp:62:11:62:16 | true_upon_entry.cpp:66:8:66:8 | IR only | | true_upon_entry.cpp:98:11:98:16 | true_upon_entry.cpp:105:8:105:8 | IR only | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected index f7cedc063eee..945bb7136ce6 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected @@ -44,6 +44,11 @@ | lambdas.cpp:29:3:29:6 | t | lambdas.cpp:8:10:8:15 | call to source | | lambdas.cpp:35:8:35:8 | a | lambdas.cpp:8:10:8:15 | call to source | | lambdas.cpp:41:8:41:8 | (reference dereference) | lambdas.cpp:8:10:8:15 | call to source | +| lambdas.cpp:46:7:46:7 | w | lambdas.cpp:43:7:43:12 | call to source | +| ref.cpp:56:10:56:11 | x1 | ref.cpp:55:23:55:28 | call to source | +| ref.cpp:59:10:59:11 | x2 | ref.cpp:58:19:58:24 | call to source | +| ref.cpp:62:10:62:11 | x3 | ref.cpp:29:11:29:16 | call to source | +| ref.cpp:65:10:65:11 | x4 | ref.cpp:44:11:44:16 | call to source | | ref.cpp:123:13:123:15 | val | ref.cpp:122:23:122:28 | call to source | | ref.cpp:126:13:126:15 | val | ref.cpp:125:19:125:24 | call to source | | ref.cpp:129:13:129:15 | val | ref.cpp:94:15:94:20 | call to source | @@ -77,6 +82,7 @@ | test.cpp:394:10:394:12 | tmp | test.cpp:388:53:388:59 | source1 | | test.cpp:450:9:450:22 | (statement expression) | test.cpp:449:26:449:32 | source1 | | test.cpp:461:8:461:12 | local | test.cpp:449:26:449:32 | source1 | +| test.cpp:471:8:471:8 | x | test.cpp:465:8:465:13 | call to source | | true_upon_entry.cpp:13:8:13:8 | x | true_upon_entry.cpp:9:11:9:16 | call to source | | true_upon_entry.cpp:21:8:21:8 | x | true_upon_entry.cpp:17:11:17:16 | call to source | | true_upon_entry.cpp:29:8:29:8 | x | true_upon_entry.cpp:27:9:27:14 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/fields/A.cpp b/cpp/ql/test/library-tests/dataflow/fields/A.cpp index ba6736520a9b..d65e900af960 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/A.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/A.cpp @@ -53,8 +53,8 @@ class A { B *b = new B(); b->set(new C1()); - sink(b->get()); // $ast $f-:ir - sink((new B(new C()))->get()); // $ast $f-:ir + sink(b->get()); // $ast $ir=55:12 + sink((new B(new C()))->get()); // $ast $ir } void f3() @@ -104,7 +104,7 @@ class A { if (C1 *c1 = dynamic_cast(c)) { - sink(c1->a); // $ast $ir + sink(c1->a); // $ast,ir } C *cc; if (C2 *c2 = dynamic_cast(c)) @@ -129,7 +129,7 @@ class A { B *b = new B(); f7(b); - sink(b->c); // $ast $f-:ir + sink(b->c); // $ast,ir } class D @@ -149,7 +149,7 @@ class A { B *b = new B(); D *d = new D(b, r()); - sink(d->b); // $ast=143:25 $ast=150:12 $f-:ir + sink(d->b); // $ast,ir=143:25 $ast,ir=150:12 sink(d->b->c); // $ast $f-:ir sink(b->c); // $ast,ir } diff --git a/cpp/ql/test/library-tests/dataflow/fields/C.cpp b/cpp/ql/test/library-tests/dataflow/fields/C.cpp index 896b754ff310..892d298a81d0 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/C.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/C.cpp @@ -26,9 +26,9 @@ class C void func() { - sink(s1); // $ast $f-:ir + sink(s1); // $ast $ir sink(s2); // $f-:ast $f-:ir - sink(s3); // $ast $f-:ir + sink(s3); // $ast $ir sink(s4); // $f-:ast $f-:ir } diff --git a/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll b/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll index 3451061436cf..41ddf5a17a8b 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll +++ b/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll @@ -18,7 +18,7 @@ class Conf extends Configuration { override predicate isSink(Node sink) { exists(Call c | c.getTarget().hasName("sink") and - c.getAnArgument() = sink.asExpr() + c.getAnArgument() = sink.asConvertedExpr() ) } diff --git a/cpp/ql/test/library-tests/dataflow/fields/Nodes.qll b/cpp/ql/test/library-tests/dataflow/fields/Nodes.qll new file mode 100644 index 000000000000..eb6f3247b82e --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/Nodes.qll @@ -0,0 +1,41 @@ +private import semmle.code.cpp.ir.dataflow.DataFlow as IR +private import semmle.code.cpp.dataflow.DataFlow as AST +private import cpp + +private newtype TNode = + TASTNode(AST::DataFlow::Node n) or + TIRNode(IR::DataFlow::Node n) + +class Node extends TNode { + string toString() { none() } + + IR::DataFlow::Node asIR() { none() } + + AST::DataFlow::Node asAST() { none() } + + Location getLocation() { none() } +} + +class ASTNode extends Node, TASTNode { + AST::DataFlow::Node n; + + ASTNode() { this = TASTNode(n) } + + override string toString() { result = n.toString() } + + override AST::DataFlow::Node asAST() { result = n } + + override Location getLocation() { result = n.getLocation() } +} + +class IRNode extends Node, TIRNode { + IR::DataFlow::Node n; + + IRNode() { this = TIRNode(n) } + + override string toString() { result = n.toString() } + + override IR::DataFlow::Node asIR() { result = n } + + override Location getLocation() { result = n.getLocation() } +} diff --git a/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp b/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp index f096692419be..df33dbb288e2 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp @@ -92,3 +92,116 @@ void nestedAssign() { w.s.m1 = user_input(); sink(w.s.m1); // $ast,ir } + +void addressOfField() { + S s; + s.m1 = user_input(); + + S s_copy = s; + int* px = &s_copy.m1; + sink(*px); // $f-:ast $ir +} + +void taint_a_ptr(int* pa) { + *pa = user_input(); +} + +void test_field_conflation_array_content() { + S s; + taint_a_ptr(&s.m1); + sink(s.m2); +} + +struct S_with_pointer { + int m1, m2; + int* data; +}; + +void pointer_deref(int* xs) { + taint_a_ptr(xs); + sink(xs[0]); // $f-:ast $ir +} + +void pointer_deref_sub(int* xs) { + taint_a_ptr(xs - 2); + sink(*(xs - 2)); // $f-:ast $ir +} + +void pointer_many_addrof_and_deref(int* xs) { + taint_a_ptr(xs); + sink(*&*&*xs); // $f-:ast $ir +} + +void pointer_unary_plus(int* xs) { + taint_a_ptr(+xs); + sink(*+xs); // $f-:ast $ir +} + +void pointer_member_index(S_with_pointer s) { + taint_a_ptr(s.data); + // `s.data` is points to all-aliased-memory + sink(s.data[0]); // $f-:ast,ir +} + +void member_array_different_field(S_with_pointer* s) { + taint_a_ptr(&s[0].m1); + sink(s[0].m2); +} + +struct S_with_array { + int m1, m2; + int data[10]; +}; + +void pointer_member_deref() { + S_with_array s; + taint_a_ptr(s.data); + sink(*s.data); // $ir,ast +} + +void array_member_deref() { + S_with_array s; + taint_a_ptr(s.data); + sink(s.data[0]); // $ir,ast +} + +struct S2 { + S s; + int m3; +}; + +void deep_member_field_dot() { + S2 s2; + taint_a_ptr(&s2.s.m1); + sink(s2.s.m1); // $ir,ast +} + +void deep_member_field_dot_different_fields() { + S2 s2; + taint_a_ptr(&s2.s.m1); + sink(s2.s.m2); +} + +void deep_member_field_dot_2() { + S2 s2; + taint_a_ptr(&s2.s.m1); + S2 s2_2 = s2; + sink(s2_2.s.m1); // $ir,ast +} + +void deep_member_field_dot_different_fields_2() { + S2 s2; + taint_a_ptr(&s2.s.m1); + S2 s2_2 = s2; + sink(s2_2.s.m2); +} + +void deep_member_field_arrow(S2 *ps2) { + taint_a_ptr(&ps2->s.m1); + sink(ps2->s.m1); // $ir,ast +} + +void deep_member_field_arrow_different_fields(S2 *ps2) { + taint_a_ptr(&ps2->s.m1); + sink(ps2->s.m2); +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp b/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp new file mode 100644 index 000000000000..68290dc70770 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp @@ -0,0 +1,51 @@ +void sink(void *o); +void *user_input(void); + +void local_array() { + void *arr[10] = { 0 }; + arr[0] = user_input(); + sink(arr[0]); // $ast,ir + sink(arr[1]); // $f+:ast + sink(*arr); // $ast,ir + sink(*&arr[0]); // $ast,ir +} + +void local_array_convoluted_assign() { + void *arr[10] = { 0 }; + *&arr[0] = user_input(); + sink(arr[0]); // $ast,ir + sink(arr[1]); // $f+:ast +} + +struct inner { + void *data; + int unrelated; +}; + +struct middle { + inner arr[10]; + inner *ptr; +}; + +struct outer { + middle nested; + middle *indirect; +}; + +void nested_array_1(outer o) { + o.nested.arr[1].data = user_input(); + sink(o.nested.arr[1].data); // $ast,ir + sink(o.nested.arr[0].data); // $f+:ast +} + +void nested_array_2(outer o) { + o.indirect->arr[1].data = user_input(); + sink(o.indirect->arr[1].data); // $ast $f-:ir + sink(o.indirect->arr[0].data); // $f+:ast +} + +void nested_array_3(outer o) { + o.indirect->ptr[1].data = user_input(); + sink(o.indirect->ptr[1].data); // $f-:ast,ir + sink(o.indirect->ptr[0].data); +} diff --git a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp index 6a0d61f799a3..84c3b039cb90 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp @@ -48,19 +48,19 @@ struct S { void test_setDirectly() { S s; s.setDirectly(user_input()); - sink(s.getDirectly()); // $ast $f-:ir + sink(s.getDirectly()); // $ast $ir } void test_setIndirectly() { S s; s.setIndirectly(user_input()); - sink(s.getIndirectly()); // $ast $f-:ir + sink(s.getIndirectly()); // $ast $ir } void test_setThroughNonMember() { S s; s.setThroughNonMember(user_input()); - sink(s.getThroughNonMember()); // $ast $f-:ir + sink(s.getThroughNonMember()); // $ast $ir } void test_nonMemberSetA() { @@ -109,11 +109,11 @@ void test_outer_with_ptr(Outer *pouter) { sink(outer.inner_nested.a); // $ast,ir sink(outer.inner_ptr->a); // $ast $f-:ir - sink(outer.a); // $f-:ast $f-:ir + sink(outer.a); // $ast,ir sink(pouter->inner_nested.a); // $ast,ir sink(pouter->inner_ptr->a); // $ast $f-:ir - sink(pouter->a); // $f-:ast $f-:ir + sink(pouter->a); // $ast,ir } void test_outer_with_ref(Outer *pouter) { @@ -129,9 +129,9 @@ void test_outer_with_ref(Outer *pouter) { sink(outer.inner_nested.a); // $ast,ir sink(outer.inner_ptr->a); // $ast $f-:ir - sink(outer.a); // $ast $f-:ir + sink(outer.a); // $ast,ir sink(pouter->inner_nested.a); // $ast,ir sink(pouter->inner_ptr->a); // $ast $f-:ir - sink(pouter->a); // $ast $f-:ir + sink(pouter->a); // $ast,ir } diff --git a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp index bc7ac3f341f8..cb55bdd28637 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp @@ -39,17 +39,8 @@ void sink(int x) void bar(Outer &b) { - // The library correctly finds that the four `user_input` sources can make it - // to the `sink` calls, but it also finds some source/sink combinations that - // are impossible. Those false positives here are a consequence of how the - // shared data flow library overapproximates field flow. The library only - // tracks the final two fields (`f` and `inner`) and the length (3) of the field - // access path, and then it tracks that both `a_` and `b_` have followed `f.inner` - // in _some_ access path somewhere in the search. That makes the library conclude - // that there could be flow to `b.inner.f.a_` even when the flow was actually to - // `b.inner.f.b_`. - sink(b.inner.f.a()); // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $f-:ir - sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f-:ir + sink(b.inner.f.a()); // $ast=53:19 $ast=55:19 $ir=53:19 $ir=55:19 + sink(b.inner.f.b()); // $ast=54:19 $ast=56:19 $ir=54:19 $ir=56:19 } void foo() diff --git a/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp b/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp index 5179ea363957..4816180954ea 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp @@ -25,8 +25,8 @@ class Foo void bar(Foo &f) { - sink(f.a()); //$ast=34:11 $ast=36:11 $f-:ir - sink(f.b()); //$ast=35:14 $ast=36:25 $f-:ir + sink(f.a()); //$ast=34:11 $ast=36:11 $ir=34:11 $ir=36:11 + sink(f.b()); //$ast=35:14 $ast=36:25 $ir=35:14 $ir=36:25 } void foo() diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index 04a03e5fb254..49b12c14350b 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -1,16 +1,7 @@ uniqueEnclosingCallable | C.cpp:37:24:37:33 | 0 | Node should have one enclosing callable but has 0. | | C.cpp:37:24:37:33 | new | Node should have one enclosing callable but has 0. | -uniqueTypeBound -| complex.cpp:22:11:22:17 | constructor init of field f [post-this] | Node should have one type bound but has 0. | -| complex.cpp:22:11:22:17 | constructor init of field f [pre-this] | Node should have one type bound but has 0. | -| complex.cpp:25:7:25:7 | constructor init of field inner [post-this] | Node should have one type bound but has 0. | -| complex.cpp:25:7:25:7 | constructor init of field inner [pre-this] | Node should have one type bound but has 0. | -uniqueTypeRepr -| complex.cpp:22:11:22:17 | constructor init of field f [post-this] | Node should have one type representation but has 0. | -| complex.cpp:22:11:22:17 | constructor init of field f [pre-this] | Node should have one type representation but has 0. | -| complex.cpp:25:7:25:7 | constructor init of field inner [post-this] | Node should have one type representation but has 0. | -| complex.cpp:25:7:25:7 | constructor init of field inner [pre-this] | Node should have one type representation but has 0. | +uniqueType uniqueNodeLocation missingLocation uniqueNodeToString @@ -25,7 +16,6 @@ postHasUniquePre uniquePostUpdate postIsInSameCallable reverseRead -storeIsPostUpdate argHasPostUpdate | A.cpp:41:15:41:21 | new | ArgumentNode is missing PostUpdateNode. | | A.cpp:55:12:55:19 | new | ArgumentNode is missing PostUpdateNode. | @@ -40,6 +30,120 @@ argHasPostUpdate | D.cpp:43:24:43:40 | new | ArgumentNode is missing PostUpdateNode. | | D.cpp:50:24:50:40 | new | ArgumentNode is missing PostUpdateNode. | | D.cpp:57:25:57:41 | new | ArgumentNode is missing PostUpdateNode. | +| arrays.cpp:7:8:7:13 | access to array | ArgumentNode is missing PostUpdateNode. | +| arrays.cpp:8:8:8:13 | access to array | ArgumentNode is missing PostUpdateNode. | +| arrays.cpp:9:8:9:11 | * ... | ArgumentNode is missing PostUpdateNode. | +| arrays.cpp:10:8:10:15 | * ... | ArgumentNode is missing PostUpdateNode. | +| arrays.cpp:16:8:16:13 | access to array | ArgumentNode is missing PostUpdateNode. | +| arrays.cpp:17:8:17:13 | access to array | ArgumentNode is missing PostUpdateNode. | | by_reference.cpp:51:8:51:8 | s | ArgumentNode is missing PostUpdateNode. | | by_reference.cpp:57:8:57:8 | s | ArgumentNode is missing PostUpdateNode. | | by_reference.cpp:63:8:63:8 | s | ArgumentNode is missing PostUpdateNode. | +postWithInFlow +| A.cpp:25:13:25:13 | c [post update] | PostUpdateNode should not be the target of local flow. | +| A.cpp:27:28:27:28 | c [post update] | PostUpdateNode should not be the target of local flow. | +| A.cpp:42:11:42:12 | cc [inner post update] | PostUpdateNode should not be the target of local flow. | +| A.cpp:43:11:43:12 | ct [inner post update] | PostUpdateNode should not be the target of local flow. | +| A.cpp:100:9:100:9 | a [post update] | PostUpdateNode should not be the target of local flow. | +| A.cpp:142:10:142:10 | c [post update] | PostUpdateNode should not be the target of local flow. | +| A.cpp:143:13:143:13 | b [post update] | PostUpdateNode should not be the target of local flow. | +| A.cpp:183:7:183:10 | head [post update] | PostUpdateNode should not be the target of local flow. | +| A.cpp:184:13:184:16 | next [post update] | PostUpdateNode should not be the target of local flow. | +| B.cpp:35:13:35:17 | elem1 [post update] | PostUpdateNode should not be the target of local flow. | +| B.cpp:36:13:36:17 | elem2 [post update] | PostUpdateNode should not be the target of local flow. | +| B.cpp:46:13:46:16 | box1 [post update] | PostUpdateNode should not be the target of local flow. | +| C.cpp:24:11:24:12 | s3 [post update] | PostUpdateNode should not be the target of local flow. | +| D.cpp:9:21:9:24 | elem [post update] | PostUpdateNode should not be the target of local flow. | +| D.cpp:11:29:11:32 | elem [post update] | PostUpdateNode should not be the target of local flow. | +| D.cpp:16:21:16:23 | box [post update] | PostUpdateNode should not be the target of local flow. | +| D.cpp:18:29:18:31 | box [post update] | PostUpdateNode should not be the target of local flow. | +| D.cpp:30:13:30:16 | elem [post update] | PostUpdateNode should not be the target of local flow. | +| D.cpp:44:19:44:22 | elem [post update] | PostUpdateNode should not be the target of local flow. | +| D.cpp:57:5:57:12 | boxfield [post update] | PostUpdateNode should not be the target of local flow. | +| D.cpp:58:20:58:23 | elem [post update] | PostUpdateNode should not be the target of local flow. | +| E.cpp:33:19:33:19 | p [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:9:6:9:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:13:5:13:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:17:5:17:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:25:18:25:19 | s1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:37:8:37:9 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:42:6:42:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:49:9:49:10 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:54:6:54:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:60:6:60:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:72:5:72:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:79:6:79:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:86:5:86:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:92:7:92:8 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:98:5:98:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:106:3:106:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:106:4:106:5 | pa [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:111:18:111:19 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:126:15:126:16 | xs [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:136:16:136:17 | xs [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:147:16:147:16 | s [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:147:21:147:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:175:21:175:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:181:21:181:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:187:21:187:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:194:21:194:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:200:23:200:24 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:205:23:205:24 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:6:3:6:5 | arr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:6:3:6:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:15:3:15:10 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:15:5:15:7 | arr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:36:12:36:14 | arr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:36:19:36:22 | data [post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:37:17:37:19 | arr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:38:17:38:19 | arr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:42:15:42:17 | arr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:42:22:42:25 | data [post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:43:20:43:22 | arr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:44:20:44:22 | arr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:48:15:48:17 | ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:48:22:48:25 | data [post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:49:20:49:22 | ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:50:20:50:22 | ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:12:8:12:8 | a [post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:16:11:16:11 | a [post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:68:18:68:18 | s [inner post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:84:10:84:10 | a [post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:88:9:88:9 | a [post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:92:3:92:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:92:4:92:5 | pa [inner post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:96:3:96:4 | pa [post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:102:28:102:39 | inner_nested [inner post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:104:22:104:22 | a [inner post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:106:30:106:41 | inner_nested [inner post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:108:24:108:24 | a [inner post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:123:28:123:36 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:127:30:127:38 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| complex.cpp:11:22:11:23 | a_ [post update] | PostUpdateNode should not be the target of local flow. | +| complex.cpp:12:22:12:23 | b_ [post update] | PostUpdateNode should not be the target of local flow. | +| constructors.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. | +| constructors.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:9:36:9:36 | a [post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:12:56:12:56 | a [post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:13:57:13:57 | a [post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:22:23:22:23 | a [post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:37:26:37:33 | call to getInner [inner post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:42:13:42:20 | call to getInner [inner post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:42:25:42:25 | a [post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:47:7:47:11 | outer [inner post update] | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:47:27:47:27 | a [post update] | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:49:13:49:15 | bar [inner post update] | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:49:20:49:22 | baz [post update] | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:53:13:53:15 | bar [inner post update] | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:53:35:53:43 | bufferLen [post update] | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:54:20:54:22 | bar [inner post update] | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:60:16:60:18 | ref arg dst | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:61:25:61:27 | bar [inner post update] | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:65:25:65:27 | bar [inner post update] | PostUpdateNode should not be the target of local flow. | +| simple.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. | +| simple.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. | +| simple.cpp:65:7:65:7 | i [post update] | PostUpdateNode should not be the target of local flow. | +| simple.cpp:83:12:83:13 | f1 [post update] | PostUpdateNode should not be the target of local flow. | +| simple.cpp:92:7:92:7 | i [post update] | PostUpdateNode should not be the target of local flow. | +| struct_init.c:24:11:24:12 | ab [inner post update] | PostUpdateNode should not be the target of local flow. | +| struct_init.c:36:17:36:24 | nestedAB [inner post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index ba7e3bc01257..ce9ed1850808 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -1,14 +1,10 @@ uniqueEnclosingCallable -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation -| D.cpp:1:17:1:17 | o | Node should have one location but has 3. | -| by_reference.cpp:1:17:1:17 | o | Node should have one location but has 3. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | -| qualifiers.cpp:1:17:1:17 | o | Node should have one location but has 3. | missingLocation | Nodes without location: 4 | uniqueNodeToString @@ -21,8 +17,134 @@ localCallNodes postIsNotPre postHasUniquePre | simple.cpp:65:5:65:22 | Store | PostUpdateNode should have one pre-update node but has 0. | +| simple.cpp:92:5:92:22 | Store | PostUpdateNode should have one pre-update node but has 0. | uniquePostUpdate postIsInSameCallable reverseRead -storeIsPostUpdate argHasPostUpdate +postWithInFlow +| A.cpp:25:7:25:17 | Chi | PostUpdateNode should not be the target of local flow. | +| A.cpp:27:22:27:32 | Chi | PostUpdateNode should not be the target of local flow. | +| A.cpp:98:12:98:18 | Chi | PostUpdateNode should not be the target of local flow. | +| A.cpp:100:5:100:13 | Chi | PostUpdateNode should not be the target of local flow. | +| A.cpp:142:7:142:20 | Chi | PostUpdateNode should not be the target of local flow. | +| A.cpp:143:7:143:31 | Chi | PostUpdateNode should not be the target of local flow. | +| A.cpp:183:7:183:20 | Chi | PostUpdateNode should not be the target of local flow. | +| A.cpp:184:7:184:23 | Chi | PostUpdateNode should not be the target of local flow. | +| B.cpp:6:15:6:24 | Chi | PostUpdateNode should not be the target of local flow. | +| B.cpp:15:15:15:27 | Chi | PostUpdateNode should not be the target of local flow. | +| B.cpp:35:7:35:22 | Chi | PostUpdateNode should not be the target of local flow. | +| B.cpp:36:7:36:22 | Chi | PostUpdateNode should not be the target of local flow. | +| B.cpp:46:7:46:21 | Chi | PostUpdateNode should not be the target of local flow. | +| C.cpp:22:12:22:21 | Chi | PostUpdateNode should not be the target of local flow. | +| C.cpp:22:12:22:21 | Chi | PostUpdateNode should not be the target of local flow. | +| C.cpp:24:5:24:25 | Chi | PostUpdateNode should not be the target of local flow. | +| C.cpp:24:16:24:25 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:9:21:9:28 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:11:29:11:36 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:16:21:16:27 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:18:29:18:35 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:28:15:28:24 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:35:15:35:24 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:42:15:42:24 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:49:15:49:24 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:56:15:56:24 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:57:5:57:42 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:9:3:9:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:13:3:13:21 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:17:3:17:21 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:21:12:21:12 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:21:15:21:15 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:22:12:22:12 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:22:15:22:15 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:23:12:23:12 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:23:15:23:15 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:35:12:35:12 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:35:15:35:15 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:37:3:37:24 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:40:12:40:12 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:40:15:40:15 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:42:3:42:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:47:12:47:12 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:47:15:47:15 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:49:3:49:25 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:52:12:52:12 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:52:15:52:15 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:54:3:54:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:59:12:59:12 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:59:15:59:15 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:60:3:60:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:70:19:70:19 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:70:22:70:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:72:3:72:21 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:77:19:77:19 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:77:22:77:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:79:3:79:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:84:19:84:19 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:84:22:84:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:86:3:86:21 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:91:19:91:19 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:91:22:91:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:92:3:92:23 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:98:3:98:21 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:106:3:106:20 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:111:15:111:19 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:147:15:147:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:175:15:175:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:181:15:181:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:187:15:187:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:194:15:194:22 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:200:15:200:24 | Chi | PostUpdateNode should not be the target of local flow. | +| aliasing.cpp:205:15:205:24 | Chi | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:5:18:5:23 | Chi | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:5:21:5:21 | Chi | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:6:3:6:23 | Chi | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:14:18:14:23 | Chi | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:14:21:14:21 | Chi | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:15:3:15:25 | Chi | PostUpdateNode should not be the target of local flow. | +| arrays.cpp:36:3:36:37 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:12:5:12:16 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:16:5:16:19 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:84:3:84:25 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:88:3:88:24 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:92:3:92:20 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:96:3:96:19 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:102:21:102:39 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:104:15:104:22 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:106:21:106:41 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:108:15:108:24 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:122:21:122:38 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:124:15:124:21 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:126:21:126:40 | Chi | PostUpdateNode should not be the target of local flow. | +| by_reference.cpp:128:15:128:23 | Chi | PostUpdateNode should not be the target of local flow. | +| complex.cpp:11:22:11:27 | Chi | PostUpdateNode should not be the target of local flow. | +| complex.cpp:12:22:12:27 | Chi | PostUpdateNode should not be the target of local flow. | +| complex.cpp:14:26:14:26 | Chi | PostUpdateNode should not be the target of local flow. | +| complex.cpp:14:33:14:33 | Chi | PostUpdateNode should not be the target of local flow. | +| constructors.cpp:20:24:20:29 | Chi | PostUpdateNode should not be the target of local flow. | +| constructors.cpp:21:24:21:29 | Chi | PostUpdateNode should not be the target of local flow. | +| constructors.cpp:23:28:23:28 | Chi | PostUpdateNode should not be the target of local flow. | +| constructors.cpp:23:35:23:35 | Chi | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:9:30:9:44 | Chi | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:12:49:12:64 | Chi | PostUpdateNode should not be the target of local flow. | +| qualifiers.cpp:13:51:13:65 | Chi | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:39:12:39:95 | Chi | PostUpdateNode should not be the target of local flow. | +| realistic.cpp:49:9:49:64 | Chi | PostUpdateNode should not be the target of local flow. | +| simple.cpp:20:24:20:29 | Chi | PostUpdateNode should not be the target of local flow. | +| simple.cpp:21:24:21:29 | Chi | PostUpdateNode should not be the target of local flow. | +| simple.cpp:23:28:23:28 | Chi | PostUpdateNode should not be the target of local flow. | +| simple.cpp:23:35:23:35 | Chi | PostUpdateNode should not be the target of local flow. | +| simple.cpp:65:5:65:22 | Store | PostUpdateNode should not be the target of local flow. | +| simple.cpp:83:9:83:28 | Chi | PostUpdateNode should not be the target of local flow. | +| simple.cpp:92:5:92:22 | Store | PostUpdateNode should not be the target of local flow. | +| struct_init.c:20:20:20:29 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:20:34:20:34 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:27:7:27:16 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:27:21:27:21 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:28:5:28:7 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:36:10:36:24 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:40:20:40:29 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:40:34:40:34 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:42:7:42:16 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:42:21:42:21 | Chi | PostUpdateNode should not be the target of local flow. | +| struct_init.c:43:5:43:7 | Chi | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected new file mode 100644 index 000000000000..242c09f3a3bd --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected @@ -0,0 +1,45 @@ +| A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... | AST only | +| A.cpp:47:12:47:18 | new | A.cpp:49:13:49:13 | c | AST only | +| A.cpp:64:21:64:28 | new | A.cpp:66:14:66:14 | c | AST only | +| A.cpp:73:25:73:32 | new | A.cpp:75:14:75:14 | c | AST only | +| A.cpp:98:12:98:18 | new | A.cpp:120:16:120:16 | a | AST only | +| A.cpp:142:14:142:20 | new | A.cpp:153:16:153:16 | c | AST only | +| A.cpp:159:12:159:18 | new | A.cpp:165:26:165:29 | head | AST only | +| A.cpp:159:12:159:18 | new | A.cpp:169:15:169:18 | head | AST only | +| B.cpp:6:15:6:24 | new | B.cpp:9:20:9:24 | elem1 | AST only | +| B.cpp:15:15:15:27 | new | B.cpp:19:20:19:24 | elem2 | AST only | +| D.cpp:28:15:28:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only | +| D.cpp:35:15:35:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only | +| D.cpp:42:15:42:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only | +| D.cpp:49:15:49:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only | +| D.cpp:56:15:56:24 | new | D.cpp:64:25:64:28 | elem | AST only | +| E.cpp:28:21:28:23 | ref arg raw | E.cpp:31:10:31:12 | raw | AST only | +| E.cpp:29:24:29:29 | ref arg buffer | E.cpp:32:13:32:18 | buffer | AST only | +| E.cpp:30:28:30:33 | ref arg buffer | E.cpp:21:18:21:23 | buffer | AST only | +| aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | IR only | +| aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 | IR only | +| aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | IR only | +| aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | IR only | +| aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:102:8:102:10 | * ... | IR only | +| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:122:8:122:12 | access to array | IR only | +| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:127:8:127:16 | * ... | IR only | +| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:132:8:132:14 | * ... | IR only | +| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:137:8:137:11 | * ... | IR only | +| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array | AST only | +| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | AST only | +| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:38:24:38:27 | data | AST only | +| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:43:27:43:30 | data | AST only | +| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:44:27:44:30 | data | AST only | +| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:111:25:111:25 | a | AST only | +| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:115:27:115:27 | a | AST only | +| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:131:25:131:25 | a | AST only | +| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | AST only | +| qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:23:23:23:23 | a | AST only | +| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:28:23:28:23 | a | AST only | +| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:33:23:33:23 | a | AST only | +| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:38:23:38:23 | a | AST only | +| qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:43:23:43:23 | a | AST only | +| qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:48:23:48:23 | a | AST only | +| realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:61:47:61:55 | bufferLen | AST only | +| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:33:25:33:25 | a | AST only | +| struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.ql b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.ql new file mode 100644 index 000000000000..47bee0db4923 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.ql @@ -0,0 +1,31 @@ +/** + * @kind problem + */ + +import cpp +import Nodes +import IRConfiguration as IRConf +import ASTConfiguration as ASTConf +private import semmle.code.cpp.ir.dataflow.DataFlow as IR +private import semmle.code.cpp.dataflow.DataFlow as AST + +from Node source, Node sink, IRConf::Conf irConf, ASTConf::Conf astConf, string msg +where + irConf.hasFlow(source.asIR(), sink.asIR()) and + not exists(AST::DataFlow::Node astSource, AST::DataFlow::Node astSink | + astSource.asExpr() = source.asIR().asExpr() and + astSink.asExpr() = sink.asIR().asExpr() + | + astConf.hasFlow(astSource, astSink) + ) and + msg = "IR only" + or + astConf.hasFlow(source.asAST(), sink.asAST()) and + not exists(IR::DataFlow::Node irSource, IR::DataFlow::Node irSink | + irSource.asExpr() = source.asAST().asExpr() and + irSink.asExpr() = sink.asAST().asExpr() + | + irConf.hasFlow(irSource, irSink) + ) and + msg = "AST only" +select source, sink, msg diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index 69c088fb260d..61770c0aca35 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -1,18 +1,40 @@ edges +| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:13:56:15 | call to get | +| A.cpp:55:12:55:19 | (C *)... | A.cpp:55:5:55:5 | set output argument [c] | +| A.cpp:55:12:55:19 | new | A.cpp:55:5:55:5 | set output argument [c] | +| A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:28:57:30 | call to get | +| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | B output argument [c] | | A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Store | -| A.cpp:100:5:100:13 | Chi [a] | A.cpp:101:8:101:9 | Argument 0 indirection [a] | +| A.cpp:100:5:100:13 | Chi [a] | A.cpp:103:14:103:14 | *c [a] | | A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | Chi [a] | -| A.cpp:101:8:101:9 | Argument 0 indirection [a] | A.cpp:103:14:103:14 | *c [a] | | A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a | -| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a | -| A.cpp:107:16:107:16 | a | A.cpp:107:12:107:16 | (void *)... | +| A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] | +| A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] | +| A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | set output argument [c] | +| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c | +| A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] | | A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] | | A.cpp:142:7:142:20 | Store | A.cpp:142:7:142:20 | Chi [c] | | A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Store | -| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c | +| A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] | +| A.cpp:143:7:143:31 | Store | A.cpp:143:7:143:31 | Chi [b] | +| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | Store | +| A.cpp:150:12:150:18 | new | A.cpp:151:12:151:24 | D output argument [b] | +| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b | +| A.cpp:151:12:151:24 | D output argument [b] | A.cpp:151:12:151:24 | Chi [b] | | A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c | | A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] | -| A.cpp:154:13:154:13 | c | A.cpp:154:10:154:13 | (void *)... | +| C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:27:8:27:11 | *#this [s1] | +| C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:27:8:27:11 | *#this [s3] | +| C.cpp:22:12:22:21 | Chi [s1] | C.cpp:24:5:24:25 | Chi [s1] | +| C.cpp:22:12:22:21 | Store | C.cpp:22:12:22:21 | Chi [s1] | +| C.cpp:22:12:22:21 | new | C.cpp:22:12:22:21 | Store | +| C.cpp:24:5:24:25 | Chi [s1] | C.cpp:18:12:18:18 | C output argument [s1] | +| C.cpp:24:5:24:25 | Chi [s3] | C.cpp:18:12:18:18 | C output argument [s3] | +| C.cpp:24:5:24:25 | Store | C.cpp:24:5:24:25 | Chi [s3] | +| C.cpp:24:16:24:25 | new | C.cpp:24:5:24:25 | Store | +| C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 | +| C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 | | aliasing.cpp:9:3:9:22 | Chi [m1] | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | | aliasing.cpp:9:3:9:22 | Store | aliasing.cpp:9:3:9:22 | Chi [m1] | | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | Store | @@ -32,9 +54,56 @@ edges | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | -| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | +| aliasing.cpp:98:3:98:21 | Chi [m1] | aliasing.cpp:100:14:100:14 | Store [m1] | +| aliasing.cpp:98:3:98:21 | Store | aliasing.cpp:98:3:98:21 | Chi [m1] | +| aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:98:3:98:21 | Store | +| aliasing.cpp:100:14:100:14 | Store [m1] | aliasing.cpp:102:8:102:10 | * ... | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:126:15:126:20 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:158:15:158:20 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:164:15:164:20 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] | +| aliasing.cpp:106:3:106:20 | Store | aliasing.cpp:106:3:106:20 | Chi [array content] | +| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:3:106:20 | Store | +| aliasing.cpp:121:15:121:16 | Chi [array content] | aliasing.cpp:122:8:122:12 | access to array | +| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] | aliasing.cpp:121:15:121:16 | Chi [array content] | +| aliasing.cpp:126:15:126:20 | Chi [array content] | aliasing.cpp:127:8:127:16 | * ... | +| aliasing.cpp:126:15:126:20 | taint_a_ptr output argument [array content] | aliasing.cpp:126:15:126:20 | Chi [array content] | +| aliasing.cpp:131:15:131:16 | Chi [array content] | aliasing.cpp:132:8:132:14 | * ... | +| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] | aliasing.cpp:131:15:131:16 | Chi [array content] | +| aliasing.cpp:136:15:136:17 | Chi [array content] | aliasing.cpp:137:8:137:11 | * ... | +| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] | aliasing.cpp:136:15:136:17 | Chi [array content] | +| aliasing.cpp:158:15:158:20 | Chi [array content] | aliasing.cpp:159:8:159:14 | * ... | +| aliasing.cpp:158:15:158:20 | taint_a_ptr output argument [array content] | aliasing.cpp:158:15:158:20 | Chi [array content] | +| aliasing.cpp:164:15:164:20 | Chi [array content] | aliasing.cpp:165:8:165:16 | access to array | +| aliasing.cpp:164:15:164:20 | taint_a_ptr output argument [array content] | aliasing.cpp:164:15:164:20 | Chi [array content] | +| aliasing.cpp:175:15:175:22 | Chi | aliasing.cpp:175:15:175:22 | Chi [m1] | +| aliasing.cpp:175:15:175:22 | Chi [m1] | aliasing.cpp:176:13:176:14 | m1 | +| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] | aliasing.cpp:175:15:175:22 | Chi | +| aliasing.cpp:187:15:187:22 | Chi | aliasing.cpp:187:15:187:22 | Chi [m1] | +| aliasing.cpp:187:15:187:22 | Chi [m1] | aliasing.cpp:188:13:188:14 | Store [m1] | +| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] | aliasing.cpp:187:15:187:22 | Chi | +| aliasing.cpp:188:13:188:14 | Store [m1] | aliasing.cpp:189:15:189:16 | m1 | +| aliasing.cpp:200:15:200:24 | Chi | aliasing.cpp:200:15:200:24 | Chi [m1] | +| aliasing.cpp:200:15:200:24 | Chi [m1] | aliasing.cpp:201:15:201:16 | m1 | +| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] | aliasing.cpp:200:15:200:24 | Chi | +| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array | +| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... | +| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | +| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array | +| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data | +| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | by_reference.cpp:51:10:51:20 | call to getDirectly | +| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | +| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly | +| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | +| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | +| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | +| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | -| by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | | by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | | by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | | by_reference.cpp:84:3:84:25 | Store | by_reference.cpp:84:3:84:25 | Chi [a] | @@ -43,49 +112,137 @@ edges | by_reference.cpp:88:3:88:24 | Chi [a] | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | | by_reference.cpp:88:3:88:24 | Store | by_reference.cpp:88:3:88:24 | Chi [a] | | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | Store | +| by_reference.cpp:92:3:92:20 | Chi [array content] | by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] | +| by_reference.cpp:92:3:92:20 | Chi [array content] | by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] | +| by_reference.cpp:92:3:92:20 | Store | by_reference.cpp:92:3:92:20 | Chi [array content] | +| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:3:92:20 | Store | +| by_reference.cpp:96:3:96:19 | Chi [array content] | by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] | +| by_reference.cpp:96:3:96:19 | Chi [array content] | by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | +| by_reference.cpp:96:3:96:19 | Store | by_reference.cpp:96:3:96:19 | Chi [array content] | +| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:96:3:96:19 | Store | | by_reference.cpp:102:21:102:39 | Chi [a] | by_reference.cpp:110:27:110:27 | a | | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | by_reference.cpp:102:21:102:39 | Chi [a] | +| by_reference.cpp:104:15:104:22 | Chi | by_reference.cpp:104:15:104:22 | Chi [a] | +| by_reference.cpp:104:15:104:22 | Chi [a] | by_reference.cpp:112:14:112:14 | a | +| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] | by_reference.cpp:104:15:104:22 | Chi | | by_reference.cpp:106:21:106:41 | Chi [a] | by_reference.cpp:114:29:114:29 | a | | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | by_reference.cpp:106:21:106:41 | Chi [a] | +| by_reference.cpp:108:15:108:24 | Chi | by_reference.cpp:108:15:108:24 | Chi [a] | +| by_reference.cpp:108:15:108:24 | Chi [a] | by_reference.cpp:116:16:116:16 | a | +| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] | by_reference.cpp:108:15:108:24 | Chi | | by_reference.cpp:122:21:122:38 | Chi [a] | by_reference.cpp:130:27:130:27 | a | | by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | by_reference.cpp:122:21:122:38 | Chi [a] | +| by_reference.cpp:124:15:124:21 | Chi | by_reference.cpp:124:15:124:21 | Chi [a] | +| by_reference.cpp:124:15:124:21 | Chi [a] | by_reference.cpp:132:14:132:14 | a | +| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] | by_reference.cpp:124:15:124:21 | Chi | | by_reference.cpp:126:21:126:40 | Chi [a] | by_reference.cpp:134:29:134:29 | a | | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | by_reference.cpp:126:21:126:40 | Chi [a] | +| by_reference.cpp:128:15:128:23 | Chi | by_reference.cpp:128:15:128:23 | Chi [a] | +| by_reference.cpp:128:15:128:23 | Chi [a] | by_reference.cpp:136:16:136:16 | a | +| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | by_reference.cpp:128:15:128:23 | Chi | +| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:42:18:42:18 | call to a | +| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | a output argument [b_] | +| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] | +| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | +| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] | +| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] | +| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] | +| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | +| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] | +| constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:12:28:12 | call to a | +| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] | +| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:12:29:12 | call to b | +| constructors.cpp:28:10:28:10 | a output argument [b_] | constructors.cpp:29:12:29:12 | call to b | +| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | Foo output argument [a_] | +| constructors.cpp:34:11:34:26 | Foo output argument [a_] | constructors.cpp:26:15:26:15 | *f [a_] | +| constructors.cpp:35:11:35:26 | Foo output argument [b_] | constructors.cpp:26:15:26:15 | *f [b_] | +| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | Foo output argument [b_] | +| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [a_] | +| constructors.cpp:36:11:36:37 | Foo output argument [a_] | constructors.cpp:26:15:26:15 | *f [a_] | +| constructors.cpp:36:11:36:37 | Foo output argument [b_] | constructors.cpp:26:15:26:15 | *f [b_] | +| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [b_] | +| simple.cpp:26:15:26:15 | *f [a_] | simple.cpp:28:12:28:12 | call to a | +| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:28:10:28:10 | a output argument [b_] | +| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:29:12:29:12 | call to b | +| simple.cpp:28:10:28:10 | a output argument [b_] | simple.cpp:29:12:29:12 | call to b | +| simple.cpp:39:5:39:5 | setA output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] | +| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:5:39:5 | setA output argument [a_] | +| simple.cpp:40:5:40:5 | setB output argument [b_] | simple.cpp:26:15:26:15 | *f [b_] | +| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:5:40:5 | setB output argument [b_] | +| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] | +| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] | +| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:5:41:5 | setA output argument [a_] | +| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] | +| simple.cpp:42:5:42:5 | setB output argument [b_] | simple.cpp:26:15:26:15 | *f [b_] | +| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:5:42:5 | setB output argument [b_] | | simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] | | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] | | simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i | -| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | +| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | call to getf2f1 | | simple.cpp:83:9:83:28 | Store | simple.cpp:83:9:83:28 | Chi [f1] | | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Store | -| simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | simple.cpp:84:14:84:20 | call to getf2f1 | +| simple.cpp:92:5:92:22 | Store [i] | simple.cpp:93:20:93:20 | Store [i] | +| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | Store [i] | +| simple.cpp:93:20:93:20 | Store [i] | simple.cpp:94:13:94:13 | i | | struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:12:15:12 | a | -| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:24:10:24:12 | Argument 0 indirection [a] | +| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:14:24:14:25 | *ab [a] | | struct_init.c:20:20:20:29 | Store | struct_init.c:20:20:20:29 | Chi [a] | | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | Store | | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a | -| struct_init.c:24:10:24:12 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] | -| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:36:10:36:24 | Argument 0 indirection [a] | +| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:14:24:14:25 | *ab [a] | | struct_init.c:27:7:27:16 | Store | struct_init.c:27:7:27:16 | Chi [a] | | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:7:27:16 | Store | | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a | -| struct_init.c:36:10:36:24 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] | nodes +| A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] | +| A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... | +| A.cpp:55:12:55:19 | new | semmle.label | new | +| A.cpp:56:13:56:15 | call to get | semmle.label | call to get | +| A.cpp:57:11:57:24 | B output argument [c] | semmle.label | B output argument [c] | +| A.cpp:57:17:57:23 | new | semmle.label | new | +| A.cpp:57:28:57:30 | call to get | semmle.label | call to get | | A.cpp:98:12:98:18 | new | semmle.label | new | | A.cpp:100:5:100:13 | Chi [a] | semmle.label | Chi [a] | | A.cpp:100:5:100:13 | Store | semmle.label | Store | -| A.cpp:101:8:101:9 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | | A.cpp:103:14:103:14 | *c [a] | semmle.label | *c [a] | -| A.cpp:107:12:107:16 | (void *)... | semmle.label | (void *)... | -| A.cpp:107:16:107:16 | a | semmle.label | a | | A.cpp:107:16:107:16 | a | semmle.label | a | +| A.cpp:126:5:126:5 | Chi [c] | semmle.label | Chi [c] | +| A.cpp:126:5:126:5 | set output argument [c] | semmle.label | set output argument [c] | +| A.cpp:126:12:126:18 | new | semmle.label | new | +| A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] | +| A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] | +| A.cpp:132:13:132:13 | c | semmle.label | c | | A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] | | A.cpp:142:7:142:20 | Store | semmle.label | Store | | A.cpp:142:14:142:20 | new | semmle.label | new | +| A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] | +| A.cpp:143:7:143:31 | Store | semmle.label | Store | +| A.cpp:143:25:143:31 | new | semmle.label | new | +| A.cpp:150:12:150:18 | new | semmle.label | new | +| A.cpp:151:12:151:24 | Chi [b] | semmle.label | Chi [b] | +| A.cpp:151:12:151:24 | D output argument [b] | semmle.label | D output argument [b] | | A.cpp:151:18:151:18 | Chi [c] | semmle.label | Chi [c] | | A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] | -| A.cpp:154:10:154:13 | (void *)... | semmle.label | (void *)... | -| A.cpp:154:13:154:13 | c | semmle.label | c | +| A.cpp:152:13:152:13 | b | semmle.label | b | | A.cpp:154:13:154:13 | c | semmle.label | c | +| C.cpp:18:12:18:18 | C output argument [s1] | semmle.label | C output argument [s1] | +| C.cpp:18:12:18:18 | C output argument [s3] | semmle.label | C output argument [s3] | +| C.cpp:22:12:22:21 | Chi [s1] | semmle.label | Chi [s1] | +| C.cpp:22:12:22:21 | Store | semmle.label | Store | +| C.cpp:22:12:22:21 | new | semmle.label | new | +| C.cpp:24:5:24:25 | Chi [s1] | semmle.label | Chi [s1] | +| C.cpp:24:5:24:25 | Chi [s3] | semmle.label | Chi [s3] | +| C.cpp:24:5:24:25 | Store | semmle.label | Store | +| C.cpp:24:16:24:25 | new | semmle.label | new | +| C.cpp:27:8:27:11 | *#this [s1] | semmle.label | *#this [s1] | +| C.cpp:27:8:27:11 | *#this [s3] | semmle.label | *#this [s3] | +| C.cpp:29:10:29:11 | s1 | semmle.label | s1 | +| C.cpp:31:10:31:11 | s3 | semmle.label | s3 | | aliasing.cpp:9:3:9:22 | Chi [m1] | semmle.label | Chi [m1] | | aliasing.cpp:9:3:9:22 | Store | semmle.label | Store | | aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input | @@ -113,28 +270,146 @@ nodes | aliasing.cpp:87:12:87:13 | m1 | semmle.label | m1 | | aliasing.cpp:92:12:92:21 | call to user_input | semmle.label | call to user_input | | aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 | +| aliasing.cpp:98:3:98:21 | Chi [m1] | semmle.label | Chi [m1] | +| aliasing.cpp:98:3:98:21 | Store | semmle.label | Store | +| aliasing.cpp:98:10:98:19 | call to user_input | semmle.label | call to user_input | +| aliasing.cpp:100:14:100:14 | Store [m1] | semmle.label | Store [m1] | +| aliasing.cpp:102:8:102:10 | * ... | semmle.label | * ... | +| aliasing.cpp:106:3:106:20 | Chi [array content] | semmle.label | Chi [array content] | +| aliasing.cpp:106:3:106:20 | Store | semmle.label | Store | +| aliasing.cpp:106:9:106:18 | call to user_input | semmle.label | call to user_input | +| aliasing.cpp:121:15:121:16 | Chi [array content] | semmle.label | Chi [array content] | +| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:122:8:122:12 | access to array | semmle.label | access to array | +| aliasing.cpp:126:15:126:20 | Chi [array content] | semmle.label | Chi [array content] | +| aliasing.cpp:126:15:126:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:127:8:127:16 | * ... | semmle.label | * ... | +| aliasing.cpp:131:15:131:16 | Chi [array content] | semmle.label | Chi [array content] | +| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:132:8:132:14 | * ... | semmle.label | * ... | +| aliasing.cpp:136:15:136:17 | Chi [array content] | semmle.label | Chi [array content] | +| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:137:8:137:11 | * ... | semmle.label | * ... | +| aliasing.cpp:158:15:158:20 | Chi [array content] | semmle.label | Chi [array content] | +| aliasing.cpp:158:15:158:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:159:8:159:14 | * ... | semmle.label | * ... | +| aliasing.cpp:164:15:164:20 | Chi [array content] | semmle.label | Chi [array content] | +| aliasing.cpp:164:15:164:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:165:8:165:16 | access to array | semmle.label | access to array | +| aliasing.cpp:175:15:175:22 | Chi | semmle.label | Chi | +| aliasing.cpp:175:15:175:22 | Chi [m1] | semmle.label | Chi [m1] | +| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:176:13:176:14 | m1 | semmle.label | m1 | +| aliasing.cpp:187:15:187:22 | Chi | semmle.label | Chi | +| aliasing.cpp:187:15:187:22 | Chi [m1] | semmle.label | Chi [m1] | +| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:188:13:188:14 | Store [m1] | semmle.label | Store [m1] | +| aliasing.cpp:189:15:189:16 | m1 | semmle.label | m1 | +| aliasing.cpp:200:15:200:24 | Chi | semmle.label | Chi | +| aliasing.cpp:200:15:200:24 | Chi [m1] | semmle.label | Chi [m1] | +| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | +| aliasing.cpp:201:15:201:16 | m1 | semmle.label | m1 | +| arrays.cpp:6:12:6:21 | call to user_input | semmle.label | call to user_input | +| arrays.cpp:7:8:7:13 | access to array | semmle.label | access to array | +| arrays.cpp:9:8:9:11 | * ... | semmle.label | * ... | +| arrays.cpp:10:8:10:15 | * ... | semmle.label | * ... | +| arrays.cpp:15:14:15:23 | call to user_input | semmle.label | call to user_input | +| arrays.cpp:16:8:16:13 | access to array | semmle.label | access to array | +| arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input | +| arrays.cpp:37:24:37:27 | data | semmle.label | data | +| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] | +| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:51:10:51:20 | call to getDirectly | semmle.label | call to getDirectly | +| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | semmle.label | setIndirectly output argument [a] | +| by_reference.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:57:10:57:22 | call to getIndirectly | semmle.label | call to getIndirectly | +| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | semmle.label | setThroughNonMember output argument [a] | +| by_reference.cpp:62:25:62:34 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | semmle.label | call to getThroughNonMember | | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] | | by_reference.cpp:68:21:68:30 | call to user_input | semmle.label | call to user_input | | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | semmle.label | call to nonMemberGetA | -| by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | | by_reference.cpp:84:3:84:25 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:84:3:84:25 | Store | semmle.label | Store | | by_reference.cpp:84:14:84:23 | call to user_input | semmle.label | call to user_input | | by_reference.cpp:88:3:88:24 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:88:3:88:24 | Store | semmle.label | Store | | by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:92:3:92:20 | Chi [array content] | semmle.label | Chi [array content] | +| by_reference.cpp:92:3:92:20 | Store | semmle.label | Store | +| by_reference.cpp:92:9:92:18 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:96:3:96:19 | Chi [array content] | semmle.label | Chi [array content] | +| by_reference.cpp:96:3:96:19 | Store | semmle.label | Store | +| by_reference.cpp:96:8:96:17 | call to user_input | semmle.label | call to user_input | | by_reference.cpp:102:21:102:39 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] | +| by_reference.cpp:104:15:104:22 | Chi | semmle.label | Chi | +| by_reference.cpp:104:15:104:22 | Chi [a] | semmle.label | Chi [a] | +| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | | by_reference.cpp:106:21:106:41 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] | +| by_reference.cpp:108:15:108:24 | Chi | semmle.label | Chi | +| by_reference.cpp:108:15:108:24 | Chi [a] | semmle.label | Chi [a] | +| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] | | by_reference.cpp:110:27:110:27 | a | semmle.label | a | +| by_reference.cpp:112:14:112:14 | a | semmle.label | a | | by_reference.cpp:114:29:114:29 | a | semmle.label | a | +| by_reference.cpp:116:16:116:16 | a | semmle.label | a | | by_reference.cpp:122:21:122:38 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] | +| by_reference.cpp:124:15:124:21 | Chi | semmle.label | Chi | +| by_reference.cpp:124:15:124:21 | Chi [a] | semmle.label | Chi [a] | +| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] | semmle.label | taint_a_ref output argument [array content] | | by_reference.cpp:126:21:126:40 | Chi [a] | semmle.label | Chi [a] | | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] | +| by_reference.cpp:128:15:128:23 | Chi | semmle.label | Chi | +| by_reference.cpp:128:15:128:23 | Chi [a] | semmle.label | Chi [a] | +| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | semmle.label | taint_a_ref output argument [array content] | | by_reference.cpp:130:27:130:27 | a | semmle.label | a | +| by_reference.cpp:132:14:132:14 | a | semmle.label | a | | by_reference.cpp:134:29:134:29 | a | semmle.label | a | +| by_reference.cpp:136:16:136:16 | a | semmle.label | a | +| complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] | +| complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] | +| complex.cpp:42:16:42:16 | a output argument [b_] | semmle.label | a output argument [b_] | +| complex.cpp:42:18:42:18 | call to a | semmle.label | call to a | +| complex.cpp:43:18:43:18 | call to b | semmle.label | call to b | +| complex.cpp:53:12:53:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:54:12:54:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:55:12:55:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:56:12:56:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | +| complex.cpp:56:12:56:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| complex.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input | +| constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] | +| constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] | +| constructors.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] | +| constructors.cpp:28:12:28:12 | call to a | semmle.label | call to a | +| constructors.cpp:29:12:29:12 | call to b | semmle.label | call to b | +| constructors.cpp:34:11:34:20 | call to user_input | semmle.label | call to user_input | +| constructors.cpp:34:11:34:26 | Foo output argument [a_] | semmle.label | Foo output argument [a_] | +| constructors.cpp:35:11:35:26 | Foo output argument [b_] | semmle.label | Foo output argument [b_] | +| constructors.cpp:35:14:35:23 | call to user_input | semmle.label | call to user_input | +| constructors.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input | +| constructors.cpp:36:11:36:37 | Foo output argument [a_] | semmle.label | Foo output argument [a_] | +| constructors.cpp:36:11:36:37 | Foo output argument [b_] | semmle.label | Foo output argument [b_] | +| constructors.cpp:36:25:36:34 | call to user_input | semmle.label | call to user_input | +| simple.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] | +| simple.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] | +| simple.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] | +| simple.cpp:28:12:28:12 | call to a | semmle.label | call to a | +| simple.cpp:29:12:29:12 | call to b | semmle.label | call to b | +| simple.cpp:39:5:39:5 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| simple.cpp:39:12:39:21 | call to user_input | semmle.label | call to user_input | +| simple.cpp:40:5:40:5 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| simple.cpp:40:12:40:21 | call to user_input | semmle.label | call to user_input | +| simple.cpp:41:5:41:5 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| simple.cpp:41:12:41:21 | call to user_input | semmle.label | call to user_input | +| simple.cpp:42:5:42:5 | setB output argument [a_] | semmle.label | setB output argument [a_] | +| simple.cpp:42:5:42:5 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| simple.cpp:42:12:42:21 | call to user_input | semmle.label | call to user_input | | simple.cpp:65:5:65:22 | Store [i] | semmle.label | Store [i] | | simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input | | simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] | @@ -142,25 +417,32 @@ nodes | simple.cpp:83:9:83:28 | Chi [f1] | semmle.label | Chi [f1] | | simple.cpp:83:9:83:28 | Store | semmle.label | Store | | simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input | -| simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | semmle.label | Argument -1 indirection [f1] | | simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 | +| simple.cpp:92:5:92:22 | Store [i] | semmle.label | Store [i] | +| simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input | +| simple.cpp:93:20:93:20 | Store [i] | semmle.label | Store [i] | +| simple.cpp:94:13:94:13 | i | semmle.label | i | | struct_init.c:14:24:14:25 | *ab [a] | semmle.label | *ab [a] | | struct_init.c:15:12:15:12 | a | semmle.label | a | | struct_init.c:20:20:20:29 | Chi [a] | semmle.label | Chi [a] | | struct_init.c:20:20:20:29 | Store | semmle.label | Store | | struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input | | struct_init.c:22:11:22:11 | a | semmle.label | a | -| struct_init.c:24:10:24:12 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | | struct_init.c:27:7:27:16 | Chi [a] | semmle.label | Chi [a] | | struct_init.c:27:7:27:16 | Store | semmle.label | Store | | struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input | | struct_init.c:31:23:31:23 | a | semmle.label | a | -| struct_init.c:36:10:36:24 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] | #select -| A.cpp:107:12:107:16 | (void *)... | A.cpp:98:12:98:18 | new | A.cpp:107:12:107:16 | (void *)... | (void *)... flows from $@ | A.cpp:98:12:98:18 | new | new | +| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... | +| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new | +| A.cpp:57:28:57:30 | call to get | A.cpp:57:17:57:23 | new | A.cpp:57:28:57:30 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new | new | | A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new | -| A.cpp:154:10:154:13 | (void *)... | A.cpp:142:14:142:20 | new | A.cpp:154:10:154:13 | (void *)... | (void *)... flows from $@ | A.cpp:142:14:142:20 | new | new | +| A.cpp:132:13:132:13 | c | A.cpp:126:12:126:18 | new | A.cpp:132:13:132:13 | c | c flows from $@ | A.cpp:126:12:126:18 | new | new | +| A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new | new | +| A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new | new | | A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new | new | +| C.cpp:29:10:29:11 | s1 | C.cpp:22:12:22:21 | new | C.cpp:29:10:29:11 | s1 | s1 flows from $@ | C.cpp:22:12:22:21 | new | new | +| C.cpp:31:10:31:11 | s3 | C.cpp:24:16:24:25 | new | C.cpp:31:10:31:11 | s3 | s3 flows from $@ | C.cpp:24:16:24:25 | new | new | | aliasing.cpp:29:11:29:12 | m1 | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:29:11:29:12 | m1 | m1 flows from $@ | aliasing.cpp:9:11:9:20 | call to user_input | call to user_input | | aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:30:11:30:12 | m1 | m1 flows from $@ | aliasing.cpp:13:10:13:19 | call to user_input | call to user_input | | aliasing.cpp:38:11:38:12 | m1 | aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | m1 flows from $@ | aliasing.cpp:37:13:37:22 | call to user_input | call to user_input | @@ -169,13 +451,48 @@ nodes | aliasing.cpp:80:12:80:13 | m1 | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | m1 flows from $@ | aliasing.cpp:79:11:79:20 | call to user_input | call to user_input | | aliasing.cpp:87:12:87:13 | m1 | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | m1 flows from $@ | aliasing.cpp:86:10:86:19 | call to user_input | call to user_input | | aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input | +| aliasing.cpp:102:8:102:10 | * ... | aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:102:8:102:10 | * ... | * ... flows from $@ | aliasing.cpp:98:10:98:19 | call to user_input | call to user_input | +| aliasing.cpp:122:8:122:12 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:122:8:122:12 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:127:8:127:16 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:127:8:127:16 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:132:8:132:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:132:8:132:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:137:8:137:11 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:137:8:137:11 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:159:8:159:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:159:8:159:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:165:8:165:16 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:165:8:165:16 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:176:13:176:14 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:176:13:176:14 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:189:15:189:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:189:15:189:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:201:15:201:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:201:15:201:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| arrays.cpp:7:8:7:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input | +| arrays.cpp:9:8:9:11 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input | +| arrays.cpp:10:8:10:15 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input | +| arrays.cpp:16:8:16:13 | access to array | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array | access to array flows from $@ | arrays.cpp:15:14:15:23 | call to user_input | call to user_input | +| arrays.cpp:37:24:37:27 | data | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data | data flows from $@ | arrays.cpp:36:26:36:35 | call to user_input | call to user_input | +| by_reference.cpp:51:10:51:20 | call to getDirectly | by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:51:10:51:20 | call to getDirectly | call to getDirectly flows from $@ | by_reference.cpp:50:17:50:26 | call to user_input | call to user_input | +| by_reference.cpp:57:10:57:22 | call to getIndirectly | by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:57:10:57:22 | call to getIndirectly | call to getIndirectly flows from $@ | by_reference.cpp:56:19:56:28 | call to user_input | call to user_input | +| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | call to getThroughNonMember flows from $@ | by_reference.cpp:62:25:62:34 | call to user_input | call to user_input | | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | call to nonMemberGetA flows from $@ | by_reference.cpp:68:21:68:30 | call to user_input | call to user_input | | by_reference.cpp:110:27:110:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:110:27:110:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input | +| by_reference.cpp:112:14:112:14 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:112:14:112:14 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input | | by_reference.cpp:114:29:114:29 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:114:29:114:29 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input | +| by_reference.cpp:116:16:116:16 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:116:16:116:16 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input | | by_reference.cpp:130:27:130:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:130:27:130:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | +| by_reference.cpp:132:14:132:14 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:132:14:132:14 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | +| by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input | +| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input | +| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | +| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input | +| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:36:25:36:34 | call to user_input | call to user_input | +| simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input | call to user_input | +| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input | +| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input | +| simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input | | simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input | +| simple.cpp:94:13:94:13 | i | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:94:13:94:13 | i | i flows from $@ | simple.cpp:92:11:92:20 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input | | struct_init.c:22:11:22:11 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected index 889f789da8d7..51b43e6be1c6 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected @@ -157,6 +157,70 @@ | aliasing.cpp:86:5:86:6 | m1 | AST only | | aliasing.cpp:92:3:92:3 | w | AST only | | aliasing.cpp:92:7:92:8 | m1 | AST only | +| aliasing.cpp:98:5:98:6 | m1 | AST only | +| aliasing.cpp:106:3:106:5 | * ... | AST only | +| aliasing.cpp:111:15:111:19 | & ... | AST only | +| aliasing.cpp:121:15:121:16 | xs | AST only | +| aliasing.cpp:126:15:126:20 | ... - ... | AST only | +| aliasing.cpp:131:15:131:16 | xs | AST only | +| aliasing.cpp:136:15:136:17 | + ... | AST only | +| aliasing.cpp:141:15:141:15 | s | AST only | +| aliasing.cpp:141:17:141:20 | data | AST only | +| aliasing.cpp:147:15:147:22 | & ... | AST only | +| aliasing.cpp:158:15:158:15 | s | AST only | +| aliasing.cpp:158:17:158:20 | data | AST only | +| aliasing.cpp:164:15:164:15 | s | AST only | +| aliasing.cpp:164:17:164:20 | data | AST only | +| aliasing.cpp:175:15:175:22 | & ... | AST only | +| aliasing.cpp:175:16:175:17 | s2 | AST only | +| aliasing.cpp:181:15:181:22 | & ... | AST only | +| aliasing.cpp:181:16:181:17 | s2 | AST only | +| aliasing.cpp:187:15:187:22 | & ... | AST only | +| aliasing.cpp:187:16:187:17 | s2 | AST only | +| aliasing.cpp:194:15:194:22 | & ... | AST only | +| aliasing.cpp:194:16:194:17 | s2 | AST only | +| aliasing.cpp:200:15:200:24 | & ... | AST only | +| aliasing.cpp:200:16:200:18 | ps2 | AST only | +| aliasing.cpp:205:15:205:24 | & ... | AST only | +| aliasing.cpp:205:16:205:18 | ps2 | AST only | +| arrays.cpp:6:3:6:8 | access to array | AST only | +| arrays.cpp:6:3:6:23 | arr | IR only | +| arrays.cpp:15:3:15:10 | * ... | AST only | +| arrays.cpp:36:3:36:3 | o | AST only | +| arrays.cpp:36:5:36:10 | nested | AST only | +| arrays.cpp:36:19:36:22 | data | AST only | +| arrays.cpp:37:8:37:8 | o | AST only | +| arrays.cpp:37:8:37:22 | access to array | AST only | +| arrays.cpp:37:10:37:15 | nested | AST only | +| arrays.cpp:37:24:37:27 | data | AST only | +| arrays.cpp:38:8:38:8 | o | AST only | +| arrays.cpp:38:8:38:22 | access to array | AST only | +| arrays.cpp:38:10:38:15 | nested | AST only | +| arrays.cpp:38:24:38:27 | data | AST only | +| arrays.cpp:42:3:42:3 | o | AST only | +| arrays.cpp:42:3:42:20 | access to array | AST only | +| arrays.cpp:42:5:42:12 | indirect | AST only | +| arrays.cpp:42:22:42:25 | data | AST only | +| arrays.cpp:43:8:43:8 | o | AST only | +| arrays.cpp:43:8:43:25 | access to array | AST only | +| arrays.cpp:43:10:43:17 | indirect | AST only | +| arrays.cpp:43:27:43:30 | data | AST only | +| arrays.cpp:44:8:44:8 | o | AST only | +| arrays.cpp:44:8:44:25 | access to array | AST only | +| arrays.cpp:44:10:44:17 | indirect | AST only | +| arrays.cpp:44:27:44:30 | data | AST only | +| arrays.cpp:48:3:48:3 | o | AST only | +| arrays.cpp:48:3:48:20 | access to array | AST only | +| arrays.cpp:48:5:48:12 | indirect | AST only | +| arrays.cpp:48:22:48:25 | data | AST only | +| arrays.cpp:49:8:49:8 | o | AST only | +| arrays.cpp:49:8:49:25 | access to array | AST only | +| arrays.cpp:49:10:49:17 | indirect | AST only | +| arrays.cpp:49:27:49:30 | data | AST only | +| arrays.cpp:50:8:50:8 | o | AST only | +| arrays.cpp:50:8:50:25 | access to array | AST only | +| arrays.cpp:50:10:50:17 | indirect | AST only | +| arrays.cpp:50:27:50:30 | data | AST only | | by_reference.cpp:12:8:12:8 | a | AST only | | by_reference.cpp:16:11:16:11 | a | AST only | | by_reference.cpp:20:5:20:8 | this | AST only | @@ -177,18 +241,16 @@ | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | AST only | | by_reference.cpp:84:10:84:10 | a | AST only | | by_reference.cpp:88:9:88:9 | a | AST only | +| by_reference.cpp:92:3:92:5 | * ... | AST only | +| by_reference.cpp:96:3:96:4 | pa | AST only | | by_reference.cpp:102:21:102:39 | & ... | AST only | -| by_reference.cpp:102:22:102:26 | outer | AST only | | by_reference.cpp:103:21:103:25 | outer | AST only | | by_reference.cpp:103:27:103:35 | inner_ptr | AST only | | by_reference.cpp:104:15:104:22 | & ... | AST only | -| by_reference.cpp:104:16:104:20 | outer | AST only | | by_reference.cpp:106:21:106:41 | & ... | AST only | -| by_reference.cpp:106:22:106:27 | pouter | AST only | | by_reference.cpp:107:21:107:26 | pouter | AST only | | by_reference.cpp:107:29:107:37 | inner_ptr | AST only | | by_reference.cpp:108:15:108:24 | & ... | AST only | -| by_reference.cpp:108:16:108:21 | pouter | AST only | | by_reference.cpp:110:8:110:12 | outer | AST only | | by_reference.cpp:110:14:110:25 | inner_nested | AST only | | by_reference.cpp:110:27:110:27 | a | AST only | @@ -205,17 +267,13 @@ | by_reference.cpp:115:27:115:27 | a | AST only | | by_reference.cpp:116:8:116:13 | pouter | AST only | | by_reference.cpp:116:16:116:16 | a | AST only | -| by_reference.cpp:122:21:122:25 | outer | AST only | | by_reference.cpp:122:27:122:38 | inner_nested | AST only | | by_reference.cpp:123:21:123:36 | * ... | AST only | | by_reference.cpp:123:22:123:26 | outer | AST only | -| by_reference.cpp:124:15:124:19 | outer | AST only | | by_reference.cpp:124:21:124:21 | a | AST only | -| by_reference.cpp:126:21:126:26 | pouter | AST only | | by_reference.cpp:126:29:126:40 | inner_nested | AST only | | by_reference.cpp:127:21:127:38 | * ... | AST only | | by_reference.cpp:127:22:127:27 | pouter | AST only | -| by_reference.cpp:128:15:128:20 | pouter | AST only | | by_reference.cpp:128:23:128:23 | a | AST only | | by_reference.cpp:130:8:130:12 | outer | AST only | | by_reference.cpp:130:14:130:25 | inner_nested | AST only | @@ -235,28 +293,28 @@ | by_reference.cpp:136:16:136:16 | a | AST only | | complex.cpp:11:22:11:23 | a_ | AST only | | complex.cpp:12:22:12:23 | b_ | AST only | -| complex.cpp:51:8:51:8 | b | AST only | -| complex.cpp:51:10:51:14 | inner | AST only | -| complex.cpp:51:16:51:16 | f | AST only | -| complex.cpp:52:8:52:8 | b | AST only | -| complex.cpp:52:10:52:14 | inner | AST only | -| complex.cpp:52:16:52:16 | f | AST only | -| complex.cpp:62:3:62:4 | b1 | AST only | -| complex.cpp:62:6:62:10 | inner | AST only | -| complex.cpp:62:12:62:12 | f | AST only | -| complex.cpp:63:3:63:4 | b2 | AST only | -| complex.cpp:63:6:63:10 | inner | AST only | -| complex.cpp:63:12:63:12 | f | AST only | -| complex.cpp:64:3:64:4 | b3 | AST only | -| complex.cpp:64:6:64:10 | inner | AST only | -| complex.cpp:64:12:64:12 | f | AST only | -| complex.cpp:65:3:65:4 | b3 | AST only | -| complex.cpp:65:6:65:10 | inner | AST only | -| complex.cpp:65:12:65:12 | f | AST only | -| complex.cpp:68:7:68:8 | b1 | AST only | -| complex.cpp:71:7:71:8 | b2 | AST only | -| complex.cpp:74:7:74:8 | b3 | AST only | -| complex.cpp:77:7:77:8 | b4 | AST only | +| complex.cpp:42:8:42:8 | b | AST only | +| complex.cpp:42:10:42:14 | inner | AST only | +| complex.cpp:42:16:42:16 | f | AST only | +| complex.cpp:43:8:43:8 | b | AST only | +| complex.cpp:43:10:43:14 | inner | AST only | +| complex.cpp:43:16:43:16 | f | AST only | +| complex.cpp:53:3:53:4 | b1 | AST only | +| complex.cpp:53:6:53:10 | inner | AST only | +| complex.cpp:53:12:53:12 | f | AST only | +| complex.cpp:54:3:54:4 | b2 | AST only | +| complex.cpp:54:6:54:10 | inner | AST only | +| complex.cpp:54:12:54:12 | f | AST only | +| complex.cpp:55:3:55:4 | b3 | AST only | +| complex.cpp:55:6:55:10 | inner | AST only | +| complex.cpp:55:12:55:12 | f | AST only | +| complex.cpp:56:3:56:4 | b3 | AST only | +| complex.cpp:56:6:56:10 | inner | AST only | +| complex.cpp:56:12:56:12 | f | AST only | +| complex.cpp:59:7:59:8 | b1 | AST only | +| complex.cpp:62:7:62:8 | b2 | AST only | +| complex.cpp:65:7:65:8 | b3 | AST only | +| complex.cpp:68:7:68:8 | b4 | AST only | | constructors.cpp:20:24:20:25 | a_ | AST only | | constructors.cpp:21:24:21:25 | b_ | AST only | | constructors.cpp:28:10:28:10 | f | AST only | @@ -304,6 +362,32 @@ | qualifiers.cpp:48:10:48:14 | outer | AST only | | qualifiers.cpp:48:16:48:20 | inner | AST only | | qualifiers.cpp:48:23:48:23 | a | AST only | +| realistic.cpp:26:5:26:10 | offset | AST only | +| realistic.cpp:42:20:42:20 | o | AST only | +| realistic.cpp:49:9:49:11 | foo | AST only | +| realistic.cpp:49:20:49:22 | baz | AST only | +| realistic.cpp:53:9:53:11 | foo | AST only | +| realistic.cpp:53:9:53:18 | access to array | AST only | +| realistic.cpp:53:20:53:22 | baz | AST only | +| realistic.cpp:53:25:53:33 | userInput | AST only | +| realistic.cpp:53:35:53:43 | bufferLen | AST only | +| realistic.cpp:54:16:54:18 | foo | AST only | +| realistic.cpp:54:16:54:25 | access to array | AST only | +| realistic.cpp:54:27:54:29 | baz | AST only | +| realistic.cpp:54:32:54:40 | userInput | AST only | +| realistic.cpp:54:42:54:47 | buffer | AST only | +| realistic.cpp:60:16:60:18 | dst | AST only | +| realistic.cpp:61:21:61:23 | foo | AST only | +| realistic.cpp:61:21:61:30 | access to array | AST only | +| realistic.cpp:61:32:61:34 | baz | AST only | +| realistic.cpp:61:37:61:45 | userInput | AST only | +| realistic.cpp:61:47:61:55 | bufferLen | AST only | +| realistic.cpp:65:21:65:23 | foo | AST only | +| realistic.cpp:65:21:65:30 | access to array | AST only | +| realistic.cpp:65:32:65:34 | baz | AST only | +| realistic.cpp:65:37:65:45 | userInput | AST only | +| realistic.cpp:65:47:65:52 | buffer | AST only | +| realistic.cpp:66:21:66:23 | dst | AST only | | simple.cpp:20:24:20:25 | a_ | AST only | | simple.cpp:21:24:21:25 | b_ | AST only | | simple.cpp:28:10:28:10 | f | AST only | @@ -320,6 +404,7 @@ | simple.cpp:83:9:83:10 | this | AST only | | simple.cpp:83:12:83:13 | f1 | AST only | | simple.cpp:84:14:84:20 | this | AST only | +| simple.cpp:92:7:92:7 | i | AST only | | struct_init.c:15:8:15:9 | ab | AST only | | struct_init.c:15:12:15:12 | a | AST only | | struct_init.c:16:8:16:9 | ab | AST only | @@ -342,6 +427,5 @@ | struct_init.c:34:14:34:22 | pointerAB | AST only | | struct_init.c:34:25:34:25 | b | AST only | | struct_init.c:36:10:36:24 | & ... | AST only | -| struct_init.c:36:11:36:15 | outer | AST only | | struct_init.c:46:10:46:14 | outer | AST only | | struct_init.c:46:16:46:24 | pointerAB | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql index 8f6296290e9c..d8b6b4e0e69a 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql @@ -5,43 +5,18 @@ import cpp import semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as IR import semmle.code.cpp.dataflow.DataFlow::DataFlow as AST +import Nodes -newtype TNode = - TASTNode(AST::Node n) or - TIRNode(IR::Node n) - -class Node extends TNode { - string toString() { none() } - - IR::Node asIR() { none() } - - AST::Node asAST() { none() } - - Location getLocation() { none() } -} - -class ASTNode extends Node, TASTNode { - AST::Node n; - - ASTNode() { this = TASTNode(n) } +class ASTPartialDefNode extends ASTNode { + ASTPartialDefNode() { exists(n.asPartialDefinition()) } override string toString() { result = n.asPartialDefinition().toString() } - - override AST::Node asAST() { result = n } - - override Location getLocation() { result = n.getLocation() } } -class IRNode extends Node, TIRNode { - IR::Node n; - - IRNode() { this = TIRNode(n) } +class IRPartialDefNode extends IRNode { + IRPartialDefNode() { exists(n.asPartialDefinition()) } override string toString() { result = n.asPartialDefinition().toString() } - - override IR::Node asIR() { result = n } - - override Location getLocation() { result = n.getLocation() } } from Node node, AST::Node astNode, IR::Node irNode, string msg diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected index 050f4bc47d55..a66dd2869551 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected @@ -26,10 +26,29 @@ | aliasing.cpp:79:3:79:3 | s | | aliasing.cpp:86:3:86:3 | s | | aliasing.cpp:92:5:92:5 | s | +| aliasing.cpp:98:3:98:3 | s | +| aliasing.cpp:111:16:111:16 | s | +| aliasing.cpp:147:16:147:19 | access to array | +| aliasing.cpp:175:19:175:19 | s | +| aliasing.cpp:181:19:181:19 | s | +| aliasing.cpp:187:19:187:19 | s | +| aliasing.cpp:194:19:194:19 | s | +| aliasing.cpp:200:21:200:21 | s | +| aliasing.cpp:205:21:205:21 | s | +| arrays.cpp:6:3:6:5 | arr | +| arrays.cpp:36:3:36:17 | access to array | | by_reference.cpp:12:5:12:5 | s | | by_reference.cpp:16:5:16:8 | this | | by_reference.cpp:84:3:84:7 | inner | | by_reference.cpp:88:3:88:7 | inner | +| by_reference.cpp:102:22:102:26 | outer | +| by_reference.cpp:104:16:104:20 | outer | +| by_reference.cpp:106:22:106:27 | pouter | +| by_reference.cpp:108:16:108:21 | pouter | +| by_reference.cpp:122:21:122:25 | outer | +| by_reference.cpp:124:15:124:19 | outer | +| by_reference.cpp:126:21:126:26 | pouter | +| by_reference.cpp:128:15:128:20 | pouter | | complex.cpp:11:22:11:23 | this | | complex.cpp:12:22:12:23 | this | | constructors.cpp:20:24:20:25 | this | @@ -37,7 +56,10 @@ | qualifiers.cpp:9:30:9:33 | this | | qualifiers.cpp:12:49:12:53 | inner | | qualifiers.cpp:13:51:13:55 | inner | +| realistic.cpp:49:9:49:18 | access to array | | simple.cpp:20:24:20:25 | this | | simple.cpp:21:24:21:25 | this | | simple.cpp:65:5:65:5 | a | | simple.cpp:83:9:83:10 | f2 | +| simple.cpp:92:5:92:5 | a | +| struct_init.c:36:11:36:15 | outer | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected index 3f5a2e497d85..6c58d51ff742 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected @@ -185,6 +185,79 @@ | aliasing.cpp:92:3:92:3 | w | | aliasing.cpp:92:5:92:5 | s | | aliasing.cpp:92:7:92:8 | m1 | +| aliasing.cpp:98:3:98:3 | s | +| aliasing.cpp:98:5:98:6 | m1 | +| aliasing.cpp:106:3:106:5 | * ... | +| aliasing.cpp:111:15:111:19 | & ... | +| aliasing.cpp:111:16:111:16 | s | +| aliasing.cpp:121:15:121:16 | xs | +| aliasing.cpp:126:15:126:20 | ... - ... | +| aliasing.cpp:131:15:131:16 | xs | +| aliasing.cpp:136:15:136:17 | + ... | +| aliasing.cpp:141:15:141:15 | s | +| aliasing.cpp:141:17:141:20 | data | +| aliasing.cpp:147:15:147:22 | & ... | +| aliasing.cpp:147:16:147:19 | access to array | +| aliasing.cpp:158:15:158:15 | s | +| aliasing.cpp:158:17:158:20 | data | +| aliasing.cpp:164:15:164:15 | s | +| aliasing.cpp:164:17:164:20 | data | +| aliasing.cpp:175:15:175:22 | & ... | +| aliasing.cpp:175:16:175:17 | s2 | +| aliasing.cpp:175:19:175:19 | s | +| aliasing.cpp:181:15:181:22 | & ... | +| aliasing.cpp:181:16:181:17 | s2 | +| aliasing.cpp:181:19:181:19 | s | +| aliasing.cpp:187:15:187:22 | & ... | +| aliasing.cpp:187:16:187:17 | s2 | +| aliasing.cpp:187:19:187:19 | s | +| aliasing.cpp:194:15:194:22 | & ... | +| aliasing.cpp:194:16:194:17 | s2 | +| aliasing.cpp:194:19:194:19 | s | +| aliasing.cpp:200:15:200:24 | & ... | +| aliasing.cpp:200:16:200:18 | ps2 | +| aliasing.cpp:200:21:200:21 | s | +| aliasing.cpp:205:15:205:24 | & ... | +| aliasing.cpp:205:16:205:18 | ps2 | +| aliasing.cpp:205:21:205:21 | s | +| arrays.cpp:6:3:6:8 | access to array | +| arrays.cpp:15:3:15:10 | * ... | +| arrays.cpp:36:3:36:3 | o | +| arrays.cpp:36:3:36:17 | access to array | +| arrays.cpp:36:5:36:10 | nested | +| arrays.cpp:36:19:36:22 | data | +| arrays.cpp:37:8:37:8 | o | +| arrays.cpp:37:8:37:22 | access to array | +| arrays.cpp:37:10:37:15 | nested | +| arrays.cpp:37:24:37:27 | data | +| arrays.cpp:38:8:38:8 | o | +| arrays.cpp:38:8:38:22 | access to array | +| arrays.cpp:38:10:38:15 | nested | +| arrays.cpp:38:24:38:27 | data | +| arrays.cpp:42:3:42:3 | o | +| arrays.cpp:42:3:42:20 | access to array | +| arrays.cpp:42:5:42:12 | indirect | +| arrays.cpp:42:22:42:25 | data | +| arrays.cpp:43:8:43:8 | o | +| arrays.cpp:43:8:43:25 | access to array | +| arrays.cpp:43:10:43:17 | indirect | +| arrays.cpp:43:27:43:30 | data | +| arrays.cpp:44:8:44:8 | o | +| arrays.cpp:44:8:44:25 | access to array | +| arrays.cpp:44:10:44:17 | indirect | +| arrays.cpp:44:27:44:30 | data | +| arrays.cpp:48:3:48:3 | o | +| arrays.cpp:48:3:48:20 | access to array | +| arrays.cpp:48:5:48:12 | indirect | +| arrays.cpp:48:22:48:25 | data | +| arrays.cpp:49:8:49:8 | o | +| arrays.cpp:49:8:49:25 | access to array | +| arrays.cpp:49:10:49:17 | indirect | +| arrays.cpp:49:27:49:30 | data | +| arrays.cpp:50:8:50:8 | o | +| arrays.cpp:50:8:50:25 | access to array | +| arrays.cpp:50:10:50:17 | indirect | +| arrays.cpp:50:27:50:30 | data | | by_reference.cpp:12:5:12:5 | s | | by_reference.cpp:12:8:12:8 | a | | by_reference.cpp:16:5:16:8 | this | @@ -209,6 +282,8 @@ | by_reference.cpp:84:10:84:10 | a | | by_reference.cpp:88:3:88:7 | inner | | by_reference.cpp:88:9:88:9 | a | +| by_reference.cpp:92:3:92:5 | * ... | +| by_reference.cpp:96:3:96:4 | pa | | by_reference.cpp:102:21:102:39 | & ... | | by_reference.cpp:102:22:102:26 | outer | | by_reference.cpp:103:21:103:25 | outer | @@ -269,28 +344,28 @@ | complex.cpp:11:22:11:23 | this | | complex.cpp:12:22:12:23 | b_ | | complex.cpp:12:22:12:23 | this | -| complex.cpp:51:8:51:8 | b | -| complex.cpp:51:10:51:14 | inner | -| complex.cpp:51:16:51:16 | f | -| complex.cpp:52:8:52:8 | b | -| complex.cpp:52:10:52:14 | inner | -| complex.cpp:52:16:52:16 | f | -| complex.cpp:62:3:62:4 | b1 | -| complex.cpp:62:6:62:10 | inner | -| complex.cpp:62:12:62:12 | f | -| complex.cpp:63:3:63:4 | b2 | -| complex.cpp:63:6:63:10 | inner | -| complex.cpp:63:12:63:12 | f | -| complex.cpp:64:3:64:4 | b3 | -| complex.cpp:64:6:64:10 | inner | -| complex.cpp:64:12:64:12 | f | -| complex.cpp:65:3:65:4 | b3 | -| complex.cpp:65:6:65:10 | inner | -| complex.cpp:65:12:65:12 | f | -| complex.cpp:68:7:68:8 | b1 | -| complex.cpp:71:7:71:8 | b2 | -| complex.cpp:74:7:74:8 | b3 | -| complex.cpp:77:7:77:8 | b4 | +| complex.cpp:42:8:42:8 | b | +| complex.cpp:42:10:42:14 | inner | +| complex.cpp:42:16:42:16 | f | +| complex.cpp:43:8:43:8 | b | +| complex.cpp:43:10:43:14 | inner | +| complex.cpp:43:16:43:16 | f | +| complex.cpp:53:3:53:4 | b1 | +| complex.cpp:53:6:53:10 | inner | +| complex.cpp:53:12:53:12 | f | +| complex.cpp:54:3:54:4 | b2 | +| complex.cpp:54:6:54:10 | inner | +| complex.cpp:54:12:54:12 | f | +| complex.cpp:55:3:55:4 | b3 | +| complex.cpp:55:6:55:10 | inner | +| complex.cpp:55:12:55:12 | f | +| complex.cpp:56:3:56:4 | b3 | +| complex.cpp:56:6:56:10 | inner | +| complex.cpp:56:12:56:12 | f | +| complex.cpp:59:7:59:8 | b1 | +| complex.cpp:62:7:62:8 | b2 | +| complex.cpp:65:7:65:8 | b3 | +| complex.cpp:68:7:68:8 | b4 | | constructors.cpp:20:24:20:25 | a_ | | constructors.cpp:20:24:20:25 | this | | constructors.cpp:21:24:21:25 | b_ | @@ -343,6 +418,33 @@ | qualifiers.cpp:48:10:48:14 | outer | | qualifiers.cpp:48:16:48:20 | inner | | qualifiers.cpp:48:23:48:23 | a | +| realistic.cpp:26:5:26:10 | offset | +| realistic.cpp:42:20:42:20 | o | +| realistic.cpp:49:9:49:11 | foo | +| realistic.cpp:49:9:49:18 | access to array | +| realistic.cpp:49:20:49:22 | baz | +| realistic.cpp:53:9:53:11 | foo | +| realistic.cpp:53:9:53:18 | access to array | +| realistic.cpp:53:20:53:22 | baz | +| realistic.cpp:53:25:53:33 | userInput | +| realistic.cpp:53:35:53:43 | bufferLen | +| realistic.cpp:54:16:54:18 | foo | +| realistic.cpp:54:16:54:25 | access to array | +| realistic.cpp:54:27:54:29 | baz | +| realistic.cpp:54:32:54:40 | userInput | +| realistic.cpp:54:42:54:47 | buffer | +| realistic.cpp:60:16:60:18 | dst | +| realistic.cpp:61:21:61:23 | foo | +| realistic.cpp:61:21:61:30 | access to array | +| realistic.cpp:61:32:61:34 | baz | +| realistic.cpp:61:37:61:45 | userInput | +| realistic.cpp:61:47:61:55 | bufferLen | +| realistic.cpp:65:21:65:23 | foo | +| realistic.cpp:65:21:65:30 | access to array | +| realistic.cpp:65:32:65:34 | baz | +| realistic.cpp:65:37:65:45 | userInput | +| realistic.cpp:65:47:65:52 | buffer | +| realistic.cpp:66:21:66:23 | dst | | simple.cpp:20:24:20:25 | a_ | | simple.cpp:20:24:20:25 | this | | simple.cpp:21:24:21:25 | b_ | @@ -363,6 +465,8 @@ | simple.cpp:83:9:83:10 | this | | simple.cpp:83:12:83:13 | f1 | | simple.cpp:84:14:84:20 | this | +| simple.cpp:92:5:92:5 | a | +| simple.cpp:92:7:92:7 | i | | struct_init.c:15:8:15:9 | ab | | struct_init.c:15:12:15:12 | a | | struct_init.c:16:8:16:9 | ab | diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected index d505ff5d87e9..b932e0395f49 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected @@ -51,14 +51,14 @@ edges | A.cpp:160:29:160:29 | b | A.cpp:160:18:160:60 | call to MyList [head] | | A.cpp:161:18:161:40 | call to MyList [next, head] | A.cpp:162:38:162:39 | l2 [next, head] | | A.cpp:161:38:161:39 | l1 [head] | A.cpp:161:18:161:40 | call to MyList [next, head] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | A.cpp:167:44:167:44 | l [next, next, ... (3)] | -| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | -| A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | A.cpp:165:14:165:17 | next [next, head] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | A.cpp:165:10:165:11 | l3 [next, next, head] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | A.cpp:167:44:167:44 | l [next, next, head] | +| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, head] | +| A.cpp:165:10:165:11 | l3 [next, next, head] | A.cpp:165:14:165:17 | next [next, head] | | A.cpp:165:14:165:17 | next [next, head] | A.cpp:165:20:165:23 | next [head] | | A.cpp:165:20:165:23 | next [head] | A.cpp:165:26:165:29 | head | | A.cpp:167:44:167:44 | l [next, head] | A.cpp:167:47:167:50 | next [head] | -| A.cpp:167:44:167:44 | l [next, next, ... (3)] | A.cpp:167:47:167:50 | next [next, head] | +| A.cpp:167:44:167:44 | l [next, next, head] | A.cpp:167:47:167:50 | next [next, head] | | A.cpp:167:47:167:50 | next [head] | A.cpp:169:12:169:12 | l [head] | | A.cpp:167:47:167:50 | next [next, head] | A.cpp:167:44:167:44 | l [next, head] | | A.cpp:169:12:169:12 | l [head] | A.cpp:169:15:169:18 | head | @@ -113,14 +113,14 @@ edges | D.cpp:51:27:51:27 | e | D.cpp:51:8:51:14 | ref arg call to getBox1 [elem] | | D.cpp:52:14:52:14 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] | | D.cpp:56:15:56:24 | new | D.cpp:58:5:58:27 | ... = ... | -| D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | -| D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | +| D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | +| D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | D.cpp:59:5:59:7 | this [boxfield, box, elem] | | D.cpp:58:5:58:27 | ... = ... | D.cpp:58:15:58:17 | box [post update] [elem] | | D.cpp:58:15:58:17 | box [post update] [elem] | D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | -| D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | -| D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | +| D.cpp:59:5:59:7 | this [boxfield, box, elem] | D.cpp:63:8:63:10 | this [boxfield, box, elem] | +| D.cpp:63:8:63:10 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | this [boxfield, box, elem] | | D.cpp:64:10:64:17 | boxfield [box, elem] | D.cpp:64:20:64:22 | box [elem] | -| D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | D.cpp:64:10:64:17 | boxfield [box, elem] | +| D.cpp:64:10:64:17 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | boxfield [box, elem] | | D.cpp:64:20:64:22 | box [elem] | D.cpp:64:25:64:28 | elem | | E.cpp:19:27:19:27 | p [data, buffer] | E.cpp:21:10:21:10 | p [data, buffer] | | E.cpp:21:10:21:10 | p [data, buffer] | E.cpp:21:13:21:16 | data [buffer] | @@ -155,6 +155,74 @@ edges | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:92:3:92:23 | ... = ... | | aliasing.cpp:93:8:93:8 | w [s, m1] | aliasing.cpp:93:10:93:10 | s [m1] | | aliasing.cpp:93:10:93:10 | s [m1] | aliasing.cpp:93:12:93:13 | m1 | +| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:158:17:158:20 | ref arg data | +| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:164:17:164:20 | ref arg data | +| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:175:15:175:22 | ref arg & ... | +| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:187:15:187:22 | ref arg & ... | +| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:200:15:200:24 | ref arg & ... | +| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:4:106:5 | pa [inner post update] | +| aliasing.cpp:158:15:158:15 | s [post update] [data] | aliasing.cpp:159:9:159:9 | s [data] | +| aliasing.cpp:158:17:158:20 | ref arg data | aliasing.cpp:158:15:158:15 | s [post update] [data] | +| aliasing.cpp:159:9:159:9 | s [data] | aliasing.cpp:159:11:159:14 | data | +| aliasing.cpp:159:11:159:14 | data | aliasing.cpp:159:8:159:14 | * ... | +| aliasing.cpp:164:15:164:15 | s [post update] [data] | aliasing.cpp:165:8:165:8 | s [data] | +| aliasing.cpp:164:17:164:20 | ref arg data | aliasing.cpp:164:15:164:15 | s [post update] [data] | +| aliasing.cpp:165:8:165:8 | s [data] | aliasing.cpp:165:10:165:13 | data | +| aliasing.cpp:165:10:165:13 | data | aliasing.cpp:165:8:165:16 | access to array | +| aliasing.cpp:175:15:175:22 | ref arg & ... | aliasing.cpp:175:21:175:22 | m1 [inner post update] | +| aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | aliasing.cpp:176:8:176:9 | s2 [s, m1] | +| aliasing.cpp:175:19:175:19 | s [post update] [m1] | aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | +| aliasing.cpp:175:21:175:22 | m1 [inner post update] | aliasing.cpp:175:19:175:19 | s [post update] [m1] | +| aliasing.cpp:176:8:176:9 | s2 [s, m1] | aliasing.cpp:176:11:176:11 | s [m1] | +| aliasing.cpp:176:11:176:11 | s [m1] | aliasing.cpp:176:13:176:14 | m1 | +| aliasing.cpp:187:15:187:22 | ref arg & ... | aliasing.cpp:187:21:187:22 | m1 [inner post update] | +| aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | aliasing.cpp:189:8:189:11 | s2_2 [s, m1] | +| aliasing.cpp:187:19:187:19 | s [post update] [m1] | aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | +| aliasing.cpp:187:21:187:22 | m1 [inner post update] | aliasing.cpp:187:19:187:19 | s [post update] [m1] | +| aliasing.cpp:189:8:189:11 | s2_2 [s, m1] | aliasing.cpp:189:13:189:13 | s [m1] | +| aliasing.cpp:189:13:189:13 | s [m1] | aliasing.cpp:189:15:189:16 | m1 | +| aliasing.cpp:200:15:200:24 | ref arg & ... | aliasing.cpp:200:23:200:24 | m1 [inner post update] | +| aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | aliasing.cpp:201:8:201:10 | ps2 [s, m1] | +| aliasing.cpp:200:21:200:21 | s [post update] [m1] | aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | +| aliasing.cpp:200:23:200:24 | m1 [inner post update] | aliasing.cpp:200:21:200:21 | s [post update] [m1] | +| aliasing.cpp:201:8:201:10 | ps2 [s, m1] | aliasing.cpp:201:13:201:13 | s [m1] | +| aliasing.cpp:201:13:201:13 | s [m1] | aliasing.cpp:201:15:201:16 | m1 | +| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array | +| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array | +| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... | +| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | +| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array | +| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:37:8:37:8 | o [nested, arr, data] | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:38:8:38:8 | o [nested, arr, data] | +| arrays.cpp:36:3:36:17 | access to array [post update] [data] | arrays.cpp:36:12:36:14 | arr [inner post update] [data] | +| arrays.cpp:36:3:36:37 | ... = ... | arrays.cpp:36:3:36:17 | access to array [post update] [data] | +| arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | +| arrays.cpp:36:12:36:14 | arr [inner post update] [data] | arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | +| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:36:3:36:37 | ... = ... | +| arrays.cpp:37:8:37:8 | o [nested, arr, data] | arrays.cpp:37:10:37:15 | nested [arr, data] | +| arrays.cpp:37:8:37:22 | access to array [data] | arrays.cpp:37:24:37:27 | data | +| arrays.cpp:37:10:37:15 | nested [arr, data] | arrays.cpp:37:17:37:19 | arr [data] | +| arrays.cpp:37:17:37:19 | arr [data] | arrays.cpp:37:8:37:22 | access to array [data] | +| arrays.cpp:38:8:38:8 | o [nested, arr, data] | arrays.cpp:38:10:38:15 | nested [arr, data] | +| arrays.cpp:38:8:38:22 | access to array [data] | arrays.cpp:38:24:38:27 | data | +| arrays.cpp:38:10:38:15 | nested [arr, data] | arrays.cpp:38:17:38:19 | arr [data] | +| arrays.cpp:38:17:38:19 | arr [data] | arrays.cpp:38:8:38:22 | access to array [data] | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:43:8:43:8 | o [indirect, arr, data] | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:44:8:44:8 | o [indirect, arr, data] | +| arrays.cpp:42:3:42:20 | access to array [post update] [data] | arrays.cpp:42:15:42:17 | arr [inner post update] [data] | +| arrays.cpp:42:3:42:40 | ... = ... | arrays.cpp:42:3:42:20 | access to array [post update] [data] | +| arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | +| arrays.cpp:42:15:42:17 | arr [inner post update] [data] | arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | +| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:42:3:42:40 | ... = ... | +| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | arrays.cpp:43:10:43:17 | indirect [arr, data] | +| arrays.cpp:43:8:43:25 | access to array [data] | arrays.cpp:43:27:43:30 | data | +| arrays.cpp:43:10:43:17 | indirect [arr, data] | arrays.cpp:43:20:43:22 | arr [data] | +| arrays.cpp:43:20:43:22 | arr [data] | arrays.cpp:43:8:43:25 | access to array [data] | +| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | arrays.cpp:44:10:44:17 | indirect [arr, data] | +| arrays.cpp:44:8:44:25 | access to array [data] | arrays.cpp:44:27:44:30 | data | +| arrays.cpp:44:10:44:17 | indirect [arr, data] | arrays.cpp:44:20:44:22 | arr [data] | +| arrays.cpp:44:20:44:22 | arr [data] | arrays.cpp:44:8:44:25 | access to array [data] | | by_reference.cpp:50:3:50:3 | ref arg s [a] | by_reference.cpp:51:8:51:8 | s [a] | | by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | ref arg s [a] | | by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:51:10:51:20 | call to getDirectly | @@ -184,6 +252,9 @@ edges | by_reference.cpp:88:3:88:7 | inner [post update] [a] | by_reference.cpp:127:21:127:38 | ref arg * ... [a] | | by_reference.cpp:88:3:88:24 | ... = ... | by_reference.cpp:88:3:88:7 | inner [post update] [a] | | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | ... = ... | +| by_reference.cpp:92:4:92:5 | pa [inner post update] | by_reference.cpp:104:15:104:22 | ref arg & ... | +| by_reference.cpp:92:4:92:5 | pa [inner post update] | by_reference.cpp:108:15:108:24 | ref arg & ... | +| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:4:92:5 | pa [inner post update] | | by_reference.cpp:95:25:95:26 | pa | by_reference.cpp:124:21:124:21 | ref arg a | | by_reference.cpp:95:25:95:26 | pa | by_reference.cpp:128:23:128:23 | ref arg a | | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:95:25:95:26 | pa | @@ -192,19 +263,27 @@ edges | by_reference.cpp:102:28:102:39 | inner_nested [inner post update] [a] | by_reference.cpp:102:22:102:26 | outer [post update] [inner_nested, a] | | by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] | by_reference.cpp:111:8:111:12 | outer [inner_ptr, a] | | by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] | by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] | +| by_reference.cpp:104:15:104:22 | ref arg & ... | by_reference.cpp:104:22:104:22 | a [inner post update] | +| by_reference.cpp:104:16:104:20 | outer [post update] [a] | by_reference.cpp:112:8:112:12 | outer [a] | +| by_reference.cpp:104:22:104:22 | a [inner post update] | by_reference.cpp:104:16:104:20 | outer [post update] [a] | | by_reference.cpp:106:21:106:41 | ref arg & ... [a] | by_reference.cpp:106:30:106:41 | inner_nested [inner post update] [a] | | by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] | by_reference.cpp:114:8:114:13 | pouter [inner_nested, a] | | by_reference.cpp:106:30:106:41 | inner_nested [inner post update] [a] | by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] | | by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] | by_reference.cpp:115:8:115:13 | pouter [inner_ptr, a] | | by_reference.cpp:107:29:107:37 | ref arg inner_ptr [a] | by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] | +| by_reference.cpp:108:15:108:24 | ref arg & ... | by_reference.cpp:108:24:108:24 | a [inner post update] | +| by_reference.cpp:108:16:108:21 | pouter [post update] [a] | by_reference.cpp:116:8:116:13 | pouter [a] | +| by_reference.cpp:108:24:108:24 | a [inner post update] | by_reference.cpp:108:16:108:21 | pouter [post update] [a] | | by_reference.cpp:110:8:110:12 | outer [inner_nested, a] | by_reference.cpp:110:14:110:25 | inner_nested [a] | | by_reference.cpp:110:14:110:25 | inner_nested [a] | by_reference.cpp:110:27:110:27 | a | | by_reference.cpp:111:8:111:12 | outer [inner_ptr, a] | by_reference.cpp:111:14:111:22 | inner_ptr [a] | | by_reference.cpp:111:14:111:22 | inner_ptr [a] | by_reference.cpp:111:25:111:25 | a | +| by_reference.cpp:112:8:112:12 | outer [a] | by_reference.cpp:112:14:112:14 | a | | by_reference.cpp:114:8:114:13 | pouter [inner_nested, a] | by_reference.cpp:114:16:114:27 | inner_nested [a] | | by_reference.cpp:114:16:114:27 | inner_nested [a] | by_reference.cpp:114:29:114:29 | a | | by_reference.cpp:115:8:115:13 | pouter [inner_ptr, a] | by_reference.cpp:115:16:115:24 | inner_ptr [a] | | by_reference.cpp:115:16:115:24 | inner_ptr [a] | by_reference.cpp:115:27:115:27 | a | +| by_reference.cpp:116:8:116:13 | pouter [a] | by_reference.cpp:116:16:116:16 | a | | by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | by_reference.cpp:130:8:130:12 | outer [inner_nested, a] | | by_reference.cpp:122:27:122:38 | ref arg inner_nested [a] | by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | | by_reference.cpp:123:21:123:36 | ref arg * ... [a] | by_reference.cpp:123:28:123:36 | inner_ptr [inner post update] [a] | @@ -229,33 +308,34 @@ edges | by_reference.cpp:135:8:135:13 | pouter [inner_ptr, a] | by_reference.cpp:135:16:135:24 | inner_ptr [a] | | by_reference.cpp:135:16:135:24 | inner_ptr [a] | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter [a] | by_reference.cpp:136:16:136:16 | a | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | -| complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | complex.cpp:51:10:51:14 | inner [f, a_] | -| complex.cpp:51:10:51:14 | inner [f, a_] | complex.cpp:51:16:51:16 | f [a_] | -| complex.cpp:51:16:51:16 | f [a_] | complex.cpp:51:18:51:18 | call to a | -| complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | complex.cpp:52:10:52:14 | inner [f, b_] | -| complex.cpp:52:10:52:14 | inner [f, b_] | complex.cpp:52:16:52:16 | f [b_] | -| complex.cpp:52:16:52:16 | f [b_] | complex.cpp:52:18:52:18 | call to b | -| complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | -| complex.cpp:62:6:62:10 | inner [post update] [f, a_] | complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | -| complex.cpp:62:12:62:12 | ref arg f [a_] | complex.cpp:62:6:62:10 | inner [post update] [f, a_] | -| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:62:12:62:12 | ref arg f [a_] | -| complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | -| complex.cpp:63:6:63:10 | inner [post update] [f, b_] | complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | -| complex.cpp:63:12:63:12 | ref arg f [b_] | complex.cpp:63:6:63:10 | inner [post update] [f, b_] | -| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:63:12:63:12 | ref arg f [b_] | -| complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | -| complex.cpp:64:6:64:10 | inner [post update] [f, a_] | complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:64:12:64:12 | ref arg f [a_] | complex.cpp:64:6:64:10 | inner [post update] [f, a_] | -| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:64:12:64:12 | ref arg f [a_] | -| complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | -| complex.cpp:65:6:65:10 | inner [post update] [f, b_] | complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | ref arg f [b_] | complex.cpp:65:6:65:10 | inner [post update] [f, b_] | -| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:65:12:65:12 | ref arg f [b_] | -| complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | -| complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | -| complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | +| complex.cpp:40:17:40:17 | b [inner, f, a_] | complex.cpp:42:8:42:8 | b [inner, f, a_] | +| complex.cpp:40:17:40:17 | b [inner, f, b_] | complex.cpp:43:8:43:8 | b [inner, f, b_] | +| complex.cpp:42:8:42:8 | b [inner, f, a_] | complex.cpp:42:10:42:14 | inner [f, a_] | +| complex.cpp:42:10:42:14 | inner [f, a_] | complex.cpp:42:16:42:16 | f [a_] | +| complex.cpp:42:16:42:16 | f [a_] | complex.cpp:42:18:42:18 | call to a | +| complex.cpp:43:8:43:8 | b [inner, f, b_] | complex.cpp:43:10:43:14 | inner [f, b_] | +| complex.cpp:43:10:43:14 | inner [f, b_] | complex.cpp:43:16:43:16 | f [b_] | +| complex.cpp:43:16:43:16 | f [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | complex.cpp:59:7:59:8 | b1 [inner, f, a_] | +| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | +| complex.cpp:53:12:53:12 | ref arg f [a_] | complex.cpp:53:6:53:10 | inner [post update] [f, a_] | +| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:12:53:12 | ref arg f [a_] | +| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | complex.cpp:62:7:62:8 | b2 [inner, f, b_] | +| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | +| complex.cpp:54:12:54:12 | ref arg f [b_] | complex.cpp:54:6:54:10 | inner [post update] [f, b_] | +| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:12:54:12 | ref arg f [b_] | +| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | complex.cpp:65:7:65:8 | b3 [inner, f, a_] | +| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | +| complex.cpp:55:12:55:12 | ref arg f [a_] | complex.cpp:55:6:55:10 | inner [post update] [f, a_] | +| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:12:55:12 | ref arg f [a_] | +| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | complex.cpp:65:7:65:8 | b3 [inner, f, b_] | +| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | +| complex.cpp:56:12:56:12 | ref arg f [b_] | complex.cpp:56:6:56:10 | inner [post update] [f, b_] | +| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:12:56:12 | ref arg f [b_] | +| complex.cpp:59:7:59:8 | b1 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] | +| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] | | constructors.cpp:26:15:26:15 | f [a_] | constructors.cpp:28:10:28:10 | f [a_] | | constructors.cpp:26:15:26:15 | f [b_] | constructors.cpp:29:10:29:10 | f [b_] | | constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:28:12:28:12 | call to a | @@ -307,6 +387,18 @@ edges | qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:47:5:47:42 | ... = ... | | qualifiers.cpp:48:10:48:14 | outer [inner, a] | qualifiers.cpp:48:16:48:20 | inner [a] | | qualifiers.cpp:48:16:48:20 | inner [a] | qualifiers.cpp:48:23:48:23 | a | +| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | +| realistic.cpp:53:9:53:66 | ... = ... | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | +| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | +| realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | +| realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:53:9:53:66 | ... = ... | +| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | +| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | +| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | +| realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | realistic.cpp:61:37:61:45 | userInput [bufferLen] | +| realistic.cpp:61:37:61:45 | userInput [bufferLen] | realistic.cpp:61:47:61:55 | bufferLen | | simple.cpp:26:15:26:15 | f [a_] | simple.cpp:28:10:28:10 | f [a_] | | simple.cpp:26:15:26:15 | f [b_] | simple.cpp:29:10:29:10 | f [b_] | | simple.cpp:28:10:28:10 | f [a_] | simple.cpp:28:12:28:12 | call to a | @@ -332,6 +424,10 @@ edges | simple.cpp:83:9:83:28 | ... = ... | simple.cpp:83:9:83:10 | f2 [post update] [f1] | | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | ... = ... | | simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 | +| simple.cpp:92:5:92:5 | a [post update] [i] | simple.cpp:94:10:94:11 | a2 [i] | +| simple.cpp:92:5:92:22 | ... = ... | simple.cpp:92:5:92:5 | a [post update] [i] | +| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | ... = ... | +| simple.cpp:94:10:94:11 | a2 [i] | simple.cpp:94:13:94:13 | i | | struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [a] | | struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a | | struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:22:8:22:9 | ab [a] | @@ -422,14 +518,14 @@ nodes | A.cpp:160:29:160:29 | b | semmle.label | b | | A.cpp:161:18:161:40 | call to MyList [next, head] | semmle.label | call to MyList [next, head] | | A.cpp:161:38:161:39 | l1 [head] | semmle.label | l1 [head] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | semmle.label | call to MyList [next, next, ... (3)] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | semmle.label | call to MyList [next, next, head] | | A.cpp:162:38:162:39 | l2 [next, head] | semmle.label | l2 [next, head] | -| A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | semmle.label | l3 [next, next, ... (3)] | +| A.cpp:165:10:165:11 | l3 [next, next, head] | semmle.label | l3 [next, next, head] | | A.cpp:165:14:165:17 | next [next, head] | semmle.label | next [next, head] | | A.cpp:165:20:165:23 | next [head] | semmle.label | next [head] | | A.cpp:165:26:165:29 | head | semmle.label | head | | A.cpp:167:44:167:44 | l [next, head] | semmle.label | l [next, head] | -| A.cpp:167:44:167:44 | l [next, next, ... (3)] | semmle.label | l [next, next, ... (3)] | +| A.cpp:167:44:167:44 | l [next, next, head] | semmle.label | l [next, next, head] | | A.cpp:167:47:167:50 | next [head] | semmle.label | next [head] | | A.cpp:167:47:167:50 | next [next, head] | semmle.label | next [next, head] | | A.cpp:169:12:169:12 | l [head] | semmle.label | l [head] | @@ -491,13 +587,13 @@ nodes | D.cpp:52:14:52:14 | b [box, elem] | semmle.label | b [box, elem] | | D.cpp:56:15:56:24 | new | semmle.label | new | | D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | semmle.label | boxfield [post update] [box, elem] | -| D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | semmle.label | this [post update] [boxfield, box, ... (3)] | +| D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | semmle.label | this [post update] [boxfield, box, elem] | | D.cpp:58:5:58:27 | ... = ... | semmle.label | ... = ... | | D.cpp:58:15:58:17 | box [post update] [elem] | semmle.label | box [post update] [elem] | -| D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | -| D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | +| D.cpp:59:5:59:7 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | +| D.cpp:63:8:63:10 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | | D.cpp:64:10:64:17 | boxfield [box, elem] | semmle.label | boxfield [box, elem] | -| D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | +| D.cpp:64:10:64:17 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | | D.cpp:64:20:64:22 | box [elem] | semmle.label | box [elem] | | D.cpp:64:25:64:28 | elem | semmle.label | elem | | E.cpp:19:27:19:27 | p [data, buffer] | semmle.label | p [data, buffer] | @@ -539,6 +635,79 @@ nodes | aliasing.cpp:93:8:93:8 | w [s, m1] | semmle.label | w [s, m1] | | aliasing.cpp:93:10:93:10 | s [m1] | semmle.label | s [m1] | | aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 | +| aliasing.cpp:106:4:106:5 | pa [inner post update] | semmle.label | pa [inner post update] | +| aliasing.cpp:106:9:106:18 | call to user_input | semmle.label | call to user_input | +| aliasing.cpp:158:15:158:15 | s [post update] [data] | semmle.label | s [post update] [data] | +| aliasing.cpp:158:17:158:20 | ref arg data | semmle.label | ref arg data | +| aliasing.cpp:159:8:159:14 | * ... | semmle.label | * ... | +| aliasing.cpp:159:9:159:9 | s [data] | semmle.label | s [data] | +| aliasing.cpp:159:11:159:14 | data | semmle.label | data | +| aliasing.cpp:164:15:164:15 | s [post update] [data] | semmle.label | s [post update] [data] | +| aliasing.cpp:164:17:164:20 | ref arg data | semmle.label | ref arg data | +| aliasing.cpp:165:8:165:8 | s [data] | semmle.label | s [data] | +| aliasing.cpp:165:8:165:16 | access to array | semmle.label | access to array | +| aliasing.cpp:165:10:165:13 | data | semmle.label | data | +| aliasing.cpp:175:15:175:22 | ref arg & ... | semmle.label | ref arg & ... | +| aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | semmle.label | s2 [post update] [s, m1] | +| aliasing.cpp:175:19:175:19 | s [post update] [m1] | semmle.label | s [post update] [m1] | +| aliasing.cpp:175:21:175:22 | m1 [inner post update] | semmle.label | m1 [inner post update] | +| aliasing.cpp:176:8:176:9 | s2 [s, m1] | semmle.label | s2 [s, m1] | +| aliasing.cpp:176:11:176:11 | s [m1] | semmle.label | s [m1] | +| aliasing.cpp:176:13:176:14 | m1 | semmle.label | m1 | +| aliasing.cpp:187:15:187:22 | ref arg & ... | semmle.label | ref arg & ... | +| aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | semmle.label | s2 [post update] [s, m1] | +| aliasing.cpp:187:19:187:19 | s [post update] [m1] | semmle.label | s [post update] [m1] | +| aliasing.cpp:187:21:187:22 | m1 [inner post update] | semmle.label | m1 [inner post update] | +| aliasing.cpp:189:8:189:11 | s2_2 [s, m1] | semmle.label | s2_2 [s, m1] | +| aliasing.cpp:189:13:189:13 | s [m1] | semmle.label | s [m1] | +| aliasing.cpp:189:15:189:16 | m1 | semmle.label | m1 | +| aliasing.cpp:200:15:200:24 | ref arg & ... | semmle.label | ref arg & ... | +| aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | semmle.label | ps2 [post update] [s, m1] | +| aliasing.cpp:200:21:200:21 | s [post update] [m1] | semmle.label | s [post update] [m1] | +| aliasing.cpp:200:23:200:24 | m1 [inner post update] | semmle.label | m1 [inner post update] | +| aliasing.cpp:201:8:201:10 | ps2 [s, m1] | semmle.label | ps2 [s, m1] | +| aliasing.cpp:201:13:201:13 | s [m1] | semmle.label | s [m1] | +| aliasing.cpp:201:15:201:16 | m1 | semmle.label | m1 | +| arrays.cpp:6:12:6:21 | call to user_input | semmle.label | call to user_input | +| arrays.cpp:7:8:7:13 | access to array | semmle.label | access to array | +| arrays.cpp:8:8:8:13 | access to array | semmle.label | access to array | +| arrays.cpp:9:8:9:11 | * ... | semmle.label | * ... | +| arrays.cpp:10:8:10:15 | * ... | semmle.label | * ... | +| arrays.cpp:15:14:15:23 | call to user_input | semmle.label | call to user_input | +| arrays.cpp:16:8:16:13 | access to array | semmle.label | access to array | +| arrays.cpp:17:8:17:13 | access to array | semmle.label | access to array | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | semmle.label | o [post update] [nested, arr, data] | +| arrays.cpp:36:3:36:17 | access to array [post update] [data] | semmle.label | access to array [post update] [data] | +| arrays.cpp:36:3:36:37 | ... = ... | semmle.label | ... = ... | +| arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | semmle.label | nested [post update] [arr, data] | +| arrays.cpp:36:12:36:14 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] | +| arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input | +| arrays.cpp:37:8:37:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] | +| arrays.cpp:37:8:37:22 | access to array [data] | semmle.label | access to array [data] | +| arrays.cpp:37:10:37:15 | nested [arr, data] | semmle.label | nested [arr, data] | +| arrays.cpp:37:17:37:19 | arr [data] | semmle.label | arr [data] | +| arrays.cpp:37:24:37:27 | data | semmle.label | data | +| arrays.cpp:38:8:38:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] | +| arrays.cpp:38:8:38:22 | access to array [data] | semmle.label | access to array [data] | +| arrays.cpp:38:10:38:15 | nested [arr, data] | semmle.label | nested [arr, data] | +| arrays.cpp:38:17:38:19 | arr [data] | semmle.label | arr [data] | +| arrays.cpp:38:24:38:27 | data | semmle.label | data | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | semmle.label | o [post update] [indirect, arr, data] | +| arrays.cpp:42:3:42:20 | access to array [post update] [data] | semmle.label | access to array [post update] [data] | +| arrays.cpp:42:3:42:40 | ... = ... | semmle.label | ... = ... | +| arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | semmle.label | indirect [post update] [arr, data] | +| arrays.cpp:42:15:42:17 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] | +| arrays.cpp:42:29:42:38 | call to user_input | semmle.label | call to user_input | +| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] | +| arrays.cpp:43:8:43:25 | access to array [data] | semmle.label | access to array [data] | +| arrays.cpp:43:10:43:17 | indirect [arr, data] | semmle.label | indirect [arr, data] | +| arrays.cpp:43:20:43:22 | arr [data] | semmle.label | arr [data] | +| arrays.cpp:43:27:43:30 | data | semmle.label | data | +| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] | +| arrays.cpp:44:8:44:25 | access to array [data] | semmle.label | access to array [data] | +| arrays.cpp:44:10:44:17 | indirect [arr, data] | semmle.label | indirect [arr, data] | +| arrays.cpp:44:20:44:22 | arr [data] | semmle.label | arr [data] | +| arrays.cpp:44:27:44:30 | data | semmle.label | data | | by_reference.cpp:50:3:50:3 | ref arg s [a] | semmle.label | ref arg s [a] | | by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input | | by_reference.cpp:51:8:51:8 | s [a] | semmle.label | s [a] | @@ -562,6 +731,8 @@ nodes | by_reference.cpp:88:3:88:7 | inner [post update] [a] | semmle.label | inner [post update] [a] | | by_reference.cpp:88:3:88:24 | ... = ... | semmle.label | ... = ... | | by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input | +| by_reference.cpp:92:4:92:5 | pa [inner post update] | semmle.label | pa [inner post update] | +| by_reference.cpp:92:9:92:18 | call to user_input | semmle.label | call to user_input | | by_reference.cpp:95:25:95:26 | pa | semmle.label | pa | | by_reference.cpp:96:8:96:17 | call to user_input | semmle.label | call to user_input | | by_reference.cpp:102:21:102:39 | ref arg & ... [a] | semmle.label | ref arg & ... [a] | @@ -569,23 +740,33 @@ nodes | by_reference.cpp:102:28:102:39 | inner_nested [inner post update] [a] | semmle.label | inner_nested [inner post update] [a] | | by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] | semmle.label | outer [post update] [inner_ptr, a] | | by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] | semmle.label | ref arg inner_ptr [a] | +| by_reference.cpp:104:15:104:22 | ref arg & ... | semmle.label | ref arg & ... | +| by_reference.cpp:104:16:104:20 | outer [post update] [a] | semmle.label | outer [post update] [a] | +| by_reference.cpp:104:22:104:22 | a [inner post update] | semmle.label | a [inner post update] | | by_reference.cpp:106:21:106:41 | ref arg & ... [a] | semmle.label | ref arg & ... [a] | | by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] | semmle.label | pouter [post update] [inner_nested, a] | | by_reference.cpp:106:30:106:41 | inner_nested [inner post update] [a] | semmle.label | inner_nested [inner post update] [a] | | by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] | semmle.label | pouter [post update] [inner_ptr, a] | | by_reference.cpp:107:29:107:37 | ref arg inner_ptr [a] | semmle.label | ref arg inner_ptr [a] | +| by_reference.cpp:108:15:108:24 | ref arg & ... | semmle.label | ref arg & ... | +| by_reference.cpp:108:16:108:21 | pouter [post update] [a] | semmle.label | pouter [post update] [a] | +| by_reference.cpp:108:24:108:24 | a [inner post update] | semmle.label | a [inner post update] | | by_reference.cpp:110:8:110:12 | outer [inner_nested, a] | semmle.label | outer [inner_nested, a] | | by_reference.cpp:110:14:110:25 | inner_nested [a] | semmle.label | inner_nested [a] | | by_reference.cpp:110:27:110:27 | a | semmle.label | a | | by_reference.cpp:111:8:111:12 | outer [inner_ptr, a] | semmle.label | outer [inner_ptr, a] | | by_reference.cpp:111:14:111:22 | inner_ptr [a] | semmle.label | inner_ptr [a] | | by_reference.cpp:111:25:111:25 | a | semmle.label | a | +| by_reference.cpp:112:8:112:12 | outer [a] | semmle.label | outer [a] | +| by_reference.cpp:112:14:112:14 | a | semmle.label | a | | by_reference.cpp:114:8:114:13 | pouter [inner_nested, a] | semmle.label | pouter [inner_nested, a] | | by_reference.cpp:114:16:114:27 | inner_nested [a] | semmle.label | inner_nested [a] | | by_reference.cpp:114:29:114:29 | a | semmle.label | a | | by_reference.cpp:115:8:115:13 | pouter [inner_ptr, a] | semmle.label | pouter [inner_ptr, a] | | by_reference.cpp:115:16:115:24 | inner_ptr [a] | semmle.label | inner_ptr [a] | | by_reference.cpp:115:27:115:27 | a | semmle.label | a | +| by_reference.cpp:116:8:116:13 | pouter [a] | semmle.label | pouter [a] | +| by_reference.cpp:116:16:116:16 | a | semmle.label | a | | by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | semmle.label | outer [post update] [inner_nested, a] | | by_reference.cpp:122:27:122:38 | ref arg inner_nested [a] | semmle.label | ref arg inner_nested [a] | | by_reference.cpp:123:21:123:36 | ref arg * ... [a] | semmle.label | ref arg * ... [a] | @@ -616,34 +797,36 @@ nodes | by_reference.cpp:135:27:135:27 | a | semmle.label | a | | by_reference.cpp:136:8:136:13 | pouter [a] | semmle.label | pouter [a] | | by_reference.cpp:136:16:136:16 | a | semmle.label | a | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:51:10:51:14 | inner [f, a_] | semmle.label | inner [f, a_] | -| complex.cpp:51:16:51:16 | f [a_] | semmle.label | f [a_] | -| complex.cpp:51:18:51:18 | call to a | semmle.label | call to a | -| complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:52:10:52:14 | inner [f, b_] | semmle.label | inner [f, b_] | -| complex.cpp:52:16:52:16 | f [b_] | semmle.label | f [b_] | -| complex.cpp:52:18:52:18 | call to b | semmle.label | call to b | -| complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | semmle.label | b1 [post update] [inner, f, ... (3)] | -| complex.cpp:62:6:62:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | -| complex.cpp:62:12:62:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | -| complex.cpp:62:19:62:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | semmle.label | b2 [post update] [inner, f, ... (3)] | -| complex.cpp:63:6:63:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | -| complex.cpp:63:12:63:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | -| complex.cpp:63:19:63:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | semmle.label | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:64:6:64:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | -| complex.cpp:64:12:64:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | -| complex.cpp:64:19:64:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | semmle.label | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:65:6:65:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | -| complex.cpp:65:12:65:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | -| complex.cpp:65:19:65:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | semmle.label | b1 [inner, f, ... (3)] | -| complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | semmle.label | b2 [inner, f, ... (3)] | -| complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | semmle.label | b3 [inner, f, ... (3)] | +| complex.cpp:40:17:40:17 | b [inner, f, a_] | semmle.label | b [inner, f, a_] | +| complex.cpp:40:17:40:17 | b [inner, f, b_] | semmle.label | b [inner, f, b_] | +| complex.cpp:42:8:42:8 | b [inner, f, a_] | semmle.label | b [inner, f, a_] | +| complex.cpp:42:10:42:14 | inner [f, a_] | semmle.label | inner [f, a_] | +| complex.cpp:42:16:42:16 | f [a_] | semmle.label | f [a_] | +| complex.cpp:42:18:42:18 | call to a | semmle.label | call to a | +| complex.cpp:43:8:43:8 | b [inner, f, b_] | semmle.label | b [inner, f, b_] | +| complex.cpp:43:10:43:14 | inner [f, b_] | semmle.label | inner [f, b_] | +| complex.cpp:43:16:43:16 | f [b_] | semmle.label | f [b_] | +| complex.cpp:43:18:43:18 | call to b | semmle.label | call to b | +| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | semmle.label | b1 [post update] [inner, f, a_] | +| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | +| complex.cpp:53:12:53:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | +| complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | semmle.label | b2 [post update] [inner, f, b_] | +| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | +| complex.cpp:54:12:54:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | +| complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | semmle.label | b3 [post update] [inner, f, a_] | +| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | +| complex.cpp:55:12:55:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | +| complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | semmle.label | b3 [post update] [inner, f, b_] | +| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | +| complex.cpp:56:12:56:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | +| complex.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:59:7:59:8 | b1 [inner, f, a_] | semmle.label | b1 [inner, f, a_] | +| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | semmle.label | b2 [inner, f, b_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | semmle.label | b3 [inner, f, a_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | semmle.label | b3 [inner, f, b_] | | constructors.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] | | constructors.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] | | constructors.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] | @@ -703,6 +886,19 @@ nodes | qualifiers.cpp:48:10:48:14 | outer [inner, a] | semmle.label | outer [inner, a] | | qualifiers.cpp:48:16:48:20 | inner [a] | semmle.label | inner [a] | | qualifiers.cpp:48:23:48:23 | a | semmle.label | a | +| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | semmle.label | foo [post update] [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | semmle.label | access to array [post update] [baz, userInput, bufferLen] | +| realistic.cpp:53:9:53:66 | ... = ... | semmle.label | ... = ... | +| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | semmle.label | bar [inner post update] [baz, userInput, bufferLen] | +| realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | semmle.label | baz [post update] [userInput, bufferLen] | +| realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | semmle.label | userInput [post update] [bufferLen] | +| realistic.cpp:53:55:53:64 | call to user_input | semmle.label | call to user_input | +| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | semmle.label | foo [bar, baz, userInput, bufferLen] | +| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | semmle.label | access to array [baz, userInput, bufferLen] | +| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | semmle.label | bar [baz, userInput, bufferLen] | +| realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | semmle.label | baz [userInput, bufferLen] | +| realistic.cpp:61:37:61:45 | userInput [bufferLen] | semmle.label | userInput [bufferLen] | +| realistic.cpp:61:47:61:55 | bufferLen | semmle.label | bufferLen | | simple.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] | | simple.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] | | simple.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] | @@ -732,6 +928,11 @@ nodes | simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 | | simple.cpp:84:14:84:20 | this [f2, f1] | semmle.label | this [f2, f1] | +| simple.cpp:92:5:92:5 | a [post update] [i] | semmle.label | a [post update] [i] | +| simple.cpp:92:5:92:22 | ... = ... | semmle.label | ... = ... | +| simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input | +| simple.cpp:94:10:94:11 | a2 [i] | semmle.label | a2 [i] | +| simple.cpp:94:13:94:13 | i | semmle.label | i | | struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] | | struct_init.c:15:8:15:9 | ab [a] | semmle.label | ab [a] | | struct_init.c:15:12:15:12 | a | semmle.label | a | @@ -792,28 +993,41 @@ nodes | aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:30:11:30:12 | m1 | m1 flows from $@ | aliasing.cpp:13:10:13:19 | call to user_input | call to user_input | | aliasing.cpp:62:14:62:15 | m1 | aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:62:14:62:15 | m1 | m1 flows from $@ | aliasing.cpp:60:11:60:20 | call to user_input | call to user_input | | aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input | +| aliasing.cpp:159:8:159:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:159:8:159:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:165:8:165:16 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:165:8:165:16 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:176:13:176:14 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:176:13:176:14 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:189:15:189:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:189:15:189:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| aliasing.cpp:201:15:201:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:201:15:201:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input | +| arrays.cpp:7:8:7:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input | +| arrays.cpp:8:8:8:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input | +| arrays.cpp:9:8:9:11 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input | +| arrays.cpp:10:8:10:15 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input | +| arrays.cpp:16:8:16:13 | access to array | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array | access to array flows from $@ | arrays.cpp:15:14:15:23 | call to user_input | call to user_input | +| arrays.cpp:17:8:17:13 | access to array | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | access to array flows from $@ | arrays.cpp:15:14:15:23 | call to user_input | call to user_input | +| arrays.cpp:37:24:37:27 | data | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data | data flows from $@ | arrays.cpp:36:26:36:35 | call to user_input | call to user_input | +| arrays.cpp:38:24:38:27 | data | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:38:24:38:27 | data | data flows from $@ | arrays.cpp:36:26:36:35 | call to user_input | call to user_input | +| arrays.cpp:43:27:43:30 | data | arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:43:27:43:30 | data | data flows from $@ | arrays.cpp:42:29:42:38 | call to user_input | call to user_input | +| arrays.cpp:44:27:44:30 | data | arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:44:27:44:30 | data | data flows from $@ | arrays.cpp:42:29:42:38 | call to user_input | call to user_input | | by_reference.cpp:51:10:51:20 | call to getDirectly | by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:51:10:51:20 | call to getDirectly | call to getDirectly flows from $@ | by_reference.cpp:50:17:50:26 | call to user_input | call to user_input | | by_reference.cpp:57:10:57:22 | call to getIndirectly | by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:57:10:57:22 | call to getIndirectly | call to getIndirectly flows from $@ | by_reference.cpp:56:19:56:28 | call to user_input | call to user_input | | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | call to getThroughNonMember flows from $@ | by_reference.cpp:62:25:62:34 | call to user_input | call to user_input | | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | call to nonMemberGetA flows from $@ | by_reference.cpp:68:21:68:30 | call to user_input | call to user_input | | by_reference.cpp:110:27:110:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:110:27:110:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input | | by_reference.cpp:111:25:111:25 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:111:25:111:25 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input | +| by_reference.cpp:112:14:112:14 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:112:14:112:14 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input | | by_reference.cpp:114:29:114:29 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:114:29:114:29 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input | | by_reference.cpp:115:27:115:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:115:27:115:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input | +| by_reference.cpp:116:16:116:16 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:116:16:116:16 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input | | by_reference.cpp:130:27:130:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:130:27:130:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:131:25:131:25 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:131:25:131:25 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:132:14:132:14 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:132:14:132:14 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:135:27:135:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | | constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input | @@ -824,12 +1038,14 @@ nodes | qualifiers.cpp:38:23:38:23 | a | qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:38:23:38:23 | a | a flows from $@ | qualifiers.cpp:37:38:37:47 | call to user_input | call to user_input | | qualifiers.cpp:43:23:43:23 | a | qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:43:23:43:23 | a | a flows from $@ | qualifiers.cpp:42:29:42:38 | call to user_input | call to user_input | | qualifiers.cpp:48:23:48:23 | a | qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:48:23:48:23 | a | a flows from $@ | qualifiers.cpp:47:31:47:40 | call to user_input | call to user_input | +| realistic.cpp:61:47:61:55 | bufferLen | realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:61:47:61:55 | bufferLen | bufferLen flows from $@ | realistic.cpp:53:55:53:64 | call to user_input | call to user_input | | simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input | call to user_input | | simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input | | simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input | | simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input | | simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input | +| simple.cpp:94:13:94:13 | i | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:94:13:94:13 | i | i flows from $@ | simple.cpp:92:11:92:20 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:40:20:40:29 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp b/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp new file mode 100644 index 000000000000..f121dc58b4d7 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp @@ -0,0 +1,70 @@ +typedef unsigned char u8; +typedef unsigned long size_t; +struct UserInput { + size_t bufferLen; + u8 buffer[256]; +}; +struct Baz { + int foo; + struct UserInput userInput; +}; +struct Bar { + u8* foo; + struct Baz * baz; +}; +struct Foo { + struct Bar bar[128]; +}; +void printf(const char *fmt, ...) { + return; +} +void * malloc(size_t size) { + static unsigned char buffer[0x1000]; + static unsigned int offset; + if (size + offset >= sizeof(buffer)) return nullptr; + void* m = (void*)&buffer[offset]; + offset += size; + return m; +} +void * memcpy ( void * destination, const void * source, size_t num ) { + u8* d = (u8*)destination; + u8* s = (u8*)source; + u8* e = d + num; + while(d != e) { + *d++ = *s++; + } + return destination; +} +void *user_input(void) { + return (void*)"\x0a\x00\x00\x00\x00\x00\x00\x00The quick brown fox jumps over the lazy dog"; +} +void sink(void *o) { + printf("%p\n", o); +} +#define MAX_BAZ 3 +int main(int argc, char** argv) { + char dst[256]; + struct Foo foo; + for (int i = 0; i < MAX_BAZ; i++) { + foo.bar[i].baz = (struct Baz*)malloc(sizeof(struct Baz)); + } + int i = 0; + while(i < MAX_BAZ) { + foo.bar[i].baz->userInput.bufferLen = (size_t)user_input(); + memcpy(foo.bar[i].baz->userInput.buffer, user_input(), sizeof(foo.bar[i].baz->userInput.buffer)); + if(foo.bar[i].baz->userInput.bufferLen > sizeof(foo.bar[i].baz->userInput.buffer)) + { + printf("The user-supplied input 0x%lx is larger than the buffer 0x%lx!\n", foo.bar[i].baz->userInput.bufferLen, sizeof(foo.bar[i].baz->userInput.buffer)); + return -1; + } + memcpy(dst, foo.bar[i].baz->userInput.buffer, foo.bar[i].baz->userInput.bufferLen); + sink((void*)foo.bar[i].baz->userInput.bufferLen); // $ast $f-:ir + // There is no flow to the following two `sink` calls because the + // source is the _pointer_ returned by `user_input` rather than the + // _data_ to which it points. + sink((void*)foo.bar[i].baz->userInput.buffer); // $f-:ast,ir + sink((void*)dst); // $f-:ast,ir + i++; + } + return 0; +} diff --git a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp index 4a3a15a0b176..3f7c91f77471 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp @@ -25,8 +25,8 @@ class Foo void bar(Foo &f) { - sink(f.a()); //$ast=39:12 $ast=41:12 $f-:ir - sink(f.b()); //$ast=40:12 $ast=42:12 $f-:ir + sink(f.a()); //$ast=39:12 $ast=41:12 $ir=39:12 $ir=41:12 + sink(f.b()); //$ast=40:12 $ast=42:12 $ir=40:12 $ir=42:12 } void foo() @@ -85,4 +85,13 @@ struct C2 } }; +typedef A A_typedef; + +void single_field_test_typedef(A_typedef a) +{ + a.i = user_input(); + A_typedef a2 = a; + sink(a2.i); //$ast,ir +} + } // namespace Simple diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll b/cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll index aa24c2629c7c..715d1883525a 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll @@ -1,4 +1,5 @@ import cpp +import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.dataflow.TaintTracking /** Common data flow configuration to be used by tests. */ @@ -6,7 +7,7 @@ class TestAllocationConfig extends TaintTracking::Configuration { TestAllocationConfig() { this = "TestAllocationConfig" } override predicate isSource(DataFlow::Node source) { - source.asExpr().(FunctionCall).getTarget().getName() = "source" + source.(DataFlow::ExprNode).getConvertedExpr().(FunctionCall).getTarget().getName() = "source" or source.asParameter().getName().matches("source%") or @@ -17,7 +18,16 @@ class TestAllocationConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { exists(FunctionCall call | call.getTarget().getName() = "sink" and - sink.asExpr() = call.getAnArgument() + sink.(DataFlow::ExprNode).getConvertedExpr() = call.getAnArgument() + or + call.getTarget().getName() = "sink" and + sink.asExpr() = call.getAnArgument() and + sink.(DataFlow::ExprNode).getConvertedExpr() instanceof ReferenceDereferenceExpr + ) + or + exists(ReadSideEffectInstruction read | + read.getSideEffectOperand() = sink.asOperand() and + read.getPrimaryInstruction().(CallInstruction).getStaticCallTarget().hasName("sink") ) } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp new file mode 100644 index 000000000000..b86d2bf02346 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp @@ -0,0 +1,147 @@ + +int source(); +void sink(int); +void sink(class MyInt); +void sink(class MyArray); + +void test_pointer_deref_assignment() +{ + int x = 0; + int *p_x = &x; + int *p2_x = &x; + int &r_x = x; + + *p_x = source(); + + sink(x); // tainted [DETECTED BY IR ONLY] + sink(*p_x); // tainted + sink(*p2_x); // tainted [DETECTED BY IR ONLY] + sink(r_x); // tainted [DETECTED BY IR ONLY] +} + +void test_reference_deref_assignment() +{ + int x = 0; + int *p_x = &x; + int &r_x = x; + int &r2_x = x; + + r_x = source(); + + sink(x); // tainted [DETECTED BY IR ONLY] + sink(*p_x); // tainted [DETECTED BY IR ONLY] + sink(r_x); // tainted + sink(r2_x); // tainted [DETECTED BY IR ONLY] +} + +class MyInt +{ +public: + MyInt() : i(0) {} + + int &get() { return i; } + + MyInt &operator=(const int &other); + MyInt &operator=(const MyInt &other); + + int i; +}; + +void test_myint_member_assignment() +{ + MyInt mi; + + mi.i = source(); + + sink(mi); // tainted [DETECTED BY IR ONLY] + sink(mi.get()); // tainted +} + +void test_myint_method_assignment() +{ + MyInt mi; + + mi.get() = source(); + + sink(mi); // tainted [DETECTED BY IR ONLY] + sink(mi.get()); // tainted +} + +void test_myint_overloaded_assignment() +{ + MyInt mi, mi2; + + mi = source(); + mi2 = mi; + + sink(mi); // tainted [NOT DETECTED] + sink(mi.get()); // tainted [NOT DETECTED] + sink(mi2); // tainted [NOT DETECTED] + sink(mi2.get()); // tainted [NOT DETECTED] +} + +class MyArray +{ +public: + MyArray() : values({0}) {} + + int &get(int i) { return values[i]; } + + int &operator[](int i); + + int values[10]; +}; + +void test_myarray_member_assignment() +{ + MyArray ma; + + ma.values[0] = source(); + + sink(ma.values[0]); // tainted +} + +void test_myarray_method_assignment() +{ + MyArray ma; + + ma.get(0) = source(); + + sink(ma.get(0)); // tainted [NOT DETECTED] +} + +void test_myarray_overloaded_assignment() +{ + MyArray ma, ma2; + + ma[0] = source(); + ma2 = ma; + + sink(ma[0]); // tainted [NOT DETECTED] + sink(ma2[0]); // tainted [NOT DETECTED] +} + +void sink(int *); + +void test_array_reference_assignment() +{ + int arr1[10] = {0}; + int arr2[10] = {0}; + int arr3[10] = {0}; + int &ref1 = arr1[5]; + int *ptr2, *ptr3; + + ref1 = source(); + sink(ref1); // tainted + sink(arr1[5]); // tainted [DETECTED BY IR ONLY] + + ptr2 = &(arr2[5]); + *ptr2 = source(); + sink(*ptr2); // tainted + sink(arr2[5]); // tainted [DETECTED BY IR ONLY] + + ptr3 = arr3; + ptr3[5] = source(); + sink(ptr3[5]); // tainted + sink(arr3[5]); // tainted [DETECTED BY IR ONLY] +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp new file mode 100644 index 000000000000..d5745bcb7133 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp @@ -0,0 +1,69 @@ + +int source(); +void sink(...) {}; + +class MyCopyableClass { +public: + MyCopyableClass() {} // Constructor + MyCopyableClass(int _v) : v(_v) {} // ConversionConstructor + MyCopyableClass(const MyCopyableClass &other) : v(other.v) {} // CopyConstructor + MyCopyableClass &operator=(const MyCopyableClass &other) { // CopyAssignmentOperator + v = other.v; + return *this; + } + + int v; +}; + +void test_copyableclass() +{ + { + MyCopyableClass s1(1); + MyCopyableClass s2 = 1; + MyCopyableClass s3(s1); + MyCopyableClass s4; + s4 = 1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + MyCopyableClass s1(source()); + MyCopyableClass s2 = source(); + MyCopyableClass s3(s1); + MyCopyableClass s4; + s4 = source(); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + sink(s4); // tainted + } + + { + MyCopyableClass s1; + MyCopyableClass s2 = s1; + MyCopyableClass s3(s1); + MyCopyableClass s4; + s4 = s1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + MyCopyableClass s1 = MyCopyableClass(source()); + MyCopyableClass s2; + MyCopyableClass s3; + s2 = MyCopyableClass(source()); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3 = source()); // tainted + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp new file mode 100644 index 000000000000..67f6a45fbe7c --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp @@ -0,0 +1,69 @@ + +int source(); +void sink(...) {}; + +class MyCopyableClassDeclOnly { +public: + MyCopyableClassDeclOnly(); // Constructor + MyCopyableClassDeclOnly(int _v); // ConversionConstructor + MyCopyableClassDeclOnly(const MyCopyableClassDeclOnly &other); // CopyConstructor + MyCopyableClassDeclOnly &operator=(const MyCopyableClassDeclOnly &other); // CopyAssignmentOperator + + + + + int v; +}; + +void test_copyableclass() +{ + { + MyCopyableClassDeclOnly s1(1); + MyCopyableClassDeclOnly s2 = 1; + MyCopyableClassDeclOnly s3(s1); + MyCopyableClassDeclOnly s4; + s4 = 1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + MyCopyableClassDeclOnly s1(source()); + MyCopyableClassDeclOnly s2 = source(); + MyCopyableClassDeclOnly s3(s1); + MyCopyableClassDeclOnly s4; + s4 = source(); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + sink(s4); // tainted + } + + { + MyCopyableClassDeclOnly s1; + MyCopyableClassDeclOnly s2 = s1; + MyCopyableClassDeclOnly s3(s1); + MyCopyableClassDeclOnly s4; + s4 = s1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + MyCopyableClassDeclOnly s1 = MyCopyableClassDeclOnly(source()); + MyCopyableClassDeclOnly s2; + MyCopyableClassDeclOnly s3; + s2 = MyCopyableClassDeclOnly(source()); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3 = source()); // tainted + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp index 90bbb1ca0cd3..fbe78cf02e10 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp @@ -112,7 +112,7 @@ void test1() { char buffer[256] = {0}; sink(mysprintf(buffer, 256, "%s", string::source())); - sink(buffer); // tainted [NOT DETECTED - implement UserDefinedFormattingFunction.getOutputParameterIndex()] + sink(buffer); // tainted } { diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 48d96a5c2c22..cc5f33b50c3c 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -1,3 +1,260 @@ +| arrayassignment.cpp:9:9:9:10 | 0 | arrayassignment.cpp:10:14:10:14 | x | | +| arrayassignment.cpp:9:9:9:10 | 0 | arrayassignment.cpp:11:15:11:15 | x | | +| arrayassignment.cpp:9:9:9:10 | 0 | arrayassignment.cpp:12:13:12:13 | x | | +| arrayassignment.cpp:9:9:9:10 | 0 | arrayassignment.cpp:16:7:16:7 | x | | +| arrayassignment.cpp:10:13:10:14 | & ... | arrayassignment.cpp:14:3:14:5 | p_x | | +| arrayassignment.cpp:10:13:10:14 | & ... | arrayassignment.cpp:17:8:17:10 | p_x | | +| arrayassignment.cpp:10:14:10:14 | x | arrayassignment.cpp:10:13:10:14 | & ... | | +| arrayassignment.cpp:11:14:11:15 | & ... | arrayassignment.cpp:18:8:18:11 | p2_x | | +| arrayassignment.cpp:11:15:11:15 | x | arrayassignment.cpp:11:14:11:15 | & ... | | +| arrayassignment.cpp:12:13:12:13 | x | arrayassignment.cpp:19:7:19:9 | r_x | | +| arrayassignment.cpp:14:2:14:5 | * ... [post update] | arrayassignment.cpp:14:3:14:5 | p_x [inner post update] | | +| arrayassignment.cpp:14:2:14:5 | * ... [post update] | arrayassignment.cpp:17:8:17:10 | p_x | | +| arrayassignment.cpp:14:2:14:16 | ... = ... | arrayassignment.cpp:14:2:14:5 | * ... [post update] | | +| arrayassignment.cpp:14:3:14:5 | p_x | arrayassignment.cpp:14:2:14:5 | * ... | TAINT | +| arrayassignment.cpp:14:9:14:14 | call to source | arrayassignment.cpp:14:2:14:16 | ... = ... | | +| arrayassignment.cpp:17:8:17:10 | p_x | arrayassignment.cpp:17:7:17:10 | * ... | TAINT | +| arrayassignment.cpp:18:8:18:11 | p2_x | arrayassignment.cpp:18:7:18:11 | * ... | TAINT | +| arrayassignment.cpp:24:9:24:10 | 0 | arrayassignment.cpp:25:14:25:14 | x | | +| arrayassignment.cpp:24:9:24:10 | 0 | arrayassignment.cpp:26:13:26:13 | x | | +| arrayassignment.cpp:24:9:24:10 | 0 | arrayassignment.cpp:27:14:27:14 | x | | +| arrayassignment.cpp:24:9:24:10 | 0 | arrayassignment.cpp:31:7:31:7 | x | | +| arrayassignment.cpp:25:13:25:14 | & ... | arrayassignment.cpp:32:8:32:10 | p_x | | +| arrayassignment.cpp:25:14:25:14 | x | arrayassignment.cpp:25:13:25:14 | & ... | | +| arrayassignment.cpp:27:14:27:14 | x | arrayassignment.cpp:34:7:34:10 | r2_x | | +| arrayassignment.cpp:29:2:29:4 | r_x [post update] | arrayassignment.cpp:33:7:33:9 | r_x | | +| arrayassignment.cpp:29:2:29:15 | ... = ... | arrayassignment.cpp:29:2:29:4 | r_x [post update] | | +| arrayassignment.cpp:29:8:29:13 | call to source | arrayassignment.cpp:29:2:29:15 | ... = ... | | +| arrayassignment.cpp:29:8:29:13 | call to source | arrayassignment.cpp:33:7:33:9 | r_x | | +| arrayassignment.cpp:32:8:32:10 | p_x | arrayassignment.cpp:32:7:32:10 | * ... | TAINT | +| arrayassignment.cpp:37:7:37:7 | Unknown literal | arrayassignment.cpp:37:7:37:7 | constructor init of field i | TAINT | +| arrayassignment.cpp:37:7:37:7 | this | arrayassignment.cpp:37:7:37:7 | constructor init of field i [pre-this] | | +| arrayassignment.cpp:40:2:40:6 | this | arrayassignment.cpp:40:12:40:15 | constructor init of field i [pre-this] | | +| arrayassignment.cpp:40:12:40:15 | 0 | arrayassignment.cpp:40:12:40:15 | constructor init of field i | TAINT | +| arrayassignment.cpp:42:7:42:9 | this | arrayassignment.cpp:42:22:42:22 | this | | +| arrayassignment.cpp:52:8:52:9 | call to MyInt | arrayassignment.cpp:54:2:54:3 | mi | | +| arrayassignment.cpp:52:8:52:9 | call to MyInt | arrayassignment.cpp:56:7:56:8 | mi | | +| arrayassignment.cpp:52:8:52:9 | call to MyInt | arrayassignment.cpp:57:7:57:8 | mi | | +| arrayassignment.cpp:54:2:54:3 | mi [post update] | arrayassignment.cpp:56:7:56:8 | mi | | +| arrayassignment.cpp:54:2:54:3 | mi [post update] | arrayassignment.cpp:57:7:57:8 | mi | | +| arrayassignment.cpp:54:2:54:16 | ... = ... | arrayassignment.cpp:54:5:54:5 | i [post update] | | +| arrayassignment.cpp:54:9:54:14 | call to source | arrayassignment.cpp:54:2:54:16 | ... = ... | | +| arrayassignment.cpp:62:8:62:9 | call to MyInt | arrayassignment.cpp:64:2:64:3 | mi | | +| arrayassignment.cpp:62:8:62:9 | call to MyInt | arrayassignment.cpp:66:7:66:8 | mi | | +| arrayassignment.cpp:62:8:62:9 | call to MyInt | arrayassignment.cpp:67:7:67:8 | mi | | +| arrayassignment.cpp:64:2:64:3 | ref arg mi | arrayassignment.cpp:66:7:66:8 | mi | | +| arrayassignment.cpp:64:2:64:3 | ref arg mi | arrayassignment.cpp:67:7:67:8 | mi | | +| arrayassignment.cpp:64:2:64:20 | ... = ... | arrayassignment.cpp:64:5:64:7 | call to get [post update] | | +| arrayassignment.cpp:64:13:64:18 | call to source | arrayassignment.cpp:64:2:64:20 | ... = ... | | +| arrayassignment.cpp:72:8:72:9 | call to MyInt | arrayassignment.cpp:74:2:74:3 | mi | | +| arrayassignment.cpp:72:8:72:9 | call to MyInt | arrayassignment.cpp:75:8:75:9 | mi | | +| arrayassignment.cpp:72:8:72:9 | call to MyInt | arrayassignment.cpp:77:7:77:8 | mi | | +| arrayassignment.cpp:72:8:72:9 | call to MyInt | arrayassignment.cpp:78:7:78:8 | mi | | +| arrayassignment.cpp:72:12:72:14 | call to MyInt | arrayassignment.cpp:75:2:75:4 | mi2 | | +| arrayassignment.cpp:72:12:72:14 | call to MyInt | arrayassignment.cpp:79:7:79:9 | mi2 | | +| arrayassignment.cpp:72:12:72:14 | call to MyInt | arrayassignment.cpp:80:7:80:9 | mi2 | | +| arrayassignment.cpp:74:2:74:3 | ref arg mi | arrayassignment.cpp:75:8:75:9 | mi | | +| arrayassignment.cpp:74:2:74:3 | ref arg mi | arrayassignment.cpp:77:7:77:8 | mi | | +| arrayassignment.cpp:74:2:74:3 | ref arg mi | arrayassignment.cpp:78:7:78:8 | mi | | +| arrayassignment.cpp:75:2:75:4 | ref arg mi2 | arrayassignment.cpp:79:7:79:9 | mi2 | | +| arrayassignment.cpp:75:2:75:4 | ref arg mi2 | arrayassignment.cpp:80:7:80:9 | mi2 | | +| arrayassignment.cpp:75:8:75:9 | mi | arrayassignment.cpp:75:2:75:4 | ref arg mi2 | TAINT | +| arrayassignment.cpp:75:8:75:9 | mi | arrayassignment.cpp:75:6:75:6 | call to operator= | TAINT | +| arrayassignment.cpp:86:2:86:8 | this | arrayassignment.cpp:86:14:86:24 | constructor init of field values [pre-this] | | +| arrayassignment.cpp:86:14:86:24 | {...} | arrayassignment.cpp:86:14:86:24 | constructor init of field values | TAINT | +| arrayassignment.cpp:86:22:86:22 | 0 | arrayassignment.cpp:86:14:86:24 | {...} | TAINT | +| arrayassignment.cpp:88:7:88:9 | this | arrayassignment.cpp:88:27:88:32 | this | | +| arrayassignment.cpp:88:15:88:15 | i | arrayassignment.cpp:88:34:88:34 | i | | +| arrayassignment.cpp:88:27:88:32 | values | arrayassignment.cpp:88:27:88:35 | access to array | TAINT | +| arrayassignment.cpp:88:34:88:34 | i | arrayassignment.cpp:88:27:88:35 | access to array | TAINT | +| arrayassignment.cpp:97:10:97:11 | call to MyArray | arrayassignment.cpp:99:2:99:3 | ma | | +| arrayassignment.cpp:97:10:97:11 | call to MyArray | arrayassignment.cpp:101:7:101:8 | ma | | +| arrayassignment.cpp:99:2:99:3 | ma [post update] | arrayassignment.cpp:101:7:101:8 | ma | | +| arrayassignment.cpp:99:2:99:13 | access to array [post update] | arrayassignment.cpp:99:5:99:10 | values [inner post update] | | +| arrayassignment.cpp:99:2:99:24 | ... = ... | arrayassignment.cpp:99:2:99:13 | access to array [post update] | | +| arrayassignment.cpp:99:5:99:10 | values | arrayassignment.cpp:99:2:99:13 | access to array | | +| arrayassignment.cpp:99:12:99:12 | 0 | arrayassignment.cpp:99:2:99:13 | access to array | TAINT | +| arrayassignment.cpp:99:17:99:22 | call to source | arrayassignment.cpp:99:2:99:24 | ... = ... | | +| arrayassignment.cpp:101:10:101:15 | values | arrayassignment.cpp:101:7:101:18 | access to array | | +| arrayassignment.cpp:101:17:101:17 | 0 | arrayassignment.cpp:101:7:101:18 | access to array | TAINT | +| arrayassignment.cpp:106:10:106:11 | call to MyArray | arrayassignment.cpp:108:2:108:3 | ma | | +| arrayassignment.cpp:106:10:106:11 | call to MyArray | arrayassignment.cpp:110:7:110:8 | ma | | +| arrayassignment.cpp:108:2:108:3 | ref arg ma | arrayassignment.cpp:110:7:110:8 | ma | | +| arrayassignment.cpp:108:2:108:21 | ... = ... | arrayassignment.cpp:108:5:108:7 | call to get [post update] | | +| arrayassignment.cpp:108:14:108:19 | call to source | arrayassignment.cpp:108:2:108:21 | ... = ... | | +| arrayassignment.cpp:115:10:115:11 | call to MyArray | arrayassignment.cpp:117:2:117:3 | ma | | +| arrayassignment.cpp:115:10:115:11 | call to MyArray | arrayassignment.cpp:118:8:118:9 | ma | | +| arrayassignment.cpp:115:10:115:11 | call to MyArray | arrayassignment.cpp:120:7:120:8 | ma | | +| arrayassignment.cpp:117:2:117:3 | ref arg ma | arrayassignment.cpp:118:8:118:9 | ma | | +| arrayassignment.cpp:117:2:117:3 | ref arg ma | arrayassignment.cpp:120:7:120:8 | ma | | +| arrayassignment.cpp:117:2:117:17 | ... = ... | arrayassignment.cpp:117:4:117:4 | call to operator[] [post update] | | +| arrayassignment.cpp:117:10:117:15 | call to source | arrayassignment.cpp:117:2:117:17 | ... = ... | | +| arrayassignment.cpp:118:8:118:9 | ma | arrayassignment.cpp:118:2:118:9 | ... = ... | | +| arrayassignment.cpp:118:8:118:9 | ma | arrayassignment.cpp:121:7:121:9 | ma2 | | +| arrayassignment.cpp:128:16:128:19 | {...} | arrayassignment.cpp:131:14:131:17 | arr1 | | +| arrayassignment.cpp:128:16:128:19 | {...} | arrayassignment.cpp:136:7:136:10 | arr1 | | +| arrayassignment.cpp:128:18:128:18 | 0 | arrayassignment.cpp:128:16:128:19 | {...} | TAINT | +| arrayassignment.cpp:129:16:129:19 | {...} | arrayassignment.cpp:138:11:138:14 | arr2 | | +| arrayassignment.cpp:129:16:129:19 | {...} | arrayassignment.cpp:141:7:141:10 | arr2 | | +| arrayassignment.cpp:129:18:129:18 | 0 | arrayassignment.cpp:129:16:129:19 | {...} | TAINT | +| arrayassignment.cpp:130:16:130:19 | {...} | arrayassignment.cpp:143:9:143:12 | arr3 | | +| arrayassignment.cpp:130:16:130:19 | {...} | arrayassignment.cpp:146:7:146:10 | arr3 | | +| arrayassignment.cpp:130:18:130:18 | 0 | arrayassignment.cpp:130:16:130:19 | {...} | TAINT | +| arrayassignment.cpp:131:14:131:17 | arr1 | arrayassignment.cpp:131:14:131:20 | access to array | TAINT | +| arrayassignment.cpp:131:19:131:19 | 5 | arrayassignment.cpp:131:14:131:20 | access to array | TAINT | +| arrayassignment.cpp:134:2:134:5 | ref1 [post update] | arrayassignment.cpp:135:7:135:10 | ref1 | | +| arrayassignment.cpp:134:2:134:16 | ... = ... | arrayassignment.cpp:134:2:134:5 | ref1 [post update] | | +| arrayassignment.cpp:134:9:134:14 | call to source | arrayassignment.cpp:134:2:134:16 | ... = ... | | +| arrayassignment.cpp:134:9:134:14 | call to source | arrayassignment.cpp:135:7:135:10 | ref1 | | +| arrayassignment.cpp:136:7:136:10 | arr1 | arrayassignment.cpp:136:7:136:13 | access to array | | +| arrayassignment.cpp:136:12:136:12 | 5 | arrayassignment.cpp:136:7:136:13 | access to array | TAINT | +| arrayassignment.cpp:138:9:138:18 | & ... | arrayassignment.cpp:138:2:138:18 | ... = ... | | +| arrayassignment.cpp:138:9:138:18 | & ... | arrayassignment.cpp:139:3:139:6 | ptr2 | | +| arrayassignment.cpp:138:9:138:18 | & ... | arrayassignment.cpp:140:8:140:11 | ptr2 | | +| arrayassignment.cpp:138:11:138:14 | arr2 | arrayassignment.cpp:138:11:138:17 | access to array | TAINT | +| arrayassignment.cpp:138:11:138:17 | access to array | arrayassignment.cpp:138:9:138:18 | & ... | | +| arrayassignment.cpp:138:16:138:16 | 5 | arrayassignment.cpp:138:11:138:17 | access to array | TAINT | +| arrayassignment.cpp:139:2:139:6 | * ... [post update] | arrayassignment.cpp:139:3:139:6 | ptr2 [inner post update] | | +| arrayassignment.cpp:139:2:139:6 | * ... [post update] | arrayassignment.cpp:140:8:140:11 | ptr2 | | +| arrayassignment.cpp:139:2:139:17 | ... = ... | arrayassignment.cpp:139:2:139:6 | * ... [post update] | | +| arrayassignment.cpp:139:3:139:6 | ptr2 | arrayassignment.cpp:139:2:139:6 | * ... | TAINT | +| arrayassignment.cpp:139:10:139:15 | call to source | arrayassignment.cpp:139:2:139:17 | ... = ... | | +| arrayassignment.cpp:140:8:140:11 | ptr2 | arrayassignment.cpp:140:7:140:11 | * ... | TAINT | +| arrayassignment.cpp:141:7:141:10 | arr2 | arrayassignment.cpp:141:7:141:13 | access to array | | +| arrayassignment.cpp:141:12:141:12 | 5 | arrayassignment.cpp:141:7:141:13 | access to array | TAINT | +| arrayassignment.cpp:143:9:143:12 | arr3 | arrayassignment.cpp:143:2:143:12 | ... = ... | | +| arrayassignment.cpp:143:9:143:12 | arr3 | arrayassignment.cpp:144:2:144:5 | ptr3 | | +| arrayassignment.cpp:143:9:143:12 | arr3 | arrayassignment.cpp:145:7:145:10 | ptr3 | | +| arrayassignment.cpp:144:2:144:5 | ptr3 | arrayassignment.cpp:144:2:144:8 | access to array | TAINT | +| arrayassignment.cpp:144:2:144:8 | access to array [post update] | arrayassignment.cpp:144:2:144:5 | ptr3 [inner post update] | | +| arrayassignment.cpp:144:2:144:8 | access to array [post update] | arrayassignment.cpp:145:7:145:10 | ptr3 | | +| arrayassignment.cpp:144:2:144:19 | ... = ... | arrayassignment.cpp:144:2:144:8 | access to array [post update] | | +| arrayassignment.cpp:144:7:144:7 | 5 | arrayassignment.cpp:144:2:144:8 | access to array | TAINT | +| arrayassignment.cpp:144:12:144:17 | call to source | arrayassignment.cpp:144:2:144:19 | ... = ... | | +| arrayassignment.cpp:145:7:145:10 | ptr3 | arrayassignment.cpp:145:7:145:13 | access to array | TAINT | +| arrayassignment.cpp:145:12:145:12 | 5 | arrayassignment.cpp:145:7:145:13 | access to array | TAINT | +| arrayassignment.cpp:146:7:146:10 | arr3 | arrayassignment.cpp:146:7:146:13 | access to array | | +| arrayassignment.cpp:146:12:146:12 | 5 | arrayassignment.cpp:146:7:146:13 | access to array | TAINT | +| copyableclass.cpp:8:2:8:16 | this | copyableclass.cpp:8:28:8:32 | constructor init of field v [pre-this] | | +| copyableclass.cpp:8:22:8:23 | _v | copyableclass.cpp:8:30:8:31 | _v | | +| copyableclass.cpp:8:30:8:31 | _v | copyableclass.cpp:8:28:8:32 | constructor init of field v | TAINT | +| copyableclass.cpp:9:2:9:16 | this | copyableclass.cpp:9:50:9:59 | constructor init of field v [pre-this] | | +| copyableclass.cpp:9:41:9:45 | other | copyableclass.cpp:9:52:9:56 | other | | +| copyableclass.cpp:9:58:9:58 | v | copyableclass.cpp:9:50:9:59 | constructor init of field v | TAINT | +| copyableclass.cpp:9:58:9:58 | v | copyableclass.cpp:9:58:9:58 | v | | +| copyableclass.cpp:10:19:10:27 | this | copyableclass.cpp:11:3:11:3 | this | | +| copyableclass.cpp:10:52:10:56 | other | copyableclass.cpp:11:7:11:11 | other | | +| copyableclass.cpp:11:3:11:3 | this | copyableclass.cpp:12:11:12:14 | this | | +| copyableclass.cpp:11:3:11:3 | this [post update] | copyableclass.cpp:12:11:12:14 | this | | +| copyableclass.cpp:11:3:11:13 | ... = ... | copyableclass.cpp:11:3:11:3 | v [post update] | | +| copyableclass.cpp:11:13:11:13 | v | copyableclass.cpp:11:3:11:13 | ... = ... | | +| copyableclass.cpp:12:11:12:14 | this | copyableclass.cpp:12:10:12:14 | * ... | TAINT | +| copyableclass.cpp:21:22:21:22 | 1 | copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | copyableclass.cpp:23:22:23:23 | s1 | | +| copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | copyableclass.cpp:27:8:27:9 | s1 | | +| copyableclass.cpp:22:23:22:24 | call to MyCopyableClass | copyableclass.cpp:28:8:28:9 | s2 | | +| copyableclass.cpp:22:24:22:24 | 1 | copyableclass.cpp:22:23:22:24 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:23:22:23:23 | s1 | copyableclass.cpp:23:22:23:24 | call to MyCopyableClass | | +| copyableclass.cpp:23:22:23:24 | call to MyCopyableClass | copyableclass.cpp:29:8:29:9 | s3 | | +| copyableclass.cpp:24:19:24:20 | call to MyCopyableClass | copyableclass.cpp:25:3:25:4 | s4 | | +| copyableclass.cpp:24:19:24:20 | call to MyCopyableClass | copyableclass.cpp:30:8:30:9 | s4 | | +| copyableclass.cpp:25:3:25:4 | ref arg s4 | copyableclass.cpp:30:8:30:9 | s4 | | +| copyableclass.cpp:25:8:25:8 | 1 | copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | copyableclass.cpp:25:3:25:4 | ref arg s4 | TAINT | +| copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | copyableclass.cpp:25:6:25:6 | call to operator= | TAINT | +| copyableclass.cpp:34:22:34:27 | call to source | copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | copyableclass.cpp:36:22:36:23 | s1 | | +| copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | copyableclass.cpp:40:8:40:9 | s1 | | +| copyableclass.cpp:35:23:35:31 | call to MyCopyableClass | copyableclass.cpp:41:8:41:9 | s2 | | +| copyableclass.cpp:35:24:35:29 | call to source | copyableclass.cpp:35:23:35:31 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:36:22:36:23 | s1 | copyableclass.cpp:36:22:36:24 | call to MyCopyableClass | | +| copyableclass.cpp:36:22:36:24 | call to MyCopyableClass | copyableclass.cpp:42:8:42:9 | s3 | | +| copyableclass.cpp:37:19:37:20 | call to MyCopyableClass | copyableclass.cpp:38:3:38:4 | s4 | | +| copyableclass.cpp:37:19:37:20 | call to MyCopyableClass | copyableclass.cpp:43:8:43:9 | s4 | | +| copyableclass.cpp:38:3:38:4 | ref arg s4 | copyableclass.cpp:43:8:43:9 | s4 | | +| copyableclass.cpp:38:8:38:13 | call to source | copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | copyableclass.cpp:38:3:38:4 | ref arg s4 | TAINT | +| copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | copyableclass.cpp:38:6:38:6 | call to operator= | TAINT | +| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:48:24:48:25 | s1 | | +| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:49:22:49:23 | s1 | | +| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:51:8:51:9 | s1 | | +| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:53:8:53:9 | s1 | | +| copyableclass.cpp:48:23:48:25 | call to MyCopyableClass | copyableclass.cpp:54:8:54:9 | s2 | | +| copyableclass.cpp:48:24:48:25 | s1 | copyableclass.cpp:48:23:48:25 | call to MyCopyableClass | | +| copyableclass.cpp:49:22:49:23 | s1 | copyableclass.cpp:49:22:49:24 | call to MyCopyableClass | | +| copyableclass.cpp:49:22:49:24 | call to MyCopyableClass | copyableclass.cpp:55:8:55:9 | s3 | | +| copyableclass.cpp:50:19:50:20 | call to MyCopyableClass | copyableclass.cpp:51:3:51:4 | s4 | | +| copyableclass.cpp:50:19:50:20 | call to MyCopyableClass | copyableclass.cpp:56:8:56:9 | s4 | | +| copyableclass.cpp:51:3:51:4 | ref arg s4 | copyableclass.cpp:56:8:56:9 | s4 | | +| copyableclass.cpp:51:8:51:9 | s1 | copyableclass.cpp:51:3:51:4 | ref arg s4 | TAINT | +| copyableclass.cpp:51:8:51:9 | s1 | copyableclass.cpp:51:6:51:6 | call to operator= | TAINT | +| copyableclass.cpp:60:23:60:48 | call to MyCopyableClass | copyableclass.cpp:65:8:65:9 | s1 | | +| copyableclass.cpp:60:40:60:45 | call to source | copyableclass.cpp:60:23:60:48 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:63:3:63:4 | s2 | | +| copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | s2 | | +| copyableclass.cpp:62:19:62:20 | call to MyCopyableClass | copyableclass.cpp:67:8:67:9 | s3 | | +| copyableclass.cpp:63:3:63:4 | ref arg s2 | copyableclass.cpp:66:8:66:9 | s2 | | +| copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | copyableclass.cpp:63:3:63:4 | ref arg s2 | TAINT | +| copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | copyableclass.cpp:63:6:63:6 | call to operator= | TAINT | +| copyableclass.cpp:63:24:63:29 | call to source | copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:67:13:67:18 | call to source | copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | TAINT | +| copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | copyableclass.cpp:67:8:67:9 | ref arg s3 | TAINT | +| copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | copyableclass.cpp:67:11:67:11 | call to operator= | TAINT | +| copyableclass_declonly.cpp:21:30:21:30 | 1 | copyableclass_declonly.cpp:21:30:21:31 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:21:30:21:31 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:23:30:23:31 | s1 | | +| copyableclass_declonly.cpp:21:30:21:31 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:27:8:27:9 | s1 | | +| copyableclass_declonly.cpp:22:31:22:32 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:28:8:28:9 | s2 | | +| copyableclass_declonly.cpp:22:32:22:32 | 1 | copyableclass_declonly.cpp:22:31:22:32 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:23:30:23:31 | s1 | copyableclass_declonly.cpp:23:30:23:32 | call to MyCopyableClassDeclOnly | | +| copyableclass_declonly.cpp:23:30:23:32 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:29:8:29:9 | s3 | | +| copyableclass_declonly.cpp:24:27:24:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:25:3:25:4 | s4 | | +| copyableclass_declonly.cpp:24:27:24:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:30:8:30:9 | s4 | | +| copyableclass_declonly.cpp:25:3:25:4 | ref arg s4 | copyableclass_declonly.cpp:30:8:30:9 | s4 | | +| copyableclass_declonly.cpp:25:8:25:8 | 1 | copyableclass_declonly.cpp:25:8:25:8 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:25:8:25:8 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:25:3:25:4 | ref arg s4 | TAINT | +| copyableclass_declonly.cpp:25:8:25:8 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:25:6:25:6 | call to operator= | TAINT | +| copyableclass_declonly.cpp:34:30:34:35 | call to source | copyableclass_declonly.cpp:34:30:34:38 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:34:30:34:38 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:36:30:36:31 | s1 | | +| copyableclass_declonly.cpp:34:30:34:38 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:40:8:40:9 | s1 | | +| copyableclass_declonly.cpp:35:31:35:39 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:41:8:41:9 | s2 | | +| copyableclass_declonly.cpp:35:32:35:37 | call to source | copyableclass_declonly.cpp:35:31:35:39 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:36:30:36:31 | s1 | copyableclass_declonly.cpp:36:30:36:32 | call to MyCopyableClassDeclOnly | | +| copyableclass_declonly.cpp:36:30:36:32 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:42:8:42:9 | s3 | | +| copyableclass_declonly.cpp:37:27:37:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:38:3:38:4 | s4 | | +| copyableclass_declonly.cpp:37:27:37:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:43:8:43:9 | s4 | | +| copyableclass_declonly.cpp:38:3:38:4 | ref arg s4 | copyableclass_declonly.cpp:43:8:43:9 | s4 | | +| copyableclass_declonly.cpp:38:8:38:13 | call to source | copyableclass_declonly.cpp:38:8:38:15 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:38:8:38:15 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:38:3:38:4 | ref arg s4 | TAINT | +| copyableclass_declonly.cpp:38:8:38:15 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:38:6:38:6 | call to operator= | TAINT | +| copyableclass_declonly.cpp:47:27:47:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:48:32:48:33 | s1 | | +| copyableclass_declonly.cpp:47:27:47:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:49:30:49:31 | s1 | | +| copyableclass_declonly.cpp:47:27:47:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:51:8:51:9 | s1 | | +| copyableclass_declonly.cpp:47:27:47:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:53:8:53:9 | s1 | | +| copyableclass_declonly.cpp:48:31:48:33 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:54:8:54:9 | s2 | | +| copyableclass_declonly.cpp:48:32:48:33 | s1 | copyableclass_declonly.cpp:48:31:48:33 | call to MyCopyableClassDeclOnly | | +| copyableclass_declonly.cpp:49:30:49:31 | s1 | copyableclass_declonly.cpp:49:30:49:32 | call to MyCopyableClassDeclOnly | | +| copyableclass_declonly.cpp:49:30:49:32 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:55:8:55:9 | s3 | | +| copyableclass_declonly.cpp:50:27:50:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:51:3:51:4 | s4 | | +| copyableclass_declonly.cpp:50:27:50:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:56:8:56:9 | s4 | | +| copyableclass_declonly.cpp:51:3:51:4 | ref arg s4 | copyableclass_declonly.cpp:56:8:56:9 | s4 | | +| copyableclass_declonly.cpp:51:8:51:9 | s1 | copyableclass_declonly.cpp:51:3:51:4 | ref arg s4 | TAINT | +| copyableclass_declonly.cpp:51:8:51:9 | s1 | copyableclass_declonly.cpp:51:6:51:6 | call to operator= | TAINT | +| copyableclass_declonly.cpp:60:31:60:64 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:65:8:65:9 | s1 | | +| copyableclass_declonly.cpp:60:56:60:61 | call to source | copyableclass_declonly.cpp:60:31:60:64 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:61:27:61:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:63:3:63:4 | s2 | | +| copyableclass_declonly.cpp:61:27:61:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:66:8:66:9 | s2 | | +| copyableclass_declonly.cpp:62:27:62:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:67:8:67:9 | s3 | | +| copyableclass_declonly.cpp:63:3:63:4 | ref arg s2 | copyableclass_declonly.cpp:66:8:66:9 | s2 | | +| copyableclass_declonly.cpp:63:8:63:40 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:63:3:63:4 | ref arg s2 | TAINT | +| copyableclass_declonly.cpp:63:8:63:40 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:63:6:63:6 | call to operator= | TAINT | +| copyableclass_declonly.cpp:63:32:63:37 | call to source | copyableclass_declonly.cpp:63:8:63:40 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:67:13:67:18 | call to source | copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | TAINT | +| copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:67:8:67:9 | ref arg s3 | TAINT | +| copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:67:11:67:11 | call to operator= | TAINT | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | @@ -89,6 +346,8 @@ | format.cpp:113:21:113:24 | {...} | format.cpp:115:8:115:13 | buffer | | | format.cpp:113:23:113:23 | 0 | format.cpp:113:21:113:24 | {...} | TAINT | | format.cpp:114:18:114:23 | ref arg buffer | format.cpp:115:8:115:13 | buffer | | +| format.cpp:114:31:114:34 | %s | format.cpp:114:18:114:23 | ref arg buffer | TAINT | +| format.cpp:114:37:114:50 | call to source | format.cpp:114:18:114:23 | ref arg buffer | TAINT | | format.cpp:119:10:119:11 | 0 | format.cpp:120:29:120:29 | i | | | format.cpp:119:10:119:11 | 0 | format.cpp:121:8:121:8 | i | | | format.cpp:120:28:120:29 | ref arg & ... | format.cpp:120:29:120:29 | i [inner post update] | | @@ -131,63 +390,4921 @@ | format.cpp:158:13:158:18 | call to wcslen | format.cpp:158:13:158:26 | ... / ... | TAINT | | format.cpp:158:13:158:26 | ... / ... | format.cpp:158:7:158:27 | ... + ... | TAINT | | format.cpp:158:26:158:26 | 2 | format.cpp:158:13:158:26 | ... / ... | TAINT | -| stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | | -| stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT | -| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | | -| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:74:7:74:7 | b | | -| stl.cpp:69:16:69:21 | call to source | stl.cpp:69:16:69:24 | call to basic_string | TAINT | -| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:73:7:73:7 | c | | -| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:75:7:75:7 | c | | -| stl.cpp:74:7:74:7 | b | stl.cpp:74:9:74:13 | call to c_str | TAINT | -| stl.cpp:75:7:75:7 | c | stl.cpp:75:9:75:13 | call to c_str | TAINT | -| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:83:2:83:4 | ss1 | | -| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:89:7:89:9 | ss1 | | -| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:94:7:94:9 | ss1 | | -| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:84:2:84:4 | ss2 | | -| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:90:7:90:9 | ss2 | | -| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:95:7:95:9 | ss2 | | -| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:85:2:85:4 | ss3 | | -| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:91:7:91:9 | ss3 | | -| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:96:7:96:9 | ss3 | | -| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:86:2:86:4 | ss4 | | -| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:92:7:92:9 | ss4 | | -| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:97:7:97:9 | ss4 | | -| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:87:2:87:4 | ss5 | | -| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:93:7:93:9 | ss5 | | -| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:98:7:98:9 | ss5 | | -| stl.cpp:81:16:81:21 | call to source | stl.cpp:81:16:81:24 | call to basic_string | TAINT | -| stl.cpp:81:16:81:24 | call to basic_string | stl.cpp:87:9:87:9 | t | | -| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:89:7:89:9 | ss1 | | -| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:94:7:94:9 | ss1 | | -| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:90:7:90:9 | ss2 | | -| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:95:7:95:9 | ss2 | | -| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:91:7:91:9 | ss3 | | -| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:96:7:96:9 | ss3 | | -| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:92:7:92:9 | ss4 | | -| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:97:7:97:9 | ss4 | | -| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:93:7:93:9 | ss5 | | -| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:98:7:98:9 | ss5 | | -| stl.cpp:101:32:101:37 | source | stl.cpp:106:9:106:14 | source | | -| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:105:2:105:4 | ss1 | | -| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:108:7:108:9 | ss1 | | -| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:110:7:110:9 | ss1 | | -| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:106:2:106:4 | ss2 | | -| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:109:7:109:9 | ss2 | | -| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:111:7:111:9 | ss2 | | -| stl.cpp:105:2:105:4 | ref arg ss1 | stl.cpp:108:7:108:9 | ss1 | | -| stl.cpp:105:2:105:4 | ref arg ss1 | stl.cpp:110:7:110:9 | ss1 | | -| stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:109:7:109:9 | ss2 | | -| stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:111:7:111:9 | ss2 | | -| stl.cpp:124:16:124:28 | call to basic_string | stl.cpp:125:7:125:11 | path1 | | -| stl.cpp:124:17:124:26 | call to user_input | stl.cpp:124:16:124:28 | call to basic_string | TAINT | -| stl.cpp:125:7:125:11 | path1 | stl.cpp:125:13:125:17 | call to c_str | TAINT | -| stl.cpp:128:10:128:19 | call to user_input | stl.cpp:128:10:128:21 | call to basic_string | TAINT | -| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:128:2:128:21 | ... = ... | | -| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:129:7:129:11 | path2 | | -| stl.cpp:129:7:129:11 | path2 | stl.cpp:129:13:129:17 | call to c_str | TAINT | -| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT | -| stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | | -| stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT | +| map.cpp:21:28:21:28 | call to pair | map.cpp:23:2:23:2 | a | | +| map.cpp:21:28:21:28 | call to pair | map.cpp:24:7:24:7 | a | | +| map.cpp:21:28:21:28 | call to pair | map.cpp:25:7:25:7 | a | | +| map.cpp:21:28:21:28 | call to pair | map.cpp:26:7:26:7 | a | | +| map.cpp:21:31:21:31 | call to pair | map.cpp:28:2:28:2 | b | | +| map.cpp:21:31:21:31 | call to pair | map.cpp:29:7:29:7 | b | | +| map.cpp:21:31:21:31 | call to pair | map.cpp:30:7:30:7 | b | | +| map.cpp:21:31:21:31 | call to pair | map.cpp:31:7:31:7 | b | | +| map.cpp:21:34:21:34 | call to pair | map.cpp:33:2:33:2 | c | | +| map.cpp:21:34:21:34 | call to pair | map.cpp:34:7:34:7 | c | | +| map.cpp:21:34:21:34 | call to pair | map.cpp:35:7:35:7 | c | | +| map.cpp:21:34:21:34 | call to pair | map.cpp:36:7:36:7 | c | | +| map.cpp:23:2:23:2 | a [post update] | map.cpp:24:7:24:7 | a | | +| map.cpp:23:2:23:2 | a [post update] | map.cpp:25:7:25:7 | a | | +| map.cpp:23:2:23:2 | a [post update] | map.cpp:26:7:26:7 | a | | +| map.cpp:23:2:23:16 | ... = ... | map.cpp:23:4:23:8 | first [post update] | | +| map.cpp:23:2:23:16 | ... = ... | map.cpp:24:9:24:13 | first | | +| map.cpp:23:12:23:16 | 123 | map.cpp:23:2:23:16 | ... = ... | | +| map.cpp:24:7:24:7 | a [post update] | map.cpp:25:7:25:7 | a | | +| map.cpp:24:7:24:7 | a [post update] | map.cpp:26:7:26:7 | a | | +| map.cpp:25:7:25:7 | a [post update] | map.cpp:26:7:26:7 | a | | +| map.cpp:28:2:28:2 | b [post update] | map.cpp:29:7:29:7 | b | | +| map.cpp:28:2:28:2 | b [post update] | map.cpp:30:7:30:7 | b | | +| map.cpp:28:2:28:2 | b [post update] | map.cpp:31:7:31:7 | b | | +| map.cpp:28:2:28:19 | ... = ... | map.cpp:28:4:28:8 | first [post update] | | +| map.cpp:28:2:28:19 | ... = ... | map.cpp:29:9:29:13 | first | | +| map.cpp:28:12:28:17 | call to source | map.cpp:28:2:28:19 | ... = ... | | +| map.cpp:29:7:29:7 | b [post update] | map.cpp:30:7:30:7 | b | | +| map.cpp:29:7:29:7 | b [post update] | map.cpp:31:7:31:7 | b | | +| map.cpp:30:7:30:7 | b [post update] | map.cpp:31:7:31:7 | b | | +| map.cpp:33:2:33:2 | c [post update] | map.cpp:34:7:34:7 | c | | +| map.cpp:33:2:33:2 | c [post update] | map.cpp:35:7:35:7 | c | | +| map.cpp:33:2:33:2 | c [post update] | map.cpp:36:7:36:7 | c | | +| map.cpp:33:2:33:20 | ... = ... | map.cpp:33:4:33:9 | second [post update] | | +| map.cpp:33:2:33:20 | ... = ... | map.cpp:35:9:35:14 | second | | +| map.cpp:33:13:33:18 | call to source | map.cpp:33:2:33:20 | ... = ... | | +| map.cpp:34:7:34:7 | c [post update] | map.cpp:35:7:35:7 | c | | +| map.cpp:34:7:34:7 | c [post update] | map.cpp:36:7:36:7 | c | | +| map.cpp:35:7:35:7 | c [post update] | map.cpp:36:7:36:7 | c | | +| map.cpp:38:30:38:42 | call to pair | map.cpp:39:7:39:7 | d | | +| map.cpp:38:30:38:42 | call to pair | map.cpp:40:7:40:7 | d | | +| map.cpp:38:30:38:42 | call to pair | map.cpp:41:7:41:7 | d | | +| map.cpp:38:37:38:41 | 456 | map.cpp:38:30:38:42 | call to pair | TAINT | +| map.cpp:39:7:39:7 | d [post update] | map.cpp:40:7:40:7 | d | | +| map.cpp:39:7:39:7 | d [post update] | map.cpp:41:7:41:7 | d | | +| map.cpp:40:7:40:7 | d [post update] | map.cpp:41:7:41:7 | d | | +| map.cpp:43:30:43:45 | call to pair | map.cpp:44:7:44:7 | e | | +| map.cpp:43:30:43:45 | call to pair | map.cpp:45:7:45:7 | e | | +| map.cpp:43:30:43:45 | call to pair | map.cpp:46:7:46:7 | e | | +| map.cpp:43:40:43:44 | 456 | map.cpp:43:30:43:45 | call to pair | TAINT | +| map.cpp:44:7:44:7 | e [post update] | map.cpp:45:7:45:7 | e | | +| map.cpp:44:7:44:7 | e [post update] | map.cpp:46:7:46:7 | e | | +| map.cpp:45:7:45:7 | e [post update] | map.cpp:46:7:46:7 | e | | +| map.cpp:48:30:48:45 | call to pair | map.cpp:49:7:49:7 | f | | +| map.cpp:48:30:48:45 | call to pair | map.cpp:50:7:50:7 | f | | +| map.cpp:48:30:48:45 | call to pair | map.cpp:51:7:51:7 | f | | +| map.cpp:48:30:48:45 | call to pair | map.cpp:53:30:53:30 | f | | +| map.cpp:48:30:48:45 | call to pair | map.cpp:59:6:59:6 | f | | +| map.cpp:48:37:48:42 | call to source | map.cpp:48:30:48:45 | call to pair | TAINT | +| map.cpp:49:7:49:7 | f [post update] | map.cpp:50:7:50:7 | f | | +| map.cpp:49:7:49:7 | f [post update] | map.cpp:51:7:51:7 | f | | +| map.cpp:49:7:49:7 | f [post update] | map.cpp:53:30:53:30 | f | | +| map.cpp:49:7:49:7 | f [post update] | map.cpp:59:6:59:6 | f | | +| map.cpp:50:7:50:7 | f [post update] | map.cpp:51:7:51:7 | f | | +| map.cpp:50:7:50:7 | f [post update] | map.cpp:53:30:53:30 | f | | +| map.cpp:50:7:50:7 | f [post update] | map.cpp:59:6:59:6 | f | | +| map.cpp:53:30:53:30 | f | map.cpp:54:7:54:7 | g | | +| map.cpp:53:30:53:30 | f | map.cpp:55:7:55:7 | g | | +| map.cpp:53:30:53:30 | f | map.cpp:56:7:56:7 | g | | +| map.cpp:54:7:54:7 | g [post update] | map.cpp:55:7:55:7 | g | | +| map.cpp:54:7:54:7 | g [post update] | map.cpp:56:7:56:7 | g | | +| map.cpp:55:7:55:7 | g [post update] | map.cpp:56:7:56:7 | g | | +| map.cpp:59:6:59:6 | f | map.cpp:59:2:59:6 | ... = ... | | +| map.cpp:59:6:59:6 | f | map.cpp:60:7:60:7 | h | | +| map.cpp:59:6:59:6 | f | map.cpp:61:7:61:7 | h | | +| map.cpp:59:6:59:6 | f | map.cpp:62:7:62:7 | h | | +| map.cpp:60:7:60:7 | h [post update] | map.cpp:61:7:61:7 | h | | +| map.cpp:60:7:60:7 | h [post update] | map.cpp:62:7:62:7 | h | | +| map.cpp:61:7:61:7 | h [post update] | map.cpp:62:7:62:7 | h | | +| map.cpp:64:30:64:42 | call to pair | map.cpp:68:3:68:3 | i | | +| map.cpp:64:30:64:42 | call to pair | map.cpp:70:7:70:7 | i | | +| map.cpp:64:30:64:42 | call to pair | map.cpp:71:7:71:7 | i | | +| map.cpp:64:30:64:42 | call to pair | map.cpp:72:7:72:7 | i | | +| map.cpp:64:37:64:41 | 456 | map.cpp:64:30:64:42 | call to pair | TAINT | +| map.cpp:65:30:65:45 | call to pair | map.cpp:68:10:68:10 | j | | +| map.cpp:65:30:65:45 | call to pair | map.cpp:73:7:73:7 | j | | +| map.cpp:65:30:65:45 | call to pair | map.cpp:74:7:74:7 | j | | +| map.cpp:65:30:65:45 | call to pair | map.cpp:75:7:75:7 | j | | +| map.cpp:65:37:65:42 | call to source | map.cpp:65:30:65:45 | call to pair | TAINT | +| map.cpp:66:30:66:45 | call to pair | map.cpp:69:2:69:2 | k | | +| map.cpp:66:30:66:45 | call to pair | map.cpp:76:7:76:7 | k | | +| map.cpp:66:30:66:45 | call to pair | map.cpp:77:7:77:7 | k | | +| map.cpp:66:30:66:45 | call to pair | map.cpp:78:7:78:7 | k | | +| map.cpp:66:37:66:42 | call to source | map.cpp:66:30:66:45 | call to pair | TAINT | +| map.cpp:67:30:67:42 | call to pair | map.cpp:69:9:69:9 | l | | +| map.cpp:67:30:67:42 | call to pair | map.cpp:79:7:79:7 | l | | +| map.cpp:67:30:67:42 | call to pair | map.cpp:80:7:80:7 | l | | +| map.cpp:67:30:67:42 | call to pair | map.cpp:81:7:81:7 | l | | +| map.cpp:67:37:67:41 | 456 | map.cpp:67:30:67:42 | call to pair | TAINT | +| map.cpp:68:3:68:3 | i | map.cpp:68:10:68:10 | ref arg j | TAINT | +| map.cpp:68:3:68:3 | ref arg i | map.cpp:70:7:70:7 | i | | +| map.cpp:68:3:68:3 | ref arg i | map.cpp:71:7:71:7 | i | | +| map.cpp:68:3:68:3 | ref arg i | map.cpp:72:7:72:7 | i | | +| map.cpp:68:10:68:10 | j | map.cpp:68:3:68:3 | ref arg i | TAINT | +| map.cpp:68:10:68:10 | ref arg j | map.cpp:73:7:73:7 | j | | +| map.cpp:68:10:68:10 | ref arg j | map.cpp:74:7:74:7 | j | | +| map.cpp:68:10:68:10 | ref arg j | map.cpp:75:7:75:7 | j | | +| map.cpp:69:2:69:2 | k | map.cpp:69:9:69:9 | ref arg l | TAINT | +| map.cpp:69:2:69:2 | ref arg k | map.cpp:76:7:76:7 | k | | +| map.cpp:69:2:69:2 | ref arg k | map.cpp:77:7:77:7 | k | | +| map.cpp:69:2:69:2 | ref arg k | map.cpp:78:7:78:7 | k | | +| map.cpp:69:9:69:9 | l | map.cpp:69:2:69:2 | ref arg k | TAINT | +| map.cpp:69:9:69:9 | ref arg l | map.cpp:79:7:79:7 | l | | +| map.cpp:69:9:69:9 | ref arg l | map.cpp:80:7:80:7 | l | | +| map.cpp:69:9:69:9 | ref arg l | map.cpp:81:7:81:7 | l | | +| map.cpp:70:7:70:7 | i [post update] | map.cpp:71:7:71:7 | i | | +| map.cpp:70:7:70:7 | i [post update] | map.cpp:72:7:72:7 | i | | +| map.cpp:71:7:71:7 | i [post update] | map.cpp:72:7:72:7 | i | | +| map.cpp:73:7:73:7 | j [post update] | map.cpp:74:7:74:7 | j | | +| map.cpp:73:7:73:7 | j [post update] | map.cpp:75:7:75:7 | j | | +| map.cpp:74:7:74:7 | j [post update] | map.cpp:75:7:75:7 | j | | +| map.cpp:76:7:76:7 | k [post update] | map.cpp:77:7:77:7 | k | | +| map.cpp:76:7:76:7 | k [post update] | map.cpp:78:7:78:7 | k | | +| map.cpp:77:7:77:7 | k [post update] | map.cpp:78:7:78:7 | k | | +| map.cpp:79:7:79:7 | l [post update] | map.cpp:80:7:80:7 | l | | +| map.cpp:79:7:79:7 | l [post update] | map.cpp:81:7:81:7 | l | | +| map.cpp:80:7:80:7 | l [post update] | map.cpp:81:7:81:7 | l | | +| map.cpp:83:7:83:15 | call to make_pair | map.cpp:83:7:83:29 | call to pair | TAINT | +| map.cpp:86:7:86:15 | call to make_pair | map.cpp:86:7:86:32 | call to pair | TAINT | +| map.cpp:89:7:89:15 | call to make_pair | map.cpp:89:7:89:32 | call to pair | TAINT | +| map.cpp:94:6:94:14 | call to make_pair | map.cpp:94:6:94:49 | call to pair | TAINT | +| map.cpp:94:6:94:49 | call to pair | map.cpp:94:2:94:49 | ... = ... | | +| map.cpp:94:6:94:49 | call to pair | map.cpp:95:7:95:7 | m | | +| map.cpp:94:6:94:49 | call to pair | map.cpp:96:7:96:7 | m | | +| map.cpp:94:6:94:49 | call to pair | map.cpp:97:7:97:7 | m | | +| map.cpp:94:6:94:49 | call to pair | map.cpp:98:7:98:7 | m | | +| map.cpp:94:6:94:49 | call to pair | map.cpp:99:7:99:7 | m | | +| map.cpp:95:7:95:7 | m | map.cpp:95:7:95:7 | call to pair | TAINT | +| map.cpp:97:7:97:7 | m [post update] | map.cpp:98:7:98:7 | m | | +| map.cpp:97:7:97:7 | m [post update] | map.cpp:99:7:99:7 | m | | +| map.cpp:98:7:98:7 | m [post update] | map.cpp:99:7:99:7 | m | | +| map.cpp:105:27:105:28 | call to map | map.cpp:107:7:107:8 | m1 | | +| map.cpp:105:27:105:28 | call to map | map.cpp:113:7:113:8 | m1 | | +| map.cpp:105:27:105:28 | call to map | map.cpp:119:7:119:8 | m1 | | +| map.cpp:105:27:105:28 | call to map | map.cpp:125:7:125:8 | m1 | | +| map.cpp:105:27:105:28 | call to map | map.cpp:146:12:146:13 | m1 | | +| map.cpp:105:27:105:28 | call to map | map.cpp:146:30:146:31 | m1 | | +| map.cpp:105:27:105:28 | call to map | map.cpp:252:1:252:1 | m1 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:108:7:108:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:114:7:114:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:120:7:120:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:126:7:126:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:133:30:133:31 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:134:32:134:33 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:136:7:136:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:152:12:152:13 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:152:30:152:31 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:182:7:182:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:183:7:183:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:184:7:184:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:185:7:185:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:186:7:186:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:187:7:187:8 | m2 | | +| map.cpp:105:31:105:32 | call to map | map.cpp:252:1:252:1 | m2 | | +| map.cpp:105:35:105:36 | call to map | map.cpp:109:7:109:8 | m3 | | +| map.cpp:105:35:105:36 | call to map | map.cpp:115:7:115:8 | m3 | | +| map.cpp:105:35:105:36 | call to map | map.cpp:121:7:121:8 | m3 | | +| map.cpp:105:35:105:36 | call to map | map.cpp:127:7:127:8 | m3 | | +| map.cpp:105:35:105:36 | call to map | map.cpp:158:12:158:13 | m3 | | +| map.cpp:105:35:105:36 | call to map | map.cpp:158:30:158:31 | m3 | | +| map.cpp:105:35:105:36 | call to map | map.cpp:252:1:252:1 | m3 | | +| map.cpp:105:39:105:40 | call to map | map.cpp:110:7:110:8 | m4 | | +| map.cpp:105:39:105:40 | call to map | map.cpp:110:17:110:18 | m4 | | +| map.cpp:105:39:105:40 | call to map | map.cpp:116:7:116:8 | m4 | | +| map.cpp:105:39:105:40 | call to map | map.cpp:122:7:122:8 | m4 | | +| map.cpp:105:39:105:40 | call to map | map.cpp:128:7:128:8 | m4 | | +| map.cpp:105:39:105:40 | call to map | map.cpp:252:1:252:1 | m4 | | +| map.cpp:105:43:105:44 | call to map | map.cpp:111:7:111:8 | m5 | | +| map.cpp:105:43:105:44 | call to map | map.cpp:117:7:117:8 | m5 | | +| map.cpp:105:43:105:44 | call to map | map.cpp:123:7:123:8 | m5 | | +| map.cpp:105:43:105:44 | call to map | map.cpp:129:7:129:8 | m5 | | +| map.cpp:105:43:105:44 | call to map | map.cpp:252:1:252:1 | m5 | | +| map.cpp:105:47:105:48 | call to map | map.cpp:112:7:112:8 | m6 | | +| map.cpp:105:47:105:48 | call to map | map.cpp:112:27:112:28 | m6 | | +| map.cpp:105:47:105:48 | call to map | map.cpp:118:7:118:8 | m6 | | +| map.cpp:105:47:105:48 | call to map | map.cpp:124:7:124:8 | m6 | | +| map.cpp:105:47:105:48 | call to map | map.cpp:130:7:130:8 | m6 | | +| map.cpp:105:47:105:48 | call to map | map.cpp:252:1:252:1 | m6 | | +| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:113:7:113:8 | m1 | | +| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:119:7:119:8 | m1 | | +| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:125:7:125:8 | m1 | | +| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:146:12:146:13 | m1 | | +| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:146:30:146:31 | m1 | | +| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:252:1:252:1 | m1 | | +| map.cpp:107:17:107:30 | call to make_pair | map.cpp:107:17:107:44 | call to pair | TAINT | +| map.cpp:107:17:107:44 | call to pair | map.cpp:107:7:107:8 | ref arg m1 | TAINT | +| map.cpp:107:17:107:44 | call to pair | map.cpp:107:10:107:15 | call to insert | TAINT | +| map.cpp:107:47:107:51 | first | map.cpp:107:7:107:51 | call to iterator | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:114:7:114:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:120:7:120:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:126:7:126:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:133:30:133:31 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:134:32:134:33 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:108:17:108:30 | call to make_pair | map.cpp:108:17:108:47 | call to pair | TAINT | +| map.cpp:108:17:108:47 | call to pair | map.cpp:108:7:108:8 | ref arg m2 | TAINT | +| map.cpp:108:17:108:47 | call to pair | map.cpp:108:10:108:15 | call to insert | TAINT | +| map.cpp:108:50:108:54 | first | map.cpp:108:7:108:54 | call to iterator | | +| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:115:7:115:8 | m3 | | +| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:121:7:121:8 | m3 | | +| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:127:7:127:8 | m3 | | +| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:158:12:158:13 | m3 | | +| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:158:30:158:31 | m3 | | +| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:252:1:252:1 | m3 | | +| map.cpp:109:17:109:30 | call to make_pair | map.cpp:109:17:109:47 | call to pair | TAINT | +| map.cpp:109:17:109:47 | call to pair | map.cpp:109:7:109:8 | ref arg m3 | TAINT | +| map.cpp:109:17:109:47 | call to pair | map.cpp:109:10:109:15 | call to insert | TAINT | +| map.cpp:109:50:109:54 | first | map.cpp:109:7:109:54 | call to iterator | | +| map.cpp:110:7:110:8 | ref arg m4 | map.cpp:116:7:116:8 | m4 | | +| map.cpp:110:7:110:8 | ref arg m4 | map.cpp:122:7:122:8 | m4 | | +| map.cpp:110:7:110:8 | ref arg m4 | map.cpp:128:7:128:8 | m4 | | +| map.cpp:110:7:110:8 | ref arg m4 | map.cpp:252:1:252:1 | m4 | | +| map.cpp:110:17:110:18 | m4 | map.cpp:110:20:110:24 | call to begin | TAINT | +| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:110:7:110:8 | m4 | | +| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:116:7:116:8 | m4 | | +| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:122:7:122:8 | m4 | | +| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:128:7:128:8 | m4 | | +| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:252:1:252:1 | m4 | | +| map.cpp:110:20:110:24 | call to begin | map.cpp:110:17:110:26 | call to iterator | TAINT | +| map.cpp:110:29:110:70 | call to pair | map.cpp:110:7:110:8 | ref arg m4 | TAINT | +| map.cpp:110:29:110:70 | call to pair | map.cpp:110:10:110:15 | call to insert | TAINT | +| map.cpp:110:29:110:70 | call to pair | map.cpp:110:29:110:70 | call to pair | TAINT | +| map.cpp:110:62:110:67 | call to source | map.cpp:110:29:110:70 | call to pair | TAINT | +| map.cpp:111:7:111:8 | ref arg m5 | map.cpp:117:7:117:8 | m5 | | +| map.cpp:111:7:111:8 | ref arg m5 | map.cpp:123:7:123:8 | m5 | | +| map.cpp:111:7:111:8 | ref arg m5 | map.cpp:129:7:129:8 | m5 | | +| map.cpp:111:7:111:8 | ref arg m5 | map.cpp:252:1:252:1 | m5 | | +| map.cpp:111:34:111:39 | call to source | map.cpp:111:7:111:8 | ref arg m5 | TAINT | +| map.cpp:111:34:111:39 | call to source | map.cpp:111:10:111:25 | call to insert_or_assign | TAINT | +| map.cpp:111:44:111:48 | first | map.cpp:111:7:111:48 | call to iterator | | +| map.cpp:112:7:112:8 | ref arg m6 | map.cpp:118:7:118:8 | m6 | | +| map.cpp:112:7:112:8 | ref arg m6 | map.cpp:124:7:124:8 | m6 | | +| map.cpp:112:7:112:8 | ref arg m6 | map.cpp:130:7:130:8 | m6 | | +| map.cpp:112:7:112:8 | ref arg m6 | map.cpp:252:1:252:1 | m6 | | +| map.cpp:112:27:112:28 | m6 | map.cpp:112:30:112:34 | call to begin | TAINT | +| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:112:7:112:8 | m6 | | +| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:118:7:118:8 | m6 | | +| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:124:7:124:8 | m6 | | +| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:130:7:130:8 | m6 | | +| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:252:1:252:1 | m6 | | +| map.cpp:112:30:112:34 | call to begin | map.cpp:112:27:112:36 | call to iterator | TAINT | +| map.cpp:112:46:112:51 | call to source | map.cpp:112:7:112:8 | ref arg m6 | TAINT | +| map.cpp:112:46:112:51 | call to source | map.cpp:112:10:112:25 | call to insert_or_assign | TAINT | +| map.cpp:113:7:113:8 | m1 | map.cpp:113:7:113:8 | call to map | | +| map.cpp:114:7:114:8 | m2 | map.cpp:114:7:114:8 | call to map | | +| map.cpp:115:7:115:8 | m3 | map.cpp:115:7:115:8 | call to map | | +| map.cpp:116:7:116:8 | m4 | map.cpp:116:7:116:8 | call to map | | +| map.cpp:117:7:117:8 | m5 | map.cpp:117:7:117:8 | call to map | | +| map.cpp:118:7:118:8 | m6 | map.cpp:118:7:118:8 | call to map | | +| map.cpp:119:7:119:8 | m1 | map.cpp:119:10:119:13 | call to find | TAINT | +| map.cpp:119:7:119:8 | ref arg m1 | map.cpp:125:7:125:8 | m1 | | +| map.cpp:119:7:119:8 | ref arg m1 | map.cpp:146:12:146:13 | m1 | | +| map.cpp:119:7:119:8 | ref arg m1 | map.cpp:146:30:146:31 | m1 | | +| map.cpp:119:7:119:8 | ref arg m1 | map.cpp:252:1:252:1 | m1 | | +| map.cpp:120:7:120:8 | m2 | map.cpp:120:10:120:13 | call to find | TAINT | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:126:7:126:8 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:133:30:133:31 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:134:32:134:33 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:121:7:121:8 | m3 | map.cpp:121:10:121:13 | call to find | TAINT | +| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:127:7:127:8 | m3 | | +| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:158:12:158:13 | m3 | | +| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:158:30:158:31 | m3 | | +| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:252:1:252:1 | m3 | | +| map.cpp:122:7:122:8 | m4 | map.cpp:122:10:122:13 | call to find | TAINT | +| map.cpp:122:7:122:8 | ref arg m4 | map.cpp:128:7:128:8 | m4 | | +| map.cpp:122:7:122:8 | ref arg m4 | map.cpp:252:1:252:1 | m4 | | +| map.cpp:123:7:123:8 | m5 | map.cpp:123:10:123:13 | call to find | TAINT | +| map.cpp:123:7:123:8 | ref arg m5 | map.cpp:129:7:129:8 | m5 | | +| map.cpp:123:7:123:8 | ref arg m5 | map.cpp:252:1:252:1 | m5 | | +| map.cpp:124:7:124:8 | m6 | map.cpp:124:10:124:13 | call to find | TAINT | +| map.cpp:124:7:124:8 | ref arg m6 | map.cpp:130:7:130:8 | m6 | | +| map.cpp:124:7:124:8 | ref arg m6 | map.cpp:252:1:252:1 | m6 | | +| map.cpp:125:7:125:8 | m1 | map.cpp:125:10:125:13 | call to find | TAINT | +| map.cpp:125:7:125:8 | ref arg m1 | map.cpp:146:12:146:13 | m1 | | +| map.cpp:125:7:125:8 | ref arg m1 | map.cpp:146:30:146:31 | m1 | | +| map.cpp:125:7:125:8 | ref arg m1 | map.cpp:252:1:252:1 | m1 | | +| map.cpp:126:7:126:8 | m2 | map.cpp:126:10:126:13 | call to find | TAINT | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:133:30:133:31 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:134:32:134:33 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:127:7:127:8 | m3 | map.cpp:127:10:127:13 | call to find | TAINT | +| map.cpp:127:7:127:8 | ref arg m3 | map.cpp:158:12:158:13 | m3 | | +| map.cpp:127:7:127:8 | ref arg m3 | map.cpp:158:30:158:31 | m3 | | +| map.cpp:127:7:127:8 | ref arg m3 | map.cpp:252:1:252:1 | m3 | | +| map.cpp:128:7:128:8 | m4 | map.cpp:128:10:128:13 | call to find | TAINT | +| map.cpp:128:7:128:8 | ref arg m4 | map.cpp:252:1:252:1 | m4 | | +| map.cpp:129:7:129:8 | m5 | map.cpp:129:10:129:13 | call to find | TAINT | +| map.cpp:129:7:129:8 | ref arg m5 | map.cpp:252:1:252:1 | m5 | | +| map.cpp:130:7:130:8 | m6 | map.cpp:130:10:130:13 | call to find | TAINT | +| map.cpp:130:7:130:8 | ref arg m6 | map.cpp:252:1:252:1 | m6 | | +| map.cpp:133:30:133:31 | m2 | map.cpp:133:30:133:32 | call to map | | +| map.cpp:133:30:133:32 | call to map | map.cpp:137:7:137:8 | m7 | | +| map.cpp:133:30:133:32 | call to map | map.cpp:140:7:140:8 | m7 | | +| map.cpp:133:30:133:32 | call to map | map.cpp:252:1:252:1 | m7 | | +| map.cpp:134:31:134:33 | call to map | map.cpp:138:7:138:8 | m8 | | +| map.cpp:134:31:134:33 | call to map | map.cpp:141:7:141:8 | m8 | | +| map.cpp:134:31:134:33 | call to map | map.cpp:252:1:252:1 | m8 | | +| map.cpp:134:32:134:33 | m2 | map.cpp:134:31:134:33 | call to map | | +| map.cpp:135:27:135:28 | call to map | map.cpp:136:2:136:3 | m9 | | +| map.cpp:135:27:135:28 | call to map | map.cpp:139:7:139:8 | m9 | | +| map.cpp:135:27:135:28 | call to map | map.cpp:142:7:142:8 | m9 | | +| map.cpp:135:27:135:28 | call to map | map.cpp:252:1:252:1 | m9 | | +| map.cpp:136:2:136:3 | ref arg m9 | map.cpp:139:7:139:8 | m9 | | +| map.cpp:136:2:136:3 | ref arg m9 | map.cpp:142:7:142:8 | m9 | | +| map.cpp:136:2:136:3 | ref arg m9 | map.cpp:252:1:252:1 | m9 | | +| map.cpp:136:7:136:8 | m2 | map.cpp:136:2:136:3 | ref arg m9 | TAINT | +| map.cpp:136:7:136:8 | m2 | map.cpp:136:5:136:5 | call to operator= | TAINT | +| map.cpp:137:7:137:8 | m7 | map.cpp:137:7:137:8 | call to map | | +| map.cpp:138:7:138:8 | m8 | map.cpp:138:7:138:8 | call to map | | +| map.cpp:139:7:139:8 | m9 | map.cpp:139:7:139:8 | call to map | | +| map.cpp:140:7:140:8 | m7 | map.cpp:140:10:140:13 | call to find | TAINT | +| map.cpp:140:7:140:8 | ref arg m7 | map.cpp:252:1:252:1 | m7 | | +| map.cpp:141:7:141:8 | m8 | map.cpp:141:10:141:13 | call to find | TAINT | +| map.cpp:141:7:141:8 | ref arg m8 | map.cpp:252:1:252:1 | m8 | | +| map.cpp:142:7:142:8 | m9 | map.cpp:142:10:142:13 | call to find | TAINT | +| map.cpp:142:7:142:8 | ref arg m9 | map.cpp:252:1:252:1 | m9 | | +| map.cpp:146:12:146:13 | m1 | map.cpp:146:15:146:19 | call to begin | TAINT | +| map.cpp:146:12:146:13 | ref arg m1 | map.cpp:146:30:146:31 | m1 | | +| map.cpp:146:12:146:13 | ref arg m1 | map.cpp:252:1:252:1 | m1 | | +| map.cpp:146:15:146:19 | call to begin | map.cpp:146:7:146:21 | ... = ... | | +| map.cpp:146:15:146:19 | call to begin | map.cpp:146:24:146:25 | i1 | | +| map.cpp:146:15:146:19 | call to begin | map.cpp:146:40:146:41 | i1 | | +| map.cpp:146:15:146:19 | call to begin | map.cpp:148:9:148:10 | i1 | | +| map.cpp:146:15:146:19 | call to begin | map.cpp:149:8:149:9 | i1 | | +| map.cpp:146:15:146:19 | call to begin | map.cpp:150:8:150:9 | i1 | | +| map.cpp:146:30:146:31 | m1 | map.cpp:146:33:146:35 | call to end | TAINT | +| map.cpp:146:30:146:31 | ref arg m1 | map.cpp:146:30:146:31 | m1 | | +| map.cpp:146:30:146:31 | ref arg m1 | map.cpp:252:1:252:1 | m1 | | +| map.cpp:146:40:146:41 | i1 | map.cpp:146:42:146:42 | call to operator++ | | +| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:146:24:146:25 | i1 | | +| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:146:40:146:41 | i1 | | +| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:148:9:148:10 | i1 | | +| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:149:8:149:9 | i1 | | +| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:150:8:150:9 | i1 | | +| map.cpp:148:8:148:8 | call to operator* | map.cpp:148:8:148:10 | call to pair | TAINT | +| map.cpp:148:9:148:10 | i1 | map.cpp:148:8:148:8 | call to operator* | TAINT | +| map.cpp:149:8:149:9 | i1 | map.cpp:149:10:149:10 | call to operator-> | TAINT | +| map.cpp:150:8:150:9 | i1 | map.cpp:150:10:150:10 | call to operator-> | TAINT | +| map.cpp:152:12:152:13 | m2 | map.cpp:152:15:152:19 | call to begin | TAINT | +| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:152:30:152:31 | m2 | | +| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:182:7:182:8 | m2 | | +| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:183:7:183:8 | m2 | | +| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:184:7:184:8 | m2 | | +| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:185:7:185:8 | m2 | | +| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:152:15:152:19 | call to begin | map.cpp:152:7:152:21 | ... = ... | | +| map.cpp:152:15:152:19 | call to begin | map.cpp:152:24:152:25 | i2 | | +| map.cpp:152:15:152:19 | call to begin | map.cpp:152:40:152:41 | i2 | | +| map.cpp:152:15:152:19 | call to begin | map.cpp:154:9:154:10 | i2 | | +| map.cpp:152:15:152:19 | call to begin | map.cpp:155:8:155:9 | i2 | | +| map.cpp:152:15:152:19 | call to begin | map.cpp:156:8:156:9 | i2 | | +| map.cpp:152:15:152:19 | call to begin | map.cpp:161:8:161:9 | i2 | | +| map.cpp:152:15:152:19 | call to begin | map.cpp:162:8:162:9 | i2 | | +| map.cpp:152:30:152:31 | m2 | map.cpp:152:33:152:35 | call to end | TAINT | +| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:152:30:152:31 | m2 | | +| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:182:7:182:8 | m2 | | +| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:183:7:183:8 | m2 | | +| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:184:7:184:8 | m2 | | +| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:185:7:185:8 | m2 | | +| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:152:40:152:41 | i2 | map.cpp:152:42:152:42 | call to operator++ | | +| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:152:24:152:25 | i2 | | +| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:152:40:152:41 | i2 | | +| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:154:9:154:10 | i2 | | +| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:155:8:155:9 | i2 | | +| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:156:8:156:9 | i2 | | +| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:161:8:161:9 | i2 | | +| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:162:8:162:9 | i2 | | +| map.cpp:154:8:154:8 | call to operator* | map.cpp:154:8:154:10 | call to pair | TAINT | +| map.cpp:154:9:154:10 | i2 | map.cpp:154:8:154:8 | call to operator* | TAINT | +| map.cpp:155:8:155:9 | i2 | map.cpp:155:10:155:10 | call to operator-> | TAINT | +| map.cpp:156:8:156:9 | i2 | map.cpp:156:10:156:10 | call to operator-> | TAINT | +| map.cpp:158:12:158:13 | m3 | map.cpp:158:15:158:19 | call to begin | TAINT | +| map.cpp:158:12:158:13 | ref arg m3 | map.cpp:158:30:158:31 | m3 | | +| map.cpp:158:12:158:13 | ref arg m3 | map.cpp:252:1:252:1 | m3 | | +| map.cpp:158:15:158:19 | call to begin | map.cpp:158:7:158:21 | ... = ... | | +| map.cpp:158:15:158:19 | call to begin | map.cpp:158:24:158:25 | i3 | | +| map.cpp:158:15:158:19 | call to begin | map.cpp:158:40:158:41 | i3 | | +| map.cpp:158:15:158:19 | call to begin | map.cpp:160:9:160:10 | i3 | | +| map.cpp:158:30:158:31 | m3 | map.cpp:158:33:158:35 | call to end | TAINT | +| map.cpp:158:30:158:31 | ref arg m3 | map.cpp:158:30:158:31 | m3 | | +| map.cpp:158:30:158:31 | ref arg m3 | map.cpp:252:1:252:1 | m3 | | +| map.cpp:158:40:158:41 | i3 | map.cpp:158:42:158:42 | call to operator++ | | +| map.cpp:158:40:158:41 | ref arg i3 | map.cpp:158:24:158:25 | i3 | | +| map.cpp:158:40:158:41 | ref arg i3 | map.cpp:158:40:158:41 | i3 | | +| map.cpp:158:40:158:41 | ref arg i3 | map.cpp:160:9:160:10 | i3 | | +| map.cpp:160:8:160:8 | call to operator* | map.cpp:160:8:160:10 | call to pair | TAINT | +| map.cpp:160:9:160:10 | i3 | map.cpp:160:8:160:8 | call to operator* | TAINT | +| map.cpp:161:8:161:9 | i2 | map.cpp:161:10:161:10 | call to operator-> | TAINT | +| map.cpp:162:8:162:9 | i2 | map.cpp:162:10:162:10 | call to operator-> | TAINT | +| map.cpp:166:27:166:29 | call to map | map.cpp:167:7:167:9 | m10 | | +| map.cpp:166:27:166:29 | call to map | map.cpp:171:7:171:9 | m10 | | +| map.cpp:166:27:166:29 | call to map | map.cpp:252:1:252:1 | m10 | | +| map.cpp:166:32:166:34 | call to map | map.cpp:168:7:168:9 | m11 | | +| map.cpp:166:32:166:34 | call to map | map.cpp:172:7:172:9 | m11 | | +| map.cpp:166:32:166:34 | call to map | map.cpp:252:1:252:1 | m11 | | +| map.cpp:166:37:166:39 | call to map | map.cpp:169:7:169:9 | m12 | | +| map.cpp:166:37:166:39 | call to map | map.cpp:173:7:173:9 | m12 | | +| map.cpp:166:37:166:39 | call to map | map.cpp:252:1:252:1 | m12 | | +| map.cpp:166:42:166:44 | call to map | map.cpp:170:7:170:9 | m13 | | +| map.cpp:166:42:166:44 | call to map | map.cpp:174:7:174:9 | m13 | | +| map.cpp:166:42:166:44 | call to map | map.cpp:252:1:252:1 | m13 | | +| map.cpp:167:7:167:9 | m10 | map.cpp:167:10:167:10 | call to operator[] | TAINT | +| map.cpp:167:7:167:9 | ref arg m10 | map.cpp:171:7:171:9 | m10 | | +| map.cpp:167:7:167:9 | ref arg m10 | map.cpp:252:1:252:1 | m10 | | +| map.cpp:167:7:167:24 | ... = ... | map.cpp:167:10:167:10 | call to operator[] [post update] | | +| map.cpp:167:10:167:10 | call to operator[] [post update] | map.cpp:167:7:167:9 | ref arg m10 | TAINT | +| map.cpp:167:20:167:24 | def | map.cpp:167:7:167:24 | ... = ... | | +| map.cpp:168:7:168:9 | m11 | map.cpp:168:10:168:10 | call to operator[] | TAINT | +| map.cpp:168:7:168:9 | ref arg m11 | map.cpp:172:7:172:9 | m11 | | +| map.cpp:168:7:168:9 | ref arg m11 | map.cpp:252:1:252:1 | m11 | | +| map.cpp:168:7:168:27 | ... = ... | map.cpp:168:10:168:10 | call to operator[] [post update] | | +| map.cpp:168:10:168:10 | call to operator[] [post update] | map.cpp:168:7:168:9 | ref arg m11 | TAINT | +| map.cpp:168:20:168:25 | call to source | map.cpp:168:7:168:27 | ... = ... | | +| map.cpp:169:7:169:9 | m12 | map.cpp:169:11:169:12 | call to at | TAINT | +| map.cpp:169:7:169:9 | ref arg m12 | map.cpp:173:7:173:9 | m12 | | +| map.cpp:169:7:169:9 | ref arg m12 | map.cpp:252:1:252:1 | m12 | | +| map.cpp:169:7:169:27 | ... = ... | map.cpp:169:11:169:12 | call to at [post update] | | +| map.cpp:169:11:169:12 | call to at [post update] | map.cpp:169:7:169:9 | ref arg m12 | TAINT | +| map.cpp:169:23:169:27 | def | map.cpp:169:7:169:27 | ... = ... | | +| map.cpp:170:7:170:9 | m13 | map.cpp:170:11:170:12 | call to at | TAINT | +| map.cpp:170:7:170:9 | ref arg m13 | map.cpp:174:7:174:9 | m13 | | +| map.cpp:170:7:170:9 | ref arg m13 | map.cpp:252:1:252:1 | m13 | | +| map.cpp:170:7:170:30 | ... = ... | map.cpp:170:11:170:12 | call to at [post update] | | +| map.cpp:170:11:170:12 | call to at [post update] | map.cpp:170:7:170:9 | ref arg m13 | TAINT | +| map.cpp:170:23:170:28 | call to source | map.cpp:170:7:170:30 | ... = ... | | +| map.cpp:171:7:171:9 | m10 | map.cpp:171:10:171:10 | call to operator[] | TAINT | +| map.cpp:171:7:171:9 | ref arg m10 | map.cpp:252:1:252:1 | m10 | | +| map.cpp:172:7:172:9 | m11 | map.cpp:172:10:172:10 | call to operator[] | TAINT | +| map.cpp:172:7:172:9 | ref arg m11 | map.cpp:252:1:252:1 | m11 | | +| map.cpp:173:7:173:9 | m12 | map.cpp:173:10:173:10 | call to operator[] | TAINT | +| map.cpp:173:7:173:9 | ref arg m12 | map.cpp:252:1:252:1 | m12 | | +| map.cpp:174:7:174:9 | m13 | map.cpp:174:10:174:10 | call to operator[] | TAINT | +| map.cpp:174:7:174:9 | ref arg m13 | map.cpp:252:1:252:1 | m13 | | +| map.cpp:177:27:177:29 | call to map | map.cpp:178:2:178:4 | m14 | | +| map.cpp:177:27:177:29 | call to map | map.cpp:179:2:179:4 | m14 | | +| map.cpp:177:27:177:29 | call to map | map.cpp:180:2:180:4 | m14 | | +| map.cpp:177:27:177:29 | call to map | map.cpp:181:2:181:4 | m14 | | +| map.cpp:177:27:177:29 | call to map | map.cpp:252:1:252:1 | m14 | | +| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:179:2:179:4 | m14 | | +| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:180:2:180:4 | m14 | | +| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | | +| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | | +| map.cpp:178:13:178:26 | call to make_pair | map.cpp:178:13:178:36 | call to pair | TAINT | +| map.cpp:178:13:178:36 | call to pair | map.cpp:178:2:178:4 | ref arg m14 | TAINT | +| map.cpp:178:13:178:36 | call to pair | map.cpp:178:6:178:11 | call to insert | TAINT | +| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:180:2:180:4 | m14 | | +| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | | +| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | | +| map.cpp:179:13:179:26 | call to make_pair | map.cpp:179:13:179:41 | call to pair | TAINT | +| map.cpp:179:13:179:41 | call to pair | map.cpp:179:2:179:4 | ref arg m14 | TAINT | +| map.cpp:179:13:179:41 | call to pair | map.cpp:179:6:179:11 | call to insert | TAINT | +| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | | +| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | | +| map.cpp:180:13:180:26 | call to make_pair | map.cpp:180:13:180:41 | call to pair | TAINT | +| map.cpp:180:13:180:41 | call to pair | map.cpp:180:2:180:4 | ref arg m14 | TAINT | +| map.cpp:180:13:180:41 | call to pair | map.cpp:180:6:180:11 | call to insert | TAINT | +| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | | +| map.cpp:181:13:181:26 | call to make_pair | map.cpp:181:13:181:36 | call to pair | TAINT | +| map.cpp:181:13:181:36 | call to pair | map.cpp:181:2:181:4 | ref arg m14 | TAINT | +| map.cpp:181:13:181:36 | call to pair | map.cpp:181:6:181:11 | call to insert | TAINT | +| map.cpp:182:7:182:8 | m2 | map.cpp:182:10:182:20 | call to lower_bound | TAINT | +| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | | +| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | | +| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | | +| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:183:7:183:8 | m2 | map.cpp:183:10:183:20 | call to upper_bound | TAINT | +| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | | +| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | | +| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:184:7:184:8 | m2 | map.cpp:184:10:184:20 | call to equal_range | TAINT | +| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | | +| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:184:27:184:31 | first | map.cpp:184:7:184:31 | call to iterator | | +| map.cpp:185:7:185:8 | m2 | map.cpp:185:10:185:20 | call to equal_range | TAINT | +| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | | +| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:185:27:185:32 | second | map.cpp:185:7:185:32 | call to iterator | | +| map.cpp:186:7:186:8 | m2 | map.cpp:186:10:186:20 | call to upper_bound | TAINT | +| map.cpp:186:7:186:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | | +| map.cpp:186:7:186:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:187:7:187:8 | m2 | map.cpp:187:10:187:20 | call to equal_range | TAINT | +| map.cpp:187:7:187:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | | +| map.cpp:187:27:187:32 | second | map.cpp:187:7:187:32 | call to iterator | | +| map.cpp:190:27:190:29 | call to map | map.cpp:191:2:191:4 | m15 | | +| map.cpp:190:27:190:29 | call to map | map.cpp:193:7:193:9 | m15 | | +| map.cpp:190:27:190:29 | call to map | map.cpp:197:2:197:4 | m15 | | +| map.cpp:190:27:190:29 | call to map | map.cpp:199:7:199:9 | m15 | | +| map.cpp:190:27:190:29 | call to map | map.cpp:252:1:252:1 | m15 | | +| map.cpp:190:32:190:34 | call to map | map.cpp:194:7:194:9 | m16 | | +| map.cpp:190:32:190:34 | call to map | map.cpp:197:11:197:13 | m16 | | +| map.cpp:190:32:190:34 | call to map | map.cpp:200:7:200:9 | m16 | | +| map.cpp:190:32:190:34 | call to map | map.cpp:252:1:252:1 | m16 | | +| map.cpp:190:37:190:39 | call to map | map.cpp:195:7:195:9 | m17 | | +| map.cpp:190:37:190:39 | call to map | map.cpp:198:2:198:4 | m17 | | +| map.cpp:190:37:190:39 | call to map | map.cpp:201:7:201:9 | m17 | | +| map.cpp:190:37:190:39 | call to map | map.cpp:252:1:252:1 | m17 | | +| map.cpp:190:42:190:44 | call to map | map.cpp:192:2:192:4 | m18 | | +| map.cpp:190:42:190:44 | call to map | map.cpp:196:7:196:9 | m18 | | +| map.cpp:190:42:190:44 | call to map | map.cpp:198:11:198:13 | m18 | | +| map.cpp:190:42:190:44 | call to map | map.cpp:202:7:202:9 | m18 | | +| map.cpp:190:42:190:44 | call to map | map.cpp:252:1:252:1 | m18 | | +| map.cpp:191:2:191:4 | ref arg m15 | map.cpp:193:7:193:9 | m15 | | +| map.cpp:191:2:191:4 | ref arg m15 | map.cpp:197:2:197:4 | m15 | | +| map.cpp:191:2:191:4 | ref arg m15 | map.cpp:199:7:199:9 | m15 | | +| map.cpp:191:2:191:4 | ref arg m15 | map.cpp:252:1:252:1 | m15 | | +| map.cpp:191:13:191:57 | call to pair | map.cpp:191:2:191:4 | ref arg m15 | TAINT | +| map.cpp:191:13:191:57 | call to pair | map.cpp:191:6:191:11 | call to insert | TAINT | +| map.cpp:191:13:191:57 | call to pair | map.cpp:191:13:191:57 | call to pair | TAINT | +| map.cpp:191:49:191:54 | call to source | map.cpp:191:13:191:57 | call to pair | TAINT | +| map.cpp:192:2:192:4 | ref arg m18 | map.cpp:196:7:196:9 | m18 | | +| map.cpp:192:2:192:4 | ref arg m18 | map.cpp:198:11:198:13 | m18 | | +| map.cpp:192:2:192:4 | ref arg m18 | map.cpp:202:7:202:9 | m18 | | +| map.cpp:192:2:192:4 | ref arg m18 | map.cpp:252:1:252:1 | m18 | | +| map.cpp:192:13:192:57 | call to pair | map.cpp:192:2:192:4 | ref arg m18 | TAINT | +| map.cpp:192:13:192:57 | call to pair | map.cpp:192:6:192:11 | call to insert | TAINT | +| map.cpp:192:13:192:57 | call to pair | map.cpp:192:13:192:57 | call to pair | TAINT | +| map.cpp:192:49:192:54 | call to source | map.cpp:192:13:192:57 | call to pair | TAINT | +| map.cpp:193:7:193:9 | m15 | map.cpp:193:7:193:9 | call to map | | +| map.cpp:194:7:194:9 | m16 | map.cpp:194:7:194:9 | call to map | | +| map.cpp:195:7:195:9 | m17 | map.cpp:195:7:195:9 | call to map | | +| map.cpp:196:7:196:9 | m18 | map.cpp:196:7:196:9 | call to map | | +| map.cpp:197:2:197:4 | m15 | map.cpp:197:11:197:13 | ref arg m16 | TAINT | +| map.cpp:197:2:197:4 | ref arg m15 | map.cpp:199:7:199:9 | m15 | | +| map.cpp:197:2:197:4 | ref arg m15 | map.cpp:252:1:252:1 | m15 | | +| map.cpp:197:11:197:13 | m16 | map.cpp:197:2:197:4 | ref arg m15 | TAINT | +| map.cpp:197:11:197:13 | ref arg m16 | map.cpp:200:7:200:9 | m16 | | +| map.cpp:197:11:197:13 | ref arg m16 | map.cpp:252:1:252:1 | m16 | | +| map.cpp:198:2:198:4 | m17 | map.cpp:198:11:198:13 | ref arg m18 | TAINT | +| map.cpp:198:2:198:4 | ref arg m17 | map.cpp:201:7:201:9 | m17 | | +| map.cpp:198:2:198:4 | ref arg m17 | map.cpp:252:1:252:1 | m17 | | +| map.cpp:198:11:198:13 | m18 | map.cpp:198:2:198:4 | ref arg m17 | TAINT | +| map.cpp:198:11:198:13 | ref arg m18 | map.cpp:202:7:202:9 | m18 | | +| map.cpp:198:11:198:13 | ref arg m18 | map.cpp:252:1:252:1 | m18 | | +| map.cpp:199:7:199:9 | m15 | map.cpp:199:7:199:9 | call to map | | +| map.cpp:200:7:200:9 | m16 | map.cpp:200:7:200:9 | call to map | | +| map.cpp:201:7:201:9 | m17 | map.cpp:201:7:201:9 | call to map | | +| map.cpp:202:7:202:9 | m18 | map.cpp:202:7:202:9 | call to map | | +| map.cpp:205:27:205:29 | call to map | map.cpp:206:2:206:4 | m19 | | +| map.cpp:205:27:205:29 | call to map | map.cpp:210:7:210:9 | m19 | | +| map.cpp:205:27:205:29 | call to map | map.cpp:214:2:214:4 | m19 | | +| map.cpp:205:27:205:29 | call to map | map.cpp:216:7:216:9 | m19 | | +| map.cpp:205:27:205:29 | call to map | map.cpp:252:1:252:1 | m19 | | +| map.cpp:205:32:205:34 | call to map | map.cpp:207:2:207:4 | m20 | | +| map.cpp:205:32:205:34 | call to map | map.cpp:211:7:211:9 | m20 | | +| map.cpp:205:32:205:34 | call to map | map.cpp:214:12:214:14 | m20 | | +| map.cpp:205:32:205:34 | call to map | map.cpp:217:7:217:9 | m20 | | +| map.cpp:205:32:205:34 | call to map | map.cpp:252:1:252:1 | m20 | | +| map.cpp:205:37:205:39 | call to map | map.cpp:208:2:208:4 | m21 | | +| map.cpp:205:37:205:39 | call to map | map.cpp:212:7:212:9 | m21 | | +| map.cpp:205:37:205:39 | call to map | map.cpp:215:2:215:4 | m21 | | +| map.cpp:205:37:205:39 | call to map | map.cpp:218:7:218:9 | m21 | | +| map.cpp:205:37:205:39 | call to map | map.cpp:252:1:252:1 | m21 | | +| map.cpp:205:42:205:44 | call to map | map.cpp:209:2:209:4 | m22 | | +| map.cpp:205:42:205:44 | call to map | map.cpp:213:7:213:9 | m22 | | +| map.cpp:205:42:205:44 | call to map | map.cpp:215:12:215:14 | m22 | | +| map.cpp:205:42:205:44 | call to map | map.cpp:219:7:219:9 | m22 | | +| map.cpp:205:42:205:44 | call to map | map.cpp:252:1:252:1 | m22 | | +| map.cpp:206:2:206:4 | ref arg m19 | map.cpp:210:7:210:9 | m19 | | +| map.cpp:206:2:206:4 | ref arg m19 | map.cpp:214:2:214:4 | m19 | | +| map.cpp:206:2:206:4 | ref arg m19 | map.cpp:216:7:216:9 | m19 | | +| map.cpp:206:2:206:4 | ref arg m19 | map.cpp:252:1:252:1 | m19 | | +| map.cpp:206:13:206:57 | call to pair | map.cpp:206:2:206:4 | ref arg m19 | TAINT | +| map.cpp:206:13:206:57 | call to pair | map.cpp:206:6:206:11 | call to insert | TAINT | +| map.cpp:206:13:206:57 | call to pair | map.cpp:206:13:206:57 | call to pair | TAINT | +| map.cpp:206:49:206:54 | call to source | map.cpp:206:13:206:57 | call to pair | TAINT | +| map.cpp:207:2:207:4 | ref arg m20 | map.cpp:211:7:211:9 | m20 | | +| map.cpp:207:2:207:4 | ref arg m20 | map.cpp:214:12:214:14 | m20 | | +| map.cpp:207:2:207:4 | ref arg m20 | map.cpp:217:7:217:9 | m20 | | +| map.cpp:207:2:207:4 | ref arg m20 | map.cpp:252:1:252:1 | m20 | | +| map.cpp:207:13:207:51 | call to pair | map.cpp:207:2:207:4 | ref arg m20 | TAINT | +| map.cpp:207:13:207:51 | call to pair | map.cpp:207:6:207:11 | call to insert | TAINT | +| map.cpp:207:13:207:51 | call to pair | map.cpp:207:13:207:51 | call to pair | TAINT | +| map.cpp:207:46:207:50 | def | map.cpp:207:13:207:51 | call to pair | TAINT | +| map.cpp:208:2:208:4 | ref arg m21 | map.cpp:212:7:212:9 | m21 | | +| map.cpp:208:2:208:4 | ref arg m21 | map.cpp:215:2:215:4 | m21 | | +| map.cpp:208:2:208:4 | ref arg m21 | map.cpp:218:7:218:9 | m21 | | +| map.cpp:208:2:208:4 | ref arg m21 | map.cpp:252:1:252:1 | m21 | | +| map.cpp:208:13:208:51 | call to pair | map.cpp:208:2:208:4 | ref arg m21 | TAINT | +| map.cpp:208:13:208:51 | call to pair | map.cpp:208:6:208:11 | call to insert | TAINT | +| map.cpp:208:13:208:51 | call to pair | map.cpp:208:13:208:51 | call to pair | TAINT | +| map.cpp:208:46:208:50 | def | map.cpp:208:13:208:51 | call to pair | TAINT | +| map.cpp:209:2:209:4 | ref arg m22 | map.cpp:213:7:213:9 | m22 | | +| map.cpp:209:2:209:4 | ref arg m22 | map.cpp:215:12:215:14 | m22 | | +| map.cpp:209:2:209:4 | ref arg m22 | map.cpp:219:7:219:9 | m22 | | +| map.cpp:209:2:209:4 | ref arg m22 | map.cpp:252:1:252:1 | m22 | | +| map.cpp:209:13:209:57 | call to pair | map.cpp:209:2:209:4 | ref arg m22 | TAINT | +| map.cpp:209:13:209:57 | call to pair | map.cpp:209:6:209:11 | call to insert | TAINT | +| map.cpp:209:13:209:57 | call to pair | map.cpp:209:13:209:57 | call to pair | TAINT | +| map.cpp:209:49:209:54 | call to source | map.cpp:209:13:209:57 | call to pair | TAINT | +| map.cpp:210:7:210:9 | m19 | map.cpp:210:7:210:9 | call to map | | +| map.cpp:211:7:211:9 | m20 | map.cpp:211:7:211:9 | call to map | | +| map.cpp:212:7:212:9 | m21 | map.cpp:212:7:212:9 | call to map | | +| map.cpp:213:7:213:9 | m22 | map.cpp:213:7:213:9 | call to map | | +| map.cpp:214:2:214:4 | ref arg m19 | map.cpp:216:7:216:9 | m19 | | +| map.cpp:214:2:214:4 | ref arg m19 | map.cpp:252:1:252:1 | m19 | | +| map.cpp:214:12:214:14 | m20 | map.cpp:214:2:214:4 | ref arg m19 | TAINT | +| map.cpp:214:12:214:14 | ref arg m20 | map.cpp:217:7:217:9 | m20 | | +| map.cpp:214:12:214:14 | ref arg m20 | map.cpp:252:1:252:1 | m20 | | +| map.cpp:215:2:215:4 | ref arg m21 | map.cpp:218:7:218:9 | m21 | | +| map.cpp:215:2:215:4 | ref arg m21 | map.cpp:252:1:252:1 | m21 | | +| map.cpp:215:12:215:14 | m22 | map.cpp:215:2:215:4 | ref arg m21 | TAINT | +| map.cpp:215:12:215:14 | ref arg m22 | map.cpp:219:7:219:9 | m22 | | +| map.cpp:215:12:215:14 | ref arg m22 | map.cpp:252:1:252:1 | m22 | | +| map.cpp:216:7:216:9 | m19 | map.cpp:216:7:216:9 | call to map | | +| map.cpp:217:7:217:9 | m20 | map.cpp:217:7:217:9 | call to map | | +| map.cpp:218:7:218:9 | m21 | map.cpp:218:7:218:9 | call to map | | +| map.cpp:219:7:219:9 | m22 | map.cpp:219:7:219:9 | call to map | | +| map.cpp:222:27:222:29 | call to map | map.cpp:223:2:223:4 | m23 | | +| map.cpp:222:27:222:29 | call to map | map.cpp:224:2:224:4 | m23 | | +| map.cpp:222:27:222:29 | call to map | map.cpp:225:7:225:9 | m23 | | +| map.cpp:222:27:222:29 | call to map | map.cpp:226:7:226:9 | m23 | | +| map.cpp:222:27:222:29 | call to map | map.cpp:226:17:226:19 | m23 | | +| map.cpp:222:27:222:29 | call to map | map.cpp:227:7:227:9 | m23 | | +| map.cpp:222:27:222:29 | call to map | map.cpp:228:2:228:4 | m23 | | +| map.cpp:222:27:222:29 | call to map | map.cpp:229:7:229:9 | m23 | | +| map.cpp:222:27:222:29 | call to map | map.cpp:252:1:252:1 | m23 | | +| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:224:2:224:4 | m23 | | +| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:225:7:225:9 | m23 | | +| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:226:7:226:9 | m23 | | +| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:226:17:226:19 | m23 | | +| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:227:7:227:9 | m23 | | +| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:228:2:228:4 | m23 | | +| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:229:7:229:9 | m23 | | +| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:252:1:252:1 | m23 | | +| map.cpp:223:13:223:57 | call to pair | map.cpp:223:2:223:4 | ref arg m23 | TAINT | +| map.cpp:223:13:223:57 | call to pair | map.cpp:223:6:223:11 | call to insert | TAINT | +| map.cpp:223:13:223:57 | call to pair | map.cpp:223:13:223:57 | call to pair | TAINT | +| map.cpp:223:49:223:54 | call to source | map.cpp:223:13:223:57 | call to pair | TAINT | +| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:225:7:225:9 | m23 | | +| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:226:7:226:9 | m23 | | +| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:226:17:226:19 | m23 | | +| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:227:7:227:9 | m23 | | +| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:228:2:228:4 | m23 | | +| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:229:7:229:9 | m23 | | +| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:252:1:252:1 | m23 | | +| map.cpp:224:13:224:57 | call to pair | map.cpp:224:2:224:4 | ref arg m23 | TAINT | +| map.cpp:224:13:224:57 | call to pair | map.cpp:224:6:224:11 | call to insert | TAINT | +| map.cpp:224:13:224:57 | call to pair | map.cpp:224:13:224:57 | call to pair | TAINT | +| map.cpp:224:49:224:54 | call to source | map.cpp:224:13:224:57 | call to pair | TAINT | +| map.cpp:225:7:225:9 | m23 | map.cpp:225:7:225:9 | call to map | | +| map.cpp:226:7:226:9 | m23 | map.cpp:226:11:226:15 | call to erase | TAINT | +| map.cpp:226:7:226:9 | ref arg m23 | map.cpp:227:7:227:9 | m23 | | +| map.cpp:226:7:226:9 | ref arg m23 | map.cpp:228:2:228:4 | m23 | | +| map.cpp:226:7:226:9 | ref arg m23 | map.cpp:229:7:229:9 | m23 | | +| map.cpp:226:7:226:9 | ref arg m23 | map.cpp:252:1:252:1 | m23 | | +| map.cpp:226:17:226:19 | m23 | map.cpp:226:21:226:25 | call to begin | TAINT | +| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:226:7:226:9 | m23 | | +| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:227:7:227:9 | m23 | | +| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:228:2:228:4 | m23 | | +| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:229:7:229:9 | m23 | | +| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:252:1:252:1 | m23 | | +| map.cpp:227:7:227:9 | m23 | map.cpp:227:7:227:9 | call to map | | +| map.cpp:228:2:228:4 | ref arg m23 | map.cpp:229:7:229:9 | m23 | | +| map.cpp:228:2:228:4 | ref arg m23 | map.cpp:252:1:252:1 | m23 | | +| map.cpp:229:7:229:9 | m23 | map.cpp:229:7:229:9 | call to map | | +| map.cpp:232:27:232:29 | call to map | map.cpp:233:7:233:9 | m24 | | +| map.cpp:232:27:232:29 | call to map | map.cpp:234:7:234:9 | m24 | | +| map.cpp:232:27:232:29 | call to map | map.cpp:235:7:235:9 | m24 | | +| map.cpp:232:27:232:29 | call to map | map.cpp:236:7:236:9 | m24 | | +| map.cpp:232:27:232:29 | call to map | map.cpp:252:1:252:1 | m24 | | +| map.cpp:232:32:232:34 | call to map | map.cpp:237:7:237:9 | m25 | | +| map.cpp:232:32:232:34 | call to map | map.cpp:237:24:237:26 | m25 | | +| map.cpp:232:32:232:34 | call to map | map.cpp:238:7:238:9 | m25 | | +| map.cpp:232:32:232:34 | call to map | map.cpp:239:7:239:9 | m25 | | +| map.cpp:232:32:232:34 | call to map | map.cpp:239:24:239:26 | m25 | | +| map.cpp:232:32:232:34 | call to map | map.cpp:240:7:240:9 | m25 | | +| map.cpp:232:32:232:34 | call to map | map.cpp:252:1:252:1 | m25 | | +| map.cpp:233:7:233:9 | m24 | map.cpp:233:11:233:17 | call to emplace | TAINT | +| map.cpp:233:7:233:9 | ref arg m24 | map.cpp:234:7:234:9 | m24 | | +| map.cpp:233:7:233:9 | ref arg m24 | map.cpp:235:7:235:9 | m24 | | +| map.cpp:233:7:233:9 | ref arg m24 | map.cpp:236:7:236:9 | m24 | | +| map.cpp:233:7:233:9 | ref arg m24 | map.cpp:252:1:252:1 | m24 | | +| map.cpp:233:26:233:30 | def | map.cpp:233:7:233:9 | ref arg m24 | TAINT | +| map.cpp:233:26:233:30 | def | map.cpp:233:11:233:17 | call to emplace | TAINT | +| map.cpp:233:33:233:37 | first | map.cpp:233:7:233:37 | call to iterator | | +| map.cpp:234:7:234:9 | m24 | map.cpp:234:7:234:9 | call to map | | +| map.cpp:235:7:235:9 | m24 | map.cpp:235:11:235:17 | call to emplace | TAINT | +| map.cpp:235:7:235:9 | ref arg m24 | map.cpp:236:7:236:9 | m24 | | +| map.cpp:235:7:235:9 | ref arg m24 | map.cpp:252:1:252:1 | m24 | | +| map.cpp:235:26:235:31 | call to source | map.cpp:235:7:235:9 | ref arg m24 | TAINT | +| map.cpp:235:26:235:31 | call to source | map.cpp:235:11:235:17 | call to emplace | TAINT | +| map.cpp:235:36:235:40 | first | map.cpp:235:7:235:40 | call to iterator | | +| map.cpp:236:7:236:9 | m24 | map.cpp:236:7:236:9 | call to map | | +| map.cpp:237:7:237:9 | m25 | map.cpp:237:11:237:22 | call to emplace_hint | TAINT | +| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:238:7:238:9 | m25 | | +| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:239:7:239:9 | m25 | | +| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:239:24:239:26 | m25 | | +| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:240:7:240:9 | m25 | | +| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:252:1:252:1 | m25 | | +| map.cpp:237:24:237:26 | m25 | map.cpp:237:28:237:32 | call to begin | TAINT | +| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:237:7:237:9 | m25 | | +| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:238:7:238:9 | m25 | | +| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:239:7:239:9 | m25 | | +| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:239:24:239:26 | m25 | | +| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:240:7:240:9 | m25 | | +| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:252:1:252:1 | m25 | | +| map.cpp:237:28:237:32 | call to begin | map.cpp:237:24:237:34 | call to iterator | TAINT | +| map.cpp:237:44:237:48 | def | map.cpp:237:7:237:9 | ref arg m25 | TAINT | +| map.cpp:237:44:237:48 | def | map.cpp:237:11:237:22 | call to emplace_hint | TAINT | +| map.cpp:238:7:238:9 | m25 | map.cpp:238:7:238:9 | call to map | | +| map.cpp:239:7:239:9 | m25 | map.cpp:239:11:239:22 | call to emplace_hint | TAINT | +| map.cpp:239:7:239:9 | ref arg m25 | map.cpp:240:7:240:9 | m25 | | +| map.cpp:239:7:239:9 | ref arg m25 | map.cpp:252:1:252:1 | m25 | | +| map.cpp:239:24:239:26 | m25 | map.cpp:239:28:239:32 | call to begin | TAINT | +| map.cpp:239:24:239:26 | ref arg m25 | map.cpp:239:7:239:9 | m25 | | +| map.cpp:239:24:239:26 | ref arg m25 | map.cpp:240:7:240:9 | m25 | | +| map.cpp:239:24:239:26 | ref arg m25 | map.cpp:252:1:252:1 | m25 | | +| map.cpp:239:28:239:32 | call to begin | map.cpp:239:24:239:34 | call to iterator | TAINT | +| map.cpp:239:44:239:49 | call to source | map.cpp:239:7:239:9 | ref arg m25 | TAINT | +| map.cpp:239:44:239:49 | call to source | map.cpp:239:11:239:22 | call to emplace_hint | TAINT | +| map.cpp:240:7:240:9 | m25 | map.cpp:240:7:240:9 | call to map | | +| map.cpp:243:27:243:29 | call to map | map.cpp:244:7:244:9 | m26 | | +| map.cpp:243:27:243:29 | call to map | map.cpp:245:7:245:9 | m26 | | +| map.cpp:243:27:243:29 | call to map | map.cpp:246:7:246:9 | m26 | | +| map.cpp:243:27:243:29 | call to map | map.cpp:247:7:247:9 | m26 | | +| map.cpp:243:27:243:29 | call to map | map.cpp:252:1:252:1 | m26 | | +| map.cpp:243:32:243:34 | call to map | map.cpp:248:7:248:9 | m27 | | +| map.cpp:243:32:243:34 | call to map | map.cpp:248:23:248:25 | m27 | | +| map.cpp:243:32:243:34 | call to map | map.cpp:249:7:249:9 | m27 | | +| map.cpp:243:32:243:34 | call to map | map.cpp:250:7:250:9 | m27 | | +| map.cpp:243:32:243:34 | call to map | map.cpp:250:23:250:25 | m27 | | +| map.cpp:243:32:243:34 | call to map | map.cpp:251:7:251:9 | m27 | | +| map.cpp:243:32:243:34 | call to map | map.cpp:252:1:252:1 | m27 | | +| map.cpp:244:7:244:9 | m26 | map.cpp:244:11:244:21 | call to try_emplace | TAINT | +| map.cpp:244:7:244:9 | ref arg m26 | map.cpp:245:7:245:9 | m26 | | +| map.cpp:244:7:244:9 | ref arg m26 | map.cpp:246:7:246:9 | m26 | | +| map.cpp:244:7:244:9 | ref arg m26 | map.cpp:247:7:247:9 | m26 | | +| map.cpp:244:7:244:9 | ref arg m26 | map.cpp:252:1:252:1 | m26 | | +| map.cpp:244:30:244:34 | def | map.cpp:244:7:244:9 | ref arg m26 | TAINT | +| map.cpp:244:30:244:34 | def | map.cpp:244:11:244:21 | call to try_emplace | TAINT | +| map.cpp:244:37:244:41 | first | map.cpp:244:7:244:41 | call to iterator | | +| map.cpp:245:7:245:9 | m26 | map.cpp:245:7:245:9 | call to map | | +| map.cpp:246:7:246:9 | m26 | map.cpp:246:11:246:21 | call to try_emplace | TAINT | +| map.cpp:246:7:246:9 | ref arg m26 | map.cpp:247:7:247:9 | m26 | | +| map.cpp:246:7:246:9 | ref arg m26 | map.cpp:252:1:252:1 | m26 | | +| map.cpp:246:30:246:35 | call to source | map.cpp:246:7:246:9 | ref arg m26 | TAINT | +| map.cpp:246:30:246:35 | call to source | map.cpp:246:11:246:21 | call to try_emplace | TAINT | +| map.cpp:246:40:246:44 | first | map.cpp:246:7:246:44 | call to iterator | | +| map.cpp:247:7:247:9 | m26 | map.cpp:247:7:247:9 | call to map | | +| map.cpp:248:7:248:9 | m27 | map.cpp:248:11:248:21 | call to try_emplace | TAINT | +| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:249:7:249:9 | m27 | | +| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:250:7:250:9 | m27 | | +| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:250:23:250:25 | m27 | | +| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:251:7:251:9 | m27 | | +| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:252:1:252:1 | m27 | | +| map.cpp:248:23:248:25 | m27 | map.cpp:248:27:248:31 | call to begin | TAINT | +| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:248:7:248:9 | m27 | | +| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:249:7:249:9 | m27 | | +| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:250:7:250:9 | m27 | | +| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:250:23:250:25 | m27 | | +| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:251:7:251:9 | m27 | | +| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:252:1:252:1 | m27 | | +| map.cpp:248:27:248:31 | call to begin | map.cpp:248:23:248:33 | call to iterator | TAINT | +| map.cpp:248:43:248:47 | def | map.cpp:248:7:248:9 | ref arg m27 | TAINT | +| map.cpp:248:43:248:47 | def | map.cpp:248:11:248:21 | call to try_emplace | TAINT | +| map.cpp:249:7:249:9 | m27 | map.cpp:249:7:249:9 | call to map | | +| map.cpp:250:7:250:9 | m27 | map.cpp:250:11:250:21 | call to try_emplace | TAINT | +| map.cpp:250:7:250:9 | ref arg m27 | map.cpp:251:7:251:9 | m27 | | +| map.cpp:250:7:250:9 | ref arg m27 | map.cpp:252:1:252:1 | m27 | | +| map.cpp:250:23:250:25 | m27 | map.cpp:250:27:250:31 | call to begin | TAINT | +| map.cpp:250:23:250:25 | ref arg m27 | map.cpp:250:7:250:9 | m27 | | +| map.cpp:250:23:250:25 | ref arg m27 | map.cpp:251:7:251:9 | m27 | | +| map.cpp:250:23:250:25 | ref arg m27 | map.cpp:252:1:252:1 | m27 | | +| map.cpp:250:27:250:31 | call to begin | map.cpp:250:23:250:33 | call to iterator | TAINT | +| map.cpp:250:43:250:48 | call to source | map.cpp:250:7:250:9 | ref arg m27 | TAINT | +| map.cpp:250:43:250:48 | call to source | map.cpp:250:11:250:21 | call to try_emplace | TAINT | +| map.cpp:251:7:251:9 | m27 | map.cpp:251:7:251:9 | call to map | | +| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:259:7:259:8 | m1 | | +| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:265:7:265:8 | m1 | | +| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:271:7:271:8 | m1 | | +| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:277:7:277:8 | m1 | | +| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:298:12:298:13 | m1 | | +| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:298:30:298:31 | m1 | | +| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:438:1:438:1 | m1 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:260:7:260:8 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:266:7:266:8 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:272:7:272:8 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:278:7:278:8 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:285:40:285:41 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:286:42:286:43 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:288:7:288:8 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:304:12:304:13 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:304:30:304:31 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:334:7:334:8 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:335:7:335:8 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:336:7:336:8 | m2 | | +| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:438:1:438:1 | m2 | | +| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:261:7:261:8 | m3 | | +| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:267:7:267:8 | m3 | | +| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:273:7:273:8 | m3 | | +| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:279:7:279:8 | m3 | | +| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:310:12:310:13 | m3 | | +| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:310:30:310:31 | m3 | | +| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:438:1:438:1 | m3 | | +| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:262:7:262:8 | m4 | | +| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:262:17:262:18 | m4 | | +| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:268:7:268:8 | m4 | | +| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:274:7:274:8 | m4 | | +| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:280:7:280:8 | m4 | | +| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:438:1:438:1 | m4 | | +| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:263:7:263:8 | m5 | | +| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:269:7:269:8 | m5 | | +| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:275:7:275:8 | m5 | | +| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:281:7:281:8 | m5 | | +| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:438:1:438:1 | m5 | | +| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:264:7:264:8 | m6 | | +| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:264:27:264:28 | m6 | | +| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:270:7:270:8 | m6 | | +| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:276:7:276:8 | m6 | | +| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:282:7:282:8 | m6 | | +| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:438:1:438:1 | m6 | | +| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:265:7:265:8 | m1 | | +| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:271:7:271:8 | m1 | | +| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:277:7:277:8 | m1 | | +| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:298:12:298:13 | m1 | | +| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:298:30:298:31 | m1 | | +| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:438:1:438:1 | m1 | | +| map.cpp:259:17:259:30 | call to make_pair | map.cpp:259:17:259:44 | call to pair | TAINT | +| map.cpp:259:17:259:44 | call to pair | map.cpp:259:7:259:8 | ref arg m1 | TAINT | +| map.cpp:259:17:259:44 | call to pair | map.cpp:259:10:259:15 | call to insert | TAINT | +| map.cpp:259:47:259:51 | first | map.cpp:259:7:259:51 | call to iterator | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:266:7:266:8 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:272:7:272:8 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:278:7:278:8 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:285:40:285:41 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:286:42:286:43 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | | +| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | | +| map.cpp:260:17:260:30 | call to make_pair | map.cpp:260:17:260:47 | call to pair | TAINT | +| map.cpp:260:17:260:47 | call to pair | map.cpp:260:7:260:8 | ref arg m2 | TAINT | +| map.cpp:260:17:260:47 | call to pair | map.cpp:260:10:260:15 | call to insert | TAINT | +| map.cpp:260:50:260:54 | first | map.cpp:260:7:260:54 | call to iterator | | +| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:267:7:267:8 | m3 | | +| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:273:7:273:8 | m3 | | +| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:279:7:279:8 | m3 | | +| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:310:12:310:13 | m3 | | +| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:310:30:310:31 | m3 | | +| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:438:1:438:1 | m3 | | +| map.cpp:261:17:261:30 | call to make_pair | map.cpp:261:17:261:47 | call to pair | TAINT | +| map.cpp:261:17:261:47 | call to pair | map.cpp:261:7:261:8 | ref arg m3 | TAINT | +| map.cpp:261:17:261:47 | call to pair | map.cpp:261:10:261:15 | call to insert | TAINT | +| map.cpp:261:50:261:54 | first | map.cpp:261:7:261:54 | call to iterator | | +| map.cpp:262:7:262:8 | ref arg m4 | map.cpp:268:7:268:8 | m4 | | +| map.cpp:262:7:262:8 | ref arg m4 | map.cpp:274:7:274:8 | m4 | | +| map.cpp:262:7:262:8 | ref arg m4 | map.cpp:280:7:280:8 | m4 | | +| map.cpp:262:7:262:8 | ref arg m4 | map.cpp:438:1:438:1 | m4 | | +| map.cpp:262:17:262:18 | m4 | map.cpp:262:20:262:24 | call to begin | TAINT | +| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:262:7:262:8 | m4 | | +| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:268:7:268:8 | m4 | | +| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:274:7:274:8 | m4 | | +| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:280:7:280:8 | m4 | | +| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:438:1:438:1 | m4 | | +| map.cpp:262:20:262:24 | call to begin | map.cpp:262:17:262:26 | call to iterator | TAINT | +| map.cpp:262:29:262:70 | call to pair | map.cpp:262:7:262:8 | ref arg m4 | TAINT | +| map.cpp:262:29:262:70 | call to pair | map.cpp:262:10:262:15 | call to insert | TAINT | +| map.cpp:262:29:262:70 | call to pair | map.cpp:262:29:262:70 | call to pair | TAINT | +| map.cpp:262:62:262:67 | call to source | map.cpp:262:29:262:70 | call to pair | TAINT | +| map.cpp:263:7:263:8 | ref arg m5 | map.cpp:269:7:269:8 | m5 | | +| map.cpp:263:7:263:8 | ref arg m5 | map.cpp:275:7:275:8 | m5 | | +| map.cpp:263:7:263:8 | ref arg m5 | map.cpp:281:7:281:8 | m5 | | +| map.cpp:263:7:263:8 | ref arg m5 | map.cpp:438:1:438:1 | m5 | | +| map.cpp:263:34:263:39 | call to source | map.cpp:263:7:263:8 | ref arg m5 | TAINT | +| map.cpp:263:34:263:39 | call to source | map.cpp:263:10:263:25 | call to insert_or_assign | TAINT | +| map.cpp:263:44:263:48 | first | map.cpp:263:7:263:48 | call to iterator | | +| map.cpp:264:7:264:8 | ref arg m6 | map.cpp:270:7:270:8 | m6 | | +| map.cpp:264:7:264:8 | ref arg m6 | map.cpp:276:7:276:8 | m6 | | +| map.cpp:264:7:264:8 | ref arg m6 | map.cpp:282:7:282:8 | m6 | | +| map.cpp:264:7:264:8 | ref arg m6 | map.cpp:438:1:438:1 | m6 | | +| map.cpp:264:27:264:28 | m6 | map.cpp:264:30:264:34 | call to begin | TAINT | +| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:264:7:264:8 | m6 | | +| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:270:7:270:8 | m6 | | +| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:276:7:276:8 | m6 | | +| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:282:7:282:8 | m6 | | +| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:438:1:438:1 | m6 | | +| map.cpp:264:30:264:34 | call to begin | map.cpp:264:27:264:36 | call to iterator | TAINT | +| map.cpp:264:46:264:51 | call to source | map.cpp:264:7:264:8 | ref arg m6 | TAINT | +| map.cpp:264:46:264:51 | call to source | map.cpp:264:10:264:25 | call to insert_or_assign | TAINT | +| map.cpp:265:7:265:8 | m1 | map.cpp:265:7:265:8 | call to unordered_map | | +| map.cpp:266:7:266:8 | m2 | map.cpp:266:7:266:8 | call to unordered_map | | +| map.cpp:267:7:267:8 | m3 | map.cpp:267:7:267:8 | call to unordered_map | | +| map.cpp:268:7:268:8 | m4 | map.cpp:268:7:268:8 | call to unordered_map | | +| map.cpp:269:7:269:8 | m5 | map.cpp:269:7:269:8 | call to unordered_map | | +| map.cpp:270:7:270:8 | m6 | map.cpp:270:7:270:8 | call to unordered_map | | +| map.cpp:271:7:271:8 | m1 | map.cpp:271:10:271:13 | call to find | TAINT | +| map.cpp:271:7:271:8 | ref arg m1 | map.cpp:277:7:277:8 | m1 | | +| map.cpp:271:7:271:8 | ref arg m1 | map.cpp:298:12:298:13 | m1 | | +| map.cpp:271:7:271:8 | ref arg m1 | map.cpp:298:30:298:31 | m1 | | +| map.cpp:271:7:271:8 | ref arg m1 | map.cpp:438:1:438:1 | m1 | | +| map.cpp:272:7:272:8 | m2 | map.cpp:272:10:272:13 | call to find | TAINT | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:278:7:278:8 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:285:40:285:41 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:286:42:286:43 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | | +| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | | +| map.cpp:273:7:273:8 | m3 | map.cpp:273:10:273:13 | call to find | TAINT | +| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:279:7:279:8 | m3 | | +| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:310:12:310:13 | m3 | | +| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:310:30:310:31 | m3 | | +| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:438:1:438:1 | m3 | | +| map.cpp:274:7:274:8 | m4 | map.cpp:274:10:274:13 | call to find | TAINT | +| map.cpp:274:7:274:8 | ref arg m4 | map.cpp:280:7:280:8 | m4 | | +| map.cpp:274:7:274:8 | ref arg m4 | map.cpp:438:1:438:1 | m4 | | +| map.cpp:275:7:275:8 | m5 | map.cpp:275:10:275:13 | call to find | TAINT | +| map.cpp:275:7:275:8 | ref arg m5 | map.cpp:281:7:281:8 | m5 | | +| map.cpp:275:7:275:8 | ref arg m5 | map.cpp:438:1:438:1 | m5 | | +| map.cpp:276:7:276:8 | m6 | map.cpp:276:10:276:13 | call to find | TAINT | +| map.cpp:276:7:276:8 | ref arg m6 | map.cpp:282:7:282:8 | m6 | | +| map.cpp:276:7:276:8 | ref arg m6 | map.cpp:438:1:438:1 | m6 | | +| map.cpp:277:7:277:8 | m1 | map.cpp:277:10:277:13 | call to find | TAINT | +| map.cpp:277:7:277:8 | ref arg m1 | map.cpp:298:12:298:13 | m1 | | +| map.cpp:277:7:277:8 | ref arg m1 | map.cpp:298:30:298:31 | m1 | | +| map.cpp:277:7:277:8 | ref arg m1 | map.cpp:438:1:438:1 | m1 | | +| map.cpp:278:7:278:8 | m2 | map.cpp:278:10:278:13 | call to find | TAINT | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:285:40:285:41 | m2 | | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:286:42:286:43 | m2 | | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | | +| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | | +| map.cpp:279:7:279:8 | m3 | map.cpp:279:10:279:13 | call to find | TAINT | +| map.cpp:279:7:279:8 | ref arg m3 | map.cpp:310:12:310:13 | m3 | | +| map.cpp:279:7:279:8 | ref arg m3 | map.cpp:310:30:310:31 | m3 | | +| map.cpp:279:7:279:8 | ref arg m3 | map.cpp:438:1:438:1 | m3 | | +| map.cpp:280:7:280:8 | m4 | map.cpp:280:10:280:13 | call to find | TAINT | +| map.cpp:280:7:280:8 | ref arg m4 | map.cpp:438:1:438:1 | m4 | | +| map.cpp:281:7:281:8 | m5 | map.cpp:281:10:281:13 | call to find | TAINT | +| map.cpp:281:7:281:8 | ref arg m5 | map.cpp:438:1:438:1 | m5 | | +| map.cpp:282:7:282:8 | m6 | map.cpp:282:10:282:13 | call to find | TAINT | +| map.cpp:282:7:282:8 | ref arg m6 | map.cpp:438:1:438:1 | m6 | | +| map.cpp:285:40:285:41 | m2 | map.cpp:285:40:285:42 | call to unordered_map | | +| map.cpp:285:40:285:42 | call to unordered_map | map.cpp:289:7:289:8 | m7 | | +| map.cpp:285:40:285:42 | call to unordered_map | map.cpp:292:7:292:8 | m7 | | +| map.cpp:285:40:285:42 | call to unordered_map | map.cpp:438:1:438:1 | m7 | | +| map.cpp:286:41:286:43 | call to unordered_map | map.cpp:290:7:290:8 | m8 | | +| map.cpp:286:41:286:43 | call to unordered_map | map.cpp:293:7:293:8 | m8 | | +| map.cpp:286:41:286:43 | call to unordered_map | map.cpp:438:1:438:1 | m8 | | +| map.cpp:286:42:286:43 | m2 | map.cpp:286:41:286:43 | call to unordered_map | | +| map.cpp:287:37:287:38 | call to unordered_map | map.cpp:288:2:288:3 | m9 | | +| map.cpp:287:37:287:38 | call to unordered_map | map.cpp:291:7:291:8 | m9 | | +| map.cpp:287:37:287:38 | call to unordered_map | map.cpp:294:7:294:8 | m9 | | +| map.cpp:287:37:287:38 | call to unordered_map | map.cpp:438:1:438:1 | m9 | | +| map.cpp:288:2:288:3 | ref arg m9 | map.cpp:291:7:291:8 | m9 | | +| map.cpp:288:2:288:3 | ref arg m9 | map.cpp:294:7:294:8 | m9 | | +| map.cpp:288:2:288:3 | ref arg m9 | map.cpp:438:1:438:1 | m9 | | +| map.cpp:288:7:288:8 | m2 | map.cpp:288:2:288:3 | ref arg m9 | TAINT | +| map.cpp:288:7:288:8 | m2 | map.cpp:288:5:288:5 | call to operator= | TAINT | +| map.cpp:289:7:289:8 | m7 | map.cpp:289:7:289:8 | call to unordered_map | | +| map.cpp:290:7:290:8 | m8 | map.cpp:290:7:290:8 | call to unordered_map | | +| map.cpp:291:7:291:8 | m9 | map.cpp:291:7:291:8 | call to unordered_map | | +| map.cpp:292:7:292:8 | m7 | map.cpp:292:10:292:13 | call to find | TAINT | +| map.cpp:292:7:292:8 | ref arg m7 | map.cpp:438:1:438:1 | m7 | | +| map.cpp:293:7:293:8 | m8 | map.cpp:293:10:293:13 | call to find | TAINT | +| map.cpp:293:7:293:8 | ref arg m8 | map.cpp:438:1:438:1 | m8 | | +| map.cpp:294:7:294:8 | m9 | map.cpp:294:10:294:13 | call to find | TAINT | +| map.cpp:294:7:294:8 | ref arg m9 | map.cpp:438:1:438:1 | m9 | | +| map.cpp:298:12:298:13 | m1 | map.cpp:298:15:298:19 | call to begin | TAINT | +| map.cpp:298:12:298:13 | ref arg m1 | map.cpp:298:30:298:31 | m1 | | +| map.cpp:298:12:298:13 | ref arg m1 | map.cpp:438:1:438:1 | m1 | | +| map.cpp:298:15:298:19 | call to begin | map.cpp:298:7:298:21 | ... = ... | | +| map.cpp:298:15:298:19 | call to begin | map.cpp:298:24:298:25 | i1 | | +| map.cpp:298:15:298:19 | call to begin | map.cpp:298:40:298:41 | i1 | | +| map.cpp:298:15:298:19 | call to begin | map.cpp:300:9:300:10 | i1 | | +| map.cpp:298:15:298:19 | call to begin | map.cpp:301:8:301:9 | i1 | | +| map.cpp:298:15:298:19 | call to begin | map.cpp:302:8:302:9 | i1 | | +| map.cpp:298:30:298:31 | m1 | map.cpp:298:33:298:35 | call to end | TAINT | +| map.cpp:298:30:298:31 | ref arg m1 | map.cpp:298:30:298:31 | m1 | | +| map.cpp:298:30:298:31 | ref arg m1 | map.cpp:438:1:438:1 | m1 | | +| map.cpp:298:40:298:41 | i1 | map.cpp:298:42:298:42 | call to operator++ | | +| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:298:24:298:25 | i1 | | +| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:298:40:298:41 | i1 | | +| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:300:9:300:10 | i1 | | +| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:301:8:301:9 | i1 | | +| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:302:8:302:9 | i1 | | +| map.cpp:300:8:300:8 | call to operator* | map.cpp:300:8:300:10 | call to pair | TAINT | +| map.cpp:300:9:300:10 | i1 | map.cpp:300:8:300:8 | call to operator* | TAINT | +| map.cpp:301:8:301:9 | i1 | map.cpp:301:10:301:10 | call to operator-> | TAINT | +| map.cpp:302:8:302:9 | i1 | map.cpp:302:10:302:10 | call to operator-> | TAINT | +| map.cpp:304:12:304:13 | m2 | map.cpp:304:15:304:19 | call to begin | TAINT | +| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:304:30:304:31 | m2 | | +| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:334:7:334:8 | m2 | | +| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:335:7:335:8 | m2 | | +| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:336:7:336:8 | m2 | | +| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:438:1:438:1 | m2 | | +| map.cpp:304:15:304:19 | call to begin | map.cpp:304:7:304:21 | ... = ... | | +| map.cpp:304:15:304:19 | call to begin | map.cpp:304:24:304:25 | i2 | | +| map.cpp:304:15:304:19 | call to begin | map.cpp:304:40:304:41 | i2 | | +| map.cpp:304:15:304:19 | call to begin | map.cpp:306:9:306:10 | i2 | | +| map.cpp:304:15:304:19 | call to begin | map.cpp:307:8:307:9 | i2 | | +| map.cpp:304:15:304:19 | call to begin | map.cpp:308:8:308:9 | i2 | | +| map.cpp:304:15:304:19 | call to begin | map.cpp:313:8:313:9 | i2 | | +| map.cpp:304:15:304:19 | call to begin | map.cpp:314:8:314:9 | i2 | | +| map.cpp:304:30:304:31 | m2 | map.cpp:304:33:304:35 | call to end | TAINT | +| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:304:30:304:31 | m2 | | +| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:334:7:334:8 | m2 | | +| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:335:7:335:8 | m2 | | +| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:336:7:336:8 | m2 | | +| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:438:1:438:1 | m2 | | +| map.cpp:304:40:304:41 | i2 | map.cpp:304:42:304:42 | call to operator++ | | +| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:304:24:304:25 | i2 | | +| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:304:40:304:41 | i2 | | +| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:306:9:306:10 | i2 | | +| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:307:8:307:9 | i2 | | +| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:308:8:308:9 | i2 | | +| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:313:8:313:9 | i2 | | +| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:314:8:314:9 | i2 | | +| map.cpp:306:8:306:8 | call to operator* | map.cpp:306:8:306:10 | call to pair | TAINT | +| map.cpp:306:9:306:10 | i2 | map.cpp:306:8:306:8 | call to operator* | TAINT | +| map.cpp:307:8:307:9 | i2 | map.cpp:307:10:307:10 | call to operator-> | TAINT | +| map.cpp:308:8:308:9 | i2 | map.cpp:308:10:308:10 | call to operator-> | TAINT | +| map.cpp:310:12:310:13 | m3 | map.cpp:310:15:310:19 | call to begin | TAINT | +| map.cpp:310:12:310:13 | ref arg m3 | map.cpp:310:30:310:31 | m3 | | +| map.cpp:310:12:310:13 | ref arg m3 | map.cpp:438:1:438:1 | m3 | | +| map.cpp:310:15:310:19 | call to begin | map.cpp:310:7:310:21 | ... = ... | | +| map.cpp:310:15:310:19 | call to begin | map.cpp:310:24:310:25 | i3 | | +| map.cpp:310:15:310:19 | call to begin | map.cpp:310:40:310:41 | i3 | | +| map.cpp:310:15:310:19 | call to begin | map.cpp:312:9:312:10 | i3 | | +| map.cpp:310:30:310:31 | m3 | map.cpp:310:33:310:35 | call to end | TAINT | +| map.cpp:310:30:310:31 | ref arg m3 | map.cpp:310:30:310:31 | m3 | | +| map.cpp:310:30:310:31 | ref arg m3 | map.cpp:438:1:438:1 | m3 | | +| map.cpp:310:40:310:41 | i3 | map.cpp:310:42:310:42 | call to operator++ | | +| map.cpp:310:40:310:41 | ref arg i3 | map.cpp:310:24:310:25 | i3 | | +| map.cpp:310:40:310:41 | ref arg i3 | map.cpp:310:40:310:41 | i3 | | +| map.cpp:310:40:310:41 | ref arg i3 | map.cpp:312:9:312:10 | i3 | | +| map.cpp:312:8:312:8 | call to operator* | map.cpp:312:8:312:10 | call to pair | TAINT | +| map.cpp:312:9:312:10 | i3 | map.cpp:312:8:312:8 | call to operator* | TAINT | +| map.cpp:313:8:313:9 | i2 | map.cpp:313:10:313:10 | call to operator-> | TAINT | +| map.cpp:314:8:314:9 | i2 | map.cpp:314:10:314:10 | call to operator-> | TAINT | +| map.cpp:318:37:318:39 | call to unordered_map | map.cpp:319:7:319:9 | m10 | | +| map.cpp:318:37:318:39 | call to unordered_map | map.cpp:323:7:323:9 | m10 | | +| map.cpp:318:37:318:39 | call to unordered_map | map.cpp:438:1:438:1 | m10 | | +| map.cpp:318:42:318:44 | call to unordered_map | map.cpp:320:7:320:9 | m11 | | +| map.cpp:318:42:318:44 | call to unordered_map | map.cpp:324:7:324:9 | m11 | | +| map.cpp:318:42:318:44 | call to unordered_map | map.cpp:438:1:438:1 | m11 | | +| map.cpp:318:47:318:49 | call to unordered_map | map.cpp:321:7:321:9 | m12 | | +| map.cpp:318:47:318:49 | call to unordered_map | map.cpp:325:7:325:9 | m12 | | +| map.cpp:318:47:318:49 | call to unordered_map | map.cpp:438:1:438:1 | m12 | | +| map.cpp:318:52:318:54 | call to unordered_map | map.cpp:322:7:322:9 | m13 | | +| map.cpp:318:52:318:54 | call to unordered_map | map.cpp:326:7:326:9 | m13 | | +| map.cpp:318:52:318:54 | call to unordered_map | map.cpp:438:1:438:1 | m13 | | +| map.cpp:319:7:319:9 | m10 | map.cpp:319:10:319:10 | call to operator[] | TAINT | +| map.cpp:319:7:319:9 | ref arg m10 | map.cpp:323:7:323:9 | m10 | | +| map.cpp:319:7:319:9 | ref arg m10 | map.cpp:438:1:438:1 | m10 | | +| map.cpp:319:7:319:24 | ... = ... | map.cpp:319:10:319:10 | call to operator[] [post update] | | +| map.cpp:319:10:319:10 | call to operator[] [post update] | map.cpp:319:7:319:9 | ref arg m10 | TAINT | +| map.cpp:319:20:319:24 | def | map.cpp:319:7:319:24 | ... = ... | | +| map.cpp:320:7:320:9 | m11 | map.cpp:320:10:320:10 | call to operator[] | TAINT | +| map.cpp:320:7:320:9 | ref arg m11 | map.cpp:324:7:324:9 | m11 | | +| map.cpp:320:7:320:9 | ref arg m11 | map.cpp:438:1:438:1 | m11 | | +| map.cpp:320:7:320:27 | ... = ... | map.cpp:320:10:320:10 | call to operator[] [post update] | | +| map.cpp:320:10:320:10 | call to operator[] [post update] | map.cpp:320:7:320:9 | ref arg m11 | TAINT | +| map.cpp:320:20:320:25 | call to source | map.cpp:320:7:320:27 | ... = ... | | +| map.cpp:321:7:321:9 | m12 | map.cpp:321:11:321:12 | call to at | TAINT | +| map.cpp:321:7:321:9 | ref arg m12 | map.cpp:325:7:325:9 | m12 | | +| map.cpp:321:7:321:9 | ref arg m12 | map.cpp:438:1:438:1 | m12 | | +| map.cpp:321:7:321:27 | ... = ... | map.cpp:321:11:321:12 | call to at [post update] | | +| map.cpp:321:11:321:12 | call to at [post update] | map.cpp:321:7:321:9 | ref arg m12 | TAINT | +| map.cpp:321:23:321:27 | def | map.cpp:321:7:321:27 | ... = ... | | +| map.cpp:322:7:322:9 | m13 | map.cpp:322:11:322:12 | call to at | TAINT | +| map.cpp:322:7:322:9 | ref arg m13 | map.cpp:326:7:326:9 | m13 | | +| map.cpp:322:7:322:9 | ref arg m13 | map.cpp:438:1:438:1 | m13 | | +| map.cpp:322:7:322:30 | ... = ... | map.cpp:322:11:322:12 | call to at [post update] | | +| map.cpp:322:11:322:12 | call to at [post update] | map.cpp:322:7:322:9 | ref arg m13 | TAINT | +| map.cpp:322:23:322:28 | call to source | map.cpp:322:7:322:30 | ... = ... | | +| map.cpp:323:7:323:9 | m10 | map.cpp:323:10:323:10 | call to operator[] | TAINT | +| map.cpp:323:7:323:9 | ref arg m10 | map.cpp:438:1:438:1 | m10 | | +| map.cpp:324:7:324:9 | m11 | map.cpp:324:10:324:10 | call to operator[] | TAINT | +| map.cpp:324:7:324:9 | ref arg m11 | map.cpp:438:1:438:1 | m11 | | +| map.cpp:325:7:325:9 | m12 | map.cpp:325:10:325:10 | call to operator[] | TAINT | +| map.cpp:325:7:325:9 | ref arg m12 | map.cpp:438:1:438:1 | m12 | | +| map.cpp:326:7:326:9 | m13 | map.cpp:326:10:326:10 | call to operator[] | TAINT | +| map.cpp:326:7:326:9 | ref arg m13 | map.cpp:438:1:438:1 | m13 | | +| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:330:2:330:4 | m14 | | +| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:331:2:331:4 | m14 | | +| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:332:2:332:4 | m14 | | +| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:333:2:333:4 | m14 | | +| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:438:1:438:1 | m14 | | +| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:331:2:331:4 | m14 | | +| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:332:2:332:4 | m14 | | +| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | | +| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | | +| map.cpp:330:13:330:26 | call to make_pair | map.cpp:330:13:330:36 | call to pair | TAINT | +| map.cpp:330:13:330:36 | call to pair | map.cpp:330:2:330:4 | ref arg m14 | TAINT | +| map.cpp:330:13:330:36 | call to pair | map.cpp:330:6:330:11 | call to insert | TAINT | +| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:332:2:332:4 | m14 | | +| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | | +| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | | +| map.cpp:331:13:331:26 | call to make_pair | map.cpp:331:13:331:41 | call to pair | TAINT | +| map.cpp:331:13:331:41 | call to pair | map.cpp:331:2:331:4 | ref arg m14 | TAINT | +| map.cpp:331:13:331:41 | call to pair | map.cpp:331:6:331:11 | call to insert | TAINT | +| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | | +| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | | +| map.cpp:332:13:332:26 | call to make_pair | map.cpp:332:13:332:41 | call to pair | TAINT | +| map.cpp:332:13:332:41 | call to pair | map.cpp:332:2:332:4 | ref arg m14 | TAINT | +| map.cpp:332:13:332:41 | call to pair | map.cpp:332:6:332:11 | call to insert | TAINT | +| map.cpp:333:2:333:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | | +| map.cpp:333:13:333:26 | call to make_pair | map.cpp:333:13:333:36 | call to pair | TAINT | +| map.cpp:333:13:333:36 | call to pair | map.cpp:333:2:333:4 | ref arg m14 | TAINT | +| map.cpp:333:13:333:36 | call to pair | map.cpp:333:6:333:11 | call to insert | TAINT | +| map.cpp:334:7:334:8 | m2 | map.cpp:334:10:334:20 | call to equal_range | TAINT | +| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | | +| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | | +| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | | +| map.cpp:334:27:334:31 | first | map.cpp:334:7:334:31 | call to iterator | | +| map.cpp:335:7:335:8 | m2 | map.cpp:335:10:335:20 | call to equal_range | TAINT | +| map.cpp:335:7:335:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | | +| map.cpp:335:7:335:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | | +| map.cpp:335:27:335:32 | second | map.cpp:335:7:335:32 | call to iterator | | +| map.cpp:336:7:336:8 | m2 | map.cpp:336:10:336:20 | call to equal_range | TAINT | +| map.cpp:336:7:336:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | | +| map.cpp:336:27:336:32 | second | map.cpp:336:7:336:32 | call to iterator | | +| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:340:2:340:4 | m15 | | +| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:342:7:342:9 | m15 | | +| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:346:2:346:4 | m15 | | +| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:348:7:348:9 | m15 | | +| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:438:1:438:1 | m15 | | +| map.cpp:339:42:339:44 | call to unordered_map | map.cpp:343:7:343:9 | m16 | | +| map.cpp:339:42:339:44 | call to unordered_map | map.cpp:346:11:346:13 | m16 | | +| map.cpp:339:42:339:44 | call to unordered_map | map.cpp:349:7:349:9 | m16 | | +| map.cpp:339:42:339:44 | call to unordered_map | map.cpp:438:1:438:1 | m16 | | +| map.cpp:339:47:339:49 | call to unordered_map | map.cpp:344:7:344:9 | m17 | | +| map.cpp:339:47:339:49 | call to unordered_map | map.cpp:347:2:347:4 | m17 | | +| map.cpp:339:47:339:49 | call to unordered_map | map.cpp:350:7:350:9 | m17 | | +| map.cpp:339:47:339:49 | call to unordered_map | map.cpp:438:1:438:1 | m17 | | +| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:341:2:341:4 | m18 | | +| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:345:7:345:9 | m18 | | +| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:347:11:347:13 | m18 | | +| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:351:7:351:9 | m18 | | +| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:438:1:438:1 | m18 | | +| map.cpp:340:2:340:4 | ref arg m15 | map.cpp:342:7:342:9 | m15 | | +| map.cpp:340:2:340:4 | ref arg m15 | map.cpp:346:2:346:4 | m15 | | +| map.cpp:340:2:340:4 | ref arg m15 | map.cpp:348:7:348:9 | m15 | | +| map.cpp:340:2:340:4 | ref arg m15 | map.cpp:438:1:438:1 | m15 | | +| map.cpp:340:13:340:57 | call to pair | map.cpp:340:2:340:4 | ref arg m15 | TAINT | +| map.cpp:340:13:340:57 | call to pair | map.cpp:340:6:340:11 | call to insert | TAINT | +| map.cpp:340:13:340:57 | call to pair | map.cpp:340:13:340:57 | call to pair | TAINT | +| map.cpp:340:49:340:54 | call to source | map.cpp:340:13:340:57 | call to pair | TAINT | +| map.cpp:341:2:341:4 | ref arg m18 | map.cpp:345:7:345:9 | m18 | | +| map.cpp:341:2:341:4 | ref arg m18 | map.cpp:347:11:347:13 | m18 | | +| map.cpp:341:2:341:4 | ref arg m18 | map.cpp:351:7:351:9 | m18 | | +| map.cpp:341:2:341:4 | ref arg m18 | map.cpp:438:1:438:1 | m18 | | +| map.cpp:341:13:341:57 | call to pair | map.cpp:341:2:341:4 | ref arg m18 | TAINT | +| map.cpp:341:13:341:57 | call to pair | map.cpp:341:6:341:11 | call to insert | TAINT | +| map.cpp:341:13:341:57 | call to pair | map.cpp:341:13:341:57 | call to pair | TAINT | +| map.cpp:341:49:341:54 | call to source | map.cpp:341:13:341:57 | call to pair | TAINT | +| map.cpp:342:7:342:9 | m15 | map.cpp:342:7:342:9 | call to unordered_map | | +| map.cpp:343:7:343:9 | m16 | map.cpp:343:7:343:9 | call to unordered_map | | +| map.cpp:344:7:344:9 | m17 | map.cpp:344:7:344:9 | call to unordered_map | | +| map.cpp:345:7:345:9 | m18 | map.cpp:345:7:345:9 | call to unordered_map | | +| map.cpp:346:2:346:4 | m15 | map.cpp:346:11:346:13 | ref arg m16 | TAINT | +| map.cpp:346:2:346:4 | ref arg m15 | map.cpp:348:7:348:9 | m15 | | +| map.cpp:346:2:346:4 | ref arg m15 | map.cpp:438:1:438:1 | m15 | | +| map.cpp:346:11:346:13 | m16 | map.cpp:346:2:346:4 | ref arg m15 | TAINT | +| map.cpp:346:11:346:13 | ref arg m16 | map.cpp:349:7:349:9 | m16 | | +| map.cpp:346:11:346:13 | ref arg m16 | map.cpp:438:1:438:1 | m16 | | +| map.cpp:347:2:347:4 | m17 | map.cpp:347:11:347:13 | ref arg m18 | TAINT | +| map.cpp:347:2:347:4 | ref arg m17 | map.cpp:350:7:350:9 | m17 | | +| map.cpp:347:2:347:4 | ref arg m17 | map.cpp:438:1:438:1 | m17 | | +| map.cpp:347:11:347:13 | m18 | map.cpp:347:2:347:4 | ref arg m17 | TAINT | +| map.cpp:347:11:347:13 | ref arg m18 | map.cpp:351:7:351:9 | m18 | | +| map.cpp:347:11:347:13 | ref arg m18 | map.cpp:438:1:438:1 | m18 | | +| map.cpp:348:7:348:9 | m15 | map.cpp:348:7:348:9 | call to unordered_map | | +| map.cpp:349:7:349:9 | m16 | map.cpp:349:7:349:9 | call to unordered_map | | +| map.cpp:350:7:350:9 | m17 | map.cpp:350:7:350:9 | call to unordered_map | | +| map.cpp:351:7:351:9 | m18 | map.cpp:351:7:351:9 | call to unordered_map | | +| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:355:2:355:4 | m19 | | +| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:359:7:359:9 | m19 | | +| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:363:2:363:4 | m19 | | +| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:365:7:365:9 | m19 | | +| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:438:1:438:1 | m19 | | +| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:356:2:356:4 | m20 | | +| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:360:7:360:9 | m20 | | +| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:363:12:363:14 | m20 | | +| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:366:7:366:9 | m20 | | +| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:438:1:438:1 | m20 | | +| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:357:2:357:4 | m21 | | +| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:361:7:361:9 | m21 | | +| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:364:2:364:4 | m21 | | +| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:367:7:367:9 | m21 | | +| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:438:1:438:1 | m21 | | +| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:358:2:358:4 | m22 | | +| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:362:7:362:9 | m22 | | +| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:364:12:364:14 | m22 | | +| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:368:7:368:9 | m22 | | +| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:438:1:438:1 | m22 | | +| map.cpp:355:2:355:4 | ref arg m19 | map.cpp:359:7:359:9 | m19 | | +| map.cpp:355:2:355:4 | ref arg m19 | map.cpp:363:2:363:4 | m19 | | +| map.cpp:355:2:355:4 | ref arg m19 | map.cpp:365:7:365:9 | m19 | | +| map.cpp:355:2:355:4 | ref arg m19 | map.cpp:438:1:438:1 | m19 | | +| map.cpp:355:13:355:57 | call to pair | map.cpp:355:2:355:4 | ref arg m19 | TAINT | +| map.cpp:355:13:355:57 | call to pair | map.cpp:355:6:355:11 | call to insert | TAINT | +| map.cpp:355:13:355:57 | call to pair | map.cpp:355:13:355:57 | call to pair | TAINT | +| map.cpp:355:49:355:54 | call to source | map.cpp:355:13:355:57 | call to pair | TAINT | +| map.cpp:356:2:356:4 | ref arg m20 | map.cpp:360:7:360:9 | m20 | | +| map.cpp:356:2:356:4 | ref arg m20 | map.cpp:363:12:363:14 | m20 | | +| map.cpp:356:2:356:4 | ref arg m20 | map.cpp:366:7:366:9 | m20 | | +| map.cpp:356:2:356:4 | ref arg m20 | map.cpp:438:1:438:1 | m20 | | +| map.cpp:356:13:356:51 | call to pair | map.cpp:356:2:356:4 | ref arg m20 | TAINT | +| map.cpp:356:13:356:51 | call to pair | map.cpp:356:6:356:11 | call to insert | TAINT | +| map.cpp:356:13:356:51 | call to pair | map.cpp:356:13:356:51 | call to pair | TAINT | +| map.cpp:356:46:356:50 | def | map.cpp:356:13:356:51 | call to pair | TAINT | +| map.cpp:357:2:357:4 | ref arg m21 | map.cpp:361:7:361:9 | m21 | | +| map.cpp:357:2:357:4 | ref arg m21 | map.cpp:364:2:364:4 | m21 | | +| map.cpp:357:2:357:4 | ref arg m21 | map.cpp:367:7:367:9 | m21 | | +| map.cpp:357:2:357:4 | ref arg m21 | map.cpp:438:1:438:1 | m21 | | +| map.cpp:357:13:357:51 | call to pair | map.cpp:357:2:357:4 | ref arg m21 | TAINT | +| map.cpp:357:13:357:51 | call to pair | map.cpp:357:6:357:11 | call to insert | TAINT | +| map.cpp:357:13:357:51 | call to pair | map.cpp:357:13:357:51 | call to pair | TAINT | +| map.cpp:357:46:357:50 | def | map.cpp:357:13:357:51 | call to pair | TAINT | +| map.cpp:358:2:358:4 | ref arg m22 | map.cpp:362:7:362:9 | m22 | | +| map.cpp:358:2:358:4 | ref arg m22 | map.cpp:364:12:364:14 | m22 | | +| map.cpp:358:2:358:4 | ref arg m22 | map.cpp:368:7:368:9 | m22 | | +| map.cpp:358:2:358:4 | ref arg m22 | map.cpp:438:1:438:1 | m22 | | +| map.cpp:358:13:358:57 | call to pair | map.cpp:358:2:358:4 | ref arg m22 | TAINT | +| map.cpp:358:13:358:57 | call to pair | map.cpp:358:6:358:11 | call to insert | TAINT | +| map.cpp:358:13:358:57 | call to pair | map.cpp:358:13:358:57 | call to pair | TAINT | +| map.cpp:358:49:358:54 | call to source | map.cpp:358:13:358:57 | call to pair | TAINT | +| map.cpp:359:7:359:9 | m19 | map.cpp:359:7:359:9 | call to unordered_map | | +| map.cpp:360:7:360:9 | m20 | map.cpp:360:7:360:9 | call to unordered_map | | +| map.cpp:361:7:361:9 | m21 | map.cpp:361:7:361:9 | call to unordered_map | | +| map.cpp:362:7:362:9 | m22 | map.cpp:362:7:362:9 | call to unordered_map | | +| map.cpp:363:2:363:4 | ref arg m19 | map.cpp:365:7:365:9 | m19 | | +| map.cpp:363:2:363:4 | ref arg m19 | map.cpp:438:1:438:1 | m19 | | +| map.cpp:363:12:363:14 | m20 | map.cpp:363:2:363:4 | ref arg m19 | TAINT | +| map.cpp:363:12:363:14 | ref arg m20 | map.cpp:366:7:366:9 | m20 | | +| map.cpp:363:12:363:14 | ref arg m20 | map.cpp:438:1:438:1 | m20 | | +| map.cpp:364:2:364:4 | ref arg m21 | map.cpp:367:7:367:9 | m21 | | +| map.cpp:364:2:364:4 | ref arg m21 | map.cpp:438:1:438:1 | m21 | | +| map.cpp:364:12:364:14 | m22 | map.cpp:364:2:364:4 | ref arg m21 | TAINT | +| map.cpp:364:12:364:14 | ref arg m22 | map.cpp:368:7:368:9 | m22 | | +| map.cpp:364:12:364:14 | ref arg m22 | map.cpp:438:1:438:1 | m22 | | +| map.cpp:365:7:365:9 | m19 | map.cpp:365:7:365:9 | call to unordered_map | | +| map.cpp:366:7:366:9 | m20 | map.cpp:366:7:366:9 | call to unordered_map | | +| map.cpp:367:7:367:9 | m21 | map.cpp:367:7:367:9 | call to unordered_map | | +| map.cpp:368:7:368:9 | m22 | map.cpp:368:7:368:9 | call to unordered_map | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:372:2:372:4 | m23 | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:373:2:373:4 | m23 | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:374:7:374:9 | m23 | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:375:7:375:9 | m23 | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:375:17:375:19 | m23 | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:376:7:376:9 | m23 | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:377:2:377:4 | m23 | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:378:7:378:9 | m23 | | +| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:438:1:438:1 | m23 | | +| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:373:2:373:4 | m23 | | +| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:374:7:374:9 | m23 | | +| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:375:7:375:9 | m23 | | +| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:375:17:375:19 | m23 | | +| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:376:7:376:9 | m23 | | +| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:377:2:377:4 | m23 | | +| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:378:7:378:9 | m23 | | +| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:438:1:438:1 | m23 | | +| map.cpp:372:13:372:57 | call to pair | map.cpp:372:2:372:4 | ref arg m23 | TAINT | +| map.cpp:372:13:372:57 | call to pair | map.cpp:372:6:372:11 | call to insert | TAINT | +| map.cpp:372:13:372:57 | call to pair | map.cpp:372:13:372:57 | call to pair | TAINT | +| map.cpp:372:49:372:54 | call to source | map.cpp:372:13:372:57 | call to pair | TAINT | +| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:374:7:374:9 | m23 | | +| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:375:7:375:9 | m23 | | +| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:375:17:375:19 | m23 | | +| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:376:7:376:9 | m23 | | +| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:377:2:377:4 | m23 | | +| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:378:7:378:9 | m23 | | +| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:438:1:438:1 | m23 | | +| map.cpp:373:13:373:57 | call to pair | map.cpp:373:2:373:4 | ref arg m23 | TAINT | +| map.cpp:373:13:373:57 | call to pair | map.cpp:373:6:373:11 | call to insert | TAINT | +| map.cpp:373:13:373:57 | call to pair | map.cpp:373:13:373:57 | call to pair | TAINT | +| map.cpp:373:49:373:54 | call to source | map.cpp:373:13:373:57 | call to pair | TAINT | +| map.cpp:374:7:374:9 | m23 | map.cpp:374:7:374:9 | call to unordered_map | | +| map.cpp:375:7:375:9 | m23 | map.cpp:375:11:375:15 | call to erase | TAINT | +| map.cpp:375:7:375:9 | ref arg m23 | map.cpp:376:7:376:9 | m23 | | +| map.cpp:375:7:375:9 | ref arg m23 | map.cpp:377:2:377:4 | m23 | | +| map.cpp:375:7:375:9 | ref arg m23 | map.cpp:378:7:378:9 | m23 | | +| map.cpp:375:7:375:9 | ref arg m23 | map.cpp:438:1:438:1 | m23 | | +| map.cpp:375:17:375:19 | m23 | map.cpp:375:21:375:25 | call to begin | TAINT | +| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:375:7:375:9 | m23 | | +| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:376:7:376:9 | m23 | | +| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:377:2:377:4 | m23 | | +| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:378:7:378:9 | m23 | | +| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:438:1:438:1 | m23 | | +| map.cpp:376:7:376:9 | m23 | map.cpp:376:7:376:9 | call to unordered_map | | +| map.cpp:377:2:377:4 | ref arg m23 | map.cpp:378:7:378:9 | m23 | | +| map.cpp:377:2:377:4 | ref arg m23 | map.cpp:438:1:438:1 | m23 | | +| map.cpp:378:7:378:9 | m23 | map.cpp:378:7:378:9 | call to unordered_map | | +| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:382:7:382:9 | m24 | | +| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:383:7:383:9 | m24 | | +| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:384:7:384:9 | m24 | | +| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:385:7:385:9 | m24 | | +| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:438:1:438:1 | m24 | | +| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:386:7:386:9 | m25 | | +| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:386:24:386:26 | m25 | | +| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:387:7:387:9 | m25 | | +| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:388:7:388:9 | m25 | | +| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:388:24:388:26 | m25 | | +| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:389:7:389:9 | m25 | | +| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:438:1:438:1 | m25 | | +| map.cpp:382:7:382:9 | m24 | map.cpp:382:11:382:17 | call to emplace | TAINT | +| map.cpp:382:7:382:9 | ref arg m24 | map.cpp:383:7:383:9 | m24 | | +| map.cpp:382:7:382:9 | ref arg m24 | map.cpp:384:7:384:9 | m24 | | +| map.cpp:382:7:382:9 | ref arg m24 | map.cpp:385:7:385:9 | m24 | | +| map.cpp:382:7:382:9 | ref arg m24 | map.cpp:438:1:438:1 | m24 | | +| map.cpp:382:26:382:30 | def | map.cpp:382:7:382:9 | ref arg m24 | TAINT | +| map.cpp:382:26:382:30 | def | map.cpp:382:11:382:17 | call to emplace | TAINT | +| map.cpp:382:33:382:37 | first | map.cpp:382:7:382:37 | call to iterator | | +| map.cpp:383:7:383:9 | m24 | map.cpp:383:7:383:9 | call to unordered_map | | +| map.cpp:384:7:384:9 | m24 | map.cpp:384:11:384:17 | call to emplace | TAINT | +| map.cpp:384:7:384:9 | ref arg m24 | map.cpp:385:7:385:9 | m24 | | +| map.cpp:384:7:384:9 | ref arg m24 | map.cpp:438:1:438:1 | m24 | | +| map.cpp:384:26:384:31 | call to source | map.cpp:384:7:384:9 | ref arg m24 | TAINT | +| map.cpp:384:26:384:31 | call to source | map.cpp:384:11:384:17 | call to emplace | TAINT | +| map.cpp:384:36:384:40 | first | map.cpp:384:7:384:40 | call to iterator | | +| map.cpp:385:7:385:9 | m24 | map.cpp:385:7:385:9 | call to unordered_map | | +| map.cpp:386:7:386:9 | m25 | map.cpp:386:11:386:22 | call to emplace_hint | TAINT | +| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:387:7:387:9 | m25 | | +| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:388:7:388:9 | m25 | | +| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:388:24:388:26 | m25 | | +| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:389:7:389:9 | m25 | | +| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:438:1:438:1 | m25 | | +| map.cpp:386:24:386:26 | m25 | map.cpp:386:28:386:32 | call to begin | TAINT | +| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:386:7:386:9 | m25 | | +| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:387:7:387:9 | m25 | | +| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:388:7:388:9 | m25 | | +| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:388:24:388:26 | m25 | | +| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:389:7:389:9 | m25 | | +| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:438:1:438:1 | m25 | | +| map.cpp:386:28:386:32 | call to begin | map.cpp:386:24:386:34 | call to iterator | TAINT | +| map.cpp:386:44:386:48 | def | map.cpp:386:7:386:9 | ref arg m25 | TAINT | +| map.cpp:386:44:386:48 | def | map.cpp:386:11:386:22 | call to emplace_hint | TAINT | +| map.cpp:387:7:387:9 | m25 | map.cpp:387:7:387:9 | call to unordered_map | | +| map.cpp:388:7:388:9 | m25 | map.cpp:388:11:388:22 | call to emplace_hint | TAINT | +| map.cpp:388:7:388:9 | ref arg m25 | map.cpp:389:7:389:9 | m25 | | +| map.cpp:388:7:388:9 | ref arg m25 | map.cpp:438:1:438:1 | m25 | | +| map.cpp:388:24:388:26 | m25 | map.cpp:388:28:388:32 | call to begin | TAINT | +| map.cpp:388:24:388:26 | ref arg m25 | map.cpp:388:7:388:9 | m25 | | +| map.cpp:388:24:388:26 | ref arg m25 | map.cpp:389:7:389:9 | m25 | | +| map.cpp:388:24:388:26 | ref arg m25 | map.cpp:438:1:438:1 | m25 | | +| map.cpp:388:28:388:32 | call to begin | map.cpp:388:24:388:34 | call to iterator | TAINT | +| map.cpp:388:44:388:49 | call to source | map.cpp:388:7:388:9 | ref arg m25 | TAINT | +| map.cpp:388:44:388:49 | call to source | map.cpp:388:11:388:22 | call to emplace_hint | TAINT | +| map.cpp:389:7:389:9 | m25 | map.cpp:389:7:389:9 | call to unordered_map | | +| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:393:7:393:9 | m26 | | +| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:394:7:394:9 | m26 | | +| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:395:7:395:9 | m26 | | +| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:396:7:396:9 | m26 | | +| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:397:7:397:9 | m26 | | +| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:398:7:398:9 | m26 | | +| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:438:1:438:1 | m26 | | +| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:399:7:399:9 | m27 | | +| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:399:23:399:25 | m27 | | +| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:400:7:400:9 | m27 | | +| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:401:7:401:9 | m27 | | +| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:401:23:401:25 | m27 | | +| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:402:7:402:9 | m27 | | +| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:438:1:438:1 | m27 | | +| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:403:7:403:9 | m28 | | +| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:403:23:403:25 | m28 | | +| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:404:7:404:9 | m28 | | +| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:405:7:405:9 | m28 | | +| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:405:23:405:25 | m28 | | +| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:406:7:406:9 | m28 | | +| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:438:1:438:1 | m28 | | +| map.cpp:393:7:393:9 | m26 | map.cpp:393:11:393:21 | call to try_emplace | TAINT | +| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:394:7:394:9 | m26 | | +| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:395:7:395:9 | m26 | | +| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:396:7:396:9 | m26 | | +| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:397:7:397:9 | m26 | | +| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:398:7:398:9 | m26 | | +| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:438:1:438:1 | m26 | | +| map.cpp:393:30:393:34 | def | map.cpp:393:7:393:9 | ref arg m26 | TAINT | +| map.cpp:393:30:393:34 | def | map.cpp:393:11:393:21 | call to try_emplace | TAINT | +| map.cpp:393:37:393:41 | first | map.cpp:393:7:393:41 | call to iterator | | +| map.cpp:394:7:394:9 | m26 | map.cpp:394:11:394:21 | call to try_emplace | TAINT | +| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:395:7:395:9 | m26 | | +| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:396:7:396:9 | m26 | | +| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:397:7:397:9 | m26 | | +| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:398:7:398:9 | m26 | | +| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:438:1:438:1 | m26 | | +| map.cpp:394:30:394:34 | def | map.cpp:394:7:394:9 | ref arg m26 | TAINT | +| map.cpp:394:30:394:34 | def | map.cpp:394:11:394:21 | call to try_emplace | TAINT | +| map.cpp:395:7:395:9 | m26 | map.cpp:395:7:395:9 | call to unordered_map | | +| map.cpp:396:7:396:9 | m26 | map.cpp:396:11:396:21 | call to try_emplace | TAINT | +| map.cpp:396:7:396:9 | ref arg m26 | map.cpp:397:7:397:9 | m26 | | +| map.cpp:396:7:396:9 | ref arg m26 | map.cpp:398:7:398:9 | m26 | | +| map.cpp:396:7:396:9 | ref arg m26 | map.cpp:438:1:438:1 | m26 | | +| map.cpp:396:30:396:35 | call to source | map.cpp:396:7:396:9 | ref arg m26 | TAINT | +| map.cpp:396:30:396:35 | call to source | map.cpp:396:11:396:21 | call to try_emplace | TAINT | +| map.cpp:396:40:396:44 | first | map.cpp:396:7:396:44 | call to iterator | | +| map.cpp:397:7:397:9 | m26 | map.cpp:397:11:397:21 | call to try_emplace | TAINT | +| map.cpp:397:7:397:9 | ref arg m26 | map.cpp:398:7:398:9 | m26 | | +| map.cpp:397:7:397:9 | ref arg m26 | map.cpp:438:1:438:1 | m26 | | +| map.cpp:397:30:397:35 | call to source | map.cpp:397:7:397:9 | ref arg m26 | TAINT | +| map.cpp:397:30:397:35 | call to source | map.cpp:397:11:397:21 | call to try_emplace | TAINT | +| map.cpp:398:7:398:9 | m26 | map.cpp:398:7:398:9 | call to unordered_map | | +| map.cpp:399:7:399:9 | m27 | map.cpp:399:11:399:21 | call to try_emplace | TAINT | +| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:400:7:400:9 | m27 | | +| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:401:7:401:9 | m27 | | +| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:401:23:401:25 | m27 | | +| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:402:7:402:9 | m27 | | +| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:438:1:438:1 | m27 | | +| map.cpp:399:23:399:25 | m27 | map.cpp:399:27:399:31 | call to begin | TAINT | +| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:399:7:399:9 | m27 | | +| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:400:7:400:9 | m27 | | +| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:401:7:401:9 | m27 | | +| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:401:23:401:25 | m27 | | +| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:402:7:402:9 | m27 | | +| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:438:1:438:1 | m27 | | +| map.cpp:399:27:399:31 | call to begin | map.cpp:399:23:399:33 | call to iterator | TAINT | +| map.cpp:399:43:399:47 | def | map.cpp:399:7:399:9 | ref arg m27 | TAINT | +| map.cpp:399:43:399:47 | def | map.cpp:399:11:399:21 | call to try_emplace | TAINT | +| map.cpp:400:7:400:9 | m27 | map.cpp:400:7:400:9 | call to unordered_map | | +| map.cpp:401:7:401:9 | m27 | map.cpp:401:11:401:21 | call to try_emplace | TAINT | +| map.cpp:401:7:401:9 | ref arg m27 | map.cpp:402:7:402:9 | m27 | | +| map.cpp:401:7:401:9 | ref arg m27 | map.cpp:438:1:438:1 | m27 | | +| map.cpp:401:23:401:25 | m27 | map.cpp:401:27:401:31 | call to begin | TAINT | +| map.cpp:401:23:401:25 | ref arg m27 | map.cpp:401:7:401:9 | m27 | | +| map.cpp:401:23:401:25 | ref arg m27 | map.cpp:402:7:402:9 | m27 | | +| map.cpp:401:23:401:25 | ref arg m27 | map.cpp:438:1:438:1 | m27 | | +| map.cpp:401:27:401:31 | call to begin | map.cpp:401:23:401:33 | call to iterator | TAINT | +| map.cpp:401:43:401:48 | call to source | map.cpp:401:7:401:9 | ref arg m27 | TAINT | +| map.cpp:401:43:401:48 | call to source | map.cpp:401:11:401:21 | call to try_emplace | TAINT | +| map.cpp:402:7:402:9 | m27 | map.cpp:402:7:402:9 | call to unordered_map | | +| map.cpp:403:7:403:9 | m28 | map.cpp:403:11:403:21 | call to try_emplace | TAINT | +| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:404:7:404:9 | m28 | | +| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:405:7:405:9 | m28 | | +| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:405:23:405:25 | m28 | | +| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:406:7:406:9 | m28 | | +| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:438:1:438:1 | m28 | | +| map.cpp:403:23:403:25 | m28 | map.cpp:403:27:403:31 | call to begin | TAINT | +| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:403:7:403:9 | m28 | | +| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:404:7:404:9 | m28 | | +| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:405:7:405:9 | m28 | | +| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:405:23:405:25 | m28 | | +| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:406:7:406:9 | m28 | | +| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:438:1:438:1 | m28 | | +| map.cpp:403:27:403:31 | call to begin | map.cpp:403:23:403:33 | call to iterator | TAINT | +| map.cpp:403:43:403:47 | def | map.cpp:403:7:403:9 | ref arg m28 | TAINT | +| map.cpp:403:43:403:47 | def | map.cpp:403:11:403:21 | call to try_emplace | TAINT | +| map.cpp:404:7:404:9 | m28 | map.cpp:404:7:404:9 | call to unordered_map | | +| map.cpp:405:7:405:9 | m28 | map.cpp:405:11:405:21 | call to try_emplace | TAINT | +| map.cpp:405:7:405:9 | ref arg m28 | map.cpp:406:7:406:9 | m28 | | +| map.cpp:405:7:405:9 | ref arg m28 | map.cpp:438:1:438:1 | m28 | | +| map.cpp:405:23:405:25 | m28 | map.cpp:405:27:405:31 | call to begin | TAINT | +| map.cpp:405:23:405:25 | ref arg m28 | map.cpp:405:7:405:9 | m28 | | +| map.cpp:405:23:405:25 | ref arg m28 | map.cpp:406:7:406:9 | m28 | | +| map.cpp:405:23:405:25 | ref arg m28 | map.cpp:438:1:438:1 | m28 | | +| map.cpp:405:27:405:31 | call to begin | map.cpp:405:23:405:33 | call to iterator | TAINT | +| map.cpp:405:46:405:50 | def | map.cpp:405:7:405:9 | ref arg m28 | TAINT | +| map.cpp:405:46:405:50 | def | map.cpp:405:11:405:21 | call to try_emplace | TAINT | +| map.cpp:406:7:406:9 | m28 | map.cpp:406:7:406:9 | call to unordered_map | | +| map.cpp:409:50:409:52 | call to unordered_map | map.cpp:410:7:410:9 | m29 | | +| map.cpp:409:50:409:52 | call to unordered_map | map.cpp:411:7:411:9 | m29 | | +| map.cpp:409:50:409:52 | call to unordered_map | map.cpp:412:7:412:9 | m29 | | +| map.cpp:409:50:409:52 | call to unordered_map | map.cpp:438:1:438:1 | m29 | | +| map.cpp:409:55:409:57 | call to unordered_map | map.cpp:413:7:413:9 | m30 | | +| map.cpp:409:55:409:57 | call to unordered_map | map.cpp:414:7:414:9 | m30 | | +| map.cpp:409:55:409:57 | call to unordered_map | map.cpp:415:7:415:9 | m30 | | +| map.cpp:409:55:409:57 | call to unordered_map | map.cpp:438:1:438:1 | m30 | | +| map.cpp:409:60:409:62 | call to unordered_map | map.cpp:416:7:416:9 | m31 | | +| map.cpp:409:60:409:62 | call to unordered_map | map.cpp:417:7:417:9 | m31 | | +| map.cpp:409:60:409:62 | call to unordered_map | map.cpp:418:7:418:9 | m31 | | +| map.cpp:409:60:409:62 | call to unordered_map | map.cpp:438:1:438:1 | m31 | | +| map.cpp:409:65:409:67 | call to unordered_map | map.cpp:419:7:419:9 | m32 | | +| map.cpp:409:65:409:67 | call to unordered_map | map.cpp:420:7:420:9 | m32 | | +| map.cpp:409:65:409:67 | call to unordered_map | map.cpp:421:7:421:9 | m32 | | +| map.cpp:409:65:409:67 | call to unordered_map | map.cpp:438:1:438:1 | m32 | | +| map.cpp:410:7:410:9 | m29 | map.cpp:410:11:410:21 | call to try_emplace | TAINT | +| map.cpp:410:7:410:9 | ref arg m29 | map.cpp:411:7:411:9 | m29 | | +| map.cpp:410:7:410:9 | ref arg m29 | map.cpp:412:7:412:9 | m29 | | +| map.cpp:410:7:410:9 | ref arg m29 | map.cpp:438:1:438:1 | m29 | | +| map.cpp:410:11:410:21 | call to try_emplace | map.cpp:410:7:410:34 | call to pair | TAINT | +| map.cpp:410:30:410:30 | 1 | map.cpp:410:7:410:9 | ref arg m29 | TAINT | +| map.cpp:410:30:410:30 | 1 | map.cpp:410:11:410:21 | call to try_emplace | TAINT | +| map.cpp:410:33:410:33 | 2 | map.cpp:410:7:410:9 | ref arg m29 | TAINT | +| map.cpp:410:33:410:33 | 2 | map.cpp:410:11:410:21 | call to try_emplace | TAINT | +| map.cpp:411:7:411:9 | m29 | map.cpp:411:7:411:9 | call to unordered_map | | +| map.cpp:412:7:412:9 | m29 | map.cpp:412:10:412:10 | call to operator[] | TAINT | +| map.cpp:412:7:412:9 | ref arg m29 | map.cpp:438:1:438:1 | m29 | | +| map.cpp:412:10:412:10 | call to operator[] | map.cpp:412:7:412:16 | call to pair | TAINT | +| map.cpp:413:7:413:9 | m30 | map.cpp:413:11:413:21 | call to try_emplace | TAINT | +| map.cpp:413:7:413:9 | ref arg m30 | map.cpp:414:7:414:9 | m30 | | +| map.cpp:413:7:413:9 | ref arg m30 | map.cpp:415:7:415:9 | m30 | | +| map.cpp:413:7:413:9 | ref arg m30 | map.cpp:438:1:438:1 | m30 | | +| map.cpp:413:11:413:21 | call to try_emplace | map.cpp:413:7:413:37 | call to pair | TAINT | +| map.cpp:413:33:413:33 | 1 | map.cpp:413:7:413:9 | ref arg m30 | TAINT | +| map.cpp:413:33:413:33 | 1 | map.cpp:413:11:413:21 | call to try_emplace | TAINT | +| map.cpp:413:36:413:36 | 2 | map.cpp:413:7:413:9 | ref arg m30 | TAINT | +| map.cpp:413:36:413:36 | 2 | map.cpp:413:11:413:21 | call to try_emplace | TAINT | +| map.cpp:414:7:414:9 | m30 | map.cpp:414:7:414:9 | call to unordered_map | | +| map.cpp:415:7:415:9 | m30 | map.cpp:415:10:415:10 | call to operator[] | TAINT | +| map.cpp:415:7:415:9 | ref arg m30 | map.cpp:438:1:438:1 | m30 | | +| map.cpp:415:10:415:10 | call to operator[] | map.cpp:415:7:415:16 | call to pair | TAINT | +| map.cpp:416:7:416:9 | m31 | map.cpp:416:11:416:21 | call to try_emplace | TAINT | +| map.cpp:416:7:416:9 | ref arg m31 | map.cpp:417:7:417:9 | m31 | | +| map.cpp:416:7:416:9 | ref arg m31 | map.cpp:418:7:418:9 | m31 | | +| map.cpp:416:7:416:9 | ref arg m31 | map.cpp:438:1:438:1 | m31 | | +| map.cpp:416:11:416:21 | call to try_emplace | map.cpp:416:7:416:41 | call to pair | TAINT | +| map.cpp:416:30:416:35 | call to source | map.cpp:416:7:416:9 | ref arg m31 | TAINT | +| map.cpp:416:30:416:35 | call to source | map.cpp:416:11:416:21 | call to try_emplace | TAINT | +| map.cpp:416:40:416:40 | 2 | map.cpp:416:7:416:9 | ref arg m31 | TAINT | +| map.cpp:416:40:416:40 | 2 | map.cpp:416:11:416:21 | call to try_emplace | TAINT | +| map.cpp:417:7:417:9 | m31 | map.cpp:417:7:417:9 | call to unordered_map | | +| map.cpp:418:7:418:9 | m31 | map.cpp:418:10:418:10 | call to operator[] | TAINT | +| map.cpp:418:7:418:9 | ref arg m31 | map.cpp:438:1:438:1 | m31 | | +| map.cpp:418:10:418:10 | call to operator[] | map.cpp:418:7:418:16 | call to pair | TAINT | +| map.cpp:419:7:419:9 | m32 | map.cpp:419:11:419:21 | call to try_emplace | TAINT | +| map.cpp:419:7:419:9 | ref arg m32 | map.cpp:420:7:420:9 | m32 | | +| map.cpp:419:7:419:9 | ref arg m32 | map.cpp:421:7:421:9 | m32 | | +| map.cpp:419:7:419:9 | ref arg m32 | map.cpp:438:1:438:1 | m32 | | +| map.cpp:419:11:419:21 | call to try_emplace | map.cpp:419:7:419:41 | call to pair | TAINT | +| map.cpp:419:30:419:30 | 1 | map.cpp:419:7:419:9 | ref arg m32 | TAINT | +| map.cpp:419:30:419:30 | 1 | map.cpp:419:11:419:21 | call to try_emplace | TAINT | +| map.cpp:419:33:419:38 | call to source | map.cpp:419:7:419:9 | ref arg m32 | TAINT | +| map.cpp:419:33:419:38 | call to source | map.cpp:419:11:419:21 | call to try_emplace | TAINT | +| map.cpp:420:7:420:9 | m32 | map.cpp:420:7:420:9 | call to unordered_map | | +| map.cpp:421:7:421:9 | m32 | map.cpp:421:10:421:10 | call to operator[] | TAINT | +| map.cpp:421:7:421:9 | ref arg m32 | map.cpp:438:1:438:1 | m32 | | +| map.cpp:421:10:421:10 | call to operator[] | map.cpp:421:7:421:16 | call to pair | TAINT | +| map.cpp:424:37:424:39 | call to unordered_map | map.cpp:425:7:425:9 | m33 | | +| map.cpp:424:37:424:39 | call to unordered_map | map.cpp:426:7:426:9 | m33 | | +| map.cpp:424:37:424:39 | call to unordered_map | map.cpp:438:1:438:1 | m33 | | +| map.cpp:425:7:425:9 | m33 | map.cpp:425:11:425:17 | call to emplace | TAINT | +| map.cpp:425:7:425:9 | ref arg m33 | map.cpp:426:7:426:9 | m33 | | +| map.cpp:425:7:425:9 | ref arg m33 | map.cpp:438:1:438:1 | m33 | | +| map.cpp:425:29:425:33 | def | map.cpp:425:7:425:9 | ref arg m33 | TAINT | +| map.cpp:425:29:425:33 | def | map.cpp:425:11:425:17 | call to emplace | TAINT | +| map.cpp:425:36:425:40 | first | map.cpp:425:7:425:40 | call to iterator | | +| map.cpp:426:7:426:9 | m33 | map.cpp:426:7:426:9 | call to unordered_map | | +| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:429:7:429:9 | m34 | | +| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:430:7:430:9 | m34 | | +| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:431:7:431:9 | m34 | | +| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:432:7:432:9 | m34 | | +| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:433:7:433:9 | m34 | | +| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:433:24:433:26 | m34 | | +| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:438:1:438:1 | m34 | | +| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:434:7:434:9 | m35 | | +| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:435:7:435:9 | m35 | | +| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:436:7:436:9 | m35 | | +| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:437:7:437:9 | m35 | | +| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:438:1:438:1 | m35 | | +| map.cpp:429:7:429:9 | m34 | map.cpp:429:11:429:17 | call to emplace | TAINT | +| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:430:7:430:9 | m34 | | +| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:431:7:431:9 | m34 | | +| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:432:7:432:9 | m34 | | +| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:433:7:433:9 | m34 | | +| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:433:24:433:26 | m34 | | +| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:438:1:438:1 | m34 | | +| map.cpp:429:19:429:57 | call to pair | map.cpp:429:7:429:9 | ref arg m34 | TAINT | +| map.cpp:429:19:429:57 | call to pair | map.cpp:429:11:429:17 | call to emplace | TAINT | +| map.cpp:429:52:429:56 | def | map.cpp:429:19:429:57 | call to pair | TAINT | +| map.cpp:429:60:429:64 | first | map.cpp:429:7:429:64 | call to iterator | | +| map.cpp:430:7:430:9 | m34 | map.cpp:430:7:430:9 | call to unordered_map | | +| map.cpp:431:7:431:9 | m34 | map.cpp:431:11:431:17 | call to emplace | TAINT | +| map.cpp:431:7:431:9 | ref arg m34 | map.cpp:432:7:432:9 | m34 | | +| map.cpp:431:7:431:9 | ref arg m34 | map.cpp:433:7:433:9 | m34 | | +| map.cpp:431:7:431:9 | ref arg m34 | map.cpp:433:24:433:26 | m34 | | +| map.cpp:431:7:431:9 | ref arg m34 | map.cpp:438:1:438:1 | m34 | | +| map.cpp:431:19:431:60 | call to pair | map.cpp:431:7:431:9 | ref arg m34 | TAINT | +| map.cpp:431:19:431:60 | call to pair | map.cpp:431:11:431:17 | call to emplace | TAINT | +| map.cpp:431:52:431:57 | call to source | map.cpp:431:19:431:60 | call to pair | TAINT | +| map.cpp:431:63:431:67 | first | map.cpp:431:7:431:67 | call to iterator | | +| map.cpp:432:7:432:9 | m34 | map.cpp:432:7:432:9 | call to unordered_map | | +| map.cpp:433:7:433:9 | m34 | map.cpp:433:11:433:22 | call to emplace_hint | TAINT | +| map.cpp:433:7:433:9 | ref arg m34 | map.cpp:438:1:438:1 | m34 | | +| map.cpp:433:24:433:26 | m34 | map.cpp:433:28:433:32 | call to begin | TAINT | +| map.cpp:433:24:433:26 | ref arg m34 | map.cpp:433:7:433:9 | m34 | | +| map.cpp:433:24:433:26 | ref arg m34 | map.cpp:438:1:438:1 | m34 | | +| map.cpp:433:28:433:32 | call to begin | map.cpp:433:24:433:34 | call to iterator | TAINT | +| map.cpp:433:44:433:48 | def | map.cpp:433:7:433:9 | ref arg m34 | TAINT | +| map.cpp:433:44:433:48 | def | map.cpp:433:11:433:22 | call to emplace_hint | TAINT | +| map.cpp:434:7:434:9 | m35 | map.cpp:434:7:434:9 | ref arg m35 | TAINT | +| map.cpp:434:7:434:9 | m35 | map.cpp:434:11:434:17 | call to emplace | TAINT | +| map.cpp:434:7:434:9 | ref arg m35 | map.cpp:435:7:435:9 | m35 | | +| map.cpp:434:7:434:9 | ref arg m35 | map.cpp:436:7:436:9 | m35 | | +| map.cpp:434:7:434:9 | ref arg m35 | map.cpp:437:7:437:9 | m35 | | +| map.cpp:434:7:434:9 | ref arg m35 | map.cpp:438:1:438:1 | m35 | | +| map.cpp:434:21:434:25 | first | map.cpp:434:7:434:25 | call to iterator | | +| map.cpp:435:7:435:9 | m35 | map.cpp:435:7:435:9 | call to unordered_map | | +| map.cpp:436:7:436:9 | m35 | map.cpp:436:11:436:17 | call to emplace | TAINT | +| map.cpp:436:7:436:9 | ref arg m35 | map.cpp:437:7:437:9 | m35 | | +| map.cpp:436:7:436:9 | ref arg m35 | map.cpp:438:1:438:1 | m35 | | +| map.cpp:436:19:436:60 | call to pair | map.cpp:436:7:436:9 | ref arg m35 | TAINT | +| map.cpp:436:19:436:60 | call to pair | map.cpp:436:11:436:17 | call to emplace | TAINT | +| map.cpp:436:55:436:59 | def | map.cpp:436:19:436:60 | call to pair | TAINT | +| map.cpp:436:63:436:67 | first | map.cpp:436:7:436:67 | call to iterator | | +| map.cpp:437:7:437:9 | m35 | map.cpp:437:7:437:9 | call to unordered_map | | +| movableclass.cpp:8:2:8:15 | this | movableclass.cpp:8:27:8:31 | constructor init of field v [pre-this] | | +| movableclass.cpp:8:21:8:22 | _v | movableclass.cpp:8:29:8:30 | _v | | +| movableclass.cpp:8:29:8:30 | _v | movableclass.cpp:8:27:8:31 | constructor init of field v | TAINT | +| movableclass.cpp:9:2:9:15 | this | movableclass.cpp:10:3:10:3 | this | | +| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:9:34:9:38 | other | | +| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:10:7:10:11 | other | | +| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:11:3:11:7 | other | | +| movableclass.cpp:10:3:10:13 | ... = ... | movableclass.cpp:10:3:10:3 | v [post update] | | +| movableclass.cpp:10:13:10:13 | v | movableclass.cpp:10:3:10:13 | ... = ... | | +| movableclass.cpp:11:3:11:7 | other [post update] | movableclass.cpp:9:34:9:38 | other | | +| movableclass.cpp:11:3:11:13 | ... = ... | movableclass.cpp:11:9:11:9 | v [post update] | | +| movableclass.cpp:11:13:11:13 | 0 | movableclass.cpp:11:3:11:13 | ... = ... | | +| movableclass.cpp:13:18:13:26 | this | movableclass.cpp:14:3:14:3 | this | | +| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:13:45:13:49 | other | | +| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:14:7:14:11 | other | | +| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:15:3:15:7 | other | | +| movableclass.cpp:14:3:14:3 | this | movableclass.cpp:16:11:16:14 | this | | +| movableclass.cpp:14:3:14:3 | this [post update] | movableclass.cpp:16:11:16:14 | this | | +| movableclass.cpp:14:3:14:13 | ... = ... | movableclass.cpp:14:3:14:3 | v [post update] | | +| movableclass.cpp:14:13:14:13 | v | movableclass.cpp:14:3:14:13 | ... = ... | | +| movableclass.cpp:15:3:15:7 | other [post update] | movableclass.cpp:13:45:13:49 | other | | +| movableclass.cpp:15:3:15:13 | ... = ... | movableclass.cpp:15:9:15:9 | v [post update] | | +| movableclass.cpp:15:13:15:13 | 0 | movableclass.cpp:15:3:15:13 | ... = ... | | +| movableclass.cpp:16:11:16:14 | this | movableclass.cpp:16:10:16:14 | * ... | TAINT | +| movableclass.cpp:22:57:22:57 | 1 | movableclass.cpp:22:42:22:58 | call to MyMovableClass | TAINT | +| movableclass.cpp:23:55:23:60 | call to source | movableclass.cpp:23:40:23:63 | call to MyMovableClass | TAINT | +| movableclass.cpp:28:21:28:21 | 1 | movableclass.cpp:28:21:28:22 | call to MyMovableClass | TAINT | +| movableclass.cpp:28:21:28:22 | call to MyMovableClass | movableclass.cpp:33:8:33:9 | s1 | | +| movableclass.cpp:29:22:29:23 | call to MyMovableClass | movableclass.cpp:34:8:34:9 | s2 | | +| movableclass.cpp:29:23:29:23 | 1 | movableclass.cpp:29:22:29:23 | call to MyMovableClass | TAINT | +| movableclass.cpp:30:18:30:19 | call to MyMovableClass | movableclass.cpp:31:3:31:4 | s3 | | +| movableclass.cpp:30:18:30:19 | call to MyMovableClass | movableclass.cpp:35:8:35:9 | s3 | | +| movableclass.cpp:31:3:31:4 | ref arg s3 | movableclass.cpp:35:8:35:9 | s3 | | +| movableclass.cpp:31:8:31:8 | 1 | movableclass.cpp:31:8:31:8 | call to MyMovableClass | TAINT | +| movableclass.cpp:31:8:31:8 | call to MyMovableClass | movableclass.cpp:31:3:31:4 | ref arg s3 | TAINT | +| movableclass.cpp:31:8:31:8 | call to MyMovableClass | movableclass.cpp:31:6:31:6 | call to operator= | TAINT | +| movableclass.cpp:39:21:39:26 | call to source | movableclass.cpp:39:21:39:29 | call to MyMovableClass | TAINT | +| movableclass.cpp:39:21:39:29 | call to MyMovableClass | movableclass.cpp:44:8:44:9 | s1 | | +| movableclass.cpp:40:22:40:30 | call to MyMovableClass | movableclass.cpp:45:8:45:9 | s2 | | +| movableclass.cpp:40:23:40:28 | call to source | movableclass.cpp:40:22:40:30 | call to MyMovableClass | TAINT | +| movableclass.cpp:41:18:41:19 | call to MyMovableClass | movableclass.cpp:42:3:42:4 | s3 | | +| movableclass.cpp:41:18:41:19 | call to MyMovableClass | movableclass.cpp:46:8:46:9 | s3 | | +| movableclass.cpp:42:3:42:4 | ref arg s3 | movableclass.cpp:46:8:46:9 | s3 | | +| movableclass.cpp:42:8:42:13 | call to source | movableclass.cpp:42:8:42:15 | call to MyMovableClass | TAINT | +| movableclass.cpp:42:8:42:15 | call to MyMovableClass | movableclass.cpp:42:3:42:4 | ref arg s3 | TAINT | +| movableclass.cpp:42:8:42:15 | call to MyMovableClass | movableclass.cpp:42:6:42:6 | call to operator= | TAINT | +| movableclass.cpp:50:22:50:46 | call to MyMovableClass | movableclass.cpp:54:8:54:9 | s1 | | +| movableclass.cpp:50:38:50:43 | call to source | movableclass.cpp:50:22:50:46 | call to MyMovableClass | TAINT | +| movableclass.cpp:51:18:51:19 | call to MyMovableClass | movableclass.cpp:52:3:52:4 | s2 | | +| movableclass.cpp:51:18:51:19 | call to MyMovableClass | movableclass.cpp:55:8:55:9 | s2 | | +| movableclass.cpp:52:3:52:4 | ref arg s2 | movableclass.cpp:55:8:55:9 | s2 | | +| movableclass.cpp:52:8:52:31 | call to MyMovableClass | movableclass.cpp:52:3:52:4 | ref arg s2 | TAINT | +| movableclass.cpp:52:8:52:31 | call to MyMovableClass | movableclass.cpp:52:6:52:6 | call to operator= | TAINT | +| movableclass.cpp:52:23:52:28 | call to source | movableclass.cpp:52:8:52:31 | call to MyMovableClass | TAINT | +| movableclass.cpp:59:21:59:32 | call to getUnTainted | movableclass.cpp:59:21:59:35 | call to MyMovableClass | | +| movableclass.cpp:59:21:59:35 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s1 | | +| movableclass.cpp:60:21:60:30 | call to getTainted | movableclass.cpp:60:21:60:33 | call to MyMovableClass | | +| movableclass.cpp:60:21:60:33 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s2 | | +| movableclass.cpp:61:18:61:19 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | s3 | | +| movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT | +| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT | +| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT | +| set.cpp:17:19:17:20 | call to set | set.cpp:19:7:19:8 | s1 | | +| set.cpp:17:19:17:20 | call to set | set.cpp:23:12:23:13 | s1 | | +| set.cpp:17:19:17:20 | call to set | set.cpp:23:24:23:25 | s1 | | +| set.cpp:17:19:17:20 | call to set | set.cpp:25:7:25:8 | s1 | | +| set.cpp:17:19:17:20 | call to set | set.cpp:31:7:31:8 | s1 | | +| set.cpp:17:19:17:20 | call to set | set.cpp:55:12:55:13 | s1 | | +| set.cpp:17:19:17:20 | call to set | set.cpp:55:30:55:31 | s1 | | +| set.cpp:17:19:17:20 | call to set | set.cpp:126:1:126:1 | s1 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:20:7:20:8 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:24:12:24:13 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:24:24:24:25 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:26:7:26:8 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:32:7:32:8 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:39:22:39:23 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:40:24:40:25 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:41:22:41:23 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:41:34:41:35 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:43:8:43:9 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:59:12:59:13 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:59:30:59:31 | s2 | | +| set.cpp:17:23:17:24 | call to set | set.cpp:126:1:126:1 | s2 | | +| set.cpp:17:27:17:28 | call to set | set.cpp:21:7:21:8 | s3 | | +| set.cpp:17:27:17:28 | call to set | set.cpp:21:17:21:18 | s3 | | +| set.cpp:17:27:17:28 | call to set | set.cpp:27:7:27:8 | s3 | | +| set.cpp:17:27:17:28 | call to set | set.cpp:33:7:33:8 | s3 | | +| set.cpp:17:27:17:28 | call to set | set.cpp:126:1:126:1 | s3 | | +| set.cpp:17:31:17:32 | call to set | set.cpp:22:7:22:8 | s4 | | +| set.cpp:17:31:17:32 | call to set | set.cpp:22:17:22:18 | s4 | | +| set.cpp:17:31:17:32 | call to set | set.cpp:28:7:28:8 | s4 | | +| set.cpp:17:31:17:32 | call to set | set.cpp:34:7:34:8 | s4 | | +| set.cpp:17:31:17:32 | call to set | set.cpp:126:1:126:1 | s4 | | +| set.cpp:17:35:17:36 | call to set | set.cpp:23:2:23:3 | s5 | | +| set.cpp:17:35:17:36 | call to set | set.cpp:29:7:29:8 | s5 | | +| set.cpp:17:35:17:36 | call to set | set.cpp:35:7:35:8 | s5 | | +| set.cpp:17:35:17:36 | call to set | set.cpp:126:1:126:1 | s5 | | +| set.cpp:17:39:17:40 | call to set | set.cpp:24:2:24:3 | s6 | | +| set.cpp:17:39:17:40 | call to set | set.cpp:30:7:30:8 | s6 | | +| set.cpp:17:39:17:40 | call to set | set.cpp:36:7:36:8 | s6 | | +| set.cpp:17:39:17:40 | call to set | set.cpp:126:1:126:1 | s6 | | +| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:23:12:23:13 | s1 | | +| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:23:24:23:25 | s1 | | +| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:25:7:25:8 | s1 | | +| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:31:7:31:8 | s1 | | +| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:55:12:55:13 | s1 | | +| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:55:30:55:31 | s1 | | +| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:126:1:126:1 | s1 | | +| set.cpp:19:17:19:21 | abc | set.cpp:19:7:19:8 | ref arg s1 | TAINT | +| set.cpp:19:17:19:21 | abc | set.cpp:19:10:19:15 | call to insert | TAINT | +| set.cpp:19:24:19:28 | first | set.cpp:19:7:19:28 | call to iterator | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:24:12:24:13 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:24:24:24:25 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:26:7:26:8 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:32:7:32:8 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:39:22:39:23 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:40:24:40:25 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:41:22:41:23 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:41:34:41:35 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:43:8:43:9 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:59:12:59:13 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:59:30:59:31 | s2 | | +| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:126:1:126:1 | s2 | | +| set.cpp:20:17:20:22 | call to source | set.cpp:20:7:20:8 | ref arg s2 | TAINT | +| set.cpp:20:17:20:22 | call to source | set.cpp:20:10:20:15 | call to insert | TAINT | +| set.cpp:20:27:20:31 | first | set.cpp:20:7:20:31 | call to iterator | | +| set.cpp:21:7:21:8 | ref arg s3 | set.cpp:27:7:27:8 | s3 | | +| set.cpp:21:7:21:8 | ref arg s3 | set.cpp:33:7:33:8 | s3 | | +| set.cpp:21:7:21:8 | ref arg s3 | set.cpp:126:1:126:1 | s3 | | +| set.cpp:21:17:21:18 | ref arg s3 | set.cpp:21:7:21:8 | s3 | | +| set.cpp:21:17:21:18 | ref arg s3 | set.cpp:27:7:27:8 | s3 | | +| set.cpp:21:17:21:18 | ref arg s3 | set.cpp:33:7:33:8 | s3 | | +| set.cpp:21:17:21:18 | ref arg s3 | set.cpp:126:1:126:1 | s3 | | +| set.cpp:21:17:21:18 | s3 | set.cpp:21:20:21:24 | call to begin | TAINT | +| set.cpp:21:20:21:24 | call to begin | set.cpp:21:17:21:26 | call to iterator | TAINT | +| set.cpp:21:29:21:33 | abc | set.cpp:21:7:21:8 | ref arg s3 | TAINT | +| set.cpp:21:29:21:33 | abc | set.cpp:21:10:21:15 | call to insert | TAINT | +| set.cpp:22:7:22:8 | ref arg s4 | set.cpp:28:7:28:8 | s4 | | +| set.cpp:22:7:22:8 | ref arg s4 | set.cpp:34:7:34:8 | s4 | | +| set.cpp:22:7:22:8 | ref arg s4 | set.cpp:126:1:126:1 | s4 | | +| set.cpp:22:17:22:18 | ref arg s4 | set.cpp:22:7:22:8 | s4 | | +| set.cpp:22:17:22:18 | ref arg s4 | set.cpp:28:7:28:8 | s4 | | +| set.cpp:22:17:22:18 | ref arg s4 | set.cpp:34:7:34:8 | s4 | | +| set.cpp:22:17:22:18 | ref arg s4 | set.cpp:126:1:126:1 | s4 | | +| set.cpp:22:17:22:18 | s4 | set.cpp:22:20:22:24 | call to begin | TAINT | +| set.cpp:22:20:22:24 | call to begin | set.cpp:22:17:22:26 | call to iterator | TAINT | +| set.cpp:22:29:22:34 | call to source | set.cpp:22:7:22:8 | ref arg s4 | TAINT | +| set.cpp:22:29:22:34 | call to source | set.cpp:22:10:22:15 | call to insert | TAINT | +| set.cpp:23:2:23:3 | ref arg s5 | set.cpp:29:7:29:8 | s5 | | +| set.cpp:23:2:23:3 | ref arg s5 | set.cpp:35:7:35:8 | s5 | | +| set.cpp:23:2:23:3 | ref arg s5 | set.cpp:126:1:126:1 | s5 | | +| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:23:24:23:25 | s1 | | +| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:25:7:25:8 | s1 | | +| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:31:7:31:8 | s1 | | +| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:55:12:55:13 | s1 | | +| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:55:30:55:31 | s1 | | +| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:126:1:126:1 | s1 | | +| set.cpp:23:12:23:13 | s1 | set.cpp:23:15:23:19 | call to begin | TAINT | +| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:25:7:25:8 | s1 | | +| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:31:7:31:8 | s1 | | +| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:55:12:55:13 | s1 | | +| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:55:30:55:31 | s1 | | +| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:126:1:126:1 | s1 | | +| set.cpp:23:24:23:25 | s1 | set.cpp:23:27:23:29 | call to end | TAINT | +| set.cpp:23:27:23:29 | call to end | set.cpp:23:2:23:3 | ref arg s5 | TAINT | +| set.cpp:23:27:23:29 | call to end | set.cpp:23:5:23:10 | call to insert | TAINT | +| set.cpp:24:2:24:3 | ref arg s6 | set.cpp:30:7:30:8 | s6 | | +| set.cpp:24:2:24:3 | ref arg s6 | set.cpp:36:7:36:8 | s6 | | +| set.cpp:24:2:24:3 | ref arg s6 | set.cpp:126:1:126:1 | s6 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:24:24:24:25 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:26:7:26:8 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:32:7:32:8 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:39:22:39:23 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:40:24:40:25 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:41:22:41:23 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:41:34:41:35 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:43:8:43:9 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:59:12:59:13 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:59:30:59:31 | s2 | | +| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:126:1:126:1 | s2 | | +| set.cpp:24:12:24:13 | s2 | set.cpp:24:15:24:19 | call to begin | TAINT | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:26:7:26:8 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:32:7:32:8 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:39:22:39:23 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:40:24:40:25 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:41:22:41:23 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:41:34:41:35 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:43:8:43:9 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:59:12:59:13 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:59:30:59:31 | s2 | | +| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:126:1:126:1 | s2 | | +| set.cpp:24:24:24:25 | s2 | set.cpp:24:27:24:29 | call to end | TAINT | +| set.cpp:24:27:24:29 | call to end | set.cpp:24:2:24:3 | ref arg s6 | TAINT | +| set.cpp:24:27:24:29 | call to end | set.cpp:24:5:24:10 | call to insert | TAINT | +| set.cpp:25:7:25:8 | s1 | set.cpp:25:7:25:8 | call to set | | +| set.cpp:26:7:26:8 | s2 | set.cpp:26:7:26:8 | call to set | | +| set.cpp:27:7:27:8 | s3 | set.cpp:27:7:27:8 | call to set | | +| set.cpp:28:7:28:8 | s4 | set.cpp:28:7:28:8 | call to set | | +| set.cpp:29:7:29:8 | s5 | set.cpp:29:7:29:8 | call to set | | +| set.cpp:30:7:30:8 | s6 | set.cpp:30:7:30:8 | call to set | | +| set.cpp:31:7:31:8 | ref arg s1 | set.cpp:55:12:55:13 | s1 | | +| set.cpp:31:7:31:8 | ref arg s1 | set.cpp:55:30:55:31 | s1 | | +| set.cpp:31:7:31:8 | ref arg s1 | set.cpp:126:1:126:1 | s1 | | +| set.cpp:31:7:31:8 | s1 | set.cpp:31:10:31:13 | call to find | TAINT | +| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:39:22:39:23 | s2 | | +| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:40:24:40:25 | s2 | | +| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:41:22:41:23 | s2 | | +| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:41:34:41:35 | s2 | | +| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:43:8:43:9 | s2 | | +| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:59:12:59:13 | s2 | | +| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:59:30:59:31 | s2 | | +| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:126:1:126:1 | s2 | | +| set.cpp:32:7:32:8 | s2 | set.cpp:32:10:32:13 | call to find | TAINT | +| set.cpp:33:7:33:8 | ref arg s3 | set.cpp:126:1:126:1 | s3 | | +| set.cpp:33:7:33:8 | s3 | set.cpp:33:10:33:13 | call to find | TAINT | +| set.cpp:34:7:34:8 | ref arg s4 | set.cpp:126:1:126:1 | s4 | | +| set.cpp:34:7:34:8 | s4 | set.cpp:34:10:34:13 | call to find | TAINT | +| set.cpp:35:7:35:8 | ref arg s5 | set.cpp:126:1:126:1 | s5 | | +| set.cpp:35:7:35:8 | s5 | set.cpp:35:10:35:13 | call to find | TAINT | +| set.cpp:36:7:36:8 | ref arg s6 | set.cpp:126:1:126:1 | s6 | | +| set.cpp:36:7:36:8 | s6 | set.cpp:36:10:36:13 | call to find | TAINT | +| set.cpp:39:22:39:23 | s2 | set.cpp:39:22:39:24 | call to set | | +| set.cpp:39:22:39:24 | call to set | set.cpp:44:7:44:8 | s7 | | +| set.cpp:39:22:39:24 | call to set | set.cpp:48:7:48:8 | s7 | | +| set.cpp:39:22:39:24 | call to set | set.cpp:126:1:126:1 | s7 | | +| set.cpp:40:23:40:25 | call to set | set.cpp:45:7:45:8 | s8 | | +| set.cpp:40:23:40:25 | call to set | set.cpp:49:7:49:8 | s8 | | +| set.cpp:40:23:40:25 | call to set | set.cpp:126:1:126:1 | s8 | | +| set.cpp:40:24:40:25 | s2 | set.cpp:40:23:40:25 | call to set | | +| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:41:34:41:35 | s2 | | +| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:43:8:43:9 | s2 | | +| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:59:12:59:13 | s2 | | +| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:59:30:59:31 | s2 | | +| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:126:1:126:1 | s2 | | +| set.cpp:41:22:41:23 | s2 | set.cpp:41:25:41:29 | call to begin | TAINT | +| set.cpp:41:22:41:42 | call to set | set.cpp:46:7:46:8 | s9 | | +| set.cpp:41:22:41:42 | call to set | set.cpp:50:7:50:8 | s9 | | +| set.cpp:41:22:41:42 | call to set | set.cpp:126:1:126:1 | s9 | | +| set.cpp:41:25:41:29 | call to begin | set.cpp:41:22:41:42 | call to set | TAINT | +| set.cpp:41:34:41:35 | ref arg s2 | set.cpp:43:8:43:9 | s2 | | +| set.cpp:41:34:41:35 | ref arg s2 | set.cpp:59:12:59:13 | s2 | | +| set.cpp:41:34:41:35 | ref arg s2 | set.cpp:59:30:59:31 | s2 | | +| set.cpp:41:34:41:35 | ref arg s2 | set.cpp:126:1:126:1 | s2 | | +| set.cpp:41:34:41:35 | s2 | set.cpp:41:37:41:39 | call to end | TAINT | +| set.cpp:41:37:41:39 | call to end | set.cpp:41:22:41:42 | call to set | TAINT | +| set.cpp:42:19:42:21 | call to set | set.cpp:43:2:43:4 | s10 | | +| set.cpp:42:19:42:21 | call to set | set.cpp:47:7:47:9 | s10 | | +| set.cpp:42:19:42:21 | call to set | set.cpp:51:7:51:9 | s10 | | +| set.cpp:42:19:42:21 | call to set | set.cpp:126:1:126:1 | s10 | | +| set.cpp:43:2:43:4 | ref arg s10 | set.cpp:47:7:47:9 | s10 | | +| set.cpp:43:2:43:4 | ref arg s10 | set.cpp:51:7:51:9 | s10 | | +| set.cpp:43:2:43:4 | ref arg s10 | set.cpp:126:1:126:1 | s10 | | +| set.cpp:43:8:43:9 | s2 | set.cpp:43:2:43:4 | ref arg s10 | TAINT | +| set.cpp:43:8:43:9 | s2 | set.cpp:43:6:43:6 | call to operator= | TAINT | +| set.cpp:44:7:44:8 | s7 | set.cpp:44:7:44:8 | call to set | | +| set.cpp:45:7:45:8 | s8 | set.cpp:45:7:45:8 | call to set | | +| set.cpp:46:7:46:8 | s9 | set.cpp:46:7:46:8 | call to set | | +| set.cpp:47:7:47:9 | s10 | set.cpp:47:7:47:9 | call to set | | +| set.cpp:48:7:48:8 | ref arg s7 | set.cpp:126:1:126:1 | s7 | | +| set.cpp:48:7:48:8 | s7 | set.cpp:48:10:48:13 | call to find | TAINT | +| set.cpp:49:7:49:8 | ref arg s8 | set.cpp:126:1:126:1 | s8 | | +| set.cpp:49:7:49:8 | s8 | set.cpp:49:10:49:13 | call to find | TAINT | +| set.cpp:50:7:50:8 | ref arg s9 | set.cpp:126:1:126:1 | s9 | | +| set.cpp:50:7:50:8 | s9 | set.cpp:50:10:50:13 | call to find | TAINT | +| set.cpp:51:7:51:9 | ref arg s10 | set.cpp:126:1:126:1 | s10 | | +| set.cpp:51:7:51:9 | s10 | set.cpp:51:11:51:14 | call to find | TAINT | +| set.cpp:55:12:55:13 | ref arg s1 | set.cpp:55:30:55:31 | s1 | | +| set.cpp:55:12:55:13 | ref arg s1 | set.cpp:126:1:126:1 | s1 | | +| set.cpp:55:12:55:13 | s1 | set.cpp:55:15:55:19 | call to begin | TAINT | +| set.cpp:55:15:55:19 | call to begin | set.cpp:55:7:55:21 | ... = ... | | +| set.cpp:55:15:55:19 | call to begin | set.cpp:55:24:55:25 | i1 | | +| set.cpp:55:15:55:19 | call to begin | set.cpp:55:40:55:41 | i1 | | +| set.cpp:55:15:55:19 | call to begin | set.cpp:57:9:57:10 | i1 | | +| set.cpp:55:30:55:31 | ref arg s1 | set.cpp:55:30:55:31 | s1 | | +| set.cpp:55:30:55:31 | ref arg s1 | set.cpp:126:1:126:1 | s1 | | +| set.cpp:55:30:55:31 | s1 | set.cpp:55:33:55:35 | call to end | TAINT | +| set.cpp:55:40:55:41 | i1 | set.cpp:55:42:55:42 | call to operator++ | | +| set.cpp:55:40:55:41 | ref arg i1 | set.cpp:55:24:55:25 | i1 | | +| set.cpp:55:40:55:41 | ref arg i1 | set.cpp:55:40:55:41 | i1 | | +| set.cpp:55:40:55:41 | ref arg i1 | set.cpp:57:9:57:10 | i1 | | +| set.cpp:57:9:57:10 | i1 | set.cpp:57:8:57:8 | call to operator* | TAINT | +| set.cpp:59:12:59:13 | ref arg s2 | set.cpp:59:30:59:31 | s2 | | +| set.cpp:59:12:59:13 | ref arg s2 | set.cpp:126:1:126:1 | s2 | | +| set.cpp:59:12:59:13 | s2 | set.cpp:59:15:59:19 | call to begin | TAINT | +| set.cpp:59:15:59:19 | call to begin | set.cpp:59:7:59:21 | ... = ... | | +| set.cpp:59:15:59:19 | call to begin | set.cpp:59:24:59:25 | i2 | | +| set.cpp:59:15:59:19 | call to begin | set.cpp:59:40:59:41 | i2 | | +| set.cpp:59:15:59:19 | call to begin | set.cpp:61:9:61:10 | i2 | | +| set.cpp:59:30:59:31 | ref arg s2 | set.cpp:59:30:59:31 | s2 | | +| set.cpp:59:30:59:31 | ref arg s2 | set.cpp:126:1:126:1 | s2 | | +| set.cpp:59:30:59:31 | s2 | set.cpp:59:33:59:35 | call to end | TAINT | +| set.cpp:59:40:59:41 | i2 | set.cpp:59:42:59:42 | call to operator++ | | +| set.cpp:59:40:59:41 | ref arg i2 | set.cpp:59:24:59:25 | i2 | | +| set.cpp:59:40:59:41 | ref arg i2 | set.cpp:59:40:59:41 | i2 | | +| set.cpp:59:40:59:41 | ref arg i2 | set.cpp:61:9:61:10 | i2 | | +| set.cpp:61:9:61:10 | i2 | set.cpp:61:8:61:8 | call to operator* | TAINT | +| set.cpp:65:19:65:21 | call to set | set.cpp:66:2:66:4 | s11 | | +| set.cpp:65:19:65:21 | call to set | set.cpp:67:2:67:4 | s11 | | +| set.cpp:65:19:65:21 | call to set | set.cpp:68:2:68:4 | s11 | | +| set.cpp:65:19:65:21 | call to set | set.cpp:69:7:69:9 | s11 | | +| set.cpp:65:19:65:21 | call to set | set.cpp:70:7:70:9 | s11 | | +| set.cpp:65:19:65:21 | call to set | set.cpp:71:7:71:9 | s11 | | +| set.cpp:65:19:65:21 | call to set | set.cpp:72:7:72:9 | s11 | | +| set.cpp:65:19:65:21 | call to set | set.cpp:126:1:126:1 | s11 | | +| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:67:2:67:4 | s11 | | +| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:68:2:68:4 | s11 | | +| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:69:7:69:9 | s11 | | +| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:70:7:70:9 | s11 | | +| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:71:7:71:9 | s11 | | +| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:72:7:72:9 | s11 | | +| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:126:1:126:1 | s11 | | +| set.cpp:66:13:66:15 | a | set.cpp:66:2:66:4 | ref arg s11 | TAINT | +| set.cpp:66:13:66:15 | a | set.cpp:66:6:66:11 | call to insert | TAINT | +| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:68:2:68:4 | s11 | | +| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:69:7:69:9 | s11 | | +| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:70:7:70:9 | s11 | | +| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:71:7:71:9 | s11 | | +| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:72:7:72:9 | s11 | | +| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:126:1:126:1 | s11 | | +| set.cpp:67:13:67:18 | call to source | set.cpp:67:2:67:4 | ref arg s11 | TAINT | +| set.cpp:67:13:67:18 | call to source | set.cpp:67:6:67:11 | call to insert | TAINT | +| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:69:7:69:9 | s11 | | +| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:70:7:70:9 | s11 | | +| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:71:7:71:9 | s11 | | +| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:72:7:72:9 | s11 | | +| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:126:1:126:1 | s11 | | +| set.cpp:68:13:68:15 | c | set.cpp:68:2:68:4 | ref arg s11 | TAINT | +| set.cpp:68:13:68:15 | c | set.cpp:68:6:68:11 | call to insert | TAINT | +| set.cpp:69:7:69:9 | ref arg s11 | set.cpp:70:7:70:9 | s11 | | +| set.cpp:69:7:69:9 | ref arg s11 | set.cpp:71:7:71:9 | s11 | | +| set.cpp:69:7:69:9 | ref arg s11 | set.cpp:72:7:72:9 | s11 | | +| set.cpp:69:7:69:9 | ref arg s11 | set.cpp:126:1:126:1 | s11 | | +| set.cpp:69:7:69:9 | s11 | set.cpp:69:11:69:21 | call to lower_bound | TAINT | +| set.cpp:70:7:70:9 | ref arg s11 | set.cpp:71:7:71:9 | s11 | | +| set.cpp:70:7:70:9 | ref arg s11 | set.cpp:72:7:72:9 | s11 | | +| set.cpp:70:7:70:9 | ref arg s11 | set.cpp:126:1:126:1 | s11 | | +| set.cpp:70:7:70:9 | s11 | set.cpp:70:11:70:21 | call to upper_bound | TAINT | +| set.cpp:71:7:71:9 | ref arg s11 | set.cpp:72:7:72:9 | s11 | | +| set.cpp:71:7:71:9 | ref arg s11 | set.cpp:126:1:126:1 | s11 | | +| set.cpp:71:7:71:9 | s11 | set.cpp:71:11:71:21 | call to equal_range | TAINT | +| set.cpp:71:28:71:32 | first | set.cpp:71:7:71:32 | call to iterator | | +| set.cpp:72:7:72:9 | ref arg s11 | set.cpp:126:1:126:1 | s11 | | +| set.cpp:72:7:72:9 | s11 | set.cpp:72:11:72:21 | call to equal_range | TAINT | +| set.cpp:72:28:72:33 | second | set.cpp:72:7:72:33 | call to iterator | | +| set.cpp:75:19:75:21 | call to set | set.cpp:76:2:76:4 | s12 | | +| set.cpp:75:19:75:21 | call to set | set.cpp:78:7:78:9 | s12 | | +| set.cpp:75:19:75:21 | call to set | set.cpp:82:2:82:4 | s12 | | +| set.cpp:75:19:75:21 | call to set | set.cpp:84:7:84:9 | s12 | | +| set.cpp:75:19:75:21 | call to set | set.cpp:126:1:126:1 | s12 | | +| set.cpp:75:24:75:26 | call to set | set.cpp:79:7:79:9 | s13 | | +| set.cpp:75:24:75:26 | call to set | set.cpp:82:11:82:13 | s13 | | +| set.cpp:75:24:75:26 | call to set | set.cpp:85:7:85:9 | s13 | | +| set.cpp:75:24:75:26 | call to set | set.cpp:126:1:126:1 | s13 | | +| set.cpp:75:29:75:31 | call to set | set.cpp:80:7:80:9 | s14 | | +| set.cpp:75:29:75:31 | call to set | set.cpp:83:2:83:4 | s14 | | +| set.cpp:75:29:75:31 | call to set | set.cpp:86:7:86:9 | s14 | | +| set.cpp:75:29:75:31 | call to set | set.cpp:126:1:126:1 | s14 | | +| set.cpp:75:34:75:36 | call to set | set.cpp:77:2:77:4 | s15 | | +| set.cpp:75:34:75:36 | call to set | set.cpp:81:7:81:9 | s15 | | +| set.cpp:75:34:75:36 | call to set | set.cpp:83:11:83:13 | s15 | | +| set.cpp:75:34:75:36 | call to set | set.cpp:87:7:87:9 | s15 | | +| set.cpp:75:34:75:36 | call to set | set.cpp:126:1:126:1 | s15 | | +| set.cpp:76:2:76:4 | ref arg s12 | set.cpp:78:7:78:9 | s12 | | +| set.cpp:76:2:76:4 | ref arg s12 | set.cpp:82:2:82:4 | s12 | | +| set.cpp:76:2:76:4 | ref arg s12 | set.cpp:84:7:84:9 | s12 | | +| set.cpp:76:2:76:4 | ref arg s12 | set.cpp:126:1:126:1 | s12 | | +| set.cpp:76:13:76:18 | call to source | set.cpp:76:2:76:4 | ref arg s12 | TAINT | +| set.cpp:76:13:76:18 | call to source | set.cpp:76:6:76:11 | call to insert | TAINT | +| set.cpp:77:2:77:4 | ref arg s15 | set.cpp:81:7:81:9 | s15 | | +| set.cpp:77:2:77:4 | ref arg s15 | set.cpp:83:11:83:13 | s15 | | +| set.cpp:77:2:77:4 | ref arg s15 | set.cpp:87:7:87:9 | s15 | | +| set.cpp:77:2:77:4 | ref arg s15 | set.cpp:126:1:126:1 | s15 | | +| set.cpp:77:13:77:18 | call to source | set.cpp:77:2:77:4 | ref arg s15 | TAINT | +| set.cpp:77:13:77:18 | call to source | set.cpp:77:6:77:11 | call to insert | TAINT | +| set.cpp:78:7:78:9 | s12 | set.cpp:78:7:78:9 | call to set | | +| set.cpp:79:7:79:9 | s13 | set.cpp:79:7:79:9 | call to set | | +| set.cpp:80:7:80:9 | s14 | set.cpp:80:7:80:9 | call to set | | +| set.cpp:81:7:81:9 | s15 | set.cpp:81:7:81:9 | call to set | | +| set.cpp:82:2:82:4 | ref arg s12 | set.cpp:84:7:84:9 | s12 | | +| set.cpp:82:2:82:4 | ref arg s12 | set.cpp:126:1:126:1 | s12 | | +| set.cpp:82:2:82:4 | s12 | set.cpp:82:11:82:13 | ref arg s13 | TAINT | +| set.cpp:82:11:82:13 | ref arg s13 | set.cpp:85:7:85:9 | s13 | | +| set.cpp:82:11:82:13 | ref arg s13 | set.cpp:126:1:126:1 | s13 | | +| set.cpp:82:11:82:13 | s13 | set.cpp:82:2:82:4 | ref arg s12 | TAINT | +| set.cpp:83:2:83:4 | ref arg s14 | set.cpp:86:7:86:9 | s14 | | +| set.cpp:83:2:83:4 | ref arg s14 | set.cpp:126:1:126:1 | s14 | | +| set.cpp:83:2:83:4 | s14 | set.cpp:83:11:83:13 | ref arg s15 | TAINT | +| set.cpp:83:11:83:13 | ref arg s15 | set.cpp:87:7:87:9 | s15 | | +| set.cpp:83:11:83:13 | ref arg s15 | set.cpp:126:1:126:1 | s15 | | +| set.cpp:83:11:83:13 | s15 | set.cpp:83:2:83:4 | ref arg s14 | TAINT | +| set.cpp:84:7:84:9 | s12 | set.cpp:84:7:84:9 | call to set | | +| set.cpp:85:7:85:9 | s13 | set.cpp:85:7:85:9 | call to set | | +| set.cpp:86:7:86:9 | s14 | set.cpp:86:7:86:9 | call to set | | +| set.cpp:87:7:87:9 | s15 | set.cpp:87:7:87:9 | call to set | | +| set.cpp:90:19:90:21 | call to set | set.cpp:91:2:91:4 | s16 | | +| set.cpp:90:19:90:21 | call to set | set.cpp:95:7:95:9 | s16 | | +| set.cpp:90:19:90:21 | call to set | set.cpp:99:2:99:4 | s16 | | +| set.cpp:90:19:90:21 | call to set | set.cpp:101:7:101:9 | s16 | | +| set.cpp:90:19:90:21 | call to set | set.cpp:126:1:126:1 | s16 | | +| set.cpp:90:24:90:26 | call to set | set.cpp:92:2:92:4 | s17 | | +| set.cpp:90:24:90:26 | call to set | set.cpp:96:7:96:9 | s17 | | +| set.cpp:90:24:90:26 | call to set | set.cpp:99:12:99:14 | s17 | | +| set.cpp:90:24:90:26 | call to set | set.cpp:102:7:102:9 | s17 | | +| set.cpp:90:24:90:26 | call to set | set.cpp:126:1:126:1 | s17 | | +| set.cpp:90:29:90:31 | call to set | set.cpp:93:2:93:4 | s18 | | +| set.cpp:90:29:90:31 | call to set | set.cpp:97:7:97:9 | s18 | | +| set.cpp:90:29:90:31 | call to set | set.cpp:100:2:100:4 | s18 | | +| set.cpp:90:29:90:31 | call to set | set.cpp:103:7:103:9 | s18 | | +| set.cpp:90:29:90:31 | call to set | set.cpp:126:1:126:1 | s18 | | +| set.cpp:90:34:90:36 | call to set | set.cpp:94:2:94:4 | s19 | | +| set.cpp:90:34:90:36 | call to set | set.cpp:98:7:98:9 | s19 | | +| set.cpp:90:34:90:36 | call to set | set.cpp:100:12:100:14 | s19 | | +| set.cpp:90:34:90:36 | call to set | set.cpp:104:7:104:9 | s19 | | +| set.cpp:90:34:90:36 | call to set | set.cpp:126:1:126:1 | s19 | | +| set.cpp:91:2:91:4 | ref arg s16 | set.cpp:95:7:95:9 | s16 | | +| set.cpp:91:2:91:4 | ref arg s16 | set.cpp:99:2:99:4 | s16 | | +| set.cpp:91:2:91:4 | ref arg s16 | set.cpp:101:7:101:9 | s16 | | +| set.cpp:91:2:91:4 | ref arg s16 | set.cpp:126:1:126:1 | s16 | | +| set.cpp:91:13:91:18 | call to source | set.cpp:91:2:91:4 | ref arg s16 | TAINT | +| set.cpp:91:13:91:18 | call to source | set.cpp:91:6:91:11 | call to insert | TAINT | +| set.cpp:92:2:92:4 | ref arg s17 | set.cpp:96:7:96:9 | s17 | | +| set.cpp:92:2:92:4 | ref arg s17 | set.cpp:99:12:99:14 | s17 | | +| set.cpp:92:2:92:4 | ref arg s17 | set.cpp:102:7:102:9 | s17 | | +| set.cpp:92:2:92:4 | ref arg s17 | set.cpp:126:1:126:1 | s17 | | +| set.cpp:92:13:92:17 | abc | set.cpp:92:2:92:4 | ref arg s17 | TAINT | +| set.cpp:92:13:92:17 | abc | set.cpp:92:6:92:11 | call to insert | TAINT | +| set.cpp:93:2:93:4 | ref arg s18 | set.cpp:97:7:97:9 | s18 | | +| set.cpp:93:2:93:4 | ref arg s18 | set.cpp:100:2:100:4 | s18 | | +| set.cpp:93:2:93:4 | ref arg s18 | set.cpp:103:7:103:9 | s18 | | +| set.cpp:93:2:93:4 | ref arg s18 | set.cpp:126:1:126:1 | s18 | | +| set.cpp:93:13:93:17 | def | set.cpp:93:2:93:4 | ref arg s18 | TAINT | +| set.cpp:93:13:93:17 | def | set.cpp:93:6:93:11 | call to insert | TAINT | +| set.cpp:94:2:94:4 | ref arg s19 | set.cpp:98:7:98:9 | s19 | | +| set.cpp:94:2:94:4 | ref arg s19 | set.cpp:100:12:100:14 | s19 | | +| set.cpp:94:2:94:4 | ref arg s19 | set.cpp:104:7:104:9 | s19 | | +| set.cpp:94:2:94:4 | ref arg s19 | set.cpp:126:1:126:1 | s19 | | +| set.cpp:94:13:94:18 | call to source | set.cpp:94:2:94:4 | ref arg s19 | TAINT | +| set.cpp:94:13:94:18 | call to source | set.cpp:94:6:94:11 | call to insert | TAINT | +| set.cpp:95:7:95:9 | s16 | set.cpp:95:7:95:9 | call to set | | +| set.cpp:96:7:96:9 | s17 | set.cpp:96:7:96:9 | call to set | | +| set.cpp:97:7:97:9 | s18 | set.cpp:97:7:97:9 | call to set | | +| set.cpp:98:7:98:9 | s19 | set.cpp:98:7:98:9 | call to set | | +| set.cpp:99:2:99:4 | ref arg s16 | set.cpp:101:7:101:9 | s16 | | +| set.cpp:99:2:99:4 | ref arg s16 | set.cpp:126:1:126:1 | s16 | | +| set.cpp:99:12:99:14 | ref arg s17 | set.cpp:102:7:102:9 | s17 | | +| set.cpp:99:12:99:14 | ref arg s17 | set.cpp:126:1:126:1 | s17 | | +| set.cpp:99:12:99:14 | s17 | set.cpp:99:2:99:4 | ref arg s16 | TAINT | +| set.cpp:100:2:100:4 | ref arg s18 | set.cpp:103:7:103:9 | s18 | | +| set.cpp:100:2:100:4 | ref arg s18 | set.cpp:126:1:126:1 | s18 | | +| set.cpp:100:12:100:14 | ref arg s19 | set.cpp:104:7:104:9 | s19 | | +| set.cpp:100:12:100:14 | ref arg s19 | set.cpp:126:1:126:1 | s19 | | +| set.cpp:100:12:100:14 | s19 | set.cpp:100:2:100:4 | ref arg s18 | TAINT | +| set.cpp:101:7:101:9 | s16 | set.cpp:101:7:101:9 | call to set | | +| set.cpp:102:7:102:9 | s17 | set.cpp:102:7:102:9 | call to set | | +| set.cpp:103:7:103:9 | s18 | set.cpp:103:7:103:9 | call to set | | +| set.cpp:104:7:104:9 | s19 | set.cpp:104:7:104:9 | call to set | | +| set.cpp:107:19:107:21 | call to set | set.cpp:108:2:108:4 | s20 | | +| set.cpp:107:19:107:21 | call to set | set.cpp:109:2:109:4 | s20 | | +| set.cpp:107:19:107:21 | call to set | set.cpp:110:7:110:9 | s20 | | +| set.cpp:107:19:107:21 | call to set | set.cpp:111:7:111:9 | s20 | | +| set.cpp:107:19:107:21 | call to set | set.cpp:111:17:111:19 | s20 | | +| set.cpp:107:19:107:21 | call to set | set.cpp:112:7:112:9 | s20 | | +| set.cpp:107:19:107:21 | call to set | set.cpp:113:2:113:4 | s20 | | +| set.cpp:107:19:107:21 | call to set | set.cpp:114:7:114:9 | s20 | | +| set.cpp:107:19:107:21 | call to set | set.cpp:126:1:126:1 | s20 | | +| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:109:2:109:4 | s20 | | +| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:110:7:110:9 | s20 | | +| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:111:7:111:9 | s20 | | +| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:111:17:111:19 | s20 | | +| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:112:7:112:9 | s20 | | +| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:113:2:113:4 | s20 | | +| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:114:7:114:9 | s20 | | +| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:126:1:126:1 | s20 | | +| set.cpp:108:13:108:18 | call to source | set.cpp:108:2:108:4 | ref arg s20 | TAINT | +| set.cpp:108:13:108:18 | call to source | set.cpp:108:6:108:11 | call to insert | TAINT | +| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:110:7:110:9 | s20 | | +| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:111:7:111:9 | s20 | | +| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:111:17:111:19 | s20 | | +| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:112:7:112:9 | s20 | | +| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:113:2:113:4 | s20 | | +| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:114:7:114:9 | s20 | | +| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:126:1:126:1 | s20 | | +| set.cpp:109:13:109:18 | call to source | set.cpp:109:2:109:4 | ref arg s20 | TAINT | +| set.cpp:109:13:109:18 | call to source | set.cpp:109:6:109:11 | call to insert | TAINT | +| set.cpp:110:7:110:9 | s20 | set.cpp:110:7:110:9 | call to set | | +| set.cpp:111:7:111:9 | ref arg s20 | set.cpp:112:7:112:9 | s20 | | +| set.cpp:111:7:111:9 | ref arg s20 | set.cpp:113:2:113:4 | s20 | | +| set.cpp:111:7:111:9 | ref arg s20 | set.cpp:114:7:114:9 | s20 | | +| set.cpp:111:7:111:9 | ref arg s20 | set.cpp:126:1:126:1 | s20 | | +| set.cpp:111:7:111:9 | s20 | set.cpp:111:11:111:15 | call to erase | TAINT | +| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:111:7:111:9 | s20 | | +| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:112:7:112:9 | s20 | | +| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:113:2:113:4 | s20 | | +| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:114:7:114:9 | s20 | | +| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:126:1:126:1 | s20 | | +| set.cpp:111:17:111:19 | s20 | set.cpp:111:21:111:25 | call to begin | TAINT | +| set.cpp:112:7:112:9 | s20 | set.cpp:112:7:112:9 | call to set | | +| set.cpp:113:2:113:4 | ref arg s20 | set.cpp:114:7:114:9 | s20 | | +| set.cpp:113:2:113:4 | ref arg s20 | set.cpp:126:1:126:1 | s20 | | +| set.cpp:114:7:114:9 | s20 | set.cpp:114:7:114:9 | call to set | | +| set.cpp:117:19:117:21 | call to set | set.cpp:118:7:118:9 | s21 | | +| set.cpp:117:19:117:21 | call to set | set.cpp:119:7:119:9 | s21 | | +| set.cpp:117:19:117:21 | call to set | set.cpp:120:7:120:9 | s21 | | +| set.cpp:117:19:117:21 | call to set | set.cpp:121:7:121:9 | s21 | | +| set.cpp:117:19:117:21 | call to set | set.cpp:126:1:126:1 | s21 | | +| set.cpp:117:24:117:26 | call to set | set.cpp:122:7:122:9 | s22 | | +| set.cpp:117:24:117:26 | call to set | set.cpp:122:24:122:26 | s22 | | +| set.cpp:117:24:117:26 | call to set | set.cpp:123:7:123:9 | s22 | | +| set.cpp:117:24:117:26 | call to set | set.cpp:124:7:124:9 | s22 | | +| set.cpp:117:24:117:26 | call to set | set.cpp:124:24:124:26 | s22 | | +| set.cpp:117:24:117:26 | call to set | set.cpp:125:7:125:9 | s22 | | +| set.cpp:117:24:117:26 | call to set | set.cpp:126:1:126:1 | s22 | | +| set.cpp:118:7:118:9 | ref arg s21 | set.cpp:119:7:119:9 | s21 | | +| set.cpp:118:7:118:9 | ref arg s21 | set.cpp:120:7:120:9 | s21 | | +| set.cpp:118:7:118:9 | ref arg s21 | set.cpp:121:7:121:9 | s21 | | +| set.cpp:118:7:118:9 | ref arg s21 | set.cpp:126:1:126:1 | s21 | | +| set.cpp:118:7:118:9 | s21 | set.cpp:118:11:118:17 | call to emplace | TAINT | +| set.cpp:118:19:118:23 | abc | set.cpp:118:7:118:9 | ref arg s21 | TAINT | +| set.cpp:118:19:118:23 | abc | set.cpp:118:11:118:17 | call to emplace | TAINT | +| set.cpp:118:26:118:30 | first | set.cpp:118:7:118:30 | call to iterator | | +| set.cpp:119:7:119:9 | s21 | set.cpp:119:7:119:9 | call to set | | +| set.cpp:120:7:120:9 | ref arg s21 | set.cpp:121:7:121:9 | s21 | | +| set.cpp:120:7:120:9 | ref arg s21 | set.cpp:126:1:126:1 | s21 | | +| set.cpp:120:7:120:9 | s21 | set.cpp:120:11:120:17 | call to emplace | TAINT | +| set.cpp:120:19:120:24 | call to source | set.cpp:120:7:120:9 | ref arg s21 | TAINT | +| set.cpp:120:19:120:24 | call to source | set.cpp:120:11:120:17 | call to emplace | TAINT | +| set.cpp:120:29:120:33 | first | set.cpp:120:7:120:33 | call to iterator | | +| set.cpp:121:7:121:9 | s21 | set.cpp:121:7:121:9 | call to set | | +| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:123:7:123:9 | s22 | | +| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:124:7:124:9 | s22 | | +| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:124:24:124:26 | s22 | | +| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:125:7:125:9 | s22 | | +| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:126:1:126:1 | s22 | | +| set.cpp:122:7:122:9 | s22 | set.cpp:122:11:122:22 | call to emplace_hint | TAINT | +| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:122:7:122:9 | s22 | | +| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:123:7:123:9 | s22 | | +| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:124:7:124:9 | s22 | | +| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:124:24:124:26 | s22 | | +| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:125:7:125:9 | s22 | | +| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:126:1:126:1 | s22 | | +| set.cpp:122:24:122:26 | s22 | set.cpp:122:28:122:32 | call to begin | TAINT | +| set.cpp:122:24:122:34 | call to iterator | set.cpp:122:7:122:9 | ref arg s22 | TAINT | +| set.cpp:122:24:122:34 | call to iterator | set.cpp:122:11:122:22 | call to emplace_hint | TAINT | +| set.cpp:122:28:122:32 | call to begin | set.cpp:122:24:122:34 | call to iterator | TAINT | +| set.cpp:122:37:122:41 | abc | set.cpp:122:7:122:9 | ref arg s22 | TAINT | +| set.cpp:122:37:122:41 | abc | set.cpp:122:11:122:22 | call to emplace_hint | TAINT | +| set.cpp:123:7:123:9 | s22 | set.cpp:123:7:123:9 | call to set | | +| set.cpp:124:7:124:9 | ref arg s22 | set.cpp:125:7:125:9 | s22 | | +| set.cpp:124:7:124:9 | ref arg s22 | set.cpp:126:1:126:1 | s22 | | +| set.cpp:124:7:124:9 | s22 | set.cpp:124:11:124:22 | call to emplace_hint | TAINT | +| set.cpp:124:24:124:26 | ref arg s22 | set.cpp:124:7:124:9 | s22 | | +| set.cpp:124:24:124:26 | ref arg s22 | set.cpp:125:7:125:9 | s22 | | +| set.cpp:124:24:124:26 | ref arg s22 | set.cpp:126:1:126:1 | s22 | | +| set.cpp:124:24:124:26 | s22 | set.cpp:124:28:124:32 | call to begin | TAINT | +| set.cpp:124:24:124:34 | call to iterator | set.cpp:124:7:124:9 | ref arg s22 | TAINT | +| set.cpp:124:24:124:34 | call to iterator | set.cpp:124:11:124:22 | call to emplace_hint | TAINT | +| set.cpp:124:28:124:32 | call to begin | set.cpp:124:24:124:34 | call to iterator | TAINT | +| set.cpp:124:37:124:42 | call to source | set.cpp:124:7:124:9 | ref arg s22 | TAINT | +| set.cpp:124:37:124:42 | call to source | set.cpp:124:11:124:22 | call to emplace_hint | TAINT | +| set.cpp:125:7:125:9 | s22 | set.cpp:125:7:125:9 | call to set | | +| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:133:7:133:8 | s1 | | +| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:137:12:137:13 | s1 | | +| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:137:24:137:25 | s1 | | +| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:139:7:139:8 | s1 | | +| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:145:7:145:8 | s1 | | +| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:169:12:169:13 | s1 | | +| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:169:30:169:31 | s1 | | +| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:238:1:238:1 | s1 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:134:7:134:8 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:138:12:138:13 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:138:24:138:25 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:140:7:140:8 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:146:7:146:8 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:153:32:153:33 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:154:34:154:35 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:155:32:155:33 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:155:44:155:45 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:157:8:157:9 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:173:12:173:13 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:173:30:173:31 | s2 | | +| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:238:1:238:1 | s2 | | +| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:135:7:135:8 | s3 | | +| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:135:17:135:18 | s3 | | +| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:141:7:141:8 | s3 | | +| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:147:7:147:8 | s3 | | +| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:238:1:238:1 | s3 | | +| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:136:7:136:8 | s4 | | +| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:136:17:136:18 | s4 | | +| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:142:7:142:8 | s4 | | +| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:148:7:148:8 | s4 | | +| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:238:1:238:1 | s4 | | +| set.cpp:131:45:131:46 | call to unordered_set | set.cpp:137:2:137:3 | s5 | | +| set.cpp:131:45:131:46 | call to unordered_set | set.cpp:143:7:143:8 | s5 | | +| set.cpp:131:45:131:46 | call to unordered_set | set.cpp:149:7:149:8 | s5 | | +| set.cpp:131:45:131:46 | call to unordered_set | set.cpp:238:1:238:1 | s5 | | +| set.cpp:131:49:131:50 | call to unordered_set | set.cpp:138:2:138:3 | s6 | | +| set.cpp:131:49:131:50 | call to unordered_set | set.cpp:144:7:144:8 | s6 | | +| set.cpp:131:49:131:50 | call to unordered_set | set.cpp:150:7:150:8 | s6 | | +| set.cpp:131:49:131:50 | call to unordered_set | set.cpp:238:1:238:1 | s6 | | +| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:137:12:137:13 | s1 | | +| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:137:24:137:25 | s1 | | +| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:139:7:139:8 | s1 | | +| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:145:7:145:8 | s1 | | +| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:169:12:169:13 | s1 | | +| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:169:30:169:31 | s1 | | +| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:238:1:238:1 | s1 | | +| set.cpp:133:17:133:21 | abc | set.cpp:133:7:133:8 | ref arg s1 | TAINT | +| set.cpp:133:17:133:21 | abc | set.cpp:133:10:133:15 | call to insert | TAINT | +| set.cpp:133:24:133:28 | first | set.cpp:133:7:133:28 | call to iterator | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:138:12:138:13 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:138:24:138:25 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:140:7:140:8 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:146:7:146:8 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:153:32:153:33 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:154:34:154:35 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:155:32:155:33 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:155:44:155:45 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:157:8:157:9 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:173:12:173:13 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:173:30:173:31 | s2 | | +| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:238:1:238:1 | s2 | | +| set.cpp:134:17:134:22 | call to source | set.cpp:134:7:134:8 | ref arg s2 | TAINT | +| set.cpp:134:17:134:22 | call to source | set.cpp:134:10:134:15 | call to insert | TAINT | +| set.cpp:134:27:134:31 | first | set.cpp:134:7:134:31 | call to iterator | | +| set.cpp:135:7:135:8 | ref arg s3 | set.cpp:141:7:141:8 | s3 | | +| set.cpp:135:7:135:8 | ref arg s3 | set.cpp:147:7:147:8 | s3 | | +| set.cpp:135:7:135:8 | ref arg s3 | set.cpp:238:1:238:1 | s3 | | +| set.cpp:135:17:135:18 | ref arg s3 | set.cpp:135:7:135:8 | s3 | | +| set.cpp:135:17:135:18 | ref arg s3 | set.cpp:141:7:141:8 | s3 | | +| set.cpp:135:17:135:18 | ref arg s3 | set.cpp:147:7:147:8 | s3 | | +| set.cpp:135:17:135:18 | ref arg s3 | set.cpp:238:1:238:1 | s3 | | +| set.cpp:135:17:135:18 | s3 | set.cpp:135:20:135:24 | call to begin | TAINT | +| set.cpp:135:20:135:24 | call to begin | set.cpp:135:17:135:26 | call to iterator | TAINT | +| set.cpp:135:29:135:33 | abc | set.cpp:135:7:135:8 | ref arg s3 | TAINT | +| set.cpp:135:29:135:33 | abc | set.cpp:135:10:135:15 | call to insert | TAINT | +| set.cpp:136:7:136:8 | ref arg s4 | set.cpp:142:7:142:8 | s4 | | +| set.cpp:136:7:136:8 | ref arg s4 | set.cpp:148:7:148:8 | s4 | | +| set.cpp:136:7:136:8 | ref arg s4 | set.cpp:238:1:238:1 | s4 | | +| set.cpp:136:17:136:18 | ref arg s4 | set.cpp:136:7:136:8 | s4 | | +| set.cpp:136:17:136:18 | ref arg s4 | set.cpp:142:7:142:8 | s4 | | +| set.cpp:136:17:136:18 | ref arg s4 | set.cpp:148:7:148:8 | s4 | | +| set.cpp:136:17:136:18 | ref arg s4 | set.cpp:238:1:238:1 | s4 | | +| set.cpp:136:17:136:18 | s4 | set.cpp:136:20:136:24 | call to begin | TAINT | +| set.cpp:136:20:136:24 | call to begin | set.cpp:136:17:136:26 | call to iterator | TAINT | +| set.cpp:136:29:136:34 | call to source | set.cpp:136:7:136:8 | ref arg s4 | TAINT | +| set.cpp:136:29:136:34 | call to source | set.cpp:136:10:136:15 | call to insert | TAINT | +| set.cpp:137:2:137:3 | ref arg s5 | set.cpp:143:7:143:8 | s5 | | +| set.cpp:137:2:137:3 | ref arg s5 | set.cpp:149:7:149:8 | s5 | | +| set.cpp:137:2:137:3 | ref arg s5 | set.cpp:238:1:238:1 | s5 | | +| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:137:24:137:25 | s1 | | +| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:139:7:139:8 | s1 | | +| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:145:7:145:8 | s1 | | +| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:169:12:169:13 | s1 | | +| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:169:30:169:31 | s1 | | +| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:238:1:238:1 | s1 | | +| set.cpp:137:12:137:13 | s1 | set.cpp:137:15:137:19 | call to begin | TAINT | +| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:139:7:139:8 | s1 | | +| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:145:7:145:8 | s1 | | +| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:169:12:169:13 | s1 | | +| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:169:30:169:31 | s1 | | +| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:238:1:238:1 | s1 | | +| set.cpp:137:24:137:25 | s1 | set.cpp:137:27:137:29 | call to end | TAINT | +| set.cpp:137:27:137:29 | call to end | set.cpp:137:2:137:3 | ref arg s5 | TAINT | +| set.cpp:137:27:137:29 | call to end | set.cpp:137:5:137:10 | call to insert | TAINT | +| set.cpp:138:2:138:3 | ref arg s6 | set.cpp:144:7:144:8 | s6 | | +| set.cpp:138:2:138:3 | ref arg s6 | set.cpp:150:7:150:8 | s6 | | +| set.cpp:138:2:138:3 | ref arg s6 | set.cpp:238:1:238:1 | s6 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:138:24:138:25 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:140:7:140:8 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:146:7:146:8 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:153:32:153:33 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:154:34:154:35 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:155:32:155:33 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:155:44:155:45 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:157:8:157:9 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:173:12:173:13 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:173:30:173:31 | s2 | | +| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:238:1:238:1 | s2 | | +| set.cpp:138:12:138:13 | s2 | set.cpp:138:15:138:19 | call to begin | TAINT | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:140:7:140:8 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:146:7:146:8 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:153:32:153:33 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:154:34:154:35 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:155:32:155:33 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:155:44:155:45 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:157:8:157:9 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:173:12:173:13 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:173:30:173:31 | s2 | | +| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:238:1:238:1 | s2 | | +| set.cpp:138:24:138:25 | s2 | set.cpp:138:27:138:29 | call to end | TAINT | +| set.cpp:138:27:138:29 | call to end | set.cpp:138:2:138:3 | ref arg s6 | TAINT | +| set.cpp:138:27:138:29 | call to end | set.cpp:138:5:138:10 | call to insert | TAINT | +| set.cpp:139:7:139:8 | s1 | set.cpp:139:7:139:8 | call to unordered_set | | +| set.cpp:140:7:140:8 | s2 | set.cpp:140:7:140:8 | call to unordered_set | | +| set.cpp:141:7:141:8 | s3 | set.cpp:141:7:141:8 | call to unordered_set | | +| set.cpp:142:7:142:8 | s4 | set.cpp:142:7:142:8 | call to unordered_set | | +| set.cpp:143:7:143:8 | s5 | set.cpp:143:7:143:8 | call to unordered_set | | +| set.cpp:144:7:144:8 | s6 | set.cpp:144:7:144:8 | call to unordered_set | | +| set.cpp:145:7:145:8 | ref arg s1 | set.cpp:169:12:169:13 | s1 | | +| set.cpp:145:7:145:8 | ref arg s1 | set.cpp:169:30:169:31 | s1 | | +| set.cpp:145:7:145:8 | ref arg s1 | set.cpp:238:1:238:1 | s1 | | +| set.cpp:145:7:145:8 | s1 | set.cpp:145:10:145:13 | call to find | TAINT | +| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:153:32:153:33 | s2 | | +| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:154:34:154:35 | s2 | | +| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:155:32:155:33 | s2 | | +| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:155:44:155:45 | s2 | | +| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:157:8:157:9 | s2 | | +| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:173:12:173:13 | s2 | | +| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:173:30:173:31 | s2 | | +| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:238:1:238:1 | s2 | | +| set.cpp:146:7:146:8 | s2 | set.cpp:146:10:146:13 | call to find | TAINT | +| set.cpp:147:7:147:8 | ref arg s3 | set.cpp:238:1:238:1 | s3 | | +| set.cpp:147:7:147:8 | s3 | set.cpp:147:10:147:13 | call to find | TAINT | +| set.cpp:148:7:148:8 | ref arg s4 | set.cpp:238:1:238:1 | s4 | | +| set.cpp:148:7:148:8 | s4 | set.cpp:148:10:148:13 | call to find | TAINT | +| set.cpp:149:7:149:8 | ref arg s5 | set.cpp:238:1:238:1 | s5 | | +| set.cpp:149:7:149:8 | s5 | set.cpp:149:10:149:13 | call to find | TAINT | +| set.cpp:150:7:150:8 | ref arg s6 | set.cpp:238:1:238:1 | s6 | | +| set.cpp:150:7:150:8 | s6 | set.cpp:150:10:150:13 | call to find | TAINT | +| set.cpp:153:32:153:33 | s2 | set.cpp:153:32:153:34 | call to unordered_set | | +| set.cpp:153:32:153:34 | call to unordered_set | set.cpp:158:7:158:8 | s7 | | +| set.cpp:153:32:153:34 | call to unordered_set | set.cpp:162:7:162:8 | s7 | | +| set.cpp:153:32:153:34 | call to unordered_set | set.cpp:238:1:238:1 | s7 | | +| set.cpp:154:33:154:35 | call to unordered_set | set.cpp:159:7:159:8 | s8 | | +| set.cpp:154:33:154:35 | call to unordered_set | set.cpp:163:7:163:8 | s8 | | +| set.cpp:154:33:154:35 | call to unordered_set | set.cpp:238:1:238:1 | s8 | | +| set.cpp:154:34:154:35 | s2 | set.cpp:154:33:154:35 | call to unordered_set | | +| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:155:44:155:45 | s2 | | +| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:157:8:157:9 | s2 | | +| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:173:12:173:13 | s2 | | +| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:173:30:173:31 | s2 | | +| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:238:1:238:1 | s2 | | +| set.cpp:155:32:155:33 | s2 | set.cpp:155:35:155:39 | call to begin | TAINT | +| set.cpp:155:32:155:52 | call to unordered_set | set.cpp:160:7:160:8 | s9 | | +| set.cpp:155:32:155:52 | call to unordered_set | set.cpp:164:7:164:8 | s9 | | +| set.cpp:155:32:155:52 | call to unordered_set | set.cpp:238:1:238:1 | s9 | | +| set.cpp:155:35:155:39 | call to begin | set.cpp:155:32:155:52 | call to unordered_set | TAINT | +| set.cpp:155:44:155:45 | ref arg s2 | set.cpp:157:8:157:9 | s2 | | +| set.cpp:155:44:155:45 | ref arg s2 | set.cpp:173:12:173:13 | s2 | | +| set.cpp:155:44:155:45 | ref arg s2 | set.cpp:173:30:173:31 | s2 | | +| set.cpp:155:44:155:45 | ref arg s2 | set.cpp:238:1:238:1 | s2 | | +| set.cpp:155:44:155:45 | s2 | set.cpp:155:47:155:49 | call to end | TAINT | +| set.cpp:155:47:155:49 | call to end | set.cpp:155:32:155:52 | call to unordered_set | TAINT | +| set.cpp:156:29:156:31 | call to unordered_set | set.cpp:157:2:157:4 | s10 | | +| set.cpp:156:29:156:31 | call to unordered_set | set.cpp:161:7:161:9 | s10 | | +| set.cpp:156:29:156:31 | call to unordered_set | set.cpp:165:7:165:9 | s10 | | +| set.cpp:156:29:156:31 | call to unordered_set | set.cpp:238:1:238:1 | s10 | | +| set.cpp:157:2:157:4 | ref arg s10 | set.cpp:161:7:161:9 | s10 | | +| set.cpp:157:2:157:4 | ref arg s10 | set.cpp:165:7:165:9 | s10 | | +| set.cpp:157:2:157:4 | ref arg s10 | set.cpp:238:1:238:1 | s10 | | +| set.cpp:157:8:157:9 | s2 | set.cpp:157:2:157:4 | ref arg s10 | TAINT | +| set.cpp:157:8:157:9 | s2 | set.cpp:157:6:157:6 | call to operator= | TAINT | +| set.cpp:158:7:158:8 | s7 | set.cpp:158:7:158:8 | call to unordered_set | | +| set.cpp:159:7:159:8 | s8 | set.cpp:159:7:159:8 | call to unordered_set | | +| set.cpp:160:7:160:8 | s9 | set.cpp:160:7:160:8 | call to unordered_set | | +| set.cpp:161:7:161:9 | s10 | set.cpp:161:7:161:9 | call to unordered_set | | +| set.cpp:162:7:162:8 | ref arg s7 | set.cpp:238:1:238:1 | s7 | | +| set.cpp:162:7:162:8 | s7 | set.cpp:162:10:162:13 | call to find | TAINT | +| set.cpp:163:7:163:8 | ref arg s8 | set.cpp:238:1:238:1 | s8 | | +| set.cpp:163:7:163:8 | s8 | set.cpp:163:10:163:13 | call to find | TAINT | +| set.cpp:164:7:164:8 | ref arg s9 | set.cpp:238:1:238:1 | s9 | | +| set.cpp:164:7:164:8 | s9 | set.cpp:164:10:164:13 | call to find | TAINT | +| set.cpp:165:7:165:9 | ref arg s10 | set.cpp:238:1:238:1 | s10 | | +| set.cpp:165:7:165:9 | s10 | set.cpp:165:11:165:14 | call to find | TAINT | +| set.cpp:169:12:169:13 | ref arg s1 | set.cpp:169:30:169:31 | s1 | | +| set.cpp:169:12:169:13 | ref arg s1 | set.cpp:238:1:238:1 | s1 | | +| set.cpp:169:12:169:13 | s1 | set.cpp:169:15:169:19 | call to begin | TAINT | +| set.cpp:169:15:169:19 | call to begin | set.cpp:169:7:169:21 | ... = ... | | +| set.cpp:169:15:169:19 | call to begin | set.cpp:169:24:169:25 | i1 | | +| set.cpp:169:15:169:19 | call to begin | set.cpp:169:40:169:41 | i1 | | +| set.cpp:169:15:169:19 | call to begin | set.cpp:171:9:171:10 | i1 | | +| set.cpp:169:30:169:31 | ref arg s1 | set.cpp:169:30:169:31 | s1 | | +| set.cpp:169:30:169:31 | ref arg s1 | set.cpp:238:1:238:1 | s1 | | +| set.cpp:169:30:169:31 | s1 | set.cpp:169:33:169:35 | call to end | TAINT | +| set.cpp:169:40:169:41 | i1 | set.cpp:169:42:169:42 | call to operator++ | | +| set.cpp:169:40:169:41 | ref arg i1 | set.cpp:169:24:169:25 | i1 | | +| set.cpp:169:40:169:41 | ref arg i1 | set.cpp:169:40:169:41 | i1 | | +| set.cpp:169:40:169:41 | ref arg i1 | set.cpp:171:9:171:10 | i1 | | +| set.cpp:171:9:171:10 | i1 | set.cpp:171:8:171:8 | call to operator* | TAINT | +| set.cpp:173:12:173:13 | ref arg s2 | set.cpp:173:30:173:31 | s2 | | +| set.cpp:173:12:173:13 | ref arg s2 | set.cpp:238:1:238:1 | s2 | | +| set.cpp:173:12:173:13 | s2 | set.cpp:173:15:173:19 | call to begin | TAINT | +| set.cpp:173:15:173:19 | call to begin | set.cpp:173:7:173:21 | ... = ... | | +| set.cpp:173:15:173:19 | call to begin | set.cpp:173:24:173:25 | i2 | | +| set.cpp:173:15:173:19 | call to begin | set.cpp:173:40:173:41 | i2 | | +| set.cpp:173:15:173:19 | call to begin | set.cpp:175:9:175:10 | i2 | | +| set.cpp:173:30:173:31 | ref arg s2 | set.cpp:173:30:173:31 | s2 | | +| set.cpp:173:30:173:31 | ref arg s2 | set.cpp:238:1:238:1 | s2 | | +| set.cpp:173:30:173:31 | s2 | set.cpp:173:33:173:35 | call to end | TAINT | +| set.cpp:173:40:173:41 | i2 | set.cpp:173:42:173:42 | call to operator++ | | +| set.cpp:173:40:173:41 | ref arg i2 | set.cpp:173:24:173:25 | i2 | | +| set.cpp:173:40:173:41 | ref arg i2 | set.cpp:173:40:173:41 | i2 | | +| set.cpp:173:40:173:41 | ref arg i2 | set.cpp:175:9:175:10 | i2 | | +| set.cpp:175:9:175:10 | i2 | set.cpp:175:8:175:8 | call to operator* | TAINT | +| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:180:2:180:4 | s11 | | +| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:181:2:181:4 | s11 | | +| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:182:2:182:4 | s11 | | +| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:183:7:183:9 | s11 | | +| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:184:7:184:9 | s11 | | +| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:238:1:238:1 | s11 | | +| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:181:2:181:4 | s11 | | +| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:182:2:182:4 | s11 | | +| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:183:7:183:9 | s11 | | +| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:184:7:184:9 | s11 | | +| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:238:1:238:1 | s11 | | +| set.cpp:180:13:180:15 | a | set.cpp:180:2:180:4 | ref arg s11 | TAINT | +| set.cpp:180:13:180:15 | a | set.cpp:180:6:180:11 | call to insert | TAINT | +| set.cpp:181:2:181:4 | ref arg s11 | set.cpp:182:2:182:4 | s11 | | +| set.cpp:181:2:181:4 | ref arg s11 | set.cpp:183:7:183:9 | s11 | | +| set.cpp:181:2:181:4 | ref arg s11 | set.cpp:184:7:184:9 | s11 | | +| set.cpp:181:2:181:4 | ref arg s11 | set.cpp:238:1:238:1 | s11 | | +| set.cpp:181:13:181:18 | call to source | set.cpp:181:2:181:4 | ref arg s11 | TAINT | +| set.cpp:181:13:181:18 | call to source | set.cpp:181:6:181:11 | call to insert | TAINT | +| set.cpp:182:2:182:4 | ref arg s11 | set.cpp:183:7:183:9 | s11 | | +| set.cpp:182:2:182:4 | ref arg s11 | set.cpp:184:7:184:9 | s11 | | +| set.cpp:182:2:182:4 | ref arg s11 | set.cpp:238:1:238:1 | s11 | | +| set.cpp:182:13:182:15 | c | set.cpp:182:2:182:4 | ref arg s11 | TAINT | +| set.cpp:182:13:182:15 | c | set.cpp:182:6:182:11 | call to insert | TAINT | +| set.cpp:183:7:183:9 | ref arg s11 | set.cpp:184:7:184:9 | s11 | | +| set.cpp:183:7:183:9 | ref arg s11 | set.cpp:238:1:238:1 | s11 | | +| set.cpp:183:7:183:9 | s11 | set.cpp:183:11:183:21 | call to equal_range | TAINT | +| set.cpp:183:28:183:32 | first | set.cpp:183:7:183:32 | call to iterator | | +| set.cpp:184:7:184:9 | ref arg s11 | set.cpp:238:1:238:1 | s11 | | +| set.cpp:184:7:184:9 | s11 | set.cpp:184:11:184:21 | call to equal_range | TAINT | +| set.cpp:184:28:184:33 | second | set.cpp:184:7:184:33 | call to iterator | | +| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:188:2:188:4 | s12 | | +| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:190:7:190:9 | s12 | | +| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:194:2:194:4 | s12 | | +| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:196:7:196:9 | s12 | | +| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:238:1:238:1 | s12 | | +| set.cpp:187:34:187:36 | call to unordered_set | set.cpp:191:7:191:9 | s13 | | +| set.cpp:187:34:187:36 | call to unordered_set | set.cpp:194:11:194:13 | s13 | | +| set.cpp:187:34:187:36 | call to unordered_set | set.cpp:197:7:197:9 | s13 | | +| set.cpp:187:34:187:36 | call to unordered_set | set.cpp:238:1:238:1 | s13 | | +| set.cpp:187:39:187:41 | call to unordered_set | set.cpp:192:7:192:9 | s14 | | +| set.cpp:187:39:187:41 | call to unordered_set | set.cpp:195:2:195:4 | s14 | | +| set.cpp:187:39:187:41 | call to unordered_set | set.cpp:198:7:198:9 | s14 | | +| set.cpp:187:39:187:41 | call to unordered_set | set.cpp:238:1:238:1 | s14 | | +| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:189:2:189:4 | s15 | | +| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:193:7:193:9 | s15 | | +| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:195:11:195:13 | s15 | | +| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:199:7:199:9 | s15 | | +| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:238:1:238:1 | s15 | | +| set.cpp:188:2:188:4 | ref arg s12 | set.cpp:190:7:190:9 | s12 | | +| set.cpp:188:2:188:4 | ref arg s12 | set.cpp:194:2:194:4 | s12 | | +| set.cpp:188:2:188:4 | ref arg s12 | set.cpp:196:7:196:9 | s12 | | +| set.cpp:188:2:188:4 | ref arg s12 | set.cpp:238:1:238:1 | s12 | | +| set.cpp:188:13:188:18 | call to source | set.cpp:188:2:188:4 | ref arg s12 | TAINT | +| set.cpp:188:13:188:18 | call to source | set.cpp:188:6:188:11 | call to insert | TAINT | +| set.cpp:189:2:189:4 | ref arg s15 | set.cpp:193:7:193:9 | s15 | | +| set.cpp:189:2:189:4 | ref arg s15 | set.cpp:195:11:195:13 | s15 | | +| set.cpp:189:2:189:4 | ref arg s15 | set.cpp:199:7:199:9 | s15 | | +| set.cpp:189:2:189:4 | ref arg s15 | set.cpp:238:1:238:1 | s15 | | +| set.cpp:189:13:189:18 | call to source | set.cpp:189:2:189:4 | ref arg s15 | TAINT | +| set.cpp:189:13:189:18 | call to source | set.cpp:189:6:189:11 | call to insert | TAINT | +| set.cpp:190:7:190:9 | s12 | set.cpp:190:7:190:9 | call to unordered_set | | +| set.cpp:191:7:191:9 | s13 | set.cpp:191:7:191:9 | call to unordered_set | | +| set.cpp:192:7:192:9 | s14 | set.cpp:192:7:192:9 | call to unordered_set | | +| set.cpp:193:7:193:9 | s15 | set.cpp:193:7:193:9 | call to unordered_set | | +| set.cpp:194:2:194:4 | ref arg s12 | set.cpp:196:7:196:9 | s12 | | +| set.cpp:194:2:194:4 | ref arg s12 | set.cpp:238:1:238:1 | s12 | | +| set.cpp:194:2:194:4 | s12 | set.cpp:194:11:194:13 | ref arg s13 | TAINT | +| set.cpp:194:11:194:13 | ref arg s13 | set.cpp:197:7:197:9 | s13 | | +| set.cpp:194:11:194:13 | ref arg s13 | set.cpp:238:1:238:1 | s13 | | +| set.cpp:194:11:194:13 | s13 | set.cpp:194:2:194:4 | ref arg s12 | TAINT | +| set.cpp:195:2:195:4 | ref arg s14 | set.cpp:198:7:198:9 | s14 | | +| set.cpp:195:2:195:4 | ref arg s14 | set.cpp:238:1:238:1 | s14 | | +| set.cpp:195:2:195:4 | s14 | set.cpp:195:11:195:13 | ref arg s15 | TAINT | +| set.cpp:195:11:195:13 | ref arg s15 | set.cpp:199:7:199:9 | s15 | | +| set.cpp:195:11:195:13 | ref arg s15 | set.cpp:238:1:238:1 | s15 | | +| set.cpp:195:11:195:13 | s15 | set.cpp:195:2:195:4 | ref arg s14 | TAINT | +| set.cpp:196:7:196:9 | s12 | set.cpp:196:7:196:9 | call to unordered_set | | +| set.cpp:197:7:197:9 | s13 | set.cpp:197:7:197:9 | call to unordered_set | | +| set.cpp:198:7:198:9 | s14 | set.cpp:198:7:198:9 | call to unordered_set | | +| set.cpp:199:7:199:9 | s15 | set.cpp:199:7:199:9 | call to unordered_set | | +| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:203:2:203:4 | s16 | | +| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:207:7:207:9 | s16 | | +| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:211:2:211:4 | s16 | | +| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:213:7:213:9 | s16 | | +| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:238:1:238:1 | s16 | | +| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:204:2:204:4 | s17 | | +| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:208:7:208:9 | s17 | | +| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:211:12:211:14 | s17 | | +| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:214:7:214:9 | s17 | | +| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:238:1:238:1 | s17 | | +| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:205:2:205:4 | s18 | | +| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:209:7:209:9 | s18 | | +| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:212:2:212:4 | s18 | | +| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:215:7:215:9 | s18 | | +| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:238:1:238:1 | s18 | | +| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:206:2:206:4 | s19 | | +| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:210:7:210:9 | s19 | | +| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:212:12:212:14 | s19 | | +| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:216:7:216:9 | s19 | | +| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:238:1:238:1 | s19 | | +| set.cpp:203:2:203:4 | ref arg s16 | set.cpp:207:7:207:9 | s16 | | +| set.cpp:203:2:203:4 | ref arg s16 | set.cpp:211:2:211:4 | s16 | | +| set.cpp:203:2:203:4 | ref arg s16 | set.cpp:213:7:213:9 | s16 | | +| set.cpp:203:2:203:4 | ref arg s16 | set.cpp:238:1:238:1 | s16 | | +| set.cpp:203:13:203:18 | call to source | set.cpp:203:2:203:4 | ref arg s16 | TAINT | +| set.cpp:203:13:203:18 | call to source | set.cpp:203:6:203:11 | call to insert | TAINT | +| set.cpp:204:2:204:4 | ref arg s17 | set.cpp:208:7:208:9 | s17 | | +| set.cpp:204:2:204:4 | ref arg s17 | set.cpp:211:12:211:14 | s17 | | +| set.cpp:204:2:204:4 | ref arg s17 | set.cpp:214:7:214:9 | s17 | | +| set.cpp:204:2:204:4 | ref arg s17 | set.cpp:238:1:238:1 | s17 | | +| set.cpp:204:13:204:17 | abc | set.cpp:204:2:204:4 | ref arg s17 | TAINT | +| set.cpp:204:13:204:17 | abc | set.cpp:204:6:204:11 | call to insert | TAINT | +| set.cpp:205:2:205:4 | ref arg s18 | set.cpp:209:7:209:9 | s18 | | +| set.cpp:205:2:205:4 | ref arg s18 | set.cpp:212:2:212:4 | s18 | | +| set.cpp:205:2:205:4 | ref arg s18 | set.cpp:215:7:215:9 | s18 | | +| set.cpp:205:2:205:4 | ref arg s18 | set.cpp:238:1:238:1 | s18 | | +| set.cpp:205:13:205:17 | def | set.cpp:205:2:205:4 | ref arg s18 | TAINT | +| set.cpp:205:13:205:17 | def | set.cpp:205:6:205:11 | call to insert | TAINT | +| set.cpp:206:2:206:4 | ref arg s19 | set.cpp:210:7:210:9 | s19 | | +| set.cpp:206:2:206:4 | ref arg s19 | set.cpp:212:12:212:14 | s19 | | +| set.cpp:206:2:206:4 | ref arg s19 | set.cpp:216:7:216:9 | s19 | | +| set.cpp:206:2:206:4 | ref arg s19 | set.cpp:238:1:238:1 | s19 | | +| set.cpp:206:13:206:18 | call to source | set.cpp:206:2:206:4 | ref arg s19 | TAINT | +| set.cpp:206:13:206:18 | call to source | set.cpp:206:6:206:11 | call to insert | TAINT | +| set.cpp:207:7:207:9 | s16 | set.cpp:207:7:207:9 | call to unordered_set | | +| set.cpp:208:7:208:9 | s17 | set.cpp:208:7:208:9 | call to unordered_set | | +| set.cpp:209:7:209:9 | s18 | set.cpp:209:7:209:9 | call to unordered_set | | +| set.cpp:210:7:210:9 | s19 | set.cpp:210:7:210:9 | call to unordered_set | | +| set.cpp:211:2:211:4 | ref arg s16 | set.cpp:213:7:213:9 | s16 | | +| set.cpp:211:2:211:4 | ref arg s16 | set.cpp:238:1:238:1 | s16 | | +| set.cpp:211:12:211:14 | ref arg s17 | set.cpp:214:7:214:9 | s17 | | +| set.cpp:211:12:211:14 | ref arg s17 | set.cpp:238:1:238:1 | s17 | | +| set.cpp:211:12:211:14 | s17 | set.cpp:211:2:211:4 | ref arg s16 | TAINT | +| set.cpp:212:2:212:4 | ref arg s18 | set.cpp:215:7:215:9 | s18 | | +| set.cpp:212:2:212:4 | ref arg s18 | set.cpp:238:1:238:1 | s18 | | +| set.cpp:212:12:212:14 | ref arg s19 | set.cpp:216:7:216:9 | s19 | | +| set.cpp:212:12:212:14 | ref arg s19 | set.cpp:238:1:238:1 | s19 | | +| set.cpp:212:12:212:14 | s19 | set.cpp:212:2:212:4 | ref arg s18 | TAINT | +| set.cpp:213:7:213:9 | s16 | set.cpp:213:7:213:9 | call to unordered_set | | +| set.cpp:214:7:214:9 | s17 | set.cpp:214:7:214:9 | call to unordered_set | | +| set.cpp:215:7:215:9 | s18 | set.cpp:215:7:215:9 | call to unordered_set | | +| set.cpp:216:7:216:9 | s19 | set.cpp:216:7:216:9 | call to unordered_set | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:220:2:220:4 | s20 | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:221:2:221:4 | s20 | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:222:7:222:9 | s20 | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:223:7:223:9 | s20 | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:223:17:223:19 | s20 | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:224:7:224:9 | s20 | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:225:2:225:4 | s20 | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:226:7:226:9 | s20 | | +| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:238:1:238:1 | s20 | | +| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:221:2:221:4 | s20 | | +| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:222:7:222:9 | s20 | | +| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:223:7:223:9 | s20 | | +| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:223:17:223:19 | s20 | | +| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:224:7:224:9 | s20 | | +| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:225:2:225:4 | s20 | | +| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:226:7:226:9 | s20 | | +| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:238:1:238:1 | s20 | | +| set.cpp:220:13:220:18 | call to source | set.cpp:220:2:220:4 | ref arg s20 | TAINT | +| set.cpp:220:13:220:18 | call to source | set.cpp:220:6:220:11 | call to insert | TAINT | +| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:222:7:222:9 | s20 | | +| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:223:7:223:9 | s20 | | +| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:223:17:223:19 | s20 | | +| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:224:7:224:9 | s20 | | +| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:225:2:225:4 | s20 | | +| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:226:7:226:9 | s20 | | +| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:238:1:238:1 | s20 | | +| set.cpp:221:13:221:18 | call to source | set.cpp:221:2:221:4 | ref arg s20 | TAINT | +| set.cpp:221:13:221:18 | call to source | set.cpp:221:6:221:11 | call to insert | TAINT | +| set.cpp:222:7:222:9 | s20 | set.cpp:222:7:222:9 | call to unordered_set | | +| set.cpp:223:7:223:9 | ref arg s20 | set.cpp:224:7:224:9 | s20 | | +| set.cpp:223:7:223:9 | ref arg s20 | set.cpp:225:2:225:4 | s20 | | +| set.cpp:223:7:223:9 | ref arg s20 | set.cpp:226:7:226:9 | s20 | | +| set.cpp:223:7:223:9 | ref arg s20 | set.cpp:238:1:238:1 | s20 | | +| set.cpp:223:7:223:9 | s20 | set.cpp:223:11:223:15 | call to erase | TAINT | +| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:223:7:223:9 | s20 | | +| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:224:7:224:9 | s20 | | +| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:225:2:225:4 | s20 | | +| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:226:7:226:9 | s20 | | +| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:238:1:238:1 | s20 | | +| set.cpp:223:17:223:19 | s20 | set.cpp:223:21:223:25 | call to begin | TAINT | +| set.cpp:224:7:224:9 | s20 | set.cpp:224:7:224:9 | call to unordered_set | | +| set.cpp:225:2:225:4 | ref arg s20 | set.cpp:226:7:226:9 | s20 | | +| set.cpp:225:2:225:4 | ref arg s20 | set.cpp:238:1:238:1 | s20 | | +| set.cpp:226:7:226:9 | s20 | set.cpp:226:7:226:9 | call to unordered_set | | +| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:230:7:230:9 | s21 | | +| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:231:7:231:9 | s21 | | +| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:232:7:232:9 | s21 | | +| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:233:7:233:9 | s21 | | +| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:238:1:238:1 | s21 | | +| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:234:7:234:9 | s22 | | +| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:234:24:234:26 | s22 | | +| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:235:7:235:9 | s22 | | +| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:236:7:236:9 | s22 | | +| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:236:24:236:26 | s22 | | +| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:237:7:237:9 | s22 | | +| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:238:1:238:1 | s22 | | +| set.cpp:230:7:230:9 | ref arg s21 | set.cpp:231:7:231:9 | s21 | | +| set.cpp:230:7:230:9 | ref arg s21 | set.cpp:232:7:232:9 | s21 | | +| set.cpp:230:7:230:9 | ref arg s21 | set.cpp:233:7:233:9 | s21 | | +| set.cpp:230:7:230:9 | ref arg s21 | set.cpp:238:1:238:1 | s21 | | +| set.cpp:230:7:230:9 | s21 | set.cpp:230:11:230:17 | call to emplace | TAINT | +| set.cpp:230:19:230:23 | abc | set.cpp:230:7:230:9 | ref arg s21 | TAINT | +| set.cpp:230:19:230:23 | abc | set.cpp:230:11:230:17 | call to emplace | TAINT | +| set.cpp:230:26:230:30 | first | set.cpp:230:7:230:30 | call to iterator | | +| set.cpp:231:7:231:9 | s21 | set.cpp:231:7:231:9 | call to unordered_set | | +| set.cpp:232:7:232:9 | ref arg s21 | set.cpp:233:7:233:9 | s21 | | +| set.cpp:232:7:232:9 | ref arg s21 | set.cpp:238:1:238:1 | s21 | | +| set.cpp:232:7:232:9 | s21 | set.cpp:232:11:232:17 | call to emplace | TAINT | +| set.cpp:232:19:232:24 | call to source | set.cpp:232:7:232:9 | ref arg s21 | TAINT | +| set.cpp:232:19:232:24 | call to source | set.cpp:232:11:232:17 | call to emplace | TAINT | +| set.cpp:232:29:232:33 | first | set.cpp:232:7:232:33 | call to iterator | | +| set.cpp:233:7:233:9 | s21 | set.cpp:233:7:233:9 | call to unordered_set | | +| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:235:7:235:9 | s22 | | +| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:236:7:236:9 | s22 | | +| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:236:24:236:26 | s22 | | +| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:237:7:237:9 | s22 | | +| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:238:1:238:1 | s22 | | +| set.cpp:234:7:234:9 | s22 | set.cpp:234:11:234:22 | call to emplace_hint | TAINT | +| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:234:7:234:9 | s22 | | +| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:235:7:235:9 | s22 | | +| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:236:7:236:9 | s22 | | +| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:236:24:236:26 | s22 | | +| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:237:7:237:9 | s22 | | +| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:238:1:238:1 | s22 | | +| set.cpp:234:24:234:26 | s22 | set.cpp:234:28:234:32 | call to begin | TAINT | +| set.cpp:234:24:234:34 | call to iterator | set.cpp:234:7:234:9 | ref arg s22 | TAINT | +| set.cpp:234:24:234:34 | call to iterator | set.cpp:234:11:234:22 | call to emplace_hint | TAINT | +| set.cpp:234:28:234:32 | call to begin | set.cpp:234:24:234:34 | call to iterator | TAINT | +| set.cpp:234:37:234:41 | abc | set.cpp:234:7:234:9 | ref arg s22 | TAINT | +| set.cpp:234:37:234:41 | abc | set.cpp:234:11:234:22 | call to emplace_hint | TAINT | +| set.cpp:235:7:235:9 | s22 | set.cpp:235:7:235:9 | call to unordered_set | | +| set.cpp:236:7:236:9 | ref arg s22 | set.cpp:237:7:237:9 | s22 | | +| set.cpp:236:7:236:9 | ref arg s22 | set.cpp:238:1:238:1 | s22 | | +| set.cpp:236:7:236:9 | s22 | set.cpp:236:11:236:22 | call to emplace_hint | TAINT | +| set.cpp:236:24:236:26 | ref arg s22 | set.cpp:236:7:236:9 | s22 | | +| set.cpp:236:24:236:26 | ref arg s22 | set.cpp:237:7:237:9 | s22 | | +| set.cpp:236:24:236:26 | ref arg s22 | set.cpp:238:1:238:1 | s22 | | +| set.cpp:236:24:236:26 | s22 | set.cpp:236:28:236:32 | call to begin | TAINT | +| set.cpp:236:24:236:34 | call to iterator | set.cpp:236:7:236:9 | ref arg s22 | TAINT | +| set.cpp:236:24:236:34 | call to iterator | set.cpp:236:11:236:22 | call to emplace_hint | TAINT | +| set.cpp:236:28:236:32 | call to begin | set.cpp:236:24:236:34 | call to iterator | TAINT | +| set.cpp:236:37:236:42 | call to source | set.cpp:236:7:236:9 | ref arg s22 | TAINT | +| set.cpp:236:37:236:42 | call to source | set.cpp:236:11:236:22 | call to emplace_hint | TAINT | +| set.cpp:237:7:237:9 | s22 | set.cpp:237:7:237:9 | call to unordered_set | | +| smart_pointer.cpp:11:30:11:50 | call to make_shared | smart_pointer.cpp:12:11:12:11 | p | | +| smart_pointer.cpp:11:30:11:50 | call to make_shared | smart_pointer.cpp:13:10:13:10 | p | | +| smart_pointer.cpp:11:52:11:57 | call to source | smart_pointer.cpp:11:30:11:50 | call to make_shared | TAINT | +| smart_pointer.cpp:12:11:12:11 | p | smart_pointer.cpp:12:10:12:10 | call to operator* | TAINT | +| smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:18:11:18:11 | p | | +| smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:19:10:19:10 | p | | +| smart_pointer.cpp:18:11:18:11 | p | smart_pointer.cpp:18:10:18:10 | call to operator* | TAINT | +| smart_pointer.cpp:23:30:23:50 | call to make_unique | smart_pointer.cpp:24:11:24:11 | p | | +| smart_pointer.cpp:23:30:23:50 | call to make_unique | smart_pointer.cpp:25:10:25:10 | p | | +| smart_pointer.cpp:23:52:23:57 | call to source | smart_pointer.cpp:23:30:23:50 | call to make_unique | TAINT | +| smart_pointer.cpp:24:11:24:11 | p | smart_pointer.cpp:24:10:24:10 | call to operator* | TAINT | +| smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:30:11:30:11 | p | | +| smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:31:10:31:10 | p | | +| smart_pointer.cpp:30:11:30:11 | p | smart_pointer.cpp:30:10:30:10 | call to operator* | TAINT | +| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:37:6:37:6 | p | | +| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:38:10:38:10 | p | | +| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:39:11:39:11 | p | | +| smart_pointer.cpp:37:5:37:17 | ... = ... | smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | | +| smart_pointer.cpp:37:6:37:6 | p | smart_pointer.cpp:37:5:37:5 | call to operator* | TAINT | +| smart_pointer.cpp:37:10:37:15 | call to source | smart_pointer.cpp:37:5:37:17 | ... = ... | | +| smart_pointer.cpp:38:10:38:10 | ref arg p | smart_pointer.cpp:39:11:39:11 | p | | +| smart_pointer.cpp:39:11:39:11 | p | smart_pointer.cpp:39:10:39:10 | call to operator* | TAINT | +| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:45:6:45:6 | p | | +| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:46:10:46:10 | p | | +| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:47:11:47:11 | p | | +| smart_pointer.cpp:45:5:45:17 | ... = ... | smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | | +| smart_pointer.cpp:45:6:45:6 | p | smart_pointer.cpp:45:5:45:5 | call to operator* | TAINT | +| smart_pointer.cpp:45:10:45:15 | call to source | smart_pointer.cpp:45:5:45:17 | ... = ... | | +| smart_pointer.cpp:46:10:46:10 | ref arg p | smart_pointer.cpp:47:11:47:11 | p | | +| smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | TAINT | +| smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | | +| smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT | +| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | TAINT | +| smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | | +| smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT | +| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | TAINT | +| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | | +| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | | +| smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT | +| smart_pointer.cpp:65:58:65:58 | 0 | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT | +| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:39:45:39:51 | source1 | | +| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | | +| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | | +| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:42:14:42:20 | source1 | | +| standalone_iterators.cpp:40:11:40:17 | source1 | standalone_iterators.cpp:40:10:40:10 | call to operator* | TAINT | +| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | | +| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:42:14:42:20 | source1 | | +| standalone_iterators.cpp:41:12:41:18 | source1 | standalone_iterators.cpp:41:19:41:19 | call to operator++ | | +| standalone_iterators.cpp:41:19:41:19 | call to operator++ | standalone_iterators.cpp:41:10:41:10 | call to operator* | TAINT | +| standalone_iterators.cpp:42:12:42:12 | call to operator++ | standalone_iterators.cpp:42:10:42:10 | call to operator* | TAINT | +| standalone_iterators.cpp:42:14:42:20 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | | +| standalone_iterators.cpp:42:14:42:20 | source1 | standalone_iterators.cpp:42:12:42:12 | call to operator++ | | +| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:45:39:45:45 | source1 | | +| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:46:11:46:17 | source1 | | +| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:47:12:47:18 | source1 | | +| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:48:14:48:20 | source1 | | +| standalone_iterators.cpp:46:11:46:17 | source1 | standalone_iterators.cpp:46:10:46:10 | call to operator* | TAINT | +| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | | +| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:48:14:48:20 | source1 | | +| standalone_iterators.cpp:47:12:47:18 | source1 | standalone_iterators.cpp:47:19:47:19 | call to operator++ | | +| standalone_iterators.cpp:47:19:47:19 | call to operator++ | standalone_iterators.cpp:47:10:47:10 | call to operator* | TAINT | +| standalone_iterators.cpp:48:12:48:12 | call to operator++ | standalone_iterators.cpp:48:10:48:10 | call to operator* | TAINT | +| standalone_iterators.cpp:48:14:48:20 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | | +| standalone_iterators.cpp:48:14:48:20 | source1 | standalone_iterators.cpp:48:12:48:12 | call to operator++ | | +| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:52:11:52:17 | source1 | | +| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:53:12:53:18 | source1 | | +| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:54:14:54:20 | source1 | | +| standalone_iterators.cpp:53:12:53:18 | ref arg source1 | standalone_iterators.cpp:54:14:54:20 | source1 | | +| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT | +| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT | +| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT | +| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT | +| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT | +| stl.h:241:53:241:63 | 0 | stl.h:241:46:241:64 | (no string representation) | TAINT | +| stl.h:334:9:334:9 | Unknown literal | stl.h:334:9:334:9 | constructor init of field first | TAINT | +| stl.h:334:9:334:9 | Unknown literal | stl.h:334:9:334:9 | constructor init of field second | TAINT | +| stl.h:334:9:334:9 | constructor init of field first [post-this] | stl.h:334:9:334:9 | constructor init of field second [pre-this] | | +| stl.h:334:9:334:9 | constructor init of field first [pre-this] | stl.h:334:9:334:9 | constructor init of field second [pre-this] | | +| stl.h:334:9:334:9 | this | stl.h:334:9:334:9 | constructor init of field first [pre-this] | | +| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | | +| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | | +| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | | +| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | | +| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | | +| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | | +| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | | +| stl.h:341:3:341:6 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | | +| stl.h:341:18:341:18 | x | stl.h:341:18:341:18 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:18:341:18 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:18:341:18 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:18:341:18 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | | +| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | | +| stl.h:341:31:341:31 | y | stl.h:341:31:341:31 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:31:341:31 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:31:341:31 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:31:341:31 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | | +| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | | +| stl.h:341:36:341:43 | call to unknown function | stl.h:341:36:341:43 | constructor init of field first | TAINT | +| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | | +| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT | +| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT | +| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT | +| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT | +| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT | +| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT | +| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT | +| stl.h:341:46:341:54 | call to unknown function | stl.h:341:46:341:54 | constructor init of field second | TAINT | +| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT | +| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT | +| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT | +| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT | +| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT | +| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT | +| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT | +| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | | +| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | | +| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | | +| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | | +| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | | +| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | | +| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | | +| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | | +| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | | +| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | | +| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | | +| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | | +| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | | +| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | | +| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | | +| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | | +| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | | +| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | | +| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | | +| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | | +| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | | +| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | | +| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | | +| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | | +| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | | +| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | | +| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | | +| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | | +| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:23:348:38 | ref arg call to forward | stl.h:347:109:347:109 | x | | +| stl.h:348:23:348:38 | ref arg call to forward | stl.h:347:109:347:109 | x | | +| stl.h:348:23:348:38 | ref arg call to forward | stl.h:347:109:347:109 | x | | +| stl.h:348:23:348:38 | ref arg call to forward | stl.h:347:109:347:109 | x | | +| stl.h:348:23:348:38 | ref arg call to forward | stl.h:348:40:348:40 | x [inner post update] | | +| stl.h:348:23:348:38 | ref arg call to forward | stl.h:348:40:348:40 | x [inner post update] | | +| stl.h:348:23:348:38 | ref arg call to forward | stl.h:348:40:348:40 | x [inner post update] | | +| stl.h:348:23:348:38 | ref arg call to forward | stl.h:348:40:348:40 | x [inner post update] | | +| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | | +| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | | +| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | | +| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | | +| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | | +| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | | +| stl.h:348:44:348:59 | call to forward | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:44:348:59 | call to forward | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:44:348:59 | ref arg call to forward | stl.h:347:117:347:117 | y | | +| stl.h:348:44:348:59 | ref arg call to forward | stl.h:347:117:347:117 | y | | +| stl.h:348:44:348:59 | ref arg call to forward | stl.h:347:117:347:117 | y | | +| stl.h:348:44:348:59 | ref arg call to forward | stl.h:347:117:347:117 | y | | +| stl.h:348:44:348:59 | ref arg call to forward | stl.h:348:61:348:61 | y [inner post update] | | +| stl.h:348:44:348:59 | ref arg call to forward | stl.h:348:61:348:61 | y [inner post update] | | +| stl.h:348:44:348:59 | ref arg call to forward | stl.h:348:61:348:61 | y [inner post update] | | +| stl.h:348:44:348:59 | ref arg call to forward | stl.h:348:61:348:61 | y [inner post update] | | +| stl.h:348:61:348:61 | y | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:61:348:61 | y | stl.h:348:10:348:63 | call to pair | TAINT | +| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | | +| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | | +| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | | +| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | | +| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | | +| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | | +| string.cpp:25:12:25:17 | call to source | string.cpp:29:7:29:7 | a | | +| string.cpp:26:16:26:20 | 123 | string.cpp:26:16:26:21 | call to basic_string | TAINT | +| string.cpp:26:16:26:21 | call to basic_string | string.cpp:30:7:30:7 | b | | +| string.cpp:26:16:26:21 | call to basic_string | string.cpp:32:7:32:7 | b | | +| string.cpp:27:16:27:21 | call to source | string.cpp:27:16:27:24 | call to basic_string | TAINT | +| string.cpp:27:16:27:24 | call to basic_string | string.cpp:31:7:31:7 | c | | +| string.cpp:27:16:27:24 | call to basic_string | string.cpp:33:7:33:7 | c | | +| string.cpp:32:7:32:7 | b | string.cpp:32:9:32:13 | call to c_str | TAINT | +| string.cpp:33:7:33:7 | c | string.cpp:33:9:33:13 | call to c_str | TAINT | +| string.cpp:38:16:38:28 | call to basic_string | string.cpp:39:7:39:11 | path1 | | +| string.cpp:38:17:38:26 | call to user_input | string.cpp:38:16:38:28 | call to basic_string | TAINT | +| string.cpp:39:7:39:11 | path1 | string.cpp:39:13:39:17 | call to c_str | TAINT | +| string.cpp:42:10:42:19 | call to user_input | string.cpp:42:10:42:21 | call to basic_string | TAINT | +| string.cpp:42:10:42:21 | call to basic_string | string.cpp:42:2:42:21 | ... = ... | | +| string.cpp:42:10:42:21 | call to basic_string | string.cpp:43:7:43:11 | path2 | | +| string.cpp:43:7:43:11 | path2 | string.cpp:43:13:43:17 | call to c_str | TAINT | +| string.cpp:45:15:45:24 | call to user_input | string.cpp:45:15:45:27 | call to basic_string | TAINT | +| string.cpp:45:15:45:27 | call to basic_string | string.cpp:46:7:46:11 | path3 | | +| string.cpp:46:7:46:11 | path3 | string.cpp:46:13:46:17 | call to c_str | TAINT | +| string.cpp:51:19:51:24 | call to source | string.cpp:54:17:54:18 | cs | | +| string.cpp:51:19:51:24 | call to source | string.cpp:56:7:56:8 | cs | | +| string.cpp:54:17:54:18 | cs | string.cpp:54:17:54:19 | call to basic_string | TAINT | +| string.cpp:54:17:54:19 | call to basic_string | string.cpp:57:7:57:8 | ss | | +| string.cpp:62:19:62:24 | call to source | string.cpp:65:17:65:18 | cs | | +| string.cpp:65:17:65:18 | cs | string.cpp:65:17:65:19 | call to basic_string | TAINT | +| string.cpp:65:17:65:19 | call to basic_string | string.cpp:68:7:68:8 | ss | | +| string.cpp:65:17:65:19 | call to basic_string | string.cpp:71:7:71:8 | ss | | +| string.cpp:68:7:68:8 | ss | string.cpp:68:10:68:14 | call to c_str | TAINT | +| string.cpp:68:10:68:14 | call to c_str | string.cpp:68:2:68:16 | ... = ... | | +| string.cpp:68:10:68:14 | call to c_str | string.cpp:70:7:70:8 | cs | | +| string.cpp:77:18:77:24 | hello | string.cpp:77:18:77:25 | call to basic_string | TAINT | +| string.cpp:77:18:77:25 | call to basic_string | string.cpp:82:8:82:9 | s1 | | +| string.cpp:78:19:78:26 | call to basic_string | string.cpp:83:8:83:9 | s2 | | +| string.cpp:78:20:78:26 | hello | string.cpp:78:19:78:26 | call to basic_string | TAINT | +| string.cpp:80:8:80:14 | call to basic_string | string.cpp:80:3:80:14 | ... = ... | | +| string.cpp:80:8:80:14 | call to basic_string | string.cpp:84:8:84:9 | s3 | | +| string.cpp:80:8:80:14 | hello | string.cpp:80:8:80:14 | call to basic_string | TAINT | +| string.cpp:88:18:88:23 | call to source | string.cpp:88:18:88:26 | call to basic_string | TAINT | +| string.cpp:88:18:88:26 | call to basic_string | string.cpp:93:8:93:9 | s1 | | +| string.cpp:89:19:89:27 | call to basic_string | string.cpp:94:8:94:9 | s2 | | +| string.cpp:89:20:89:25 | call to source | string.cpp:89:19:89:27 | call to basic_string | TAINT | +| string.cpp:91:8:91:13 | call to source | string.cpp:91:8:91:15 | call to basic_string | TAINT | +| string.cpp:91:8:91:15 | call to basic_string | string.cpp:91:3:91:15 | ... = ... | | +| string.cpp:91:8:91:15 | call to basic_string | string.cpp:95:8:95:9 | s3 | | +| string.cpp:99:15:99:16 | call to basic_string | string.cpp:100:20:100:21 | s1 | | +| string.cpp:99:15:99:16 | call to basic_string | string.cpp:102:8:102:9 | s1 | | +| string.cpp:99:15:99:16 | call to basic_string | string.cpp:104:8:104:9 | s1 | | +| string.cpp:100:20:100:21 | s1 | string.cpp:105:8:105:9 | s2 | | +| string.cpp:102:8:102:9 | s1 | string.cpp:102:3:102:9 | ... = ... | | +| string.cpp:102:8:102:9 | s1 | string.cpp:106:8:106:9 | s3 | | +| string.cpp:110:19:110:40 | call to basic_string | string.cpp:114:8:114:9 | s1 | | +| string.cpp:110:32:110:37 | call to source | string.cpp:110:19:110:40 | call to basic_string | TAINT | +| string.cpp:112:8:112:28 | call to basic_string | string.cpp:112:3:112:28 | ... = ... | | +| string.cpp:112:8:112:28 | call to basic_string | string.cpp:115:8:115:9 | s2 | | +| string.cpp:112:20:112:25 | call to source | string.cpp:112:8:112:28 | call to basic_string | TAINT | +| string.cpp:120:16:120:21 | call to source | string.cpp:120:16:120:24 | call to basic_string | TAINT | +| string.cpp:120:16:120:24 | call to basic_string | string.cpp:121:15:121:15 | s | | +| string.cpp:120:16:120:24 | call to basic_string | string.cpp:125:33:125:33 | s | | +| string.cpp:120:16:120:24 | call to basic_string | string.cpp:125:50:125:50 | s | | +| string.cpp:120:16:120:24 | call to basic_string | string.cpp:129:16:129:16 | s | | +| string.cpp:121:15:121:15 | (__begin) | string.cpp:121:15:121:15 | call to operator* | TAINT | +| string.cpp:121:15:121:15 | (__begin) | string.cpp:121:15:121:15 | call to operator++ | | +| string.cpp:121:15:121:15 | (__end) | string.cpp:121:15:121:15 | call to iterator | | +| string.cpp:121:15:121:15 | (__range) | string.cpp:121:15:121:15 | call to begin | TAINT | +| string.cpp:121:15:121:15 | (__range) | string.cpp:121:15:121:15 | call to end | TAINT | +| string.cpp:121:15:121:15 | call to begin | string.cpp:121:15:121:15 | (__begin) | | +| string.cpp:121:15:121:15 | call to begin | string.cpp:121:15:121:15 | (__begin) | | +| string.cpp:121:15:121:15 | call to begin | string.cpp:121:15:121:15 | (__begin) | | +| string.cpp:121:15:121:15 | call to end | string.cpp:121:15:121:15 | (__end) | | +| string.cpp:121:15:121:15 | call to operator* | string.cpp:122:8:122:8 | c | | +| string.cpp:121:15:121:15 | ref arg (__begin) | string.cpp:121:15:121:15 | (__begin) | | +| string.cpp:121:15:121:15 | ref arg (__begin) | string.cpp:121:15:121:15 | (__begin) | | +| string.cpp:121:15:121:15 | ref arg (__begin) | string.cpp:121:15:121:15 | (__begin) | | +| string.cpp:121:15:121:15 | ref arg (__range) | string.cpp:121:15:121:15 | (__range) | | +| string.cpp:121:15:121:15 | s | string.cpp:121:15:121:15 | (__range) | | +| string.cpp:121:15:121:15 | s | string.cpp:121:15:121:15 | (__range) | | +| string.cpp:121:15:121:15 | s | string.cpp:121:15:121:15 | call to operator* | TAINT | +| string.cpp:125:33:125:33 | ref arg s | string.cpp:125:50:125:50 | s | | +| string.cpp:125:33:125:33 | ref arg s | string.cpp:129:16:129:16 | s | | +| string.cpp:125:33:125:33 | s | string.cpp:125:35:125:39 | call to begin | TAINT | +| string.cpp:125:35:125:39 | call to begin | string.cpp:125:44:125:45 | it | | +| string.cpp:125:35:125:39 | call to begin | string.cpp:125:61:125:62 | it | | +| string.cpp:125:35:125:39 | call to begin | string.cpp:126:9:126:10 | it | | +| string.cpp:125:50:125:50 | ref arg s | string.cpp:125:50:125:50 | s | | +| string.cpp:125:50:125:50 | ref arg s | string.cpp:129:16:129:16 | s | | +| string.cpp:125:50:125:50 | s | string.cpp:125:52:125:54 | call to end | TAINT | +| string.cpp:125:61:125:62 | it | string.cpp:125:59:125:59 | call to operator++ | | +| string.cpp:125:61:125:62 | ref arg it | string.cpp:125:44:125:45 | it | | +| string.cpp:125:61:125:62 | ref arg it | string.cpp:125:61:125:62 | it | | +| string.cpp:125:61:125:62 | ref arg it | string.cpp:126:9:126:10 | it | | +| string.cpp:126:9:126:10 | it | string.cpp:126:8:126:8 | call to operator* | TAINT | +| string.cpp:129:16:129:16 | (__begin) | string.cpp:129:16:129:16 | call to operator* | TAINT | +| string.cpp:129:16:129:16 | (__begin) | string.cpp:129:16:129:16 | call to operator++ | | +| string.cpp:129:16:129:16 | (__end) | string.cpp:129:16:129:16 | call to iterator | | +| string.cpp:129:16:129:16 | (__range) | string.cpp:129:16:129:16 | call to begin | TAINT | +| string.cpp:129:16:129:16 | (__range) | string.cpp:129:16:129:16 | call to end | TAINT | +| string.cpp:129:16:129:16 | call to begin | string.cpp:129:16:129:16 | (__begin) | | +| string.cpp:129:16:129:16 | call to begin | string.cpp:129:16:129:16 | (__begin) | | +| string.cpp:129:16:129:16 | call to begin | string.cpp:129:16:129:16 | (__begin) | | +| string.cpp:129:16:129:16 | call to end | string.cpp:129:16:129:16 | (__end) | | +| string.cpp:129:16:129:16 | call to operator* | string.cpp:130:8:130:8 | c | | +| string.cpp:129:16:129:16 | ref arg (__begin) | string.cpp:129:16:129:16 | (__begin) | | +| string.cpp:129:16:129:16 | ref arg (__begin) | string.cpp:129:16:129:16 | (__begin) | | +| string.cpp:129:16:129:16 | ref arg (__begin) | string.cpp:129:16:129:16 | (__begin) | | +| string.cpp:129:16:129:16 | ref arg (__range) | string.cpp:129:16:129:16 | (__range) | | +| string.cpp:129:16:129:16 | s | string.cpp:129:16:129:16 | (__range) | | +| string.cpp:129:16:129:16 | s | string.cpp:129:16:129:16 | (__range) | | +| string.cpp:129:16:129:16 | s | string.cpp:129:16:129:16 | call to operator* | TAINT | +| string.cpp:133:28:133:33 | call to source | string.cpp:133:28:133:36 | call to basic_string | TAINT | +| string.cpp:133:28:133:36 | call to basic_string | string.cpp:134:22:134:28 | const_s | | +| string.cpp:134:22:134:22 | (__begin) | string.cpp:134:22:134:22 | call to operator* | TAINT | +| string.cpp:134:22:134:22 | (__begin) | string.cpp:134:22:134:22 | call to operator++ | | +| string.cpp:134:22:134:22 | (__range) | string.cpp:134:22:134:22 | call to begin | TAINT | +| string.cpp:134:22:134:22 | (__range) | string.cpp:134:22:134:22 | call to end | TAINT | +| string.cpp:134:22:134:22 | call to begin | string.cpp:134:22:134:22 | (__begin) | | +| string.cpp:134:22:134:22 | call to begin | string.cpp:134:22:134:22 | (__begin) | | +| string.cpp:134:22:134:22 | call to begin | string.cpp:134:22:134:22 | (__begin) | | +| string.cpp:134:22:134:22 | call to end | string.cpp:134:22:134:22 | (__end) | | +| string.cpp:134:22:134:22 | call to operator* | string.cpp:135:8:135:8 | c | | +| string.cpp:134:22:134:22 | ref arg (__begin) | string.cpp:134:22:134:22 | (__begin) | | +| string.cpp:134:22:134:22 | ref arg (__begin) | string.cpp:134:22:134:22 | (__begin) | | +| string.cpp:134:22:134:22 | ref arg (__begin) | string.cpp:134:22:134:22 | (__begin) | | +| string.cpp:134:22:134:28 | const_s | string.cpp:134:22:134:22 | (__range) | | +| string.cpp:134:22:134:28 | const_s | string.cpp:134:22:134:22 | (__range) | | +| string.cpp:134:22:134:28 | const_s | string.cpp:134:22:134:22 | call to operator* | TAINT | +| string.cpp:141:18:141:24 | hello | string.cpp:141:18:141:25 | call to basic_string | TAINT | +| string.cpp:141:18:141:25 | call to basic_string | string.cpp:144:8:144:9 | s1 | | +| string.cpp:141:18:141:25 | call to basic_string | string.cpp:144:13:144:14 | s1 | | +| string.cpp:141:18:141:25 | call to basic_string | string.cpp:145:8:145:9 | s1 | | +| string.cpp:141:18:141:25 | call to basic_string | string.cpp:146:13:146:14 | s1 | | +| string.cpp:141:18:141:25 | call to basic_string | string.cpp:149:8:149:9 | s1 | | +| string.cpp:141:18:141:25 | call to basic_string | string.cpp:150:8:150:9 | s1 | | +| string.cpp:142:18:142:23 | call to source | string.cpp:142:18:142:26 | call to basic_string | TAINT | +| string.cpp:142:18:142:26 | call to basic_string | string.cpp:145:13:145:14 | s2 | | +| string.cpp:142:18:142:26 | call to basic_string | string.cpp:146:8:146:9 | s2 | | +| string.cpp:142:18:142:26 | call to basic_string | string.cpp:147:8:147:9 | s2 | | +| string.cpp:142:18:142:26 | call to basic_string | string.cpp:147:13:147:14 | s2 | | +| string.cpp:144:8:144:9 | s1 | string.cpp:144:11:144:11 | call to operator+ | TAINT | +| string.cpp:144:13:144:14 | s1 | string.cpp:144:11:144:11 | call to operator+ | TAINT | +| string.cpp:145:8:145:9 | s1 | string.cpp:145:11:145:11 | call to operator+ | TAINT | +| string.cpp:145:13:145:14 | s2 | string.cpp:145:11:145:11 | call to operator+ | TAINT | +| string.cpp:146:8:146:9 | s2 | string.cpp:146:11:146:11 | call to operator+ | TAINT | +| string.cpp:146:13:146:14 | s1 | string.cpp:146:11:146:11 | call to operator+ | TAINT | +| string.cpp:147:8:147:9 | s2 | string.cpp:147:11:147:11 | call to operator+ | TAINT | +| string.cpp:147:13:147:14 | s2 | string.cpp:147:11:147:11 | call to operator+ | TAINT | +| string.cpp:149:8:149:9 | s1 | string.cpp:149:11:149:11 | call to operator+ | TAINT | +| string.cpp:149:13:149:20 | world | string.cpp:149:11:149:11 | call to operator+ | TAINT | +| string.cpp:150:8:150:9 | s1 | string.cpp:150:11:150:11 | call to operator+ | TAINT | +| string.cpp:150:13:150:18 | call to source | string.cpp:150:11:150:11 | call to operator+ | TAINT | +| string.cpp:154:18:154:22 | abc | string.cpp:154:18:154:23 | call to basic_string | TAINT | +| string.cpp:154:18:154:23 | call to basic_string | string.cpp:158:8:158:9 | s3 | | +| string.cpp:154:18:154:23 | call to basic_string | string.cpp:161:8:161:9 | s3 | | +| string.cpp:154:18:154:23 | call to basic_string | string.cpp:165:8:165:9 | s3 | | +| string.cpp:154:18:154:23 | call to basic_string | string.cpp:170:8:170:9 | s3 | | +| string.cpp:154:18:154:23 | call to basic_string | string.cpp:174:8:174:9 | s3 | | +| string.cpp:155:18:155:23 | call to source | string.cpp:155:18:155:26 | call to basic_string | TAINT | +| string.cpp:155:18:155:26 | call to basic_string | string.cpp:158:13:158:14 | s4 | | +| string.cpp:155:18:155:26 | call to basic_string | string.cpp:162:14:162:15 | s4 | | +| string.cpp:155:18:155:26 | call to basic_string | string.cpp:171:13:171:14 | s4 | | +| string.cpp:158:8:158:9 | s3 | string.cpp:158:11:158:11 | call to operator+ | TAINT | +| string.cpp:158:11:158:11 | call to operator+ | string.cpp:158:3:158:14 | ... = ... | | +| string.cpp:158:11:158:11 | call to operator+ | string.cpp:159:8:159:9 | s5 | | +| string.cpp:158:13:158:14 | s4 | string.cpp:158:11:158:11 | call to operator+ | TAINT | +| string.cpp:161:8:161:9 | s3 | string.cpp:161:3:161:9 | ... = ... | | +| string.cpp:161:8:161:9 | s3 | string.cpp:162:8:162:9 | s6 | | +| string.cpp:161:8:161:9 | s3 | string.cpp:163:8:163:9 | s6 | | +| string.cpp:162:8:162:9 | ref arg s6 | string.cpp:163:8:163:9 | s6 | | +| string.cpp:162:8:162:9 | s6 | string.cpp:162:11:162:11 | call to operator+= | TAINT | +| string.cpp:162:14:162:15 | s4 | string.cpp:162:8:162:9 | ref arg s6 | TAINT | +| string.cpp:162:14:162:15 | s4 | string.cpp:162:11:162:11 | call to operator+= | TAINT | +| string.cpp:165:8:165:9 | s3 | string.cpp:165:3:165:9 | ... = ... | | +| string.cpp:165:8:165:9 | s3 | string.cpp:166:8:166:9 | s7 | | +| string.cpp:165:8:165:9 | s3 | string.cpp:167:8:167:9 | s7 | | +| string.cpp:165:8:165:9 | s3 | string.cpp:168:8:168:9 | s7 | | +| string.cpp:166:8:166:9 | ref arg s7 | string.cpp:167:8:167:9 | s7 | | +| string.cpp:166:8:166:9 | ref arg s7 | string.cpp:168:8:168:9 | s7 | | +| string.cpp:166:8:166:9 | s7 | string.cpp:166:11:166:11 | call to operator+= | TAINT | +| string.cpp:166:14:166:19 | call to source | string.cpp:166:8:166:9 | ref arg s7 | TAINT | +| string.cpp:166:14:166:19 | call to source | string.cpp:166:11:166:11 | call to operator+= | TAINT | +| string.cpp:167:8:167:9 | ref arg s7 | string.cpp:168:8:168:9 | s7 | | +| string.cpp:167:8:167:9 | s7 | string.cpp:167:11:167:11 | call to operator+= | TAINT | +| string.cpp:167:14:167:16 | | string.cpp:167:8:167:9 | ref arg s7 | TAINT | +| string.cpp:167:14:167:16 | | string.cpp:167:11:167:11 | call to operator+= | TAINT | +| string.cpp:170:8:170:9 | s3 | string.cpp:170:3:170:9 | ... = ... | | +| string.cpp:170:8:170:9 | s3 | string.cpp:171:3:171:4 | s8 | | +| string.cpp:170:8:170:9 | s3 | string.cpp:172:8:172:9 | s8 | | +| string.cpp:171:3:171:4 | ref arg s8 | string.cpp:172:8:172:9 | s8 | | +| string.cpp:171:3:171:4 | s8 | string.cpp:171:6:171:11 | call to append | TAINT | +| string.cpp:171:13:171:14 | s4 | string.cpp:171:3:171:4 | ref arg s8 | TAINT | +| string.cpp:171:13:171:14 | s4 | string.cpp:171:6:171:11 | call to append | TAINT | +| string.cpp:174:8:174:9 | s3 | string.cpp:174:3:174:9 | ... = ... | | +| string.cpp:174:8:174:9 | s3 | string.cpp:175:3:175:4 | s9 | | +| string.cpp:174:8:174:9 | s3 | string.cpp:176:3:176:4 | s9 | | +| string.cpp:174:8:174:9 | s3 | string.cpp:177:8:177:9 | s9 | | +| string.cpp:175:3:175:4 | ref arg s9 | string.cpp:176:3:176:4 | s9 | | +| string.cpp:175:3:175:4 | ref arg s9 | string.cpp:177:8:177:9 | s9 | | +| string.cpp:175:3:175:4 | s9 | string.cpp:175:6:175:11 | call to append | TAINT | +| string.cpp:175:13:175:18 | call to source | string.cpp:175:3:175:4 | ref arg s9 | TAINT | +| string.cpp:175:13:175:18 | call to source | string.cpp:175:6:175:11 | call to append | TAINT | +| string.cpp:176:3:176:4 | ref arg s9 | string.cpp:177:8:177:9 | s9 | | +| string.cpp:176:3:176:4 | s9 | string.cpp:176:6:176:11 | call to append | TAINT | +| string.cpp:176:13:176:15 | | string.cpp:176:3:176:4 | ref arg s9 | TAINT | +| string.cpp:176:13:176:15 | | string.cpp:176:6:176:11 | call to append | TAINT | +| string.cpp:181:19:181:23 | abc | string.cpp:181:19:181:24 | call to basic_string | TAINT | +| string.cpp:181:19:181:24 | call to basic_string | string.cpp:184:3:184:5 | s10 | | +| string.cpp:181:19:181:24 | call to basic_string | string.cpp:185:8:185:10 | s10 | | +| string.cpp:182:12:182:26 | call to source | string.cpp:184:17:184:17 | c | | +| string.cpp:184:3:184:5 | ref arg s10 | string.cpp:185:8:185:10 | s10 | | +| string.cpp:184:3:184:5 | s10 | string.cpp:184:7:184:12 | call to append | TAINT | +| string.cpp:184:17:184:17 | c | string.cpp:184:3:184:5 | ref arg s10 | TAINT | +| string.cpp:184:17:184:17 | c | string.cpp:184:7:184:12 | call to append | TAINT | +| string.cpp:190:17:190:23 | hello | string.cpp:190:17:190:24 | call to basic_string | TAINT | +| string.cpp:190:17:190:24 | call to basic_string | string.cpp:196:17:196:18 | s1 | | +| string.cpp:190:17:190:24 | call to basic_string | string.cpp:205:17:205:18 | s1 | | +| string.cpp:191:17:191:22 | call to source | string.cpp:191:17:191:25 | call to basic_string | TAINT | +| string.cpp:191:17:191:25 | call to basic_string | string.cpp:199:17:199:18 | s2 | | +| string.cpp:192:11:192:25 | call to source | string.cpp:202:21:202:21 | c | | +| string.cpp:193:14:193:15 | call to basic_string | string.cpp:196:7:196:8 | s3 | | +| string.cpp:193:14:193:15 | call to basic_string | string.cpp:197:7:197:8 | s3 | | +| string.cpp:193:18:193:19 | call to basic_string | string.cpp:199:7:199:8 | s4 | | +| string.cpp:193:18:193:19 | call to basic_string | string.cpp:200:7:200:8 | s4 | | +| string.cpp:193:22:193:23 | call to basic_string | string.cpp:202:7:202:8 | s5 | | +| string.cpp:193:22:193:23 | call to basic_string | string.cpp:203:7:203:8 | s5 | | +| string.cpp:194:17:194:22 | call to source | string.cpp:194:17:194:25 | call to basic_string | TAINT | +| string.cpp:194:17:194:25 | call to basic_string | string.cpp:205:7:205:8 | s6 | | +| string.cpp:194:17:194:25 | call to basic_string | string.cpp:206:7:206:8 | s6 | | +| string.cpp:196:7:196:8 | ref arg s3 | string.cpp:197:7:197:8 | s3 | | +| string.cpp:196:17:196:18 | s1 | string.cpp:196:7:196:8 | ref arg s3 | TAINT | +| string.cpp:196:17:196:18 | s1 | string.cpp:196:10:196:15 | call to assign | TAINT | +| string.cpp:199:7:199:8 | ref arg s4 | string.cpp:200:7:200:8 | s4 | | +| string.cpp:199:17:199:18 | s2 | string.cpp:199:7:199:8 | ref arg s4 | TAINT | +| string.cpp:199:17:199:18 | s2 | string.cpp:199:10:199:15 | call to assign | TAINT | +| string.cpp:202:7:202:8 | ref arg s5 | string.cpp:203:7:203:8 | s5 | | +| string.cpp:202:21:202:21 | c | string.cpp:202:7:202:8 | ref arg s5 | TAINT | +| string.cpp:202:21:202:21 | c | string.cpp:202:10:202:15 | call to assign | TAINT | +| string.cpp:205:7:205:8 | ref arg s6 | string.cpp:206:7:206:8 | s6 | | +| string.cpp:205:17:205:18 | s1 | string.cpp:205:7:205:8 | ref arg s6 | TAINT | +| string.cpp:205:17:205:18 | s1 | string.cpp:205:10:205:15 | call to assign | TAINT | +| string.cpp:210:17:210:23 | hello | string.cpp:210:17:210:24 | call to basic_string | TAINT | +| string.cpp:210:17:210:24 | call to basic_string | string.cpp:215:7:215:8 | s1 | | +| string.cpp:210:17:210:24 | call to basic_string | string.cpp:216:20:216:21 | s1 | | +| string.cpp:210:17:210:24 | call to basic_string | string.cpp:220:20:220:21 | s1 | | +| string.cpp:210:17:210:24 | call to basic_string | string.cpp:223:7:223:8 | s1 | | +| string.cpp:210:17:210:24 | call to basic_string | string.cpp:227:7:227:8 | s1 | | +| string.cpp:211:17:211:22 | call to source | string.cpp:211:17:211:25 | call to basic_string | TAINT | +| string.cpp:211:17:211:25 | call to basic_string | string.cpp:219:7:219:8 | s2 | | +| string.cpp:211:17:211:25 | call to basic_string | string.cpp:224:20:224:21 | s2 | | +| string.cpp:212:11:212:25 | call to source | string.cpp:228:24:228:24 | c | | +| string.cpp:215:7:215:8 | s1 | string.cpp:215:2:215:8 | ... = ... | | +| string.cpp:215:7:215:8 | s1 | string.cpp:216:7:216:8 | s3 | | +| string.cpp:215:7:215:8 | s1 | string.cpp:217:7:217:8 | s3 | | +| string.cpp:216:7:216:8 | ref arg s3 | string.cpp:217:7:217:8 | s3 | | +| string.cpp:216:7:216:8 | s3 | string.cpp:216:10:216:15 | call to insert | TAINT | +| string.cpp:216:20:216:21 | s1 | string.cpp:216:7:216:8 | ref arg s3 | TAINT | +| string.cpp:216:20:216:21 | s1 | string.cpp:216:10:216:15 | call to insert | TAINT | +| string.cpp:219:7:219:8 | s2 | string.cpp:219:2:219:8 | ... = ... | | +| string.cpp:219:7:219:8 | s2 | string.cpp:220:7:220:8 | s4 | | +| string.cpp:219:7:219:8 | s2 | string.cpp:221:7:221:8 | s4 | | +| string.cpp:220:7:220:8 | ref arg s4 | string.cpp:221:7:221:8 | s4 | | +| string.cpp:220:7:220:8 | s4 | string.cpp:220:10:220:15 | call to insert | TAINT | +| string.cpp:220:20:220:21 | s1 | string.cpp:220:7:220:8 | ref arg s4 | TAINT | +| string.cpp:220:20:220:21 | s1 | string.cpp:220:10:220:15 | call to insert | TAINT | +| string.cpp:223:7:223:8 | s1 | string.cpp:223:2:223:8 | ... = ... | | +| string.cpp:223:7:223:8 | s1 | string.cpp:224:7:224:8 | s5 | | +| string.cpp:223:7:223:8 | s1 | string.cpp:225:7:225:8 | s5 | | +| string.cpp:224:7:224:8 | ref arg s5 | string.cpp:225:7:225:8 | s5 | | +| string.cpp:224:7:224:8 | s5 | string.cpp:224:10:224:15 | call to insert | TAINT | +| string.cpp:224:20:224:21 | s2 | string.cpp:224:7:224:8 | ref arg s5 | TAINT | +| string.cpp:224:20:224:21 | s2 | string.cpp:224:10:224:15 | call to insert | TAINT | +| string.cpp:227:7:227:8 | s1 | string.cpp:227:2:227:8 | ... = ... | | +| string.cpp:227:7:227:8 | s1 | string.cpp:228:7:228:8 | s6 | | +| string.cpp:227:7:227:8 | s1 | string.cpp:229:7:229:8 | s6 | | +| string.cpp:228:7:228:8 | ref arg s6 | string.cpp:229:7:229:8 | s6 | | +| string.cpp:228:7:228:8 | s6 | string.cpp:228:10:228:15 | call to insert | TAINT | +| string.cpp:228:24:228:24 | c | string.cpp:228:7:228:8 | ref arg s6 | TAINT | +| string.cpp:228:24:228:24 | c | string.cpp:228:10:228:15 | call to insert | TAINT | +| string.cpp:233:17:233:23 | hello | string.cpp:233:17:233:24 | call to basic_string | TAINT | +| string.cpp:233:17:233:24 | call to basic_string | string.cpp:238:7:238:8 | s1 | | +| string.cpp:233:17:233:24 | call to basic_string | string.cpp:239:24:239:25 | s1 | | +| string.cpp:233:17:233:24 | call to basic_string | string.cpp:243:24:243:25 | s1 | | +| string.cpp:233:17:233:24 | call to basic_string | string.cpp:246:7:246:8 | s1 | | +| string.cpp:233:17:233:24 | call to basic_string | string.cpp:250:7:250:8 | s1 | | +| string.cpp:234:17:234:22 | call to source | string.cpp:234:17:234:25 | call to basic_string | TAINT | +| string.cpp:234:17:234:25 | call to basic_string | string.cpp:242:7:242:8 | s2 | | +| string.cpp:234:17:234:25 | call to basic_string | string.cpp:247:24:247:25 | s2 | | +| string.cpp:235:11:235:25 | call to source | string.cpp:251:28:251:28 | c | | +| string.cpp:238:7:238:8 | s1 | string.cpp:238:2:238:8 | ... = ... | | +| string.cpp:238:7:238:8 | s1 | string.cpp:239:7:239:8 | s3 | | +| string.cpp:238:7:238:8 | s1 | string.cpp:240:7:240:8 | s3 | | +| string.cpp:239:7:239:8 | ref arg s3 | string.cpp:240:7:240:8 | s3 | | +| string.cpp:239:7:239:8 | s3 | string.cpp:239:10:239:16 | call to replace | TAINT | +| string.cpp:239:24:239:25 | s1 | string.cpp:239:7:239:8 | ref arg s3 | TAINT | +| string.cpp:239:24:239:25 | s1 | string.cpp:239:10:239:16 | call to replace | TAINT | +| string.cpp:242:7:242:8 | s2 | string.cpp:242:2:242:8 | ... = ... | | +| string.cpp:242:7:242:8 | s2 | string.cpp:243:7:243:8 | s4 | | +| string.cpp:242:7:242:8 | s2 | string.cpp:244:7:244:8 | s4 | | +| string.cpp:243:7:243:8 | ref arg s4 | string.cpp:244:7:244:8 | s4 | | +| string.cpp:243:7:243:8 | s4 | string.cpp:243:10:243:16 | call to replace | TAINT | +| string.cpp:243:24:243:25 | s1 | string.cpp:243:7:243:8 | ref arg s4 | TAINT | +| string.cpp:243:24:243:25 | s1 | string.cpp:243:10:243:16 | call to replace | TAINT | +| string.cpp:246:7:246:8 | s1 | string.cpp:246:2:246:8 | ... = ... | | +| string.cpp:246:7:246:8 | s1 | string.cpp:247:7:247:8 | s5 | | +| string.cpp:246:7:246:8 | s1 | string.cpp:248:7:248:8 | s5 | | +| string.cpp:247:7:247:8 | ref arg s5 | string.cpp:248:7:248:8 | s5 | | +| string.cpp:247:7:247:8 | s5 | string.cpp:247:10:247:16 | call to replace | TAINT | +| string.cpp:247:24:247:25 | s2 | string.cpp:247:7:247:8 | ref arg s5 | TAINT | +| string.cpp:247:24:247:25 | s2 | string.cpp:247:10:247:16 | call to replace | TAINT | +| string.cpp:250:7:250:8 | s1 | string.cpp:250:2:250:8 | ... = ... | | +| string.cpp:250:7:250:8 | s1 | string.cpp:251:7:251:8 | s6 | | +| string.cpp:250:7:250:8 | s1 | string.cpp:252:7:252:8 | s6 | | +| string.cpp:251:7:251:8 | ref arg s6 | string.cpp:252:7:252:8 | s6 | | +| string.cpp:251:7:251:8 | s6 | string.cpp:251:10:251:16 | call to replace | TAINT | +| string.cpp:251:28:251:28 | c | string.cpp:251:7:251:8 | ref arg s6 | TAINT | +| string.cpp:251:28:251:28 | c | string.cpp:251:10:251:16 | call to replace | TAINT | +| string.cpp:256:17:256:20 | {...} | string.cpp:261:10:261:11 | b1 | | +| string.cpp:256:17:256:20 | {...} | string.cpp:262:7:262:8 | b1 | | +| string.cpp:256:19:256:19 | 0 | string.cpp:256:17:256:20 | {...} | TAINT | +| string.cpp:257:17:257:20 | {...} | string.cpp:264:10:264:11 | b2 | | +| string.cpp:257:17:257:20 | {...} | string.cpp:265:7:265:8 | b2 | | +| string.cpp:257:19:257:19 | 0 | string.cpp:257:17:257:20 | {...} | TAINT | +| string.cpp:258:17:258:23 | hello | string.cpp:258:17:258:24 | call to basic_string | TAINT | +| string.cpp:258:17:258:24 | call to basic_string | string.cpp:261:2:261:3 | s1 | | +| string.cpp:258:17:258:24 | call to basic_string | string.cpp:261:14:261:15 | s1 | | +| string.cpp:258:17:258:24 | call to basic_string | string.cpp:264:14:264:15 | s1 | | +| string.cpp:259:17:259:22 | call to source | string.cpp:259:17:259:25 | call to basic_string | TAINT | +| string.cpp:259:17:259:25 | call to basic_string | string.cpp:264:2:264:3 | s2 | | +| string.cpp:261:2:261:3 | s1 | string.cpp:261:10:261:11 | ref arg b1 | TAINT | +| string.cpp:261:10:261:11 | ref arg b1 | string.cpp:262:7:262:8 | b1 | | +| string.cpp:264:2:264:3 | s2 | string.cpp:264:10:264:11 | ref arg b2 | TAINT | +| string.cpp:264:10:264:11 | ref arg b2 | string.cpp:265:7:265:8 | b2 | | +| string.cpp:269:17:269:23 | hello | string.cpp:269:17:269:24 | call to basic_string | TAINT | +| string.cpp:269:17:269:24 | call to basic_string | string.cpp:274:7:274:8 | s1 | | +| string.cpp:269:17:269:24 | call to basic_string | string.cpp:279:2:279:3 | s1 | | +| string.cpp:269:17:269:24 | call to basic_string | string.cpp:282:7:282:8 | s1 | | +| string.cpp:270:17:270:22 | call to source | string.cpp:270:17:270:25 | call to basic_string | TAINT | +| string.cpp:270:17:270:25 | call to basic_string | string.cpp:275:7:275:8 | s2 | | +| string.cpp:270:17:270:25 | call to basic_string | string.cpp:279:10:279:11 | s2 | | +| string.cpp:270:17:270:25 | call to basic_string | string.cpp:283:7:283:8 | s2 | | +| string.cpp:271:17:271:23 | world | string.cpp:271:17:271:24 | call to basic_string | TAINT | +| string.cpp:271:17:271:24 | call to basic_string | string.cpp:276:7:276:8 | s3 | | +| string.cpp:271:17:271:24 | call to basic_string | string.cpp:280:10:280:11 | s3 | | +| string.cpp:271:17:271:24 | call to basic_string | string.cpp:284:7:284:8 | s3 | | +| string.cpp:272:17:272:22 | call to source | string.cpp:272:17:272:25 | call to basic_string | TAINT | +| string.cpp:272:17:272:25 | call to basic_string | string.cpp:277:7:277:8 | s4 | | +| string.cpp:272:17:272:25 | call to basic_string | string.cpp:280:2:280:3 | s4 | | +| string.cpp:272:17:272:25 | call to basic_string | string.cpp:285:7:285:8 | s4 | | +| string.cpp:279:2:279:3 | ref arg s1 | string.cpp:282:7:282:8 | s1 | | +| string.cpp:279:2:279:3 | s1 | string.cpp:279:10:279:11 | ref arg s2 | TAINT | +| string.cpp:279:10:279:11 | ref arg s2 | string.cpp:283:7:283:8 | s2 | | +| string.cpp:279:10:279:11 | s2 | string.cpp:279:2:279:3 | ref arg s1 | TAINT | +| string.cpp:280:2:280:3 | ref arg s4 | string.cpp:285:7:285:8 | s4 | | +| string.cpp:280:2:280:3 | s4 | string.cpp:280:10:280:11 | ref arg s3 | TAINT | +| string.cpp:280:10:280:11 | ref arg s3 | string.cpp:284:7:284:8 | s3 | | +| string.cpp:280:10:280:11 | s3 | string.cpp:280:2:280:3 | ref arg s4 | TAINT | +| string.cpp:289:17:289:22 | call to source | string.cpp:289:17:289:25 | call to basic_string | TAINT | +| string.cpp:289:17:289:25 | call to basic_string | string.cpp:293:7:293:8 | s1 | | +| string.cpp:289:17:289:25 | call to basic_string | string.cpp:297:2:297:3 | s1 | | +| string.cpp:289:17:289:25 | call to basic_string | string.cpp:301:7:301:8 | s1 | | +| string.cpp:290:17:290:22 | call to source | string.cpp:290:17:290:25 | call to basic_string | TAINT | +| string.cpp:290:17:290:25 | call to basic_string | string.cpp:294:7:294:8 | s2 | | +| string.cpp:291:17:291:22 | call to source | string.cpp:291:17:291:25 | call to basic_string | TAINT | +| string.cpp:291:17:291:25 | call to basic_string | string.cpp:295:7:295:8 | s3 | | +| string.cpp:291:17:291:25 | call to basic_string | string.cpp:299:7:299:8 | s3 | | +| string.cpp:297:2:297:3 | ref arg s1 | string.cpp:301:7:301:8 | s1 | | +| string.cpp:298:7:298:8 | | string.cpp:298:7:298:8 | call to basic_string | TAINT | +| string.cpp:298:7:298:8 | call to basic_string | string.cpp:298:2:298:8 | ... = ... | | +| string.cpp:298:7:298:8 | call to basic_string | string.cpp:302:7:302:8 | s2 | | +| string.cpp:299:7:299:8 | s3 | string.cpp:299:2:299:8 | ... = ... | | +| string.cpp:299:7:299:8 | s3 | string.cpp:303:7:303:8 | s3 | | +| string.cpp:308:16:308:20 | 123 | string.cpp:308:16:308:21 | call to basic_string | TAINT | +| string.cpp:308:16:308:21 | call to basic_string | string.cpp:311:7:311:7 | a | | +| string.cpp:308:16:308:21 | call to basic_string | string.cpp:313:7:313:7 | a | | +| string.cpp:309:16:309:21 | call to source | string.cpp:309:16:309:24 | call to basic_string | TAINT | +| string.cpp:309:16:309:24 | call to basic_string | string.cpp:312:7:312:7 | b | | +| string.cpp:309:16:309:24 | call to basic_string | string.cpp:314:7:314:7 | b | | +| string.cpp:311:7:311:7 | a | string.cpp:311:9:311:12 | call to data | TAINT | +| string.cpp:311:7:311:7 | ref arg a | string.cpp:313:7:313:7 | a | | +| string.cpp:312:7:312:7 | b | string.cpp:312:9:312:12 | call to data | TAINT | +| string.cpp:312:7:312:7 | ref arg b | string.cpp:314:7:314:7 | b | | +| string.cpp:319:16:319:20 | 123 | string.cpp:319:16:319:21 | call to basic_string | TAINT | +| string.cpp:319:16:319:21 | call to basic_string | string.cpp:322:7:322:7 | a | | +| string.cpp:319:16:319:21 | call to basic_string | string.cpp:322:19:322:19 | a | | +| string.cpp:320:16:320:21 | call to source | string.cpp:320:16:320:24 | call to basic_string | TAINT | +| string.cpp:320:16:320:24 | call to basic_string | string.cpp:323:7:323:7 | b | | +| string.cpp:320:16:320:24 | call to basic_string | string.cpp:323:19:323:19 | b | | +| string.cpp:322:7:322:7 | a | string.cpp:322:9:322:14 | call to substr | TAINT | +| string.cpp:323:7:323:7 | b | string.cpp:323:9:323:14 | call to substr | TAINT | +| string.cpp:328:16:328:20 | 123 | string.cpp:328:16:328:21 | call to basic_string | TAINT | +| string.cpp:328:16:328:21 | call to basic_string | string.cpp:332:7:332:7 | a | | +| string.cpp:328:16:328:21 | call to basic_string | string.cpp:336:2:336:2 | a | | +| string.cpp:328:16:328:21 | call to basic_string | string.cpp:338:9:338:9 | a | | +| string.cpp:328:16:328:21 | call to basic_string | string.cpp:340:7:340:7 | a | | +| string.cpp:329:16:329:20 | 123 | string.cpp:329:16:329:21 | call to basic_string | TAINT | +| string.cpp:329:16:329:21 | call to basic_string | string.cpp:333:7:333:7 | b | | +| string.cpp:329:16:329:21 | call to basic_string | string.cpp:337:2:337:2 | b | | +| string.cpp:329:16:329:21 | call to basic_string | string.cpp:341:7:341:7 | b | | +| string.cpp:330:16:330:20 | 123 | string.cpp:330:16:330:21 | call to basic_string | TAINT | +| string.cpp:330:16:330:21 | call to basic_string | string.cpp:334:7:334:7 | c | | +| string.cpp:330:16:330:21 | call to basic_string | string.cpp:338:2:338:2 | c | | +| string.cpp:330:16:330:21 | call to basic_string | string.cpp:342:7:342:7 | c | | +| string.cpp:336:2:336:2 | a | string.cpp:336:3:336:3 | call to operator[] | TAINT | +| string.cpp:336:2:336:2 | ref arg a | string.cpp:338:9:338:9 | a | | +| string.cpp:336:2:336:2 | ref arg a | string.cpp:340:7:340:7 | a | | +| string.cpp:336:2:336:25 | ... = ... | string.cpp:336:3:336:3 | call to operator[] [post update] | | +| string.cpp:336:3:336:3 | call to operator[] [post update] | string.cpp:336:2:336:2 | ref arg a | TAINT | +| string.cpp:336:9:336:23 | call to source | string.cpp:336:2:336:25 | ... = ... | | +| string.cpp:337:2:337:2 | b | string.cpp:337:4:337:5 | call to at | TAINT | +| string.cpp:337:2:337:2 | ref arg b | string.cpp:341:7:341:7 | b | | +| string.cpp:337:2:337:28 | ... = ... | string.cpp:337:4:337:5 | call to at [post update] | | +| string.cpp:337:4:337:5 | call to at [post update] | string.cpp:337:2:337:2 | ref arg b | TAINT | +| string.cpp:337:12:337:26 | call to source | string.cpp:337:2:337:28 | ... = ... | | +| string.cpp:338:2:338:2 | c | string.cpp:338:3:338:3 | call to operator[] | TAINT | +| string.cpp:338:2:338:2 | ref arg c | string.cpp:342:7:342:7 | c | | +| string.cpp:338:2:338:12 | ... = ... | string.cpp:338:3:338:3 | call to operator[] [post update] | | +| string.cpp:338:3:338:3 | call to operator[] [post update] | string.cpp:338:2:338:2 | ref arg c | TAINT | +| string.cpp:338:9:338:9 | a | string.cpp:338:10:338:10 | call to operator[] | TAINT | +| string.cpp:338:9:338:9 | ref arg a | string.cpp:340:7:340:7 | a | | +| string.cpp:338:10:338:10 | call to operator[] | string.cpp:338:2:338:12 | ... = ... | | +| string.cpp:347:18:347:22 | 123 | string.cpp:347:18:347:23 | call to basic_string | TAINT | +| string.cpp:347:18:347:23 | call to basic_string | string.cpp:349:2:349:4 | str | | +| string.cpp:347:18:347:23 | call to basic_string | string.cpp:350:7:350:9 | str | | +| string.cpp:347:18:347:23 | call to basic_string | string.cpp:351:7:351:9 | str | | +| string.cpp:349:2:349:4 | ref arg str | string.cpp:350:7:350:9 | str | | +| string.cpp:349:2:349:4 | ref arg str | string.cpp:351:7:351:9 | str | | +| string.cpp:349:2:349:4 | str | string.cpp:349:6:349:9 | call to data | TAINT | +| string.cpp:349:2:349:14 | access to array [post update] | string.cpp:349:6:349:9 | call to data [inner post update] | | +| string.cpp:349:2:349:34 | ... = ... | string.cpp:349:2:349:14 | access to array [post update] | | +| string.cpp:349:6:349:9 | call to data | string.cpp:349:2:349:14 | access to array | TAINT | +| string.cpp:349:6:349:9 | call to data [inner post update] | string.cpp:349:2:349:4 | ref arg str | TAINT | +| string.cpp:349:13:349:13 | 1 | string.cpp:349:2:349:14 | access to array | TAINT | +| string.cpp:349:18:349:32 | call to source | string.cpp:349:2:349:34 | ... = ... | | +| string.cpp:351:7:351:9 | str | string.cpp:351:11:351:14 | call to data | TAINT | +| string.cpp:357:18:357:24 | hello | string.cpp:357:18:357:25 | call to basic_string | TAINT | +| string.cpp:357:18:357:25 | call to basic_string | string.cpp:362:8:362:9 | s1 | | +| string.cpp:357:18:357:25 | call to basic_string | string.cpp:363:8:363:9 | s1 | | +| string.cpp:357:18:357:25 | call to basic_string | string.cpp:364:8:364:9 | s1 | | +| string.cpp:358:18:358:23 | call to source | string.cpp:358:18:358:26 | call to basic_string | TAINT | +| string.cpp:358:18:358:26 | call to basic_string | string.cpp:363:18:363:19 | s2 | | +| string.cpp:358:18:358:26 | call to basic_string | string.cpp:363:30:363:31 | s2 | | +| string.cpp:359:18:359:24 | hello | string.cpp:359:18:359:25 | call to basic_string | TAINT | +| string.cpp:359:18:359:25 | call to basic_string | string.cpp:366:8:366:9 | s3 | | +| string.cpp:359:18:359:25 | call to basic_string | string.cpp:367:8:367:9 | s3 | | +| string.cpp:359:18:359:25 | call to basic_string | string.cpp:368:8:368:9 | s3 | | +| string.cpp:360:18:360:24 | world | string.cpp:360:18:360:25 | call to basic_string | TAINT | +| string.cpp:360:18:360:25 | call to basic_string | string.cpp:367:18:367:19 | s4 | | +| string.cpp:360:18:360:25 | call to basic_string | string.cpp:367:30:367:31 | s4 | | +| string.cpp:363:8:363:9 | ref arg s1 | string.cpp:364:8:364:9 | s1 | | +| string.cpp:363:8:363:9 | s1 | string.cpp:363:11:363:16 | call to append | TAINT | +| string.cpp:363:18:363:19 | ref arg s2 | string.cpp:363:30:363:31 | s2 | | +| string.cpp:363:18:363:19 | s2 | string.cpp:363:21:363:25 | call to begin | TAINT | +| string.cpp:363:21:363:25 | call to begin | string.cpp:363:8:363:9 | ref arg s1 | TAINT | +| string.cpp:363:21:363:25 | call to begin | string.cpp:363:11:363:16 | call to append | TAINT | +| string.cpp:363:30:363:31 | s2 | string.cpp:363:33:363:35 | call to end | TAINT | +| string.cpp:363:33:363:35 | call to end | string.cpp:363:8:363:9 | ref arg s1 | TAINT | +| string.cpp:363:33:363:35 | call to end | string.cpp:363:11:363:16 | call to append | TAINT | +| string.cpp:367:8:367:9 | ref arg s3 | string.cpp:368:8:368:9 | s3 | | +| string.cpp:367:8:367:9 | s3 | string.cpp:367:11:367:16 | call to append | TAINT | +| string.cpp:367:18:367:19 | ref arg s4 | string.cpp:367:30:367:31 | s4 | | +| string.cpp:367:18:367:19 | s4 | string.cpp:367:21:367:25 | call to begin | TAINT | +| string.cpp:367:21:367:25 | call to begin | string.cpp:367:8:367:9 | ref arg s3 | TAINT | +| string.cpp:367:21:367:25 | call to begin | string.cpp:367:11:367:16 | call to append | TAINT | +| string.cpp:367:30:367:31 | s4 | string.cpp:367:33:367:35 | call to end | TAINT | +| string.cpp:367:33:367:35 | call to end | string.cpp:367:8:367:9 | ref arg s3 | TAINT | +| string.cpp:367:33:367:35 | call to end | string.cpp:367:11:367:16 | call to append | TAINT | +| string.cpp:373:18:373:24 | hello | string.cpp:373:18:373:25 | call to basic_string | TAINT | +| string.cpp:373:18:373:25 | call to basic_string | string.cpp:376:28:376:29 | s1 | | +| string.cpp:374:18:374:23 | call to source | string.cpp:374:18:374:26 | call to basic_string | TAINT | +| string.cpp:374:18:374:26 | call to basic_string | string.cpp:380:28:380:29 | s2 | | +| string.cpp:376:28:376:29 | s1 | string.cpp:376:31:376:35 | call to begin | TAINT | +| string.cpp:376:31:376:35 | call to begin | string.cpp:378:9:378:13 | iter1 | | +| string.cpp:376:31:376:35 | call to begin | string.cpp:379:8:379:12 | iter1 | | +| string.cpp:378:9:378:13 | iter1 | string.cpp:378:8:378:8 | call to operator* | TAINT | +| string.cpp:379:8:379:12 | iter1 | string.cpp:379:13:379:13 | call to operator[] | TAINT | +| string.cpp:380:28:380:29 | s2 | string.cpp:380:31:380:35 | call to begin | TAINT | +| string.cpp:380:31:380:35 | call to begin | string.cpp:382:9:382:13 | iter2 | | +| string.cpp:380:31:380:35 | call to begin | string.cpp:383:8:383:12 | iter2 | | +| string.cpp:382:9:382:13 | iter2 | string.cpp:382:8:382:8 | call to operator* | TAINT | +| string.cpp:383:8:383:12 | iter2 | string.cpp:383:13:383:13 | call to operator[] | TAINT | +| string.cpp:388:18:388:24 | hello | string.cpp:388:18:388:25 | call to basic_string | TAINT | +| string.cpp:388:18:388:25 | call to basic_string | string.cpp:391:25:391:26 | s1 | | +| string.cpp:389:18:389:23 | call to source | string.cpp:389:18:389:26 | call to basic_string | TAINT | +| string.cpp:389:18:389:26 | call to basic_string | string.cpp:393:25:393:26 | s2 | | +| string.cpp:389:18:389:26 | call to basic_string | string.cpp:413:8:413:9 | s2 | | +| string.cpp:391:25:391:26 | s1 | string.cpp:391:28:391:32 | call to begin | TAINT | +| string.cpp:393:25:393:26 | ref arg s2 | string.cpp:413:8:413:9 | s2 | | +| string.cpp:393:25:393:26 | s2 | string.cpp:393:28:393:32 | call to begin | TAINT | +| string.cpp:393:28:393:32 | call to begin | string.cpp:396:10:396:11 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:397:10:397:11 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:398:8:398:9 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:400:8:400:9 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:402:8:402:9 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:405:8:405:9 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:408:8:408:9 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:410:8:410:9 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:417:9:417:10 | i2 | | +| string.cpp:393:28:393:32 | call to begin | string.cpp:420:9:420:10 | i2 | | +| string.cpp:396:10:396:11 | i2 | string.cpp:396:12:396:12 | call to operator+ | TAINT | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:397:10:397:11 | i2 | | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:398:8:398:9 | i2 | | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:400:8:400:9 | i2 | | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:402:8:402:9 | i2 | | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:405:8:405:9 | i2 | | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:408:8:408:9 | i2 | | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:410:8:410:9 | i2 | | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:417:9:417:10 | i2 | | +| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:420:9:420:10 | i2 | | +| string.cpp:396:12:396:12 | call to operator+ | string.cpp:396:8:396:8 | call to operator* | TAINT | +| string.cpp:397:10:397:11 | i2 | string.cpp:397:12:397:12 | call to operator- | TAINT | +| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:398:8:398:9 | i2 | | +| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:400:8:400:9 | i2 | | +| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:402:8:402:9 | i2 | | +| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:405:8:405:9 | i2 | | +| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:408:8:408:9 | i2 | | +| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:410:8:410:9 | i2 | | +| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:417:9:417:10 | i2 | | +| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:420:9:420:10 | i2 | | +| string.cpp:397:12:397:12 | call to operator- | string.cpp:397:8:397:8 | call to operator* | TAINT | +| string.cpp:398:8:398:9 | i2 | string.cpp:398:3:398:9 | ... = ... | | +| string.cpp:398:8:398:9 | i2 | string.cpp:399:12:399:13 | i3 | | +| string.cpp:399:10:399:10 | call to operator++ | string.cpp:399:8:399:8 | call to operator* | TAINT | +| string.cpp:399:12:399:13 | i3 | string.cpp:399:10:399:10 | call to operator++ | | +| string.cpp:400:8:400:9 | i2 | string.cpp:400:3:400:9 | ... = ... | | +| string.cpp:400:8:400:9 | i2 | string.cpp:401:12:401:13 | i4 | | +| string.cpp:401:10:401:10 | call to operator-- | string.cpp:401:8:401:8 | call to operator* | TAINT | +| string.cpp:401:12:401:13 | i4 | string.cpp:401:10:401:10 | call to operator-- | | +| string.cpp:402:8:402:9 | i2 | string.cpp:402:3:402:9 | ... = ... | | +| string.cpp:402:8:402:9 | i2 | string.cpp:403:3:403:4 | i5 | | +| string.cpp:402:8:402:9 | i2 | string.cpp:404:9:404:10 | i5 | | +| string.cpp:403:3:403:4 | i5 | string.cpp:403:5:403:5 | call to operator++ | | +| string.cpp:403:3:403:4 | ref arg i5 | string.cpp:404:9:404:10 | i5 | | +| string.cpp:404:9:404:10 | i5 | string.cpp:404:8:404:8 | call to operator* | TAINT | +| string.cpp:405:8:405:9 | i2 | string.cpp:405:3:405:9 | ... = ... | | +| string.cpp:405:8:405:9 | i2 | string.cpp:406:3:406:4 | i6 | | +| string.cpp:405:8:405:9 | i2 | string.cpp:407:9:407:10 | i6 | | +| string.cpp:406:3:406:4 | i6 | string.cpp:406:5:406:5 | call to operator-- | | +| string.cpp:406:3:406:4 | ref arg i6 | string.cpp:407:9:407:10 | i6 | | +| string.cpp:407:9:407:10 | i6 | string.cpp:407:8:407:8 | call to operator* | TAINT | +| string.cpp:408:8:408:9 | i2 | string.cpp:408:3:408:9 | ... = ... | | +| string.cpp:408:8:408:9 | i2 | string.cpp:409:10:409:11 | i7 | | +| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | | +| string.cpp:409:12:409:12 | call to operator+= | string.cpp:409:8:409:8 | call to operator* | TAINT | +| string.cpp:409:14:409:14 | 1 | string.cpp:409:12:409:12 | call to operator+= | | +| string.cpp:410:8:410:9 | i2 | string.cpp:410:3:410:9 | ... = ... | | +| string.cpp:410:8:410:9 | i2 | string.cpp:411:10:411:11 | i8 | | +| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | | +| string.cpp:411:12:411:12 | call to operator-= | string.cpp:411:8:411:8 | call to operator* | TAINT | +| string.cpp:411:14:411:14 | 1 | string.cpp:411:12:411:12 | call to operator-= | | +| string.cpp:413:8:413:9 | s2 | string.cpp:413:11:413:13 | call to end | TAINT | +| string.cpp:413:11:413:13 | call to end | string.cpp:413:3:413:15 | ... = ... | | +| string.cpp:413:11:413:13 | call to end | string.cpp:414:5:414:6 | i9 | | +| string.cpp:413:11:413:13 | call to end | string.cpp:415:9:415:10 | i9 | | +| string.cpp:414:5:414:6 | i9 | string.cpp:414:3:414:3 | call to operator-- | | +| string.cpp:414:5:414:6 | ref arg i9 | string.cpp:415:9:415:10 | i9 | | +| string.cpp:415:9:415:10 | i9 | string.cpp:415:8:415:8 | call to operator* | TAINT | +| string.cpp:417:9:417:10 | i2 | string.cpp:417:3:417:10 | ... = ... | | +| string.cpp:417:9:417:10 | i2 | string.cpp:418:10:418:12 | i10 | | +| string.cpp:417:9:417:10 | i2 | string.cpp:419:8:419:10 | i10 | | +| string.cpp:418:10:418:12 | i10 | string.cpp:418:13:418:13 | call to operator++ | | +| string.cpp:418:10:418:12 | ref arg i10 | string.cpp:419:8:419:10 | i10 | | +| string.cpp:418:13:418:13 | call to operator++ | string.cpp:418:8:418:8 | call to operator* | TAINT | +| string.cpp:419:8:419:10 | i10 | string.cpp:419:8:419:10 | call to iterator | | +| string.cpp:420:9:420:10 | i2 | string.cpp:420:3:420:10 | ... = ... | | +| string.cpp:420:9:420:10 | i2 | string.cpp:421:10:421:12 | i11 | | +| string.cpp:420:9:420:10 | i2 | string.cpp:422:8:422:10 | i11 | | +| string.cpp:421:10:421:12 | i11 | string.cpp:421:13:421:13 | call to operator-- | | +| string.cpp:421:10:421:12 | ref arg i11 | string.cpp:422:8:422:10 | i11 | | +| string.cpp:421:13:421:13 | call to operator-- | string.cpp:421:8:421:8 | call to operator* | TAINT | +| string.cpp:422:8:422:10 | i11 | string.cpp:422:8:422:10 | call to iterator | | +| string.cpp:428:17:428:20 | aa | string.cpp:428:17:428:21 | call to basic_string | TAINT | +| string.cpp:428:17:428:21 | call to basic_string | string.cpp:433:7:433:8 | s1 | | +| string.cpp:428:17:428:21 | call to basic_string | string.cpp:434:7:434:8 | s1 | | +| string.cpp:429:17:429:20 | bb | string.cpp:429:17:429:21 | call to basic_string | TAINT | +| string.cpp:429:17:429:21 | call to basic_string | string.cpp:436:7:436:8 | s2 | | +| string.cpp:429:17:429:21 | call to basic_string | string.cpp:437:7:437:8 | s2 | | +| string.cpp:430:14:430:17 | cc | string.cpp:433:20:433:22 | cs1 | | +| string.cpp:431:14:431:19 | call to source | string.cpp:436:20:436:22 | cs2 | | +| string.cpp:433:7:433:8 | ref arg s1 | string.cpp:434:7:434:8 | s1 | | +| string.cpp:433:7:433:8 | s1 | string.cpp:433:10:433:15 | call to insert | TAINT | +| string.cpp:433:20:433:22 | cs1 | string.cpp:433:7:433:8 | ref arg s1 | TAINT | +| string.cpp:433:20:433:22 | cs1 | string.cpp:433:10:433:15 | call to insert | TAINT | +| string.cpp:436:7:436:8 | ref arg s2 | string.cpp:437:7:437:8 | s2 | | +| string.cpp:436:7:436:8 | s2 | string.cpp:436:10:436:15 | call to insert | TAINT | +| string.cpp:436:20:436:22 | cs2 | string.cpp:436:7:436:8 | ref arg s2 | TAINT | +| string.cpp:436:20:436:22 | cs2 | string.cpp:436:10:436:15 | call to insert | TAINT | +| string.cpp:443:17:443:20 | aa | string.cpp:443:17:443:21 | call to basic_string | TAINT | +| string.cpp:443:17:443:21 | call to basic_string | string.cpp:446:8:446:8 | a | | +| string.cpp:443:17:443:21 | call to basic_string | string.cpp:446:17:446:17 | a | | +| string.cpp:443:17:443:21 | call to basic_string | string.cpp:447:8:447:8 | a | | +| string.cpp:444:17:444:20 | bb | string.cpp:444:17:444:21 | call to basic_string | TAINT | +| string.cpp:444:17:444:21 | call to basic_string | string.cpp:449:8:449:8 | b | | +| string.cpp:444:17:444:21 | call to basic_string | string.cpp:449:17:449:17 | b | | +| string.cpp:444:17:444:21 | call to basic_string | string.cpp:450:8:450:8 | b | | +| string.cpp:446:8:446:8 | a | string.cpp:446:10:446:15 | call to insert | TAINT | +| string.cpp:446:8:446:8 | ref arg a | string.cpp:447:8:447:8 | a | | +| string.cpp:446:17:446:17 | a | string.cpp:446:19:446:23 | call to begin | TAINT | +| string.cpp:446:17:446:17 | ref arg a | string.cpp:446:8:446:8 | a | | +| string.cpp:446:17:446:17 | ref arg a | string.cpp:447:8:447:8 | a | | +| string.cpp:446:19:446:23 | call to begin | string.cpp:446:17:446:25 | call to iterator | TAINT | +| string.cpp:446:32:446:34 | 120 | string.cpp:446:8:446:8 | ref arg a | TAINT | +| string.cpp:446:32:446:34 | 120 | string.cpp:446:10:446:15 | call to insert | TAINT | +| string.cpp:449:8:449:8 | b | string.cpp:449:10:449:15 | call to insert | TAINT | +| string.cpp:449:8:449:8 | ref arg b | string.cpp:450:8:450:8 | b | | +| string.cpp:449:17:449:17 | b | string.cpp:449:19:449:23 | call to begin | TAINT | +| string.cpp:449:17:449:17 | ref arg b | string.cpp:449:8:449:8 | b | | +| string.cpp:449:17:449:17 | ref arg b | string.cpp:450:8:450:8 | b | | +| string.cpp:449:19:449:23 | call to begin | string.cpp:449:17:449:25 | call to iterator | TAINT | +| string.cpp:449:32:449:46 | call to source | string.cpp:449:8:449:8 | ref arg b | TAINT | +| string.cpp:449:32:449:46 | call to source | string.cpp:449:10:449:15 | call to insert | TAINT | +| string.cpp:454:17:454:20 | cc | string.cpp:454:17:454:21 | call to basic_string | TAINT | +| string.cpp:454:17:454:21 | call to basic_string | string.cpp:459:8:459:8 | c | | +| string.cpp:454:17:454:21 | call to basic_string | string.cpp:459:17:459:17 | c | | +| string.cpp:454:17:454:21 | call to basic_string | string.cpp:460:8:460:8 | c | | +| string.cpp:455:17:455:20 | dd | string.cpp:455:17:455:21 | call to basic_string | TAINT | +| string.cpp:455:17:455:21 | call to basic_string | string.cpp:462:8:462:8 | d | | +| string.cpp:455:17:455:21 | call to basic_string | string.cpp:462:17:462:17 | d | | +| string.cpp:455:17:455:21 | call to basic_string | string.cpp:463:8:463:8 | d | | +| string.cpp:456:18:456:21 | 11 | string.cpp:456:18:456:22 | call to basic_string | TAINT | +| string.cpp:456:18:456:22 | call to basic_string | string.cpp:459:26:459:27 | s1 | | +| string.cpp:456:18:456:22 | call to basic_string | string.cpp:459:38:459:39 | s1 | | +| string.cpp:456:18:456:22 | call to basic_string | string.cpp:465:28:465:29 | s1 | | +| string.cpp:456:18:456:22 | call to basic_string | string.cpp:465:40:465:41 | s1 | | +| string.cpp:457:18:457:23 | call to source | string.cpp:457:18:457:26 | call to basic_string | TAINT | +| string.cpp:457:18:457:26 | call to basic_string | string.cpp:462:26:462:27 | s2 | | +| string.cpp:457:18:457:26 | call to basic_string | string.cpp:462:38:462:39 | s2 | | +| string.cpp:457:18:457:26 | call to basic_string | string.cpp:465:8:465:9 | s2 | | +| string.cpp:457:18:457:26 | call to basic_string | string.cpp:465:18:465:19 | s2 | | +| string.cpp:457:18:457:26 | call to basic_string | string.cpp:466:8:466:9 | s2 | | +| string.cpp:459:8:459:8 | c | string.cpp:459:10:459:15 | call to insert | TAINT | +| string.cpp:459:8:459:8 | ref arg c | string.cpp:460:8:460:8 | c | | +| string.cpp:459:17:459:17 | c | string.cpp:459:19:459:21 | call to end | TAINT | +| string.cpp:459:17:459:17 | ref arg c | string.cpp:459:8:459:8 | c | | +| string.cpp:459:17:459:17 | ref arg c | string.cpp:460:8:460:8 | c | | +| string.cpp:459:19:459:21 | call to end | string.cpp:459:17:459:23 | call to iterator | TAINT | +| string.cpp:459:26:459:27 | ref arg s1 | string.cpp:459:38:459:39 | s1 | | +| string.cpp:459:26:459:27 | ref arg s1 | string.cpp:465:28:465:29 | s1 | | +| string.cpp:459:26:459:27 | ref arg s1 | string.cpp:465:40:465:41 | s1 | | +| string.cpp:459:26:459:27 | s1 | string.cpp:459:29:459:33 | call to begin | TAINT | +| string.cpp:459:29:459:33 | call to begin | string.cpp:459:8:459:8 | ref arg c | TAINT | +| string.cpp:459:29:459:33 | call to begin | string.cpp:459:10:459:15 | call to insert | TAINT | +| string.cpp:459:38:459:39 | ref arg s1 | string.cpp:465:28:465:29 | s1 | | +| string.cpp:459:38:459:39 | ref arg s1 | string.cpp:465:40:465:41 | s1 | | +| string.cpp:459:38:459:39 | s1 | string.cpp:459:41:459:43 | call to end | TAINT | +| string.cpp:459:41:459:43 | call to end | string.cpp:459:8:459:8 | ref arg c | TAINT | +| string.cpp:459:41:459:43 | call to end | string.cpp:459:10:459:15 | call to insert | TAINT | +| string.cpp:462:8:462:8 | d | string.cpp:462:10:462:15 | call to insert | TAINT | +| string.cpp:462:8:462:8 | ref arg d | string.cpp:463:8:463:8 | d | | +| string.cpp:462:17:462:17 | d | string.cpp:462:19:462:21 | call to end | TAINT | +| string.cpp:462:17:462:17 | ref arg d | string.cpp:462:8:462:8 | d | | +| string.cpp:462:17:462:17 | ref arg d | string.cpp:463:8:463:8 | d | | +| string.cpp:462:19:462:21 | call to end | string.cpp:462:17:462:23 | call to iterator | TAINT | +| string.cpp:462:26:462:27 | ref arg s2 | string.cpp:462:38:462:39 | s2 | | +| string.cpp:462:26:462:27 | ref arg s2 | string.cpp:465:8:465:9 | s2 | | +| string.cpp:462:26:462:27 | ref arg s2 | string.cpp:465:18:465:19 | s2 | | +| string.cpp:462:26:462:27 | ref arg s2 | string.cpp:466:8:466:9 | s2 | | +| string.cpp:462:26:462:27 | s2 | string.cpp:462:29:462:33 | call to begin | TAINT | +| string.cpp:462:29:462:33 | call to begin | string.cpp:462:8:462:8 | ref arg d | TAINT | +| string.cpp:462:29:462:33 | call to begin | string.cpp:462:10:462:15 | call to insert | TAINT | +| string.cpp:462:38:462:39 | ref arg s2 | string.cpp:465:8:465:9 | s2 | | +| string.cpp:462:38:462:39 | ref arg s2 | string.cpp:465:18:465:19 | s2 | | +| string.cpp:462:38:462:39 | ref arg s2 | string.cpp:466:8:466:9 | s2 | | +| string.cpp:462:38:462:39 | s2 | string.cpp:462:41:462:43 | call to end | TAINT | +| string.cpp:462:41:462:43 | call to end | string.cpp:462:8:462:8 | ref arg d | TAINT | +| string.cpp:462:41:462:43 | call to end | string.cpp:462:10:462:15 | call to insert | TAINT | +| string.cpp:465:8:465:9 | ref arg s2 | string.cpp:466:8:466:9 | s2 | | +| string.cpp:465:8:465:9 | s2 | string.cpp:465:11:465:16 | call to insert | TAINT | +| string.cpp:465:18:465:19 | ref arg s2 | string.cpp:465:8:465:9 | s2 | | +| string.cpp:465:18:465:19 | ref arg s2 | string.cpp:466:8:466:9 | s2 | | +| string.cpp:465:18:465:19 | s2 | string.cpp:465:21:465:23 | call to end | TAINT | +| string.cpp:465:21:465:23 | call to end | string.cpp:465:18:465:25 | call to iterator | TAINT | +| string.cpp:465:28:465:29 | ref arg s1 | string.cpp:465:40:465:41 | s1 | | +| string.cpp:465:28:465:29 | s1 | string.cpp:465:31:465:35 | call to begin | TAINT | +| string.cpp:465:31:465:35 | call to begin | string.cpp:465:8:465:9 | ref arg s2 | TAINT | +| string.cpp:465:31:465:35 | call to begin | string.cpp:465:11:465:16 | call to insert | TAINT | +| string.cpp:465:40:465:41 | s1 | string.cpp:465:43:465:45 | call to end | TAINT | +| string.cpp:465:43:465:45 | call to end | string.cpp:465:8:465:9 | ref arg s2 | TAINT | +| string.cpp:465:43:465:45 | call to end | string.cpp:465:11:465:16 | call to insert | TAINT | +| string.cpp:470:17:470:20 | ee | string.cpp:470:17:470:21 | call to basic_string | TAINT | +| string.cpp:470:17:470:21 | call to basic_string | string.cpp:475:8:475:8 | e | | +| string.cpp:470:17:470:21 | call to basic_string | string.cpp:476:8:476:8 | e | | +| string.cpp:471:17:471:20 | ff | string.cpp:471:17:471:21 | call to basic_string | TAINT | +| string.cpp:471:17:471:21 | call to basic_string | string.cpp:478:8:478:8 | f | | +| string.cpp:471:17:471:21 | call to basic_string | string.cpp:479:8:479:8 | f | | +| string.cpp:472:18:472:21 | 33 | string.cpp:472:18:472:22 | call to basic_string | TAINT | +| string.cpp:472:18:472:22 | call to basic_string | string.cpp:475:17:475:18 | s3 | | +| string.cpp:472:18:472:22 | call to basic_string | string.cpp:475:29:475:30 | s3 | | +| string.cpp:472:18:472:22 | call to basic_string | string.cpp:481:18:481:19 | s3 | | +| string.cpp:472:18:472:22 | call to basic_string | string.cpp:481:30:481:31 | s3 | | +| string.cpp:473:18:473:23 | call to source | string.cpp:473:18:473:26 | call to basic_string | TAINT | +| string.cpp:473:18:473:26 | call to basic_string | string.cpp:478:17:478:18 | s4 | | +| string.cpp:473:18:473:26 | call to basic_string | string.cpp:478:29:478:30 | s4 | | +| string.cpp:473:18:473:26 | call to basic_string | string.cpp:481:8:481:9 | s4 | | +| string.cpp:473:18:473:26 | call to basic_string | string.cpp:482:8:482:9 | s4 | | +| string.cpp:475:8:475:8 | e | string.cpp:475:10:475:15 | call to append | TAINT | +| string.cpp:475:8:475:8 | ref arg e | string.cpp:476:8:476:8 | e | | +| string.cpp:475:17:475:18 | ref arg s3 | string.cpp:475:29:475:30 | s3 | | +| string.cpp:475:17:475:18 | ref arg s3 | string.cpp:481:18:481:19 | s3 | | +| string.cpp:475:17:475:18 | ref arg s3 | string.cpp:481:30:481:31 | s3 | | +| string.cpp:475:17:475:18 | s3 | string.cpp:475:20:475:24 | call to begin | TAINT | +| string.cpp:475:20:475:24 | call to begin | string.cpp:475:8:475:8 | ref arg e | TAINT | +| string.cpp:475:20:475:24 | call to begin | string.cpp:475:10:475:15 | call to append | TAINT | +| string.cpp:475:29:475:30 | ref arg s3 | string.cpp:481:18:481:19 | s3 | | +| string.cpp:475:29:475:30 | ref arg s3 | string.cpp:481:30:481:31 | s3 | | +| string.cpp:475:29:475:30 | s3 | string.cpp:475:32:475:34 | call to end | TAINT | +| string.cpp:475:32:475:34 | call to end | string.cpp:475:8:475:8 | ref arg e | TAINT | +| string.cpp:475:32:475:34 | call to end | string.cpp:475:10:475:15 | call to append | TAINT | +| string.cpp:478:8:478:8 | f | string.cpp:478:10:478:15 | call to append | TAINT | +| string.cpp:478:8:478:8 | ref arg f | string.cpp:479:8:479:8 | f | | +| string.cpp:478:17:478:18 | ref arg s4 | string.cpp:478:29:478:30 | s4 | | +| string.cpp:478:17:478:18 | ref arg s4 | string.cpp:481:8:481:9 | s4 | | +| string.cpp:478:17:478:18 | ref arg s4 | string.cpp:482:8:482:9 | s4 | | +| string.cpp:478:17:478:18 | s4 | string.cpp:478:20:478:24 | call to begin | TAINT | +| string.cpp:478:20:478:24 | call to begin | string.cpp:478:8:478:8 | ref arg f | TAINT | +| string.cpp:478:20:478:24 | call to begin | string.cpp:478:10:478:15 | call to append | TAINT | +| string.cpp:478:29:478:30 | ref arg s4 | string.cpp:481:8:481:9 | s4 | | +| string.cpp:478:29:478:30 | ref arg s4 | string.cpp:482:8:482:9 | s4 | | +| string.cpp:478:29:478:30 | s4 | string.cpp:478:32:478:34 | call to end | TAINT | +| string.cpp:478:32:478:34 | call to end | string.cpp:478:8:478:8 | ref arg f | TAINT | +| string.cpp:478:32:478:34 | call to end | string.cpp:478:10:478:15 | call to append | TAINT | +| string.cpp:481:8:481:9 | ref arg s4 | string.cpp:482:8:482:9 | s4 | | +| string.cpp:481:8:481:9 | s4 | string.cpp:481:11:481:16 | call to append | TAINT | +| string.cpp:481:18:481:19 | ref arg s3 | string.cpp:481:30:481:31 | s3 | | +| string.cpp:481:18:481:19 | s3 | string.cpp:481:21:481:25 | call to begin | TAINT | +| string.cpp:481:21:481:25 | call to begin | string.cpp:481:8:481:9 | ref arg s4 | TAINT | +| string.cpp:481:21:481:25 | call to begin | string.cpp:481:11:481:16 | call to append | TAINT | +| string.cpp:481:30:481:31 | s3 | string.cpp:481:33:481:35 | call to end | TAINT | +| string.cpp:481:33:481:35 | call to end | string.cpp:481:8:481:9 | ref arg s4 | TAINT | +| string.cpp:481:33:481:35 | call to end | string.cpp:481:11:481:16 | call to append | TAINT | +| string.cpp:486:17:486:20 | gg | string.cpp:486:17:486:21 | call to basic_string | TAINT | +| string.cpp:486:17:486:21 | call to basic_string | string.cpp:491:8:491:8 | g | | +| string.cpp:486:17:486:21 | call to basic_string | string.cpp:492:8:492:8 | g | | +| string.cpp:487:17:487:20 | hh | string.cpp:487:17:487:21 | call to basic_string | TAINT | +| string.cpp:487:17:487:21 | call to basic_string | string.cpp:494:8:494:8 | h | | +| string.cpp:487:17:487:21 | call to basic_string | string.cpp:495:8:495:8 | h | | +| string.cpp:488:18:488:21 | 55 | string.cpp:488:18:488:22 | call to basic_string | TAINT | +| string.cpp:488:18:488:22 | call to basic_string | string.cpp:491:17:491:18 | s5 | | +| string.cpp:488:18:488:22 | call to basic_string | string.cpp:491:30:491:31 | s5 | | +| string.cpp:488:18:488:22 | call to basic_string | string.cpp:497:18:497:19 | s5 | | +| string.cpp:488:18:488:22 | call to basic_string | string.cpp:497:31:497:32 | s5 | | +| string.cpp:489:18:489:23 | call to source | string.cpp:489:18:489:26 | call to basic_string | TAINT | +| string.cpp:489:18:489:26 | call to basic_string | string.cpp:494:17:494:18 | s6 | | +| string.cpp:489:18:489:26 | call to basic_string | string.cpp:494:30:494:31 | s6 | | +| string.cpp:489:18:489:26 | call to basic_string | string.cpp:497:8:497:9 | s6 | | +| string.cpp:489:18:489:26 | call to basic_string | string.cpp:498:8:498:9 | s6 | | +| string.cpp:491:8:491:8 | ref arg g | string.cpp:492:8:492:8 | g | | +| string.cpp:491:17:491:18 | s5 | string.cpp:491:20:491:25 | call to cbegin | TAINT | +| string.cpp:491:20:491:25 | call to cbegin | string.cpp:491:8:491:8 | ref arg g | TAINT | +| string.cpp:491:20:491:25 | call to cbegin | string.cpp:491:10:491:15 | call to assign | TAINT | +| string.cpp:491:30:491:31 | s5 | string.cpp:491:33:491:36 | call to cend | TAINT | +| string.cpp:491:33:491:36 | call to cend | string.cpp:491:8:491:8 | ref arg g | TAINT | +| string.cpp:491:33:491:36 | call to cend | string.cpp:491:10:491:15 | call to assign | TAINT | +| string.cpp:494:8:494:8 | ref arg h | string.cpp:495:8:495:8 | h | | +| string.cpp:494:17:494:18 | s6 | string.cpp:494:20:494:25 | call to cbegin | TAINT | +| string.cpp:494:20:494:25 | call to cbegin | string.cpp:494:8:494:8 | ref arg h | TAINT | +| string.cpp:494:20:494:25 | call to cbegin | string.cpp:494:10:494:15 | call to assign | TAINT | +| string.cpp:494:30:494:31 | s6 | string.cpp:494:33:494:36 | call to cend | TAINT | +| string.cpp:494:33:494:36 | call to cend | string.cpp:494:8:494:8 | ref arg h | TAINT | +| string.cpp:494:33:494:36 | call to cend | string.cpp:494:10:494:15 | call to assign | TAINT | +| string.cpp:497:8:497:9 | ref arg s6 | string.cpp:498:8:498:9 | s6 | | +| string.cpp:497:18:497:19 | s5 | string.cpp:497:21:497:26 | call to cbegin | TAINT | +| string.cpp:497:21:497:26 | call to cbegin | string.cpp:497:8:497:9 | ref arg s6 | TAINT | +| string.cpp:497:21:497:26 | call to cbegin | string.cpp:497:11:497:16 | call to assign | TAINT | +| string.cpp:497:31:497:32 | s5 | string.cpp:497:34:497:37 | call to cend | TAINT | +| string.cpp:497:34:497:37 | call to cend | string.cpp:497:8:497:9 | ref arg s6 | TAINT | +| string.cpp:497:34:497:37 | call to cend | string.cpp:497:11:497:16 | call to assign | TAINT | +| string.cpp:503:14:503:18 | abc | string.cpp:505:17:505:19 | cs1 | | +| string.cpp:504:14:504:19 | call to source | string.cpp:506:17:506:19 | cs2 | | +| string.cpp:505:17:505:19 | cs1 | string.cpp:505:17:505:20 | call to basic_string | TAINT | +| string.cpp:505:17:505:20 | call to basic_string | string.cpp:507:17:507:18 | s1 | | +| string.cpp:505:17:505:20 | call to basic_string | string.cpp:507:29:507:30 | s1 | | +| string.cpp:505:17:505:20 | call to basic_string | string.cpp:510:7:510:8 | s1 | | +| string.cpp:506:17:506:19 | cs2 | string.cpp:506:17:506:20 | call to basic_string | TAINT | +| string.cpp:506:17:506:20 | call to basic_string | string.cpp:508:17:508:18 | s2 | | +| string.cpp:506:17:506:20 | call to basic_string | string.cpp:508:29:508:30 | s2 | | +| string.cpp:506:17:506:20 | call to basic_string | string.cpp:511:7:511:8 | s2 | | +| string.cpp:507:17:507:18 | ref arg s1 | string.cpp:507:29:507:30 | s1 | | +| string.cpp:507:17:507:18 | ref arg s1 | string.cpp:510:7:510:8 | s1 | | +| string.cpp:507:17:507:18 | s1 | string.cpp:507:20:507:24 | call to begin | TAINT | +| string.cpp:507:17:507:37 | call to basic_string | string.cpp:512:7:512:8 | s3 | | +| string.cpp:507:20:507:24 | call to begin | string.cpp:507:17:507:37 | call to basic_string | TAINT | +| string.cpp:507:29:507:30 | ref arg s1 | string.cpp:510:7:510:8 | s1 | | +| string.cpp:507:29:507:30 | s1 | string.cpp:507:32:507:34 | call to end | TAINT | +| string.cpp:507:32:507:34 | call to end | string.cpp:507:17:507:37 | call to basic_string | TAINT | +| string.cpp:508:17:508:18 | ref arg s2 | string.cpp:508:29:508:30 | s2 | | +| string.cpp:508:17:508:18 | ref arg s2 | string.cpp:511:7:511:8 | s2 | | +| string.cpp:508:17:508:18 | s2 | string.cpp:508:20:508:24 | call to begin | TAINT | +| string.cpp:508:17:508:37 | call to basic_string | string.cpp:513:7:513:8 | s4 | | +| string.cpp:508:20:508:24 | call to begin | string.cpp:508:17:508:37 | call to basic_string | TAINT | +| string.cpp:508:29:508:30 | ref arg s2 | string.cpp:511:7:511:8 | s2 | | +| string.cpp:508:29:508:30 | s2 | string.cpp:508:32:508:34 | call to end | TAINT | +| string.cpp:508:32:508:34 | call to end | string.cpp:508:17:508:37 | call to basic_string | TAINT | +| string.cpp:517:16:517:19 | aa | string.cpp:517:16:517:20 | call to basic_string | TAINT | +| string.cpp:517:16:517:20 | call to basic_string | string.cpp:519:7:519:7 | a | | +| string.cpp:517:16:517:20 | call to basic_string | string.cpp:520:7:520:7 | a | | +| string.cpp:517:16:517:20 | call to basic_string | string.cpp:521:2:521:2 | a | | +| string.cpp:517:16:517:20 | call to basic_string | string.cpp:522:7:522:7 | a | | +| string.cpp:517:16:517:20 | call to basic_string | string.cpp:523:7:523:7 | a | | +| string.cpp:519:7:519:7 | a | string.cpp:519:9:519:13 | call to front | TAINT | +| string.cpp:519:7:519:7 | ref arg a | string.cpp:520:7:520:7 | a | | +| string.cpp:519:7:519:7 | ref arg a | string.cpp:521:2:521:2 | a | | +| string.cpp:519:7:519:7 | ref arg a | string.cpp:522:7:522:7 | a | | +| string.cpp:519:7:519:7 | ref arg a | string.cpp:523:7:523:7 | a | | +| string.cpp:520:7:520:7 | a | string.cpp:520:9:520:12 | call to back | TAINT | +| string.cpp:520:7:520:7 | ref arg a | string.cpp:521:2:521:2 | a | | +| string.cpp:520:7:520:7 | ref arg a | string.cpp:522:7:522:7 | a | | +| string.cpp:520:7:520:7 | ref arg a | string.cpp:523:7:523:7 | a | | +| string.cpp:521:2:521:2 | ref arg a | string.cpp:522:7:522:7 | a | | +| string.cpp:521:2:521:2 | ref arg a | string.cpp:523:7:523:7 | a | | +| string.cpp:521:14:521:28 | call to source | string.cpp:521:2:521:2 | ref arg a | TAINT | +| string.cpp:522:7:522:7 | a | string.cpp:522:9:522:13 | call to front | TAINT | +| string.cpp:522:7:522:7 | ref arg a | string.cpp:523:7:523:7 | a | | +| string.cpp:523:7:523:7 | a | string.cpp:523:9:523:12 | call to back | TAINT | +| string.cpp:528:17:528:20 | aa | string.cpp:528:17:528:21 | call to basic_string | TAINT | +| string.cpp:528:17:528:21 | call to basic_string | string.cpp:535:9:535:9 | a | | +| string.cpp:528:17:528:21 | call to basic_string | string.cpp:539:8:539:8 | a | | +| string.cpp:529:17:529:20 | bb | string.cpp:529:17:529:21 | call to basic_string | TAINT | +| string.cpp:529:17:529:21 | call to basic_string | string.cpp:535:15:535:15 | b | | +| string.cpp:529:17:529:21 | call to basic_string | string.cpp:540:8:540:8 | b | | +| string.cpp:530:17:530:20 | cc | string.cpp:530:17:530:21 | call to basic_string | TAINT | +| string.cpp:530:17:530:21 | call to basic_string | string.cpp:536:9:536:9 | c | | +| string.cpp:530:17:530:21 | call to basic_string | string.cpp:541:8:541:8 | c | | +| string.cpp:531:17:531:20 | dd | string.cpp:531:17:531:21 | call to basic_string | TAINT | +| string.cpp:531:17:531:21 | call to basic_string | string.cpp:536:15:536:15 | d | | +| string.cpp:531:17:531:21 | call to basic_string | string.cpp:542:8:542:8 | d | | +| string.cpp:532:17:532:20 | ee | string.cpp:532:17:532:21 | call to basic_string | TAINT | +| string.cpp:532:17:532:21 | call to basic_string | string.cpp:537:10:537:10 | e | | +| string.cpp:532:17:532:21 | call to basic_string | string.cpp:543:8:543:8 | e | | +| string.cpp:533:17:533:20 | ff | string.cpp:533:17:533:21 | call to basic_string | TAINT | +| string.cpp:533:17:533:21 | call to basic_string | string.cpp:538:10:538:10 | f | | +| string.cpp:533:17:533:21 | call to basic_string | string.cpp:544:8:544:8 | f | | +| string.cpp:535:9:535:9 | a | string.cpp:535:11:535:11 | call to operator+= | TAINT | +| string.cpp:535:9:535:9 | ref arg a | string.cpp:539:8:539:8 | a | | +| string.cpp:535:15:535:15 | b | string.cpp:535:17:535:17 | call to operator+= | TAINT | +| string.cpp:535:15:535:15 | ref arg b | string.cpp:540:8:540:8 | b | | +| string.cpp:535:17:535:17 | call to operator+= | string.cpp:535:9:535:9 | ref arg a | TAINT | +| string.cpp:535:17:535:17 | call to operator+= | string.cpp:535:11:535:11 | call to operator+= | TAINT | +| string.cpp:535:20:535:23 | bb | string.cpp:535:15:535:15 | ref arg b | TAINT | +| string.cpp:535:20:535:23 | bb | string.cpp:535:17:535:17 | call to operator+= | TAINT | +| string.cpp:536:9:536:9 | c | string.cpp:536:11:536:11 | call to operator+= | TAINT | +| string.cpp:536:9:536:9 | ref arg c | string.cpp:541:8:541:8 | c | | +| string.cpp:536:15:536:15 | d | string.cpp:536:17:536:17 | call to operator+= | TAINT | +| string.cpp:536:15:536:15 | ref arg d | string.cpp:542:8:542:8 | d | | +| string.cpp:536:17:536:17 | call to operator+= | string.cpp:536:9:536:9 | ref arg c | TAINT | +| string.cpp:536:17:536:17 | call to operator+= | string.cpp:536:11:536:11 | call to operator+= | TAINT | +| string.cpp:536:20:536:25 | call to source | string.cpp:536:15:536:15 | ref arg d | TAINT | +| string.cpp:536:20:536:25 | call to source | string.cpp:536:17:536:17 | call to operator+= | TAINT | +| string.cpp:537:10:537:10 | e | string.cpp:537:12:537:12 | call to operator+= | TAINT | +| string.cpp:537:10:537:10 | ref arg e | string.cpp:543:8:543:8 | e | | +| string.cpp:537:12:537:12 | call to operator+= | string.cpp:537:21:537:21 | call to operator+= | TAINT | +| string.cpp:537:12:537:12 | ref arg call to operator+= | string.cpp:537:10:537:10 | ref arg e | TAINT | +| string.cpp:537:15:537:18 | ee | string.cpp:537:10:537:10 | ref arg e | TAINT | +| string.cpp:537:15:537:18 | ee | string.cpp:537:12:537:12 | call to operator+= | TAINT | +| string.cpp:537:24:537:29 | call to source | string.cpp:537:12:537:12 | ref arg call to operator+= | TAINT | +| string.cpp:537:24:537:29 | call to source | string.cpp:537:21:537:21 | call to operator+= | TAINT | +| string.cpp:538:10:538:10 | f | string.cpp:538:12:538:12 | call to operator+= | TAINT | +| string.cpp:538:10:538:10 | ref arg f | string.cpp:544:8:544:8 | f | | +| string.cpp:538:12:538:12 | call to operator+= | string.cpp:538:25:538:25 | call to operator+= | TAINT | +| string.cpp:538:12:538:12 | ref arg call to operator+= | string.cpp:538:10:538:10 | ref arg f | TAINT | +| string.cpp:538:15:538:20 | call to source | string.cpp:538:10:538:10 | ref arg f | TAINT | +| string.cpp:538:15:538:20 | call to source | string.cpp:538:12:538:12 | call to operator+= | TAINT | +| string.cpp:538:28:538:31 | ff | string.cpp:538:12:538:12 | ref arg call to operator+= | TAINT | +| string.cpp:538:28:538:31 | ff | string.cpp:538:25:538:25 | call to operator+= | TAINT | +| string.cpp:548:17:548:20 | aa | string.cpp:548:17:548:21 | call to basic_string | TAINT | +| string.cpp:548:17:548:21 | call to basic_string | string.cpp:555:9:555:9 | a | | +| string.cpp:548:17:548:21 | call to basic_string | string.cpp:559:8:559:8 | a | | +| string.cpp:549:17:549:20 | bb | string.cpp:549:17:549:21 | call to basic_string | TAINT | +| string.cpp:549:17:549:21 | call to basic_string | string.cpp:555:18:555:18 | b | | +| string.cpp:549:17:549:21 | call to basic_string | string.cpp:560:8:560:8 | b | | +| string.cpp:550:17:550:20 | cc | string.cpp:550:17:550:21 | call to basic_string | TAINT | +| string.cpp:550:17:550:21 | call to basic_string | string.cpp:556:9:556:9 | c | | +| string.cpp:550:17:550:21 | call to basic_string | string.cpp:561:8:561:8 | c | | +| string.cpp:551:17:551:20 | dd | string.cpp:551:17:551:21 | call to basic_string | TAINT | +| string.cpp:551:17:551:21 | call to basic_string | string.cpp:556:18:556:18 | d | | +| string.cpp:551:17:551:21 | call to basic_string | string.cpp:562:8:562:8 | d | | +| string.cpp:552:17:552:20 | ee | string.cpp:552:17:552:21 | call to basic_string | TAINT | +| string.cpp:552:17:552:21 | call to basic_string | string.cpp:557:9:557:9 | e | | +| string.cpp:552:17:552:21 | call to basic_string | string.cpp:563:8:563:8 | e | | +| string.cpp:553:17:553:20 | ff | string.cpp:553:17:553:21 | call to basic_string | TAINT | +| string.cpp:553:17:553:21 | call to basic_string | string.cpp:558:9:558:9 | f | | +| string.cpp:553:17:553:21 | call to basic_string | string.cpp:564:8:564:8 | f | | +| string.cpp:555:9:555:9 | ref arg a | string.cpp:559:8:559:8 | a | | +| string.cpp:555:18:555:18 | ref arg b | string.cpp:560:8:560:8 | b | | +| string.cpp:555:20:555:25 | call to assign | string.cpp:555:9:555:9 | ref arg a | TAINT | +| string.cpp:555:20:555:25 | call to assign | string.cpp:555:11:555:16 | call to assign | TAINT | +| string.cpp:555:27:555:30 | bb | string.cpp:555:27:555:30 | call to basic_string | TAINT | +| string.cpp:555:27:555:30 | call to basic_string | string.cpp:555:18:555:18 | ref arg b | TAINT | +| string.cpp:555:27:555:30 | call to basic_string | string.cpp:555:20:555:25 | call to assign | TAINT | +| string.cpp:556:9:556:9 | ref arg c | string.cpp:561:8:561:8 | c | | +| string.cpp:556:18:556:18 | ref arg d | string.cpp:562:8:562:8 | d | | +| string.cpp:556:20:556:25 | call to assign | string.cpp:556:9:556:9 | ref arg c | TAINT | +| string.cpp:556:20:556:25 | call to assign | string.cpp:556:11:556:16 | call to assign | TAINT | +| string.cpp:556:27:556:32 | call to source | string.cpp:556:27:556:34 | call to basic_string | TAINT | +| string.cpp:556:27:556:34 | call to basic_string | string.cpp:556:18:556:18 | ref arg d | TAINT | +| string.cpp:556:27:556:34 | call to basic_string | string.cpp:556:20:556:25 | call to assign | TAINT | +| string.cpp:557:9:557:9 | ref arg e | string.cpp:563:8:563:8 | e | | +| string.cpp:557:11:557:16 | ref arg call to assign | string.cpp:557:9:557:9 | ref arg e | TAINT | +| string.cpp:557:18:557:21 | call to basic_string | string.cpp:557:9:557:9 | ref arg e | TAINT | +| string.cpp:557:18:557:21 | call to basic_string | string.cpp:557:11:557:16 | call to assign | TAINT | +| string.cpp:557:18:557:21 | ee | string.cpp:557:18:557:21 | call to basic_string | TAINT | +| string.cpp:557:31:557:36 | call to source | string.cpp:557:31:557:38 | call to basic_string | TAINT | +| string.cpp:557:31:557:38 | call to basic_string | string.cpp:557:11:557:16 | ref arg call to assign | TAINT | +| string.cpp:557:31:557:38 | call to basic_string | string.cpp:557:24:557:29 | call to assign | TAINT | +| string.cpp:558:9:558:9 | ref arg f | string.cpp:564:8:564:8 | f | | +| string.cpp:558:11:558:16 | ref arg call to assign | string.cpp:558:9:558:9 | ref arg f | TAINT | +| string.cpp:558:18:558:23 | call to source | string.cpp:558:18:558:25 | call to basic_string | TAINT | +| string.cpp:558:18:558:25 | call to basic_string | string.cpp:558:9:558:9 | ref arg f | TAINT | +| string.cpp:558:18:558:25 | call to basic_string | string.cpp:558:11:558:16 | call to assign | TAINT | +| string.cpp:558:35:558:38 | call to basic_string | string.cpp:558:11:558:16 | ref arg call to assign | TAINT | +| string.cpp:558:35:558:38 | call to basic_string | string.cpp:558:28:558:33 | call to assign | TAINT | +| string.cpp:558:35:558:38 | ff | string.cpp:558:35:558:38 | call to basic_string | TAINT | +| stringstream.cpp:26:35:26:40 | amount | stringstream.cpp:64:46:64:51 | amount | | +| stringstream.cpp:28:20:28:22 | call to basic_stringstream | stringstream.cpp:31:7:31:9 | ss1 | | +| stringstream.cpp:28:20:28:22 | call to basic_stringstream | stringstream.cpp:37:7:37:9 | ss1 | | +| stringstream.cpp:28:20:28:22 | call to basic_stringstream | stringstream.cpp:42:7:42:9 | ss1 | | +| stringstream.cpp:28:25:28:27 | call to basic_stringstream | stringstream.cpp:32:7:32:9 | ss2 | | +| stringstream.cpp:28:25:28:27 | call to basic_stringstream | stringstream.cpp:38:7:38:9 | ss2 | | +| stringstream.cpp:28:25:28:27 | call to basic_stringstream | stringstream.cpp:43:7:43:9 | ss2 | | +| stringstream.cpp:28:30:28:32 | call to basic_stringstream | stringstream.cpp:33:7:33:9 | ss3 | | +| stringstream.cpp:28:30:28:32 | call to basic_stringstream | stringstream.cpp:39:7:39:9 | ss3 | | +| stringstream.cpp:28:30:28:32 | call to basic_stringstream | stringstream.cpp:44:7:44:9 | ss3 | | +| stringstream.cpp:28:35:28:37 | call to basic_stringstream | stringstream.cpp:34:7:34:9 | ss4 | | +| stringstream.cpp:28:35:28:37 | call to basic_stringstream | stringstream.cpp:40:7:40:9 | ss4 | | +| stringstream.cpp:28:35:28:37 | call to basic_stringstream | stringstream.cpp:45:7:45:9 | ss4 | | +| stringstream.cpp:28:40:28:42 | call to basic_stringstream | stringstream.cpp:35:7:35:9 | ss5 | | +| stringstream.cpp:28:40:28:42 | call to basic_stringstream | stringstream.cpp:41:7:41:9 | ss5 | | +| stringstream.cpp:28:40:28:42 | call to basic_stringstream | stringstream.cpp:46:7:46:9 | ss5 | | +| stringstream.cpp:28:45:28:47 | call to basic_stringstream | stringstream.cpp:48:2:48:4 | ss6 | | +| stringstream.cpp:28:45:28:47 | call to basic_stringstream | stringstream.cpp:49:2:49:4 | ss6 | | +| stringstream.cpp:28:45:28:47 | call to basic_stringstream | stringstream.cpp:52:7:52:9 | ss6 | | +| stringstream.cpp:28:50:28:52 | call to basic_stringstream | stringstream.cpp:50:2:50:4 | ss7 | | +| stringstream.cpp:28:50:28:52 | call to basic_stringstream | stringstream.cpp:51:2:51:4 | ss7 | | +| stringstream.cpp:28:50:28:52 | call to basic_stringstream | stringstream.cpp:53:7:53:9 | ss7 | | +| stringstream.cpp:28:55:28:57 | call to basic_stringstream | stringstream.cpp:55:7:55:9 | ss8 | | +| stringstream.cpp:28:55:28:57 | call to basic_stringstream | stringstream.cpp:58:7:58:9 | ss8 | | +| stringstream.cpp:28:60:28:62 | call to basic_stringstream | stringstream.cpp:56:7:56:9 | ss9 | | +| stringstream.cpp:28:60:28:62 | call to basic_stringstream | stringstream.cpp:59:7:59:9 | ss9 | | +| stringstream.cpp:28:65:28:68 | call to basic_stringstream | stringstream.cpp:57:7:57:10 | ss10 | | +| stringstream.cpp:28:65:28:68 | call to basic_stringstream | stringstream.cpp:60:7:60:10 | ss10 | | +| stringstream.cpp:28:71:28:74 | call to basic_stringstream | stringstream.cpp:62:7:62:10 | ss11 | | +| stringstream.cpp:28:71:28:74 | call to basic_stringstream | stringstream.cpp:65:7:65:10 | ss11 | | +| stringstream.cpp:28:77:28:80 | call to basic_stringstream | stringstream.cpp:63:7:63:10 | ss12 | | +| stringstream.cpp:28:77:28:80 | call to basic_stringstream | stringstream.cpp:66:7:66:10 | ss12 | | +| stringstream.cpp:28:83:28:86 | call to basic_stringstream | stringstream.cpp:64:7:64:10 | ss13 | | +| stringstream.cpp:28:83:28:86 | call to basic_stringstream | stringstream.cpp:67:7:67:10 | ss13 | | +| stringstream.cpp:29:16:29:21 | call to source | stringstream.cpp:29:16:29:24 | call to basic_string | TAINT | +| stringstream.cpp:29:16:29:24 | call to basic_string | stringstream.cpp:35:14:35:14 | t | | +| stringstream.cpp:31:7:31:9 | ref arg ss1 | stringstream.cpp:37:7:37:9 | ss1 | | +| stringstream.cpp:31:7:31:9 | ref arg ss1 | stringstream.cpp:42:7:42:9 | ss1 | | +| stringstream.cpp:31:7:31:9 | ss1 | stringstream.cpp:31:11:31:11 | call to operator<< | | +| stringstream.cpp:31:14:31:19 | 1234 | stringstream.cpp:31:7:31:9 | ref arg ss1 | TAINT | +| stringstream.cpp:31:14:31:19 | 1234 | stringstream.cpp:31:11:31:11 | call to operator<< | TAINT | +| stringstream.cpp:32:7:32:9 | ref arg ss2 | stringstream.cpp:38:7:38:9 | ss2 | | +| stringstream.cpp:32:7:32:9 | ref arg ss2 | stringstream.cpp:43:7:43:9 | ss2 | | +| stringstream.cpp:32:7:32:9 | ss2 | stringstream.cpp:32:11:32:11 | call to operator<< | | +| stringstream.cpp:32:14:32:19 | call to source | stringstream.cpp:32:7:32:9 | ref arg ss2 | TAINT | +| stringstream.cpp:32:14:32:19 | call to source | stringstream.cpp:32:11:32:11 | call to operator<< | TAINT | +| stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:39:7:39:9 | ss3 | | +| stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:44:7:44:9 | ss3 | | +| stringstream.cpp:33:7:33:9 | ss3 | stringstream.cpp:33:11:33:11 | call to operator<< | | +| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | | +| stringstream.cpp:33:11:33:11 | ref arg call to operator<< | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT | +| stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT | +| stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:11:33:11 | call to operator<< | TAINT | +| stringstream.cpp:33:23:33:28 | call to source | stringstream.cpp:33:11:33:11 | ref arg call to operator<< | TAINT | +| stringstream.cpp:33:23:33:28 | call to source | stringstream.cpp:33:20:33:20 | call to operator<< | TAINT | +| stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:40:7:40:9 | ss4 | | +| stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:45:7:45:9 | ss4 | | +| stringstream.cpp:34:7:34:9 | ss4 | stringstream.cpp:34:11:34:11 | call to operator<< | | +| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | | +| stringstream.cpp:34:11:34:11 | ref arg call to operator<< | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT | +| stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT | +| stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:11:34:11 | call to operator<< | TAINT | +| stringstream.cpp:34:26:34:30 | 456 | stringstream.cpp:34:11:34:11 | ref arg call to operator<< | TAINT | +| stringstream.cpp:34:26:34:30 | 456 | stringstream.cpp:34:23:34:23 | call to operator<< | TAINT | +| stringstream.cpp:35:7:35:9 | ref arg ss5 | stringstream.cpp:41:7:41:9 | ss5 | | +| stringstream.cpp:35:7:35:9 | ref arg ss5 | stringstream.cpp:46:7:46:9 | ss5 | | +| stringstream.cpp:35:7:35:9 | ss5 | stringstream.cpp:35:11:35:11 | call to operator<< | | +| stringstream.cpp:35:14:35:14 | t | stringstream.cpp:35:7:35:9 | ref arg ss5 | TAINT | +| stringstream.cpp:35:14:35:14 | t | stringstream.cpp:35:11:35:11 | call to operator<< | TAINT | +| stringstream.cpp:42:7:42:9 | ss1 | stringstream.cpp:42:11:42:13 | call to str | TAINT | +| stringstream.cpp:43:7:43:9 | ss2 | stringstream.cpp:43:11:43:13 | call to str | TAINT | +| stringstream.cpp:44:7:44:9 | ss3 | stringstream.cpp:44:11:44:13 | call to str | TAINT | +| stringstream.cpp:45:7:45:9 | ss4 | stringstream.cpp:45:11:45:13 | call to str | TAINT | +| stringstream.cpp:46:7:46:9 | ss5 | stringstream.cpp:46:11:46:13 | call to str | TAINT | +| stringstream.cpp:48:2:48:4 | ref arg ss6 | stringstream.cpp:49:2:49:4 | ss6 | | +| stringstream.cpp:48:2:48:4 | ref arg ss6 | stringstream.cpp:52:7:52:9 | ss6 | | +| stringstream.cpp:48:2:48:4 | ss6 | stringstream.cpp:48:6:48:8 | call to str | TAINT | +| stringstream.cpp:48:10:48:14 | abc | stringstream.cpp:48:10:48:14 | call to basic_string | TAINT | +| stringstream.cpp:48:10:48:14 | call to basic_string | stringstream.cpp:48:2:48:4 | ref arg ss6 | TAINT | +| stringstream.cpp:49:2:49:4 | ref arg ss6 | stringstream.cpp:52:7:52:9 | ss6 | | +| stringstream.cpp:49:2:49:4 | ss6 | stringstream.cpp:49:6:49:8 | call to str | TAINT | +| stringstream.cpp:49:10:49:15 | call to source | stringstream.cpp:49:10:49:17 | call to basic_string | TAINT | +| stringstream.cpp:49:10:49:17 | call to basic_string | stringstream.cpp:49:2:49:4 | ref arg ss6 | TAINT | +| stringstream.cpp:50:2:50:4 | ref arg ss7 | stringstream.cpp:51:2:51:4 | ss7 | | +| stringstream.cpp:50:2:50:4 | ref arg ss7 | stringstream.cpp:53:7:53:9 | ss7 | | +| stringstream.cpp:50:2:50:4 | ss7 | stringstream.cpp:50:6:50:8 | call to str | TAINT | +| stringstream.cpp:50:10:50:15 | call to source | stringstream.cpp:50:10:50:17 | call to basic_string | TAINT | +| stringstream.cpp:50:10:50:17 | call to basic_string | stringstream.cpp:50:2:50:4 | ref arg ss7 | TAINT | +| stringstream.cpp:51:2:51:4 | ref arg ss7 | stringstream.cpp:53:7:53:9 | ss7 | | +| stringstream.cpp:51:2:51:4 | ss7 | stringstream.cpp:51:6:51:8 | call to str | TAINT | +| stringstream.cpp:51:10:51:14 | abc | stringstream.cpp:51:10:51:14 | call to basic_string | TAINT | +| stringstream.cpp:51:10:51:14 | call to basic_string | stringstream.cpp:51:2:51:4 | ref arg ss7 | TAINT | +| stringstream.cpp:55:7:55:9 | ref arg ss8 | stringstream.cpp:58:7:58:9 | ss8 | | +| stringstream.cpp:55:7:55:9 | ss8 | stringstream.cpp:55:11:55:13 | call to put | | +| stringstream.cpp:55:15:55:17 | 97 | stringstream.cpp:55:7:55:9 | ref arg ss8 | TAINT | +| stringstream.cpp:55:15:55:17 | 97 | stringstream.cpp:55:11:55:13 | call to put | TAINT | +| stringstream.cpp:56:7:56:9 | ref arg ss9 | stringstream.cpp:59:7:59:9 | ss9 | | +| stringstream.cpp:56:7:56:9 | ss9 | stringstream.cpp:56:11:56:13 | call to put | | +| stringstream.cpp:56:15:56:29 | call to source | stringstream.cpp:56:7:56:9 | ref arg ss9 | TAINT | +| stringstream.cpp:56:15:56:29 | call to source | stringstream.cpp:56:11:56:13 | call to put | TAINT | +| stringstream.cpp:57:7:57:10 | ref arg ss10 | stringstream.cpp:60:7:60:10 | ss10 | | +| stringstream.cpp:57:7:57:10 | ss10 | stringstream.cpp:57:12:57:14 | call to put | | +| stringstream.cpp:57:12:57:14 | call to put | stringstream.cpp:57:21:57:23 | call to put | | +| stringstream.cpp:57:12:57:14 | ref arg call to put | stringstream.cpp:57:7:57:10 | ref arg ss10 | TAINT | +| stringstream.cpp:57:16:57:18 | 97 | stringstream.cpp:57:7:57:10 | ref arg ss10 | TAINT | +| stringstream.cpp:57:16:57:18 | 97 | stringstream.cpp:57:12:57:14 | call to put | TAINT | +| stringstream.cpp:57:21:57:23 | call to put | stringstream.cpp:57:44:57:46 | call to put | | +| stringstream.cpp:57:21:57:23 | ref arg call to put | stringstream.cpp:57:12:57:14 | ref arg call to put | TAINT | +| stringstream.cpp:57:25:57:39 | call to source | stringstream.cpp:57:12:57:14 | ref arg call to put | TAINT | +| stringstream.cpp:57:25:57:39 | call to source | stringstream.cpp:57:21:57:23 | call to put | TAINT | +| stringstream.cpp:57:48:57:50 | 122 | stringstream.cpp:57:21:57:23 | ref arg call to put | TAINT | +| stringstream.cpp:57:48:57:50 | 122 | stringstream.cpp:57:44:57:46 | call to put | TAINT | +| stringstream.cpp:62:7:62:10 | ref arg ss11 | stringstream.cpp:65:7:65:10 | ss11 | | +| stringstream.cpp:62:7:62:10 | ss11 | stringstream.cpp:62:12:62:16 | call to write | | +| stringstream.cpp:62:18:62:24 | begin | stringstream.cpp:62:7:62:10 | ref arg ss11 | TAINT | +| stringstream.cpp:62:18:62:24 | begin | stringstream.cpp:62:12:62:16 | call to write | TAINT | +| stringstream.cpp:63:7:63:10 | ref arg ss12 | stringstream.cpp:66:7:66:10 | ss12 | | +| stringstream.cpp:63:7:63:10 | ss12 | stringstream.cpp:63:12:63:16 | call to write | | +| stringstream.cpp:63:18:63:23 | call to source | stringstream.cpp:63:7:63:10 | ref arg ss12 | TAINT | +| stringstream.cpp:63:18:63:23 | call to source | stringstream.cpp:63:12:63:16 | call to write | TAINT | +| stringstream.cpp:64:7:64:10 | ref arg ss13 | stringstream.cpp:67:7:67:10 | ss13 | | +| stringstream.cpp:64:7:64:10 | ss13 | stringstream.cpp:64:12:64:16 | call to write | | +| stringstream.cpp:64:12:64:16 | call to write | stringstream.cpp:64:30:64:34 | call to write | | +| stringstream.cpp:64:12:64:16 | ref arg call to write | stringstream.cpp:64:7:64:10 | ref arg ss13 | TAINT | +| stringstream.cpp:64:18:64:24 | begin | stringstream.cpp:64:7:64:10 | ref arg ss13 | TAINT | +| stringstream.cpp:64:18:64:24 | begin | stringstream.cpp:64:12:64:16 | call to write | TAINT | +| stringstream.cpp:64:30:64:34 | call to write | stringstream.cpp:64:54:64:58 | call to write | | +| stringstream.cpp:64:30:64:34 | ref arg call to write | stringstream.cpp:64:12:64:16 | ref arg call to write | TAINT | +| stringstream.cpp:64:36:64:41 | call to source | stringstream.cpp:64:12:64:16 | ref arg call to write | TAINT | +| stringstream.cpp:64:36:64:41 | call to source | stringstream.cpp:64:30:64:34 | call to write | TAINT | +| stringstream.cpp:64:60:64:64 | end | stringstream.cpp:64:30:64:34 | ref arg call to write | TAINT | +| stringstream.cpp:64:60:64:64 | end | stringstream.cpp:64:54:64:58 | call to write | TAINT | +| stringstream.cpp:70:32:70:37 | source | stringstream.cpp:76:14:76:19 | source | | +| stringstream.cpp:72:20:72:22 | call to basic_stringstream | stringstream.cpp:75:7:75:9 | ss1 | | +| stringstream.cpp:72:20:72:22 | call to basic_stringstream | stringstream.cpp:77:7:77:9 | ss1 | | +| stringstream.cpp:72:20:72:22 | call to basic_stringstream | stringstream.cpp:80:7:80:9 | ss1 | | +| stringstream.cpp:72:20:72:22 | call to basic_stringstream | stringstream.cpp:82:7:82:9 | ss1 | | +| stringstream.cpp:72:25:72:27 | call to basic_stringstream | stringstream.cpp:76:7:76:9 | ss2 | | +| stringstream.cpp:72:25:72:27 | call to basic_stringstream | stringstream.cpp:78:7:78:9 | ss2 | | +| stringstream.cpp:72:25:72:27 | call to basic_stringstream | stringstream.cpp:81:7:81:9 | ss2 | | +| stringstream.cpp:72:25:72:27 | call to basic_stringstream | stringstream.cpp:83:7:83:9 | ss2 | | +| stringstream.cpp:73:10:73:11 | 0 | stringstream.cpp:77:14:77:15 | v1 | | +| stringstream.cpp:73:10:73:11 | 0 | stringstream.cpp:84:7:84:8 | v1 | | +| stringstream.cpp:73:18:73:19 | 0 | stringstream.cpp:78:14:78:15 | v2 | | +| stringstream.cpp:73:18:73:19 | 0 | stringstream.cpp:85:7:85:8 | v2 | | +| stringstream.cpp:75:7:75:9 | ref arg ss1 | stringstream.cpp:77:7:77:9 | ss1 | | +| stringstream.cpp:75:7:75:9 | ref arg ss1 | stringstream.cpp:80:7:80:9 | ss1 | | +| stringstream.cpp:75:7:75:9 | ref arg ss1 | stringstream.cpp:82:7:82:9 | ss1 | | +| stringstream.cpp:75:7:75:9 | ss1 | stringstream.cpp:75:11:75:11 | call to operator<< | | +| stringstream.cpp:75:14:75:17 | 1234 | stringstream.cpp:75:7:75:9 | ref arg ss1 | TAINT | +| stringstream.cpp:75:14:75:17 | 1234 | stringstream.cpp:75:11:75:11 | call to operator<< | TAINT | +| stringstream.cpp:76:7:76:9 | ref arg ss2 | stringstream.cpp:78:7:78:9 | ss2 | | +| stringstream.cpp:76:7:76:9 | ref arg ss2 | stringstream.cpp:81:7:81:9 | ss2 | | +| stringstream.cpp:76:7:76:9 | ref arg ss2 | stringstream.cpp:83:7:83:9 | ss2 | | +| stringstream.cpp:76:7:76:9 | ss2 | stringstream.cpp:76:11:76:11 | call to operator<< | | +| stringstream.cpp:76:14:76:19 | source | stringstream.cpp:76:7:76:9 | ref arg ss2 | TAINT | +| stringstream.cpp:76:14:76:19 | source | stringstream.cpp:76:11:76:11 | call to operator<< | TAINT | +| stringstream.cpp:77:7:77:9 | ref arg ss1 | stringstream.cpp:80:7:80:9 | ss1 | | +| stringstream.cpp:77:7:77:9 | ref arg ss1 | stringstream.cpp:82:7:82:9 | ss1 | | +| stringstream.cpp:77:7:77:9 | ss1 | stringstream.cpp:77:11:77:11 | call to operator>> | | +| stringstream.cpp:77:7:77:9 | ss1 | stringstream.cpp:77:14:77:15 | ref arg v1 | TAINT | +| stringstream.cpp:77:14:77:15 | ref arg v1 | stringstream.cpp:84:7:84:8 | v1 | | +| stringstream.cpp:78:7:78:9 | ref arg ss2 | stringstream.cpp:81:7:81:9 | ss2 | | +| stringstream.cpp:78:7:78:9 | ref arg ss2 | stringstream.cpp:83:7:83:9 | ss2 | | +| stringstream.cpp:78:7:78:9 | ss2 | stringstream.cpp:78:11:78:11 | call to operator>> | | +| stringstream.cpp:78:7:78:9 | ss2 | stringstream.cpp:78:14:78:15 | ref arg v2 | TAINT | +| stringstream.cpp:78:14:78:15 | ref arg v2 | stringstream.cpp:85:7:85:8 | v2 | | +| stringstream.cpp:82:7:82:9 | ss1 | stringstream.cpp:82:11:82:13 | call to str | TAINT | +| stringstream.cpp:83:7:83:9 | ss2 | stringstream.cpp:83:11:83:13 | call to str | TAINT | +| stringstream.cpp:90:18:90:23 | call to basic_string | stringstream.cpp:92:24:92:25 | s1 | | +| stringstream.cpp:90:19:90:23 | abc | stringstream.cpp:90:18:90:23 | call to basic_string | TAINT | +| stringstream.cpp:91:18:91:26 | call to basic_string | stringstream.cpp:93:24:93:25 | s2 | | +| stringstream.cpp:91:19:91:24 | call to source | stringstream.cpp:91:18:91:26 | call to basic_string | TAINT | +| stringstream.cpp:92:24:92:25 | s1 | stringstream.cpp:92:24:92:26 | call to basic_stringstream | TAINT | +| stringstream.cpp:92:24:92:26 | call to basic_stringstream | stringstream.cpp:102:7:102:9 | ss1 | | +| stringstream.cpp:93:24:93:25 | s2 | stringstream.cpp:93:24:93:26 | call to basic_stringstream | TAINT | +| stringstream.cpp:93:24:93:26 | call to basic_stringstream | stringstream.cpp:103:7:103:9 | ss2 | | +| stringstream.cpp:94:25:94:49 | call to basic_stringstream | stringstream.cpp:104:7:104:9 | ss3 | | +| stringstream.cpp:94:44:94:48 | abc | stringstream.cpp:94:44:94:48 | call to basic_string | TAINT | +| stringstream.cpp:94:44:94:48 | call to basic_string | stringstream.cpp:94:25:94:49 | call to basic_stringstream | TAINT | +| stringstream.cpp:95:25:95:52 | call to basic_stringstream | stringstream.cpp:105:7:105:9 | ss4 | | +| stringstream.cpp:95:44:95:49 | call to source | stringstream.cpp:95:44:95:51 | call to basic_string | TAINT | +| stringstream.cpp:95:44:95:51 | call to basic_string | stringstream.cpp:95:25:95:52 | call to basic_stringstream | TAINT | +| stringstream.cpp:96:20:96:22 | call to basic_stringstream | stringstream.cpp:99:7:99:9 | ss5 | | +| stringstream.cpp:96:20:96:22 | call to basic_stringstream | stringstream.cpp:106:7:106:9 | ss5 | | +| stringstream.cpp:97:20:97:22 | call to basic_stringstream | stringstream.cpp:100:7:100:9 | ss6 | | +| stringstream.cpp:97:20:97:22 | call to basic_stringstream | stringstream.cpp:107:7:107:9 | ss6 | | +| stringstream.cpp:99:7:99:9 | ref arg ss5 | stringstream.cpp:106:7:106:9 | ss5 | | +| stringstream.cpp:99:13:99:36 | call to basic_stringstream | stringstream.cpp:99:7:99:9 | ref arg ss5 | TAINT | +| stringstream.cpp:99:13:99:36 | call to basic_stringstream | stringstream.cpp:99:11:99:11 | call to operator= | TAINT | +| stringstream.cpp:99:31:99:35 | abc | stringstream.cpp:99:31:99:35 | call to basic_string | TAINT | +| stringstream.cpp:99:31:99:35 | call to basic_string | stringstream.cpp:99:13:99:36 | call to basic_stringstream | TAINT | +| stringstream.cpp:100:7:100:9 | ref arg ss6 | stringstream.cpp:107:7:107:9 | ss6 | | +| stringstream.cpp:100:13:100:39 | call to basic_stringstream | stringstream.cpp:100:7:100:9 | ref arg ss6 | TAINT | +| stringstream.cpp:100:13:100:39 | call to basic_stringstream | stringstream.cpp:100:11:100:11 | call to operator= | TAINT | +| stringstream.cpp:100:31:100:36 | call to source | stringstream.cpp:100:31:100:38 | call to basic_string | TAINT | +| stringstream.cpp:100:31:100:38 | call to basic_string | stringstream.cpp:100:13:100:39 | call to basic_stringstream | TAINT | +| stringstream.cpp:112:24:112:28 | abc | stringstream.cpp:112:24:112:28 | call to basic_string | TAINT | +| stringstream.cpp:112:24:112:28 | call to basic_string | stringstream.cpp:112:24:112:29 | call to basic_stringstream | TAINT | +| stringstream.cpp:112:24:112:29 | call to basic_stringstream | stringstream.cpp:117:2:117:4 | ss1 | | +| stringstream.cpp:112:24:112:29 | call to basic_stringstream | stringstream.cpp:120:7:120:9 | ss1 | | +| stringstream.cpp:113:24:113:29 | call to source | stringstream.cpp:113:24:113:31 | call to basic_string | TAINT | +| stringstream.cpp:113:24:113:31 | call to basic_string | stringstream.cpp:113:24:113:32 | call to basic_stringstream | TAINT | +| stringstream.cpp:113:24:113:32 | call to basic_stringstream | stringstream.cpp:117:11:117:13 | ss2 | | +| stringstream.cpp:113:24:113:32 | call to basic_stringstream | stringstream.cpp:121:7:121:9 | ss2 | | +| stringstream.cpp:114:24:114:28 | abc | stringstream.cpp:114:24:114:28 | call to basic_string | TAINT | +| stringstream.cpp:114:24:114:28 | call to basic_string | stringstream.cpp:114:24:114:29 | call to basic_stringstream | TAINT | +| stringstream.cpp:114:24:114:29 | call to basic_stringstream | stringstream.cpp:118:11:118:13 | ss3 | | +| stringstream.cpp:114:24:114:29 | call to basic_stringstream | stringstream.cpp:122:7:122:9 | ss3 | | +| stringstream.cpp:115:24:115:29 | call to source | stringstream.cpp:115:24:115:31 | call to basic_string | TAINT | +| stringstream.cpp:115:24:115:31 | call to basic_string | stringstream.cpp:115:24:115:32 | call to basic_stringstream | TAINT | +| stringstream.cpp:115:24:115:32 | call to basic_stringstream | stringstream.cpp:118:2:118:4 | ss4 | | +| stringstream.cpp:115:24:115:32 | call to basic_stringstream | stringstream.cpp:123:7:123:9 | ss4 | | +| stringstream.cpp:117:2:117:4 | ref arg ss1 | stringstream.cpp:120:7:120:9 | ss1 | | +| stringstream.cpp:117:2:117:4 | ss1 | stringstream.cpp:117:11:117:13 | ref arg ss2 | TAINT | +| stringstream.cpp:117:11:117:13 | ref arg ss2 | stringstream.cpp:121:7:121:9 | ss2 | | +| stringstream.cpp:117:11:117:13 | ss2 | stringstream.cpp:117:2:117:4 | ref arg ss1 | TAINT | +| stringstream.cpp:118:2:118:4 | ref arg ss4 | stringstream.cpp:123:7:123:9 | ss4 | | +| stringstream.cpp:118:2:118:4 | ss4 | stringstream.cpp:118:11:118:13 | ref arg ss3 | TAINT | +| stringstream.cpp:118:11:118:13 | ref arg ss3 | stringstream.cpp:122:7:122:9 | ss3 | | +| stringstream.cpp:118:11:118:13 | ss3 | stringstream.cpp:118:2:118:4 | ref arg ss4 | TAINT | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:142:7:142:9 | ss1 | | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:145:7:145:9 | ss1 | | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:153:7:153:9 | ss1 | | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:161:7:161:9 | ss1 | | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:163:7:163:9 | ss1 | | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:165:7:165:9 | ss1 | | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:174:12:174:14 | ss1 | | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:176:12:176:14 | ss1 | | +| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:143:7:143:9 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:146:7:146:9 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:147:7:147:9 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:154:7:154:9 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:155:7:155:9 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:162:7:162:9 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:164:7:164:9 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:166:7:166:9 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:129:14:129:15 | call to basic_string | stringstream.cpp:145:14:145:15 | s1 | | +| stringstream.cpp:129:14:129:15 | call to basic_string | stringstream.cpp:148:7:148:8 | s1 | | +| stringstream.cpp:129:18:129:19 | call to basic_string | stringstream.cpp:146:14:146:15 | s2 | | +| stringstream.cpp:129:18:129:19 | call to basic_string | stringstream.cpp:149:7:149:8 | s2 | | +| stringstream.cpp:129:22:129:23 | call to basic_string | stringstream.cpp:147:14:147:15 | s3 | | +| stringstream.cpp:129:22:129:23 | call to basic_string | stringstream.cpp:150:7:150:8 | s3 | | +| stringstream.cpp:129:26:129:27 | call to basic_string | stringstream.cpp:147:20:147:21 | s4 | | +| stringstream.cpp:129:26:129:27 | call to basic_string | stringstream.cpp:151:7:151:8 | s4 | | +| stringstream.cpp:130:16:130:19 | {...} | stringstream.cpp:153:14:153:15 | b1 | | +| stringstream.cpp:130:16:130:19 | {...} | stringstream.cpp:156:7:156:8 | b1 | | +| stringstream.cpp:130:18:130:18 | 0 | stringstream.cpp:130:16:130:19 | {...} | TAINT | +| stringstream.cpp:131:16:131:19 | {...} | stringstream.cpp:154:14:154:15 | b2 | | +| stringstream.cpp:131:16:131:19 | {...} | stringstream.cpp:157:7:157:8 | b2 | | +| stringstream.cpp:131:18:131:18 | 0 | stringstream.cpp:131:16:131:19 | {...} | TAINT | +| stringstream.cpp:132:16:132:19 | {...} | stringstream.cpp:155:14:155:15 | b3 | | +| stringstream.cpp:132:16:132:19 | {...} | stringstream.cpp:158:7:158:8 | b3 | | +| stringstream.cpp:132:18:132:18 | 0 | stringstream.cpp:132:16:132:19 | {...} | TAINT | +| stringstream.cpp:133:16:133:19 | {...} | stringstream.cpp:155:20:155:21 | b4 | | +| stringstream.cpp:133:16:133:19 | {...} | stringstream.cpp:159:7:159:8 | b4 | | +| stringstream.cpp:133:18:133:18 | 0 | stringstream.cpp:133:16:133:19 | {...} | TAINT | +| stringstream.cpp:134:16:134:19 | {...} | stringstream.cpp:161:16:161:17 | b5 | | +| stringstream.cpp:134:16:134:19 | {...} | stringstream.cpp:167:7:167:8 | b5 | | +| stringstream.cpp:134:18:134:18 | 0 | stringstream.cpp:134:16:134:19 | {...} | TAINT | +| stringstream.cpp:135:16:135:19 | {...} | stringstream.cpp:162:16:162:17 | b6 | | +| stringstream.cpp:135:16:135:19 | {...} | stringstream.cpp:168:7:168:8 | b6 | | +| stringstream.cpp:135:18:135:18 | 0 | stringstream.cpp:135:16:135:19 | {...} | TAINT | +| stringstream.cpp:136:16:136:19 | {...} | stringstream.cpp:163:20:163:21 | b7 | | +| stringstream.cpp:136:16:136:19 | {...} | stringstream.cpp:169:7:169:8 | b7 | | +| stringstream.cpp:136:18:136:18 | 0 | stringstream.cpp:136:16:136:19 | {...} | TAINT | +| stringstream.cpp:137:16:137:19 | {...} | stringstream.cpp:164:20:164:21 | b8 | | +| stringstream.cpp:137:16:137:19 | {...} | stringstream.cpp:170:7:170:8 | b8 | | +| stringstream.cpp:137:18:137:18 | 0 | stringstream.cpp:137:16:137:19 | {...} | TAINT | +| stringstream.cpp:138:16:138:19 | {...} | stringstream.cpp:165:15:165:16 | b9 | | +| stringstream.cpp:138:16:138:19 | {...} | stringstream.cpp:171:7:171:8 | b9 | | +| stringstream.cpp:138:18:138:18 | 0 | stringstream.cpp:138:16:138:19 | {...} | TAINT | +| stringstream.cpp:139:17:139:20 | {...} | stringstream.cpp:166:15:166:17 | b10 | | +| stringstream.cpp:139:17:139:20 | {...} | stringstream.cpp:172:7:172:9 | b10 | | +| stringstream.cpp:139:19:139:19 | 0 | stringstream.cpp:139:17:139:20 | {...} | TAINT | +| stringstream.cpp:140:44:140:44 | 0 | stringstream.cpp:178:15:178:16 | c5 | | +| stringstream.cpp:140:44:140:44 | 0 | stringstream.cpp:184:7:184:8 | c5 | | +| stringstream.cpp:140:52:140:52 | 0 | stringstream.cpp:179:15:179:16 | c6 | | +| stringstream.cpp:140:52:140:52 | 0 | stringstream.cpp:185:7:185:8 | c6 | | +| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:145:7:145:9 | ss1 | | +| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:153:7:153:9 | ss1 | | +| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:161:7:161:9 | ss1 | | +| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:163:7:163:9 | ss1 | | +| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | | +| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | | +| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | | +| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:142:7:142:9 | ss1 | stringstream.cpp:142:11:142:11 | call to operator<< | | +| stringstream.cpp:142:14:142:18 | abc | stringstream.cpp:142:7:142:9 | ref arg ss1 | TAINT | +| stringstream.cpp:142:14:142:18 | abc | stringstream.cpp:142:11:142:11 | call to operator<< | TAINT | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:146:7:146:9 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:147:7:147:9 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:154:7:154:9 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:155:7:155:9 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:143:7:143:9 | ss2 | stringstream.cpp:143:11:143:11 | call to operator<< | | +| stringstream.cpp:143:14:143:19 | call to source | stringstream.cpp:143:7:143:9 | ref arg ss2 | TAINT | +| stringstream.cpp:143:14:143:19 | call to source | stringstream.cpp:143:11:143:11 | call to operator<< | TAINT | +| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:153:7:153:9 | ss1 | | +| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:161:7:161:9 | ss1 | | +| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:163:7:163:9 | ss1 | | +| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | | +| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | | +| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | | +| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:145:7:145:9 | ss1 | stringstream.cpp:145:11:145:11 | call to operator>> | | +| stringstream.cpp:145:7:145:9 | ss1 | stringstream.cpp:145:14:145:15 | ref arg s1 | TAINT | +| stringstream.cpp:145:14:145:15 | ref arg s1 | stringstream.cpp:148:7:148:8 | s1 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:147:7:147:9 | ss2 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:154:7:154:9 | ss2 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:155:7:155:9 | ss2 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:146:7:146:9 | ss2 | stringstream.cpp:146:11:146:11 | call to operator>> | | +| stringstream.cpp:146:7:146:9 | ss2 | stringstream.cpp:146:14:146:15 | ref arg s2 | TAINT | +| stringstream.cpp:146:14:146:15 | ref arg s2 | stringstream.cpp:149:7:149:8 | s2 | | +| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:154:7:154:9 | ss2 | | +| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:155:7:155:9 | ss2 | | +| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | | +| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | | +| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | | +| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:11:147:11 | call to operator>> | | +| stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:14:147:15 | ref arg s3 | TAINT | +| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | | +| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:20:147:21 | ref arg s4 | TAINT | +| stringstream.cpp:147:11:147:11 | ref arg call to operator>> | stringstream.cpp:147:7:147:9 | ref arg ss2 | TAINT | +| stringstream.cpp:147:14:147:15 | ref arg s3 | stringstream.cpp:150:7:150:8 | s3 | | +| stringstream.cpp:147:20:147:21 | ref arg s4 | stringstream.cpp:151:7:151:8 | s4 | | +| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:161:7:161:9 | ss1 | | +| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:163:7:163:9 | ss1 | | +| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | | +| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | | +| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | | +| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:153:7:153:9 | ss1 | stringstream.cpp:153:11:153:11 | call to operator>> | | +| stringstream.cpp:153:7:153:9 | ss1 | stringstream.cpp:153:14:153:15 | ref arg b1 | TAINT | +| stringstream.cpp:153:14:153:15 | ref arg b1 | stringstream.cpp:156:7:156:8 | b1 | | +| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:155:7:155:9 | ss2 | | +| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | | +| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | | +| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | | +| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:154:7:154:9 | ss2 | stringstream.cpp:154:11:154:11 | call to operator>> | | +| stringstream.cpp:154:7:154:9 | ss2 | stringstream.cpp:154:14:154:15 | ref arg b2 | TAINT | +| stringstream.cpp:154:14:154:15 | ref arg b2 | stringstream.cpp:157:7:157:8 | b2 | | +| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | | +| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | | +| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | | +| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:11:155:11 | call to operator>> | | +| stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:14:155:15 | ref arg b3 | TAINT | +| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | | +| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:20:155:21 | ref arg b4 | TAINT | +| stringstream.cpp:155:11:155:11 | ref arg call to operator>> | stringstream.cpp:155:7:155:9 | ref arg ss2 | TAINT | +| stringstream.cpp:155:14:155:15 | ref arg b3 | stringstream.cpp:158:7:158:8 | b3 | | +| stringstream.cpp:155:20:155:21 | ref arg b4 | stringstream.cpp:159:7:159:8 | b4 | | +| stringstream.cpp:156:7:156:8 | b1 | stringstream.cpp:156:7:156:8 | call to basic_string | TAINT | +| stringstream.cpp:157:7:157:8 | b2 | stringstream.cpp:157:7:157:8 | call to basic_string | TAINT | +| stringstream.cpp:158:7:158:8 | b3 | stringstream.cpp:158:7:158:8 | call to basic_string | TAINT | +| stringstream.cpp:159:7:159:8 | b4 | stringstream.cpp:159:7:159:8 | call to basic_string | TAINT | +| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:163:7:163:9 | ss1 | | +| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | | +| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | | +| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | | +| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:161:7:161:9 | ss1 | stringstream.cpp:161:11:161:14 | call to read | | +| stringstream.cpp:161:7:161:9 | ss1 | stringstream.cpp:161:16:161:17 | ref arg b5 | TAINT | +| stringstream.cpp:161:16:161:17 | ref arg b5 | stringstream.cpp:167:7:167:8 | b5 | | +| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | | +| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | | +| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:162:7:162:9 | ss2 | stringstream.cpp:162:11:162:14 | call to read | | +| stringstream.cpp:162:7:162:9 | ss2 | stringstream.cpp:162:16:162:17 | ref arg b6 | TAINT | +| stringstream.cpp:162:16:162:17 | ref arg b6 | stringstream.cpp:168:7:168:8 | b6 | | +| stringstream.cpp:163:7:163:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | | +| stringstream.cpp:163:7:163:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | | +| stringstream.cpp:163:7:163:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | | +| stringstream.cpp:163:7:163:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:163:7:163:9 | ss1 | stringstream.cpp:163:20:163:21 | ref arg b7 | TAINT | +| stringstream.cpp:163:20:163:21 | ref arg b7 | stringstream.cpp:169:7:169:8 | b7 | | +| stringstream.cpp:164:7:164:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | | +| stringstream.cpp:164:7:164:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:164:7:164:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:164:7:164:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:164:7:164:9 | ss2 | stringstream.cpp:164:20:164:21 | ref arg b8 | TAINT | +| stringstream.cpp:164:20:164:21 | ref arg b8 | stringstream.cpp:170:7:170:8 | b8 | | +| stringstream.cpp:165:7:165:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | | +| stringstream.cpp:165:7:165:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | | +| stringstream.cpp:165:7:165:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:165:7:165:9 | ss1 | stringstream.cpp:165:11:165:13 | call to get | | +| stringstream.cpp:165:7:165:9 | ss1 | stringstream.cpp:165:15:165:16 | ref arg b9 | TAINT | +| stringstream.cpp:165:15:165:16 | ref arg b9 | stringstream.cpp:171:7:171:8 | b9 | | +| stringstream.cpp:166:7:166:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | | +| stringstream.cpp:166:7:166:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:166:7:166:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:166:7:166:9 | ss2 | stringstream.cpp:166:11:166:13 | call to get | | +| stringstream.cpp:166:7:166:9 | ss2 | stringstream.cpp:166:15:166:17 | ref arg b10 | TAINT | +| stringstream.cpp:166:15:166:17 | ref arg b10 | stringstream.cpp:172:7:172:9 | b10 | | +| stringstream.cpp:167:7:167:8 | b5 | stringstream.cpp:167:7:167:8 | call to basic_string | TAINT | +| stringstream.cpp:168:7:168:8 | b6 | stringstream.cpp:168:7:168:8 | call to basic_string | TAINT | +| stringstream.cpp:169:7:169:8 | b7 | stringstream.cpp:169:7:169:8 | call to basic_string | TAINT | +| stringstream.cpp:170:7:170:8 | b8 | stringstream.cpp:170:7:170:8 | call to basic_string | TAINT | +| stringstream.cpp:171:7:171:8 | b9 | stringstream.cpp:171:7:171:8 | call to basic_string | TAINT | +| stringstream.cpp:172:7:172:9 | b10 | stringstream.cpp:172:7:172:9 | call to basic_string | TAINT | +| stringstream.cpp:174:7:174:8 | c1 | stringstream.cpp:174:7:174:20 | ... = ... | | +| stringstream.cpp:174:12:174:14 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | | +| stringstream.cpp:174:12:174:14 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:174:12:174:14 | ss1 | stringstream.cpp:174:16:174:18 | call to get | TAINT | +| stringstream.cpp:174:16:174:18 | call to get | stringstream.cpp:174:7:174:20 | ... = ... | | +| stringstream.cpp:174:16:174:18 | call to get | stringstream.cpp:180:7:180:8 | c1 | | +| stringstream.cpp:175:7:175:8 | c2 | stringstream.cpp:175:7:175:20 | ... = ... | | +| stringstream.cpp:175:12:175:14 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | | +| stringstream.cpp:175:12:175:14 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:175:12:175:14 | ss2 | stringstream.cpp:175:16:175:18 | call to get | TAINT | +| stringstream.cpp:175:16:175:18 | call to get | stringstream.cpp:175:7:175:20 | ... = ... | | +| stringstream.cpp:175:16:175:18 | call to get | stringstream.cpp:181:7:181:8 | c2 | | +| stringstream.cpp:176:7:176:8 | c3 | stringstream.cpp:176:7:176:21 | ... = ... | | +| stringstream.cpp:176:12:176:14 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | | +| stringstream.cpp:176:12:176:14 | ss1 | stringstream.cpp:176:16:176:19 | call to peek | TAINT | +| stringstream.cpp:176:16:176:19 | call to peek | stringstream.cpp:176:7:176:21 | ... = ... | | +| stringstream.cpp:176:16:176:19 | call to peek | stringstream.cpp:182:7:182:8 | c3 | | +| stringstream.cpp:177:7:177:8 | c4 | stringstream.cpp:177:7:177:21 | ... = ... | | +| stringstream.cpp:177:12:177:14 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | +| stringstream.cpp:177:12:177:14 | ss2 | stringstream.cpp:177:16:177:19 | call to peek | TAINT | +| stringstream.cpp:177:16:177:19 | call to peek | stringstream.cpp:177:7:177:21 | ... = ... | | +| stringstream.cpp:177:16:177:19 | call to peek | stringstream.cpp:183:7:183:8 | c4 | | +| stringstream.cpp:178:7:178:9 | ss1 | stringstream.cpp:178:11:178:13 | call to get | | +| stringstream.cpp:178:7:178:9 | ss1 | stringstream.cpp:178:15:178:16 | ref arg c5 | TAINT | +| stringstream.cpp:178:15:178:16 | ref arg c5 | stringstream.cpp:184:7:184:8 | c5 | | +| stringstream.cpp:179:7:179:9 | ss2 | stringstream.cpp:179:11:179:13 | call to get | | +| stringstream.cpp:179:7:179:9 | ss2 | stringstream.cpp:179:15:179:16 | ref arg c6 | TAINT | +| stringstream.cpp:179:15:179:16 | ref arg c6 | stringstream.cpp:185:7:185:8 | c6 | | +| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:192:7:192:8 | ss | | +| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:193:7:193:8 | ss | | +| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:194:7:194:8 | ss | | +| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:195:7:195:8 | ss | | +| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:196:7:196:8 | ss | | +| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:197:7:197:8 | ss | | +| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:193:7:193:8 | ss | | +| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:194:7:194:8 | ss | | +| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:195:7:195:8 | ss | | +| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:196:7:196:8 | ss | | +| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | | +| stringstream.cpp:192:7:192:8 | ss | stringstream.cpp:192:10:192:12 | call to put | | +| stringstream.cpp:192:14:192:16 | 97 | stringstream.cpp:192:7:192:8 | ref arg ss | TAINT | +| stringstream.cpp:192:14:192:16 | 97 | stringstream.cpp:192:10:192:12 | call to put | TAINT | +| stringstream.cpp:193:7:193:8 | ref arg ss | stringstream.cpp:194:7:194:8 | ss | | +| stringstream.cpp:193:7:193:8 | ref arg ss | stringstream.cpp:195:7:195:8 | ss | | +| stringstream.cpp:193:7:193:8 | ref arg ss | stringstream.cpp:196:7:196:8 | ss | | +| stringstream.cpp:193:7:193:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | | +| stringstream.cpp:193:7:193:8 | ss | stringstream.cpp:193:10:193:12 | call to get | TAINT | +| stringstream.cpp:194:7:194:8 | ref arg ss | stringstream.cpp:195:7:195:8 | ss | | +| stringstream.cpp:194:7:194:8 | ref arg ss | stringstream.cpp:196:7:196:8 | ss | | +| stringstream.cpp:194:7:194:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | | +| stringstream.cpp:194:7:194:8 | ss | stringstream.cpp:194:10:194:16 | call to putback | | +| stringstream.cpp:194:18:194:20 | 98 | stringstream.cpp:194:7:194:8 | ref arg ss | TAINT | +| stringstream.cpp:194:18:194:20 | 98 | stringstream.cpp:194:10:194:16 | call to putback | TAINT | +| stringstream.cpp:195:7:195:8 | ref arg ss | stringstream.cpp:196:7:196:8 | ss | | +| stringstream.cpp:195:7:195:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | | +| stringstream.cpp:195:7:195:8 | ss | stringstream.cpp:195:10:195:12 | call to get | TAINT | +| stringstream.cpp:196:7:196:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | | +| stringstream.cpp:196:7:196:8 | ss | stringstream.cpp:196:10:196:16 | call to putback | | +| stringstream.cpp:196:18:196:32 | call to source | stringstream.cpp:196:7:196:8 | ref arg ss | TAINT | +| stringstream.cpp:196:18:196:32 | call to source | stringstream.cpp:196:10:196:16 | call to putback | TAINT | +| stringstream.cpp:197:7:197:8 | ss | stringstream.cpp:197:10:197:12 | call to get | TAINT | +| stringstream.cpp:202:24:202:28 | abc | stringstream.cpp:202:24:202:28 | call to basic_string | TAINT | +| stringstream.cpp:202:24:202:28 | call to basic_string | stringstream.cpp:202:24:202:29 | call to basic_stringstream | TAINT | +| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:214:7:214:9 | ss1 | | +| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:217:7:217:9 | ss1 | | +| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:222:7:222:9 | ss1 | | +| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:225:7:225:9 | ss1 | | +| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:234:15:234:17 | ss1 | | +| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:237:15:237:17 | ss1 | | +| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:242:15:242:17 | ss1 | | +| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:245:15:245:17 | ss1 | | +| stringstream.cpp:203:24:203:29 | call to source | stringstream.cpp:203:24:203:31 | call to basic_string | TAINT | +| stringstream.cpp:203:24:203:31 | call to basic_string | stringstream.cpp:203:24:203:32 | call to basic_stringstream | TAINT | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:215:7:215:9 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:216:7:216:9 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:223:7:223:9 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:224:7:224:9 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:230:7:230:9 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:235:15:235:17 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:236:15:236:17 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:243:15:243:17 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:204:17:204:20 | {...} | stringstream.cpp:214:19:214:20 | b1 | | +| stringstream.cpp:204:17:204:20 | {...} | stringstream.cpp:218:7:218:8 | b1 | | +| stringstream.cpp:204:19:204:19 | 0 | stringstream.cpp:204:17:204:20 | {...} | TAINT | +| stringstream.cpp:205:17:205:20 | {...} | stringstream.cpp:215:19:215:20 | b2 | | +| stringstream.cpp:205:17:205:20 | {...} | stringstream.cpp:219:7:219:8 | b2 | | +| stringstream.cpp:205:19:205:19 | 0 | stringstream.cpp:205:17:205:20 | {...} | TAINT | +| stringstream.cpp:206:17:206:20 | {...} | stringstream.cpp:216:19:216:20 | b3 | | +| stringstream.cpp:206:17:206:20 | {...} | stringstream.cpp:217:19:217:20 | b3 | | +| stringstream.cpp:206:17:206:20 | {...} | stringstream.cpp:220:7:220:8 | b3 | | +| stringstream.cpp:206:19:206:19 | 0 | stringstream.cpp:206:17:206:20 | {...} | TAINT | +| stringstream.cpp:207:17:207:20 | {...} | stringstream.cpp:222:19:222:20 | b4 | | +| stringstream.cpp:207:17:207:20 | {...} | stringstream.cpp:226:7:226:8 | b4 | | +| stringstream.cpp:207:19:207:19 | 0 | stringstream.cpp:207:17:207:20 | {...} | TAINT | +| stringstream.cpp:208:17:208:20 | {...} | stringstream.cpp:223:19:223:20 | b5 | | +| stringstream.cpp:208:17:208:20 | {...} | stringstream.cpp:227:7:227:8 | b5 | | +| stringstream.cpp:208:19:208:19 | 0 | stringstream.cpp:208:17:208:20 | {...} | TAINT | +| stringstream.cpp:209:17:209:20 | {...} | stringstream.cpp:224:19:224:20 | b6 | | +| stringstream.cpp:209:17:209:20 | {...} | stringstream.cpp:225:19:225:20 | b6 | | +| stringstream.cpp:209:17:209:20 | {...} | stringstream.cpp:228:7:228:8 | b6 | | +| stringstream.cpp:209:19:209:19 | 0 | stringstream.cpp:209:17:209:20 | {...} | TAINT | +| stringstream.cpp:210:17:210:20 | {...} | stringstream.cpp:230:19:230:20 | b7 | | +| stringstream.cpp:210:17:210:20 | {...} | stringstream.cpp:231:7:231:8 | b7 | | +| stringstream.cpp:210:19:210:19 | 0 | stringstream.cpp:210:17:210:20 | {...} | TAINT | +| stringstream.cpp:211:17:211:20 | {...} | stringstream.cpp:230:37:230:38 | b8 | | +| stringstream.cpp:211:17:211:20 | {...} | stringstream.cpp:232:7:232:8 | b8 | | +| stringstream.cpp:211:19:211:19 | 0 | stringstream.cpp:211:17:211:20 | {...} | TAINT | +| stringstream.cpp:212:14:212:15 | call to basic_string | stringstream.cpp:234:20:234:21 | s1 | | +| stringstream.cpp:212:14:212:15 | call to basic_string | stringstream.cpp:238:7:238:8 | s1 | | +| stringstream.cpp:212:18:212:19 | call to basic_string | stringstream.cpp:235:20:235:21 | s2 | | +| stringstream.cpp:212:18:212:19 | call to basic_string | stringstream.cpp:239:7:239:8 | s2 | | +| stringstream.cpp:212:22:212:23 | call to basic_string | stringstream.cpp:236:20:236:21 | s3 | | +| stringstream.cpp:212:22:212:23 | call to basic_string | stringstream.cpp:237:20:237:21 | s3 | | +| stringstream.cpp:212:22:212:23 | call to basic_string | stringstream.cpp:240:7:240:8 | s3 | | +| stringstream.cpp:212:26:212:27 | call to basic_string | stringstream.cpp:242:20:242:21 | s4 | | +| stringstream.cpp:212:26:212:27 | call to basic_string | stringstream.cpp:246:7:246:8 | s4 | | +| stringstream.cpp:212:30:212:31 | call to basic_string | stringstream.cpp:243:20:243:21 | s5 | | +| stringstream.cpp:212:30:212:31 | call to basic_string | stringstream.cpp:247:7:247:8 | s5 | | +| stringstream.cpp:212:34:212:35 | call to basic_string | stringstream.cpp:244:20:244:21 | s6 | | +| stringstream.cpp:212:34:212:35 | call to basic_string | stringstream.cpp:245:20:245:21 | s6 | | +| stringstream.cpp:212:34:212:35 | call to basic_string | stringstream.cpp:248:7:248:8 | s6 | | +| stringstream.cpp:212:38:212:39 | call to basic_string | stringstream.cpp:250:28:250:29 | s7 | | +| stringstream.cpp:212:38:212:39 | call to basic_string | stringstream.cpp:251:7:251:8 | s7 | | +| stringstream.cpp:212:42:212:43 | call to basic_string | stringstream.cpp:250:33:250:34 | s8 | | +| stringstream.cpp:212:42:212:43 | call to basic_string | stringstream.cpp:252:7:252:8 | s8 | | +| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:217:7:217:9 | ss1 | | +| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:222:7:222:9 | ss1 | | +| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:225:7:225:9 | ss1 | | +| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:234:15:234:17 | ss1 | | +| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | | +| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | | +| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | | +| stringstream.cpp:214:7:214:9 | ss1 | stringstream.cpp:214:11:214:17 | call to getline | | +| stringstream.cpp:214:7:214:9 | ss1 | stringstream.cpp:214:19:214:20 | ref arg b1 | TAINT | +| stringstream.cpp:214:19:214:20 | ref arg b1 | stringstream.cpp:218:7:218:8 | b1 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:216:7:216:9 | ss2 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:223:7:223:9 | ss2 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:224:7:224:9 | ss2 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:230:7:230:9 | ss2 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:215:7:215:9 | ss2 | stringstream.cpp:215:11:215:17 | call to getline | | +| stringstream.cpp:215:7:215:9 | ss2 | stringstream.cpp:215:19:215:20 | ref arg b2 | TAINT | +| stringstream.cpp:215:19:215:20 | ref arg b2 | stringstream.cpp:219:7:219:8 | b2 | | +| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:223:7:223:9 | ss2 | | +| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:224:7:224:9 | ss2 | | +| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:230:7:230:9 | ss2 | | +| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | | +| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | | +| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | | +| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:216:7:216:9 | ss2 | stringstream.cpp:216:11:216:17 | call to getline | | +| stringstream.cpp:216:7:216:9 | ss2 | stringstream.cpp:216:19:216:20 | ref arg b3 | TAINT | +| stringstream.cpp:216:19:216:20 | ref arg b3 | stringstream.cpp:217:19:217:20 | b3 | | +| stringstream.cpp:216:19:216:20 | ref arg b3 | stringstream.cpp:220:7:220:8 | b3 | | +| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:222:7:222:9 | ss1 | | +| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:225:7:225:9 | ss1 | | +| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:234:15:234:17 | ss1 | | +| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | | +| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | | +| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | | +| stringstream.cpp:217:7:217:9 | ss1 | stringstream.cpp:217:11:217:17 | call to getline | | +| stringstream.cpp:217:7:217:9 | ss1 | stringstream.cpp:217:19:217:20 | ref arg b3 | TAINT | +| stringstream.cpp:217:19:217:20 | ref arg b3 | stringstream.cpp:220:7:220:8 | b3 | | +| stringstream.cpp:218:7:218:8 | b1 | stringstream.cpp:218:7:218:8 | call to basic_string | TAINT | +| stringstream.cpp:219:7:219:8 | b2 | stringstream.cpp:219:7:219:8 | call to basic_string | TAINT | +| stringstream.cpp:220:7:220:8 | b3 | stringstream.cpp:220:7:220:8 | call to basic_string | TAINT | +| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:225:7:225:9 | ss1 | | +| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:234:15:234:17 | ss1 | | +| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | | +| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | | +| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | | +| stringstream.cpp:222:7:222:9 | ss1 | stringstream.cpp:222:11:222:17 | call to getline | | +| stringstream.cpp:222:7:222:9 | ss1 | stringstream.cpp:222:19:222:20 | ref arg b4 | TAINT | +| stringstream.cpp:222:19:222:20 | ref arg b4 | stringstream.cpp:226:7:226:8 | b4 | | +| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:224:7:224:9 | ss2 | | +| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:230:7:230:9 | ss2 | | +| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | | +| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | | +| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | | +| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:223:7:223:9 | ss2 | stringstream.cpp:223:11:223:17 | call to getline | | +| stringstream.cpp:223:7:223:9 | ss2 | stringstream.cpp:223:19:223:20 | ref arg b5 | TAINT | +| stringstream.cpp:223:19:223:20 | ref arg b5 | stringstream.cpp:227:7:227:8 | b5 | | +| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:230:7:230:9 | ss2 | | +| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | | +| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | | +| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | | +| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:224:7:224:9 | ss2 | stringstream.cpp:224:11:224:17 | call to getline | | +| stringstream.cpp:224:7:224:9 | ss2 | stringstream.cpp:224:19:224:20 | ref arg b6 | TAINT | +| stringstream.cpp:224:19:224:20 | ref arg b6 | stringstream.cpp:225:19:225:20 | b6 | | +| stringstream.cpp:224:19:224:20 | ref arg b6 | stringstream.cpp:228:7:228:8 | b6 | | +| stringstream.cpp:225:7:225:9 | ref arg ss1 | stringstream.cpp:234:15:234:17 | ss1 | | +| stringstream.cpp:225:7:225:9 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | | +| stringstream.cpp:225:7:225:9 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | | +| stringstream.cpp:225:7:225:9 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | | +| stringstream.cpp:225:7:225:9 | ss1 | stringstream.cpp:225:11:225:17 | call to getline | | +| stringstream.cpp:225:7:225:9 | ss1 | stringstream.cpp:225:19:225:20 | ref arg b6 | TAINT | +| stringstream.cpp:225:19:225:20 | ref arg b6 | stringstream.cpp:228:7:228:8 | b6 | | +| stringstream.cpp:226:7:226:8 | b4 | stringstream.cpp:226:7:226:8 | call to basic_string | TAINT | +| stringstream.cpp:227:7:227:8 | b5 | stringstream.cpp:227:7:227:8 | call to basic_string | TAINT | +| stringstream.cpp:228:7:228:8 | b6 | stringstream.cpp:228:7:228:8 | call to basic_string | TAINT | +| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | | +| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | | +| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | | +| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:230:7:230:9 | ss2 | stringstream.cpp:230:11:230:17 | call to getline | | +| stringstream.cpp:230:7:230:9 | ss2 | stringstream.cpp:230:19:230:20 | ref arg b7 | TAINT | +| stringstream.cpp:230:11:230:17 | call to getline | stringstream.cpp:230:29:230:35 | call to getline | | +| stringstream.cpp:230:11:230:17 | call to getline | stringstream.cpp:230:37:230:38 | ref arg b8 | TAINT | +| stringstream.cpp:230:11:230:17 | ref arg call to getline | stringstream.cpp:230:7:230:9 | ref arg ss2 | TAINT | +| stringstream.cpp:230:19:230:20 | ref arg b7 | stringstream.cpp:231:7:231:8 | b7 | | +| stringstream.cpp:230:37:230:38 | ref arg b8 | stringstream.cpp:232:7:232:8 | b8 | | +| stringstream.cpp:231:7:231:8 | b7 | stringstream.cpp:231:7:231:8 | call to basic_string | TAINT | +| stringstream.cpp:232:7:232:8 | b8 | stringstream.cpp:232:7:232:8 | call to basic_string | TAINT | +| stringstream.cpp:234:15:234:17 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | | +| stringstream.cpp:234:15:234:17 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | | +| stringstream.cpp:234:15:234:17 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | | +| stringstream.cpp:234:15:234:17 | ss1 | stringstream.cpp:234:7:234:13 | call to getline | | +| stringstream.cpp:234:15:234:17 | ss1 | stringstream.cpp:234:20:234:21 | ref arg s1 | TAINT | +| stringstream.cpp:234:20:234:21 | ref arg s1 | stringstream.cpp:238:7:238:8 | s1 | | +| stringstream.cpp:235:15:235:17 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | | +| stringstream.cpp:235:15:235:17 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | | +| stringstream.cpp:235:15:235:17 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:235:15:235:17 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:235:15:235:17 | ss2 | stringstream.cpp:235:7:235:13 | call to getline | | +| stringstream.cpp:235:15:235:17 | ss2 | stringstream.cpp:235:20:235:21 | ref arg s2 | TAINT | +| stringstream.cpp:235:20:235:21 | ref arg s2 | stringstream.cpp:239:7:239:8 | s2 | | +| stringstream.cpp:236:15:236:17 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | | +| stringstream.cpp:236:15:236:17 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:236:15:236:17 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:236:15:236:17 | ss2 | stringstream.cpp:236:7:236:13 | call to getline | | +| stringstream.cpp:236:15:236:17 | ss2 | stringstream.cpp:236:20:236:21 | ref arg s3 | TAINT | +| stringstream.cpp:236:20:236:21 | ref arg s3 | stringstream.cpp:237:20:237:21 | s3 | | +| stringstream.cpp:236:20:236:21 | ref arg s3 | stringstream.cpp:240:7:240:8 | s3 | | +| stringstream.cpp:237:15:237:17 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | | +| stringstream.cpp:237:15:237:17 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | | +| stringstream.cpp:237:15:237:17 | ss1 | stringstream.cpp:237:7:237:13 | call to getline | | +| stringstream.cpp:237:15:237:17 | ss1 | stringstream.cpp:237:20:237:21 | ref arg s3 | TAINT | +| stringstream.cpp:237:20:237:21 | ref arg s3 | stringstream.cpp:240:7:240:8 | s3 | | +| stringstream.cpp:242:15:242:17 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | | +| stringstream.cpp:242:15:242:17 | ss1 | stringstream.cpp:242:7:242:13 | call to getline | | +| stringstream.cpp:242:15:242:17 | ss1 | stringstream.cpp:242:20:242:21 | ref arg s4 | TAINT | +| stringstream.cpp:242:20:242:21 | ref arg s4 | stringstream.cpp:246:7:246:8 | s4 | | +| stringstream.cpp:243:15:243:17 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | | +| stringstream.cpp:243:15:243:17 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:243:15:243:17 | ss2 | stringstream.cpp:243:7:243:13 | call to getline | | +| stringstream.cpp:243:15:243:17 | ss2 | stringstream.cpp:243:20:243:21 | ref arg s5 | TAINT | +| stringstream.cpp:243:20:243:21 | ref arg s5 | stringstream.cpp:247:7:247:8 | s5 | | +| stringstream.cpp:244:15:244:17 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | | +| stringstream.cpp:244:15:244:17 | ss2 | stringstream.cpp:244:7:244:13 | call to getline | | +| stringstream.cpp:244:15:244:17 | ss2 | stringstream.cpp:244:20:244:21 | ref arg s6 | TAINT | +| stringstream.cpp:244:20:244:21 | ref arg s6 | stringstream.cpp:245:20:245:21 | s6 | | +| stringstream.cpp:244:20:244:21 | ref arg s6 | stringstream.cpp:248:7:248:8 | s6 | | +| stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:7:245:13 | call to getline | | +| stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:20:245:21 | ref arg s6 | TAINT | +| stringstream.cpp:245:20:245:21 | ref arg s6 | stringstream.cpp:248:7:248:8 | s6 | | +| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | | +| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:33:250:34 | ref arg s8 | TAINT | +| stringstream.cpp:250:15:250:21 | ref arg call to getline | stringstream.cpp:250:23:250:25 | ref arg ss2 | TAINT | +| stringstream.cpp:250:23:250:25 | ss2 | stringstream.cpp:250:15:250:21 | call to getline | | +| stringstream.cpp:250:23:250:25 | ss2 | stringstream.cpp:250:28:250:29 | ref arg s7 | TAINT | +| stringstream.cpp:250:28:250:29 | ref arg s7 | stringstream.cpp:251:7:251:8 | s7 | | +| stringstream.cpp:250:33:250:34 | ref arg s8 | stringstream.cpp:252:7:252:8 | s8 | | +| stringstream.cpp:257:24:257:29 | call to source | stringstream.cpp:257:24:257:31 | call to basic_string | TAINT | +| stringstream.cpp:257:24:257:31 | call to basic_string | stringstream.cpp:257:24:257:32 | call to basic_stringstream | TAINT | +| stringstream.cpp:257:24:257:32 | call to basic_stringstream | stringstream.cpp:262:7:262:9 | ss1 | | +| stringstream.cpp:258:20:258:22 | call to basic_stringstream | stringstream.cpp:266:7:266:9 | ss2 | | +| stringstream.cpp:258:20:258:22 | call to basic_stringstream | stringstream.cpp:267:7:267:9 | ss2 | | +| stringstream.cpp:259:17:259:20 | {...} | stringstream.cpp:262:15:262:16 | b1 | | +| stringstream.cpp:259:17:259:20 | {...} | stringstream.cpp:263:7:263:8 | b1 | | +| stringstream.cpp:259:19:259:19 | 0 | stringstream.cpp:259:17:259:20 | {...} | TAINT | +| stringstream.cpp:260:17:260:20 | {...} | stringstream.cpp:262:36:262:37 | b2 | | +| stringstream.cpp:260:17:260:20 | {...} | stringstream.cpp:264:7:264:8 | b2 | | +| stringstream.cpp:260:19:260:19 | 0 | stringstream.cpp:260:17:260:20 | {...} | TAINT | +| stringstream.cpp:262:7:262:9 | ss1 | stringstream.cpp:262:11:262:13 | call to get | | +| stringstream.cpp:262:7:262:9 | ss1 | stringstream.cpp:262:15:262:16 | ref arg b1 | TAINT | +| stringstream.cpp:262:11:262:13 | call to get | stringstream.cpp:262:24:262:28 | call to unget | | +| stringstream.cpp:262:11:262:13 | ref arg call to get | stringstream.cpp:262:7:262:9 | ref arg ss1 | TAINT | +| stringstream.cpp:262:15:262:16 | ref arg b1 | stringstream.cpp:263:7:263:8 | b1 | | +| stringstream.cpp:262:24:262:28 | call to unget | stringstream.cpp:262:32:262:34 | call to get | | +| stringstream.cpp:262:24:262:28 | call to unget | stringstream.cpp:262:36:262:37 | ref arg b2 | TAINT | +| stringstream.cpp:262:24:262:28 | ref arg call to unget | stringstream.cpp:262:11:262:13 | ref arg call to get | TAINT | +| stringstream.cpp:262:36:262:37 | ref arg b2 | stringstream.cpp:264:7:264:8 | b2 | | +| stringstream.cpp:263:7:263:8 | b1 | stringstream.cpp:263:7:263:8 | call to basic_string | TAINT | +| stringstream.cpp:264:7:264:8 | b2 | stringstream.cpp:264:7:264:8 | call to basic_string | TAINT | +| stringstream.cpp:266:7:266:9 | ref arg ss2 | stringstream.cpp:267:7:267:9 | ss2 | | +| stringstream.cpp:266:7:266:9 | ss2 | stringstream.cpp:266:11:266:15 | call to write | | +| stringstream.cpp:266:11:266:15 | call to write | stringstream.cpp:266:27:266:31 | call to flush | | +| stringstream.cpp:266:11:266:15 | ref arg call to write | stringstream.cpp:266:7:266:9 | ref arg ss2 | TAINT | +| stringstream.cpp:266:17:266:21 | abc | stringstream.cpp:266:7:266:9 | ref arg ss2 | TAINT | +| stringstream.cpp:266:17:266:21 | abc | stringstream.cpp:266:11:266:15 | call to write | TAINT | +| stringstream.cpp:266:27:266:31 | call to flush | stringstream.cpp:266:35:266:39 | call to write | | +| stringstream.cpp:266:27:266:31 | ref arg call to flush | stringstream.cpp:266:11:266:15 | ref arg call to write | TAINT | +| stringstream.cpp:266:35:266:39 | call to write | stringstream.cpp:266:54:266:58 | call to flush | | +| stringstream.cpp:266:35:266:39 | ref arg call to write | stringstream.cpp:266:27:266:31 | ref arg call to flush | TAINT | +| stringstream.cpp:266:41:266:46 | call to source | stringstream.cpp:266:27:266:31 | ref arg call to flush | TAINT | +| stringstream.cpp:266:41:266:46 | call to source | stringstream.cpp:266:35:266:39 | call to write | TAINT | +| stringstream.cpp:266:54:266:58 | call to flush | stringstream.cpp:266:62:266:66 | call to write | | +| stringstream.cpp:266:54:266:58 | ref arg call to flush | stringstream.cpp:266:35:266:39 | ref arg call to write | TAINT | +| stringstream.cpp:266:68:266:72 | xyz | stringstream.cpp:266:54:266:58 | ref arg call to flush | TAINT | +| stringstream.cpp:266:68:266:72 | xyz | stringstream.cpp:266:62:266:66 | call to write | TAINT | +| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | +| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT | +| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | +| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | | +| structlikeclass.cpp:8:2:8:16 | this | structlikeclass.cpp:8:28:8:32 | constructor init of field v [pre-this] | | +| structlikeclass.cpp:8:22:8:23 | _v | structlikeclass.cpp:8:30:8:31 | _v | | +| structlikeclass.cpp:8:30:8:31 | _v | structlikeclass.cpp:8:28:8:32 | constructor init of field v | TAINT | +| structlikeclass.cpp:16:22:16:22 | 1 | structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | structlikeclass.cpp:18:22:18:23 | s1 | | +| structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | structlikeclass.cpp:22:8:22:9 | s1 | | +| structlikeclass.cpp:17:23:17:24 | call to StructLikeClass | structlikeclass.cpp:23:8:23:9 | s2 | | +| structlikeclass.cpp:17:24:17:24 | 1 | structlikeclass.cpp:17:23:17:24 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:18:22:18:23 | s1 | structlikeclass.cpp:24:8:24:9 | s3 | | +| structlikeclass.cpp:20:8:20:8 | 1 | structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | structlikeclass.cpp:20:3:20:8 | ... = ... | | +| structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | structlikeclass.cpp:25:8:25:9 | s4 | | +| structlikeclass.cpp:29:22:29:27 | call to source | structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | structlikeclass.cpp:31:22:31:23 | s1 | | +| structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | structlikeclass.cpp:35:8:35:9 | s1 | | +| structlikeclass.cpp:30:23:30:31 | call to StructLikeClass | structlikeclass.cpp:36:8:36:9 | s2 | | +| structlikeclass.cpp:30:24:30:29 | call to source | structlikeclass.cpp:30:23:30:31 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:31:22:31:23 | s1 | structlikeclass.cpp:37:8:37:9 | s3 | | +| structlikeclass.cpp:33:8:33:13 | call to source | structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | structlikeclass.cpp:33:3:33:15 | ... = ... | | +| structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | structlikeclass.cpp:38:8:38:9 | s4 | | +| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:43:24:43:25 | s1 | | +| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:44:22:44:23 | s1 | | +| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:46:8:46:9 | s1 | | +| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:48:8:48:9 | s1 | | +| structlikeclass.cpp:43:24:43:25 | s1 | structlikeclass.cpp:49:8:49:9 | s2 | | +| structlikeclass.cpp:44:22:44:23 | s1 | structlikeclass.cpp:50:8:50:9 | s3 | | +| structlikeclass.cpp:46:8:46:9 | s1 | structlikeclass.cpp:46:3:46:9 | ... = ... | | +| structlikeclass.cpp:46:8:46:9 | s1 | structlikeclass.cpp:51:8:51:9 | s4 | | +| structlikeclass.cpp:55:23:55:48 | call to StructLikeClass | structlikeclass.cpp:60:8:60:9 | s1 | | +| structlikeclass.cpp:55:40:55:45 | call to source | structlikeclass.cpp:55:23:55:48 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | structlikeclass.cpp:58:3:58:32 | ... = ... | | +| structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | structlikeclass.cpp:61:8:61:9 | s2 | | +| structlikeclass.cpp:58:24:58:29 | call to source | structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:62:8:62:9 | s3 | structlikeclass.cpp:62:8:62:20 | ... = ... | | +| structlikeclass.cpp:62:13:62:18 | call to source | structlikeclass.cpp:62:13:62:20 | call to StructLikeClass | TAINT | +| structlikeclass.cpp:62:13:62:20 | call to StructLikeClass | structlikeclass.cpp:62:8:62:20 | ... = ... | | +| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | +| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | | +| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | | +| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | | +| swap1.cpp:24:9:24:13 | this | swap1.cpp:24:31:24:34 | this | | +| swap1.cpp:24:23:24:26 | that | swap1.cpp:24:23:24:26 | that | | +| swap1.cpp:24:23:24:26 | that | swap1.cpp:24:36:24:39 | that | | +| swap1.cpp:24:36:24:39 | ref arg that | swap1.cpp:24:23:24:26 | that | | +| swap1.cpp:25:9:25:13 | this | swap1.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | | +| swap1.cpp:25:28:25:31 | that | swap1.cpp:25:42:25:45 | that | | +| swap1.cpp:25:47:25:51 | data1 | swap1.cpp:25:36:25:52 | constructor init of field data1 | TAINT | +| swap1.cpp:25:47:25:51 | data1 | swap1.cpp:25:47:25:51 | data1 | | +| swap1.cpp:27:16:27:24 | this | swap1.cpp:30:13:30:16 | this | | +| swap1.cpp:27:39:27:42 | that | swap1.cpp:29:24:29:27 | that | | +| swap1.cpp:29:23:29:27 | call to Class | swap1.cpp:30:18:30:20 | tmp | | +| swap1.cpp:29:24:29:27 | that | swap1.cpp:29:23:29:27 | call to Class | | +| swap1.cpp:30:13:30:16 | ref arg this | swap1.cpp:31:21:31:24 | this | | +| swap1.cpp:30:13:30:16 | this | swap1.cpp:31:21:31:24 | this | | +| swap1.cpp:31:21:31:24 | this | swap1.cpp:31:20:31:24 | * ... | TAINT | +| swap1.cpp:34:16:34:24 | this | swap1.cpp:36:13:36:16 | this | | +| swap1.cpp:34:34:34:37 | that | swap1.cpp:34:34:34:37 | that | | +| swap1.cpp:34:34:34:37 | that | swap1.cpp:36:18:36:21 | that | | +| swap1.cpp:36:13:36:16 | ref arg this | swap1.cpp:37:21:37:24 | this | | +| swap1.cpp:36:13:36:16 | this | swap1.cpp:37:21:37:24 | this | | +| swap1.cpp:36:18:36:21 | ref arg that | swap1.cpp:34:34:34:37 | that | | +| swap1.cpp:37:21:37:24 | this | swap1.cpp:37:20:37:24 | * ... | TAINT | +| swap1.cpp:40:16:40:26 | this | swap1.cpp:43:13:43:16 | this | | +| swap1.cpp:40:41:40:44 | that | swap1.cpp:42:24:42:27 | that | | +| swap1.cpp:42:23:42:27 | call to Class | swap1.cpp:43:18:43:20 | tmp | | +| swap1.cpp:42:24:42:27 | that | swap1.cpp:42:23:42:27 | call to Class | | +| swap1.cpp:43:13:43:16 | ref arg this | swap1.cpp:44:21:44:24 | this | | +| swap1.cpp:43:13:43:16 | this | swap1.cpp:44:21:44:24 | this | | +| swap1.cpp:44:21:44:24 | this | swap1.cpp:44:20:44:24 | * ... | TAINT | +| swap1.cpp:47:16:47:26 | this | swap1.cpp:49:13:49:16 | this | | +| swap1.cpp:47:36:47:39 | that | swap1.cpp:47:36:47:39 | that | | +| swap1.cpp:47:36:47:39 | that | swap1.cpp:49:18:49:21 | that | | +| swap1.cpp:49:13:49:16 | ref arg this | swap1.cpp:50:21:50:24 | this | | +| swap1.cpp:49:13:49:16 | this | swap1.cpp:50:21:50:24 | this | | +| swap1.cpp:49:18:49:21 | ref arg that | swap1.cpp:47:36:47:39 | that | | +| swap1.cpp:50:21:50:24 | this | swap1.cpp:50:20:50:24 | * ... | TAINT | +| swap1.cpp:53:14:53:17 | this | swap1.cpp:56:18:56:22 | this | | +| swap1.cpp:53:26:53:29 | that | swap1.cpp:53:26:53:29 | that | | +| swap1.cpp:53:26:53:29 | that | swap1.cpp:56:25:56:28 | that | | +| swap1.cpp:56:18:56:22 | data1 | swap1.cpp:56:30:56:34 | ref arg data1 | | +| swap1.cpp:56:25:56:28 | that | swap1.cpp:56:18:56:22 | ref arg data1 | | +| swap1.cpp:56:25:56:28 | that [post update] | swap1.cpp:53:26:53:29 | that | | +| swap1.cpp:56:30:56:34 | data1 | swap1.cpp:56:18:56:22 | ref arg data1 | | +| swap1.cpp:61:22:61:22 | x | swap1.cpp:61:22:61:22 | x | | +| swap1.cpp:61:22:61:22 | x | swap1.cpp:63:9:63:9 | x | | +| swap1.cpp:61:22:61:22 | x | swap2.cpp:61:22:61:22 | x | | +| swap1.cpp:61:22:61:22 | x | swap2.cpp:63:9:63:9 | x | | +| swap1.cpp:61:32:61:32 | y | swap1.cpp:61:32:61:32 | y | | +| swap1.cpp:61:32:61:32 | y | swap1.cpp:63:16:63:16 | y | | +| swap1.cpp:61:32:61:32 | y | swap2.cpp:61:32:61:32 | y | | +| swap1.cpp:61:32:61:32 | y | swap2.cpp:63:16:63:16 | y | | +| swap1.cpp:63:9:63:9 | ref arg x | swap1.cpp:61:22:61:22 | x | | +| swap1.cpp:63:9:63:9 | ref arg x | swap2.cpp:61:22:61:22 | x | | +| swap1.cpp:63:16:63:16 | ref arg y | swap1.cpp:61:32:61:32 | y | | +| swap1.cpp:63:16:63:16 | ref arg y | swap2.cpp:61:32:61:32 | y | | +| swap1.cpp:69:23:69:23 | x | swap1.cpp:71:5:71:5 | x | | +| swap1.cpp:69:23:69:23 | x | swap1.cpp:73:10:73:10 | x | | +| swap1.cpp:69:23:69:23 | x | swap1.cpp:76:9:76:9 | x | | +| swap1.cpp:69:23:69:23 | x | swap1.cpp:79:10:79:10 | x | | +| swap1.cpp:70:23:70:23 | y | swap1.cpp:74:10:74:10 | y | | +| swap1.cpp:70:23:70:23 | y | swap1.cpp:76:5:76:5 | y | | +| swap1.cpp:70:23:70:23 | y | swap1.cpp:78:10:78:10 | y | | +| swap1.cpp:71:5:71:5 | x [post update] | swap1.cpp:73:10:73:10 | x | | +| swap1.cpp:71:5:71:5 | x [post update] | swap1.cpp:76:9:76:9 | x | | +| swap1.cpp:71:5:71:5 | x [post update] | swap1.cpp:79:10:79:10 | x | | +| swap1.cpp:71:5:71:22 | ... = ... | swap1.cpp:71:7:71:11 | data1 [post update] | | +| swap1.cpp:71:5:71:22 | ... = ... | swap1.cpp:73:12:73:16 | data1 | | +| swap1.cpp:71:5:71:22 | ... = ... | swap1.cpp:79:12:79:16 | data1 | | +| swap1.cpp:71:15:71:20 | call to source | swap1.cpp:71:5:71:22 | ... = ... | | +| swap1.cpp:76:5:76:5 | ref arg y | swap1.cpp:78:10:78:10 | y | | +| swap1.cpp:76:9:76:9 | x | swap1.cpp:76:5:76:5 | ref arg y | TAINT | +| swap1.cpp:76:9:76:9 | x | swap1.cpp:76:7:76:7 | call to operator= | TAINT | +| swap1.cpp:81:23:81:24 | z1 | swap1.cpp:82:5:82:6 | z1 | | +| swap1.cpp:81:23:81:24 | z1 | swap1.cpp:83:10:83:11 | z1 | | +| swap1.cpp:81:23:81:24 | z1 | swap1.cpp:85:10:85:11 | z1 | | +| swap1.cpp:81:23:81:24 | z1 | swap1.cpp:88:10:88:11 | z1 | | +| swap1.cpp:81:27:81:28 | z2 | swap1.cpp:85:14:85:15 | z2 | | +| swap1.cpp:81:27:81:28 | z2 | swap1.cpp:87:10:87:11 | z2 | | +| swap1.cpp:82:5:82:6 | z1 [post update] | swap1.cpp:83:10:83:11 | z1 | | +| swap1.cpp:82:5:82:6 | z1 [post update] | swap1.cpp:85:10:85:11 | z1 | | +| swap1.cpp:82:5:82:6 | z1 [post update] | swap1.cpp:88:10:88:11 | z1 | | +| swap1.cpp:82:5:82:23 | ... = ... | swap1.cpp:82:8:82:12 | data1 [post update] | | +| swap1.cpp:82:5:82:23 | ... = ... | swap1.cpp:83:13:83:17 | data1 | | +| swap1.cpp:82:5:82:23 | ... = ... | swap1.cpp:88:13:88:17 | data1 | | +| swap1.cpp:82:16:82:21 | call to source | swap1.cpp:82:5:82:23 | ... = ... | | +| swap1.cpp:85:10:85:11 | ref arg z1 | swap1.cpp:88:10:88:11 | z1 | | +| swap1.cpp:85:14:85:15 | ref arg z2 | swap1.cpp:87:10:87:11 | z2 | | +| swap1.cpp:93:23:93:23 | x | swap1.cpp:95:5:95:5 | x | | +| swap1.cpp:93:23:93:23 | x | swap1.cpp:97:10:97:10 | x | | +| swap1.cpp:93:23:93:23 | x | swap1.cpp:100:19:100:19 | x | | +| swap1.cpp:93:23:93:23 | x | swap1.cpp:103:10:103:10 | x | | +| swap1.cpp:94:23:94:23 | y | swap1.cpp:98:10:98:10 | y | | +| swap1.cpp:94:23:94:23 | y | swap1.cpp:100:5:100:5 | y | | +| swap1.cpp:94:23:94:23 | y | swap1.cpp:102:10:102:10 | y | | +| swap1.cpp:95:5:95:5 | x [post update] | swap1.cpp:97:10:97:10 | x | | +| swap1.cpp:95:5:95:5 | x [post update] | swap1.cpp:100:19:100:19 | x | | +| swap1.cpp:95:5:95:5 | x [post update] | swap1.cpp:103:10:103:10 | x | | +| swap1.cpp:95:5:95:22 | ... = ... | swap1.cpp:95:7:95:11 | data1 [post update] | | +| swap1.cpp:95:5:95:22 | ... = ... | swap1.cpp:97:12:97:16 | data1 | | +| swap1.cpp:95:5:95:22 | ... = ... | swap1.cpp:103:12:103:16 | data1 | | +| swap1.cpp:95:15:95:20 | call to source | swap1.cpp:95:5:95:22 | ... = ... | | +| swap1.cpp:100:5:100:5 | ref arg y | swap1.cpp:102:10:102:10 | y | | +| swap1.cpp:100:9:100:17 | call to move | swap1.cpp:100:5:100:5 | ref arg y | TAINT | +| swap1.cpp:100:9:100:17 | call to move | swap1.cpp:100:7:100:7 | call to operator= | TAINT | +| swap1.cpp:100:9:100:17 | ref arg call to move | swap1.cpp:100:19:100:19 | x [inner post update] | | +| swap1.cpp:100:9:100:17 | ref arg call to move | swap1.cpp:103:10:103:10 | x | | +| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:5:100:5 | ref arg y | TAINT | +| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:7:100:7 | call to operator= | TAINT | +| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | | +| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:109:5:109:13 | move_from | | +| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:111:10:111:18 | move_from | | +| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:113:41:113:49 | move_from | | +| swap1.cpp:109:5:109:13 | move_from [post update] | swap1.cpp:111:10:111:18 | move_from | | +| swap1.cpp:109:5:109:13 | move_from [post update] | swap1.cpp:113:41:113:49 | move_from | | +| swap1.cpp:109:5:109:30 | ... = ... | swap1.cpp:109:15:109:19 | data1 [post update] | | +| swap1.cpp:109:5:109:30 | ... = ... | swap1.cpp:111:20:111:24 | data1 | | +| swap1.cpp:109:5:109:30 | ... = ... | swap1.cpp:115:18:115:22 | data1 | | +| swap1.cpp:109:23:109:28 | call to source | swap1.cpp:109:5:109:30 | ... = ... | | +| swap1.cpp:113:31:113:39 | call to move | swap1.cpp:113:31:113:51 | call to Class | | +| swap1.cpp:113:31:113:39 | ref arg call to move | swap1.cpp:113:41:113:49 | move_from [inner post update] | | +| swap1.cpp:113:31:113:51 | call to Class | swap1.cpp:115:10:115:16 | move_to | | +| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | | +| swap1.cpp:120:23:120:23 | x | swap1.cpp:122:5:122:5 | x | | +| swap1.cpp:120:23:120:23 | x | swap1.cpp:124:10:124:10 | x | | +| swap1.cpp:120:23:120:23 | x | swap1.cpp:127:19:127:19 | x | | +| swap1.cpp:120:23:120:23 | x | swap1.cpp:130:10:130:10 | x | | +| swap1.cpp:121:23:121:23 | y | swap1.cpp:125:10:125:10 | y | | +| swap1.cpp:121:23:121:23 | y | swap1.cpp:127:5:127:5 | y | | +| swap1.cpp:121:23:121:23 | y | swap1.cpp:129:10:129:10 | y | | +| swap1.cpp:122:5:122:5 | x [post update] | swap1.cpp:124:10:124:10 | x | | +| swap1.cpp:122:5:122:5 | x [post update] | swap1.cpp:127:19:127:19 | x | | +| swap1.cpp:122:5:122:5 | x [post update] | swap1.cpp:130:10:130:10 | x | | +| swap1.cpp:122:5:122:22 | ... = ... | swap1.cpp:122:7:122:11 | data1 [post update] | | +| swap1.cpp:122:5:122:22 | ... = ... | swap1.cpp:124:12:124:16 | data1 | | +| swap1.cpp:122:5:122:22 | ... = ... | swap1.cpp:130:12:130:16 | data1 | | +| swap1.cpp:122:15:122:20 | call to source | swap1.cpp:122:5:122:22 | ... = ... | | +| swap1.cpp:127:5:127:5 | ref arg y | swap1.cpp:129:10:129:10 | y | | +| swap1.cpp:135:23:135:23 | x | swap1.cpp:137:5:137:5 | x | | +| swap1.cpp:135:23:135:23 | x | swap1.cpp:139:10:139:10 | x | | +| swap1.cpp:135:23:135:23 | x | swap1.cpp:142:29:142:29 | x | | +| swap1.cpp:135:23:135:23 | x | swap1.cpp:145:10:145:10 | x | | +| swap1.cpp:136:23:136:23 | y | swap1.cpp:140:10:140:10 | y | | +| swap1.cpp:136:23:136:23 | y | swap1.cpp:142:5:142:5 | y | | +| swap1.cpp:136:23:136:23 | y | swap1.cpp:144:10:144:10 | y | | +| swap1.cpp:137:5:137:5 | x [post update] | swap1.cpp:139:10:139:10 | x | | +| swap1.cpp:137:5:137:5 | x [post update] | swap1.cpp:142:29:142:29 | x | | +| swap1.cpp:137:5:137:5 | x [post update] | swap1.cpp:145:10:145:10 | x | | +| swap1.cpp:137:5:137:22 | ... = ... | swap1.cpp:137:7:137:11 | data1 [post update] | | +| swap1.cpp:137:5:137:22 | ... = ... | swap1.cpp:139:12:139:16 | data1 | | +| swap1.cpp:137:5:137:22 | ... = ... | swap1.cpp:145:12:145:16 | data1 | | +| swap1.cpp:137:15:137:20 | call to source | swap1.cpp:137:5:137:22 | ... = ... | | +| swap1.cpp:142:5:142:5 | ref arg y | swap1.cpp:144:10:144:10 | y | | +| swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:142:29:142:29 | x [inner post update] | | +| swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:145:10:145:10 | x | | +| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | | +| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | | +| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | | +| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | | +| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | | +| swap2.cpp:24:9:24:13 | this | swap2.cpp:24:31:24:34 | this | | +| swap2.cpp:24:23:24:26 | that | swap2.cpp:24:23:24:26 | that | | +| swap2.cpp:24:23:24:26 | that | swap2.cpp:24:36:24:39 | that | | +| swap2.cpp:24:36:24:39 | ref arg that | swap2.cpp:24:23:24:26 | that | | +| swap2.cpp:25:9:25:13 | this | swap2.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | | +| swap2.cpp:25:28:25:31 | that | swap2.cpp:25:42:25:45 | that | | +| swap2.cpp:25:28:25:31 | that | swap2.cpp:25:61:25:64 | that | | +| swap2.cpp:25:36:25:52 | constructor init of field data1 [post-this] | swap2.cpp:25:55:25:71 | constructor init of field data2 [pre-this] | | +| swap2.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | swap2.cpp:25:55:25:71 | constructor init of field data2 [pre-this] | | +| swap2.cpp:25:47:25:51 | data1 | swap2.cpp:25:36:25:52 | constructor init of field data1 | TAINT | +| swap2.cpp:25:47:25:51 | data1 | swap2.cpp:25:47:25:51 | data1 | | +| swap2.cpp:25:66:25:70 | data2 | swap2.cpp:25:55:25:71 | constructor init of field data2 | TAINT | +| swap2.cpp:25:66:25:70 | data2 | swap2.cpp:25:66:25:70 | data2 | | +| swap2.cpp:27:16:27:24 | this | swap2.cpp:30:13:30:16 | this | | +| swap2.cpp:27:39:27:42 | that | swap2.cpp:29:24:29:27 | that | | +| swap2.cpp:29:23:29:27 | call to Class | swap2.cpp:30:18:30:20 | tmp | | +| swap2.cpp:29:24:29:27 | that | swap2.cpp:29:23:29:27 | call to Class | | +| swap2.cpp:30:13:30:16 | ref arg this | swap2.cpp:31:21:31:24 | this | | +| swap2.cpp:30:13:30:16 | this | swap2.cpp:31:21:31:24 | this | | +| swap2.cpp:31:21:31:24 | this | swap2.cpp:31:20:31:24 | * ... | TAINT | +| swap2.cpp:34:16:34:24 | this | swap2.cpp:36:13:36:16 | this | | +| swap2.cpp:34:34:34:37 | that | swap2.cpp:34:34:34:37 | that | | +| swap2.cpp:34:34:34:37 | that | swap2.cpp:36:18:36:21 | that | | +| swap2.cpp:36:13:36:16 | ref arg this | swap2.cpp:37:21:37:24 | this | | +| swap2.cpp:36:13:36:16 | this | swap2.cpp:37:21:37:24 | this | | +| swap2.cpp:36:18:36:21 | ref arg that | swap2.cpp:34:34:34:37 | that | | +| swap2.cpp:37:21:37:24 | this | swap2.cpp:37:20:37:24 | * ... | TAINT | +| swap2.cpp:40:16:40:26 | this | swap2.cpp:43:13:43:16 | this | | +| swap2.cpp:40:41:40:44 | that | swap2.cpp:42:24:42:27 | that | | +| swap2.cpp:42:23:42:27 | call to Class | swap2.cpp:43:18:43:20 | tmp | | +| swap2.cpp:42:24:42:27 | that | swap2.cpp:42:23:42:27 | call to Class | | +| swap2.cpp:43:13:43:16 | ref arg this | swap2.cpp:44:21:44:24 | this | | +| swap2.cpp:43:13:43:16 | this | swap2.cpp:44:21:44:24 | this | | +| swap2.cpp:44:21:44:24 | this | swap2.cpp:44:20:44:24 | * ... | TAINT | +| swap2.cpp:47:16:47:26 | this | swap2.cpp:49:13:49:16 | this | | +| swap2.cpp:47:36:47:39 | that | swap2.cpp:47:36:47:39 | that | | +| swap2.cpp:47:36:47:39 | that | swap2.cpp:49:18:49:21 | that | | +| swap2.cpp:49:13:49:16 | ref arg this | swap2.cpp:50:21:50:24 | this | | +| swap2.cpp:49:13:49:16 | this | swap2.cpp:50:21:50:24 | this | | +| swap2.cpp:49:18:49:21 | ref arg that | swap2.cpp:47:36:47:39 | that | | +| swap2.cpp:50:21:50:24 | this | swap2.cpp:50:20:50:24 | * ... | TAINT | +| swap2.cpp:53:14:53:17 | this | swap2.cpp:56:18:56:22 | this | | +| swap2.cpp:53:26:53:29 | that | swap2.cpp:53:26:53:29 | that | | +| swap2.cpp:53:26:53:29 | that | swap2.cpp:56:25:56:28 | that | | +| swap2.cpp:53:26:53:29 | that | swap2.cpp:56:50:56:53 | that | | +| swap2.cpp:56:18:56:22 | data1 | swap2.cpp:56:30:56:34 | ref arg data1 | | +| swap2.cpp:56:18:56:22 | this | swap2.cpp:56:43:56:47 | this | | +| swap2.cpp:56:18:56:22 | this [post update] | swap2.cpp:56:43:56:47 | this | | +| swap2.cpp:56:25:56:28 | that | swap2.cpp:56:18:56:22 | ref arg data1 | | +| swap2.cpp:56:25:56:28 | that [post update] | swap2.cpp:53:26:53:29 | that | | +| swap2.cpp:56:25:56:28 | that [post update] | swap2.cpp:56:50:56:53 | that | | +| swap2.cpp:56:30:56:34 | data1 | swap2.cpp:56:18:56:22 | ref arg data1 | | +| swap2.cpp:56:43:56:47 | data2 | swap2.cpp:56:55:56:59 | ref arg data2 | | +| swap2.cpp:56:50:56:53 | that | swap2.cpp:56:43:56:47 | ref arg data2 | | +| swap2.cpp:56:50:56:53 | that [post update] | swap2.cpp:53:26:53:29 | that | | +| swap2.cpp:56:55:56:59 | data2 | swap2.cpp:56:43:56:47 | ref arg data2 | | +| swap2.cpp:61:22:61:22 | x | swap1.cpp:61:22:61:22 | x | | +| swap2.cpp:61:22:61:22 | x | swap1.cpp:63:9:63:9 | x | | +| swap2.cpp:61:22:61:22 | x | swap2.cpp:61:22:61:22 | x | | +| swap2.cpp:61:22:61:22 | x | swap2.cpp:63:9:63:9 | x | | +| swap2.cpp:61:32:61:32 | y | swap1.cpp:61:32:61:32 | y | | +| swap2.cpp:61:32:61:32 | y | swap1.cpp:63:16:63:16 | y | | +| swap2.cpp:61:32:61:32 | y | swap2.cpp:61:32:61:32 | y | | +| swap2.cpp:61:32:61:32 | y | swap2.cpp:63:16:63:16 | y | | +| swap2.cpp:63:9:63:9 | ref arg x | swap1.cpp:61:22:61:22 | x | | +| swap2.cpp:63:9:63:9 | ref arg x | swap2.cpp:61:22:61:22 | x | | +| swap2.cpp:63:16:63:16 | ref arg y | swap1.cpp:61:32:61:32 | y | | +| swap2.cpp:63:16:63:16 | ref arg y | swap2.cpp:61:32:61:32 | y | | +| swap2.cpp:69:23:69:23 | x | swap2.cpp:71:5:71:5 | x | | +| swap2.cpp:69:23:69:23 | x | swap2.cpp:73:10:73:10 | x | | +| swap2.cpp:69:23:69:23 | x | swap2.cpp:76:9:76:9 | x | | +| swap2.cpp:69:23:69:23 | x | swap2.cpp:79:10:79:10 | x | | +| swap2.cpp:70:23:70:23 | y | swap2.cpp:74:10:74:10 | y | | +| swap2.cpp:70:23:70:23 | y | swap2.cpp:76:5:76:5 | y | | +| swap2.cpp:70:23:70:23 | y | swap2.cpp:78:10:78:10 | y | | +| swap2.cpp:71:5:71:5 | x [post update] | swap2.cpp:73:10:73:10 | x | | +| swap2.cpp:71:5:71:5 | x [post update] | swap2.cpp:76:9:76:9 | x | | +| swap2.cpp:71:5:71:5 | x [post update] | swap2.cpp:79:10:79:10 | x | | +| swap2.cpp:71:5:71:22 | ... = ... | swap2.cpp:71:7:71:11 | data1 [post update] | | +| swap2.cpp:71:5:71:22 | ... = ... | swap2.cpp:73:12:73:16 | data1 | | +| swap2.cpp:71:5:71:22 | ... = ... | swap2.cpp:79:12:79:16 | data1 | | +| swap2.cpp:71:15:71:20 | call to source | swap2.cpp:71:5:71:22 | ... = ... | | +| swap2.cpp:76:5:76:5 | ref arg y | swap2.cpp:78:10:78:10 | y | | +| swap2.cpp:76:9:76:9 | x | swap2.cpp:76:5:76:5 | ref arg y | TAINT | +| swap2.cpp:76:9:76:9 | x | swap2.cpp:76:7:76:7 | call to operator= | TAINT | +| swap2.cpp:81:23:81:24 | z1 | swap2.cpp:82:5:82:6 | z1 | | +| swap2.cpp:81:23:81:24 | z1 | swap2.cpp:83:10:83:11 | z1 | | +| swap2.cpp:81:23:81:24 | z1 | swap2.cpp:85:10:85:11 | z1 | | +| swap2.cpp:81:23:81:24 | z1 | swap2.cpp:88:10:88:11 | z1 | | +| swap2.cpp:81:27:81:28 | z2 | swap2.cpp:85:14:85:15 | z2 | | +| swap2.cpp:81:27:81:28 | z2 | swap2.cpp:87:10:87:11 | z2 | | +| swap2.cpp:82:5:82:6 | z1 [post update] | swap2.cpp:83:10:83:11 | z1 | | +| swap2.cpp:82:5:82:6 | z1 [post update] | swap2.cpp:85:10:85:11 | z1 | | +| swap2.cpp:82:5:82:6 | z1 [post update] | swap2.cpp:88:10:88:11 | z1 | | +| swap2.cpp:82:5:82:23 | ... = ... | swap2.cpp:82:8:82:12 | data1 [post update] | | +| swap2.cpp:82:5:82:23 | ... = ... | swap2.cpp:83:13:83:17 | data1 | | +| swap2.cpp:82:5:82:23 | ... = ... | swap2.cpp:88:13:88:17 | data1 | | +| swap2.cpp:82:16:82:21 | call to source | swap2.cpp:82:5:82:23 | ... = ... | | +| swap2.cpp:85:10:85:11 | ref arg z1 | swap2.cpp:88:10:88:11 | z1 | | +| swap2.cpp:85:14:85:15 | ref arg z2 | swap2.cpp:87:10:87:11 | z2 | | +| swap2.cpp:93:23:93:23 | x | swap2.cpp:95:5:95:5 | x | | +| swap2.cpp:93:23:93:23 | x | swap2.cpp:97:10:97:10 | x | | +| swap2.cpp:93:23:93:23 | x | swap2.cpp:100:19:100:19 | x | | +| swap2.cpp:93:23:93:23 | x | swap2.cpp:103:10:103:10 | x | | +| swap2.cpp:94:23:94:23 | y | swap2.cpp:98:10:98:10 | y | | +| swap2.cpp:94:23:94:23 | y | swap2.cpp:100:5:100:5 | y | | +| swap2.cpp:94:23:94:23 | y | swap2.cpp:102:10:102:10 | y | | +| swap2.cpp:95:5:95:5 | x [post update] | swap2.cpp:97:10:97:10 | x | | +| swap2.cpp:95:5:95:5 | x [post update] | swap2.cpp:100:19:100:19 | x | | +| swap2.cpp:95:5:95:5 | x [post update] | swap2.cpp:103:10:103:10 | x | | +| swap2.cpp:95:5:95:22 | ... = ... | swap2.cpp:95:7:95:11 | data1 [post update] | | +| swap2.cpp:95:5:95:22 | ... = ... | swap2.cpp:97:12:97:16 | data1 | | +| swap2.cpp:95:5:95:22 | ... = ... | swap2.cpp:103:12:103:16 | data1 | | +| swap2.cpp:95:15:95:20 | call to source | swap2.cpp:95:5:95:22 | ... = ... | | +| swap2.cpp:100:5:100:5 | ref arg y | swap2.cpp:102:10:102:10 | y | | +| swap2.cpp:100:9:100:17 | call to move | swap2.cpp:100:5:100:5 | ref arg y | TAINT | +| swap2.cpp:100:9:100:17 | call to move | swap2.cpp:100:7:100:7 | call to operator= | TAINT | +| swap2.cpp:100:9:100:17 | ref arg call to move | swap2.cpp:100:19:100:19 | x [inner post update] | | +| swap2.cpp:100:9:100:17 | ref arg call to move | swap2.cpp:103:10:103:10 | x | | +| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:5:100:5 | ref arg y | TAINT | +| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:7:100:7 | call to operator= | TAINT | +| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | | +| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:109:5:109:13 | move_from | | +| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:111:10:111:18 | move_from | | +| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:113:41:113:49 | move_from | | +| swap2.cpp:109:5:109:13 | move_from [post update] | swap2.cpp:111:10:111:18 | move_from | | +| swap2.cpp:109:5:109:13 | move_from [post update] | swap2.cpp:113:41:113:49 | move_from | | +| swap2.cpp:109:5:109:30 | ... = ... | swap2.cpp:109:15:109:19 | data1 [post update] | | +| swap2.cpp:109:5:109:30 | ... = ... | swap2.cpp:111:20:111:24 | data1 | | +| swap2.cpp:109:5:109:30 | ... = ... | swap2.cpp:115:18:115:22 | data1 | | +| swap2.cpp:109:23:109:28 | call to source | swap2.cpp:109:5:109:30 | ... = ... | | +| swap2.cpp:113:31:113:39 | call to move | swap2.cpp:113:31:113:51 | call to Class | | +| swap2.cpp:113:31:113:39 | ref arg call to move | swap2.cpp:113:41:113:49 | move_from [inner post update] | | +| swap2.cpp:113:31:113:51 | call to Class | swap2.cpp:115:10:115:16 | move_to | | +| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | | +| swap2.cpp:120:23:120:23 | x | swap2.cpp:122:5:122:5 | x | | +| swap2.cpp:120:23:120:23 | x | swap2.cpp:124:10:124:10 | x | | +| swap2.cpp:120:23:120:23 | x | swap2.cpp:127:19:127:19 | x | | +| swap2.cpp:120:23:120:23 | x | swap2.cpp:130:10:130:10 | x | | +| swap2.cpp:121:23:121:23 | y | swap2.cpp:125:10:125:10 | y | | +| swap2.cpp:121:23:121:23 | y | swap2.cpp:127:5:127:5 | y | | +| swap2.cpp:121:23:121:23 | y | swap2.cpp:129:10:129:10 | y | | +| swap2.cpp:122:5:122:5 | x [post update] | swap2.cpp:124:10:124:10 | x | | +| swap2.cpp:122:5:122:5 | x [post update] | swap2.cpp:127:19:127:19 | x | | +| swap2.cpp:122:5:122:5 | x [post update] | swap2.cpp:130:10:130:10 | x | | +| swap2.cpp:122:5:122:22 | ... = ... | swap2.cpp:122:7:122:11 | data1 [post update] | | +| swap2.cpp:122:5:122:22 | ... = ... | swap2.cpp:124:12:124:16 | data1 | | +| swap2.cpp:122:5:122:22 | ... = ... | swap2.cpp:130:12:130:16 | data1 | | +| swap2.cpp:122:15:122:20 | call to source | swap2.cpp:122:5:122:22 | ... = ... | | +| swap2.cpp:127:5:127:5 | ref arg y | swap2.cpp:129:10:129:10 | y | | +| swap2.cpp:135:23:135:23 | x | swap2.cpp:137:5:137:5 | x | | +| swap2.cpp:135:23:135:23 | x | swap2.cpp:139:10:139:10 | x | | +| swap2.cpp:135:23:135:23 | x | swap2.cpp:142:29:142:29 | x | | +| swap2.cpp:135:23:135:23 | x | swap2.cpp:145:10:145:10 | x | | +| swap2.cpp:136:23:136:23 | y | swap2.cpp:140:10:140:10 | y | | +| swap2.cpp:136:23:136:23 | y | swap2.cpp:142:5:142:5 | y | | +| swap2.cpp:136:23:136:23 | y | swap2.cpp:144:10:144:10 | y | | +| swap2.cpp:137:5:137:5 | x [post update] | swap2.cpp:139:10:139:10 | x | | +| swap2.cpp:137:5:137:5 | x [post update] | swap2.cpp:142:29:142:29 | x | | +| swap2.cpp:137:5:137:5 | x [post update] | swap2.cpp:145:10:145:10 | x | | +| swap2.cpp:137:5:137:22 | ... = ... | swap2.cpp:137:7:137:11 | data1 [post update] | | +| swap2.cpp:137:5:137:22 | ... = ... | swap2.cpp:139:12:139:16 | data1 | | +| swap2.cpp:137:5:137:22 | ... = ... | swap2.cpp:145:12:145:16 | data1 | | +| swap2.cpp:137:15:137:20 | call to source | swap2.cpp:137:5:137:22 | ... = ... | | +| swap2.cpp:142:5:142:5 | ref arg y | swap2.cpp:144:10:144:10 | y | | +| swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:142:29:142:29 | x [inner post update] | | +| swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:145:10:145:10 | x | | +| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | | | taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | | | taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | | | taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | | @@ -197,9 +5314,11 @@ | taint.cpp:7:3:7:8 | clean1 | taint.cpp:7:3:7:13 | ... += ... | TAINT | | taint.cpp:7:3:7:13 | ... += ... | taint.cpp:8:8:8:13 | clean1 | | | taint.cpp:7:13:7:13 | 1 | taint.cpp:7:3:7:13 | ... += ... | TAINT | +| taint.cpp:10:12:10:18 | source1 | taint.cpp:10:12:10:22 | ... = ... | | | taint.cpp:10:12:10:22 | ... = ... | taint.cpp:10:3:10:22 | ... = ... | | | taint.cpp:10:12:10:22 | ... = ... | taint.cpp:11:8:11:13 | clean1 | | | taint.cpp:10:22:10:22 | 1 | taint.cpp:10:12:10:22 | ... = ... | | +| taint.cpp:12:13:12:18 | clean1 | taint.cpp:12:13:12:29 | ... = ... | | | taint.cpp:12:13:12:29 | ... = ... | taint.cpp:12:3:12:29 | ... = ... | | | taint.cpp:12:13:12:29 | ... = ... | taint.cpp:13:3:13:9 | source1 | | | taint.cpp:12:22:12:27 | call to source | taint.cpp:12:13:12:29 | ... = ... | | @@ -211,23 +5330,34 @@ | taint.cpp:15:3:15:14 | ... += ... | taint.cpp:16:8:16:14 | source1 | | | taint.cpp:15:3:15:14 | ... += ... | taint.cpp:17:10:17:16 | source1 | | | taint.cpp:15:14:15:14 | 1 | taint.cpp:15:3:15:14 | ... += ... | TAINT | -| taint.cpp:17:10:17:16 | source1 | taint.cpp:17:8:17:16 | ++ ... | TAINT | +| taint.cpp:17:10:17:16 | source1 | taint.cpp:17:8:17:16 | ++ ... | | | taint.cpp:22:19:22:19 | x | taint.cpp:22:30:22:30 | x | | | taint.cpp:22:30:22:30 | x | taint.cpp:22:30:22:34 | ... + ... | TAINT | | taint.cpp:22:34:22:34 | 1 | taint.cpp:22:30:22:34 | ... + ... | TAINT | | taint.cpp:27:15:27:21 | global2 | taint.cpp:27:15:27:25 | ... + ... | TAINT | | taint.cpp:27:25:27:25 | 1 | taint.cpp:27:15:27:25 | ... + ... | TAINT | +| taint.cpp:34:2:34:8 | global6 [post update] | taint.cpp:40:7:40:13 | global6 | | +| taint.cpp:34:2:34:12 | ... = ... | taint.cpp:34:2:34:8 | global6 [post update] | | | taint.cpp:34:12:34:12 | 0 | taint.cpp:34:2:34:12 | ... = ... | | | taint.cpp:34:12:34:12 | 0 | taint.cpp:40:7:40:13 | global6 | | +| taint.cpp:35:2:35:8 | global7 [post update] | taint.cpp:36:12:36:18 | global7 | | +| taint.cpp:35:2:35:8 | global7 [post update] | taint.cpp:41:7:41:13 | global7 | | +| taint.cpp:35:2:35:19 | ... = ... | taint.cpp:35:2:35:8 | global7 [post update] | | | taint.cpp:35:12:35:17 | call to source | taint.cpp:35:2:35:19 | ... = ... | | | taint.cpp:35:12:35:17 | call to source | taint.cpp:36:12:36:18 | global7 | | | taint.cpp:35:12:35:17 | call to source | taint.cpp:41:7:41:13 | global7 | | +| taint.cpp:36:2:36:8 | global8 [post update] | taint.cpp:42:7:42:13 | global8 | | +| taint.cpp:36:2:36:22 | ... = ... | taint.cpp:36:2:36:8 | global8 [post update] | | | taint.cpp:36:12:36:18 | global7 | taint.cpp:36:12:36:22 | ... + ... | TAINT | | taint.cpp:36:12:36:22 | ... + ... | taint.cpp:36:2:36:22 | ... = ... | | | taint.cpp:36:12:36:22 | ... + ... | taint.cpp:42:7:42:13 | global8 | | | taint.cpp:36:22:36:22 | 1 | taint.cpp:36:12:36:22 | ... + ... | TAINT | +| taint.cpp:37:2:37:8 | global9 [post update] | taint.cpp:43:7:43:13 | global9 | | +| taint.cpp:37:2:37:30 | ... = ... | taint.cpp:37:2:37:8 | global9 [post update] | | | taint.cpp:37:12:37:20 | call to increment | taint.cpp:37:2:37:30 | ... = ... | | | taint.cpp:37:12:37:20 | call to increment | taint.cpp:43:7:43:13 | global9 | | +| taint.cpp:38:2:38:9 | global10 [post update] | taint.cpp:44:7:44:14 | global10 | | +| taint.cpp:38:2:38:26 | ... = ... | taint.cpp:38:2:38:9 | global10 [post update] | | | taint.cpp:38:13:38:16 | call to zero | taint.cpp:38:2:38:26 | ... = ... | | | taint.cpp:38:13:38:16 | call to zero | taint.cpp:44:7:44:14 | global10 | | | taint.cpp:71:2:71:8 | this | taint.cpp:71:14:71:17 | constructor init of field a [pre-this] | | @@ -239,9 +5369,12 @@ | taint.cpp:71:22:71:27 | call to source | taint.cpp:71:20:71:30 | constructor init of field b | TAINT | | taint.cpp:72:3:72:3 | this | taint.cpp:73:3:73:3 | this | | | taint.cpp:72:3:72:3 | this [post update] | taint.cpp:73:3:73:3 | this | | +| taint.cpp:72:3:72:14 | ... = ... | taint.cpp:72:3:72:3 | c [post update] | | | taint.cpp:72:7:72:12 | call to source | taint.cpp:72:3:72:14 | ... = ... | | +| taint.cpp:73:3:73:7 | ... = ... | taint.cpp:73:3:73:3 | d [post update] | | | taint.cpp:73:7:73:7 | 0 | taint.cpp:73:3:73:7 | ... = ... | | | taint.cpp:76:7:76:14 | this | taint.cpp:77:3:77:3 | this | | +| taint.cpp:77:3:77:14 | ... = ... | taint.cpp:77:3:77:3 | d [post update] | | | taint.cpp:77:7:77:12 | call to source | taint.cpp:77:3:77:14 | ... = ... | | | taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:86:2:86:4 | mc1 | | | taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:88:7:88:9 | mc1 | | @@ -261,31 +5394,49 @@ | taint.cpp:100:21:100:21 | i | taint.cpp:112:12:112:12 | i | | | taint.cpp:100:21:100:21 | i | taint.cpp:114:12:114:12 | i | | | taint.cpp:101:16:101:19 | {...} | taint.cpp:105:2:105:5 | arr1 | | +| taint.cpp:101:16:101:19 | {...} | taint.cpp:109:7:109:10 | arr1 | | +| taint.cpp:101:16:101:19 | {...} | taint.cpp:110:7:110:10 | arr1 | | | taint.cpp:101:18:101:18 | 0 | taint.cpp:101:16:101:19 | {...} | TAINT | | taint.cpp:102:16:102:19 | {...} | taint.cpp:106:2:106:5 | arr2 | | +| taint.cpp:102:16:102:19 | {...} | taint.cpp:111:7:111:10 | arr2 | | +| taint.cpp:102:16:102:19 | {...} | taint.cpp:112:7:112:10 | arr2 | | | taint.cpp:102:18:102:18 | 0 | taint.cpp:102:16:102:19 | {...} | TAINT | | taint.cpp:103:16:103:19 | {...} | taint.cpp:107:2:107:5 | arr3 | | +| taint.cpp:103:16:103:19 | {...} | taint.cpp:113:7:113:10 | arr3 | | +| taint.cpp:103:16:103:19 | {...} | taint.cpp:114:7:114:10 | arr3 | | | taint.cpp:103:18:103:18 | 0 | taint.cpp:103:16:103:19 | {...} | TAINT | -| taint.cpp:105:2:105:5 | arr1 | taint.cpp:105:2:105:8 | access to array | TAINT | +| taint.cpp:105:2:105:5 | arr1 | taint.cpp:105:2:105:8 | access to array | | +| taint.cpp:105:2:105:8 | access to array [post update] | taint.cpp:105:2:105:5 | arr1 [inner post update] | | +| taint.cpp:105:2:105:8 | access to array [post update] | taint.cpp:109:7:109:10 | arr1 | | +| taint.cpp:105:2:105:8 | access to array [post update] | taint.cpp:110:7:110:10 | arr1 | | +| taint.cpp:105:2:105:19 | ... = ... | taint.cpp:105:2:105:8 | access to array [post update] | | | taint.cpp:105:7:105:7 | 5 | taint.cpp:105:2:105:8 | access to array | TAINT | | taint.cpp:105:12:105:17 | call to source | taint.cpp:105:2:105:19 | ... = ... | | -| taint.cpp:106:2:106:5 | arr2 | taint.cpp:106:2:106:8 | access to array | TAINT | +| taint.cpp:106:2:106:5 | arr2 | taint.cpp:106:2:106:8 | access to array | | +| taint.cpp:106:2:106:8 | access to array [post update] | taint.cpp:106:2:106:5 | arr2 [inner post update] | | +| taint.cpp:106:2:106:8 | access to array [post update] | taint.cpp:111:7:111:10 | arr2 | | +| taint.cpp:106:2:106:8 | access to array [post update] | taint.cpp:112:7:112:10 | arr2 | | +| taint.cpp:106:2:106:19 | ... = ... | taint.cpp:106:2:106:8 | access to array [post update] | | | taint.cpp:106:7:106:7 | i | taint.cpp:106:2:106:8 | access to array | TAINT | | taint.cpp:106:12:106:17 | call to source | taint.cpp:106:2:106:19 | ... = ... | | -| taint.cpp:107:2:107:5 | arr3 | taint.cpp:107:2:107:8 | access to array | TAINT | +| taint.cpp:107:2:107:5 | arr3 | taint.cpp:107:2:107:8 | access to array | | +| taint.cpp:107:2:107:8 | access to array [post update] | taint.cpp:107:2:107:5 | arr3 [inner post update] | | +| taint.cpp:107:2:107:8 | access to array [post update] | taint.cpp:113:7:113:10 | arr3 | | +| taint.cpp:107:2:107:8 | access to array [post update] | taint.cpp:114:7:114:10 | arr3 | | +| taint.cpp:107:2:107:12 | ... = ... | taint.cpp:107:2:107:8 | access to array [post update] | | | taint.cpp:107:7:107:7 | 5 | taint.cpp:107:2:107:8 | access to array | TAINT | | taint.cpp:107:12:107:12 | 0 | taint.cpp:107:2:107:12 | ... = ... | | -| taint.cpp:109:7:109:10 | arr1 | taint.cpp:109:7:109:13 | access to array | TAINT | +| taint.cpp:109:7:109:10 | arr1 | taint.cpp:109:7:109:13 | access to array | | | taint.cpp:109:12:109:12 | 5 | taint.cpp:109:7:109:13 | access to array | TAINT | -| taint.cpp:110:7:110:10 | arr1 | taint.cpp:110:7:110:13 | access to array | TAINT | +| taint.cpp:110:7:110:10 | arr1 | taint.cpp:110:7:110:13 | access to array | | | taint.cpp:110:12:110:12 | i | taint.cpp:110:7:110:13 | access to array | TAINT | -| taint.cpp:111:7:111:10 | arr2 | taint.cpp:111:7:111:13 | access to array | TAINT | +| taint.cpp:111:7:111:10 | arr2 | taint.cpp:111:7:111:13 | access to array | | | taint.cpp:111:12:111:12 | 5 | taint.cpp:111:7:111:13 | access to array | TAINT | -| taint.cpp:112:7:112:10 | arr2 | taint.cpp:112:7:112:13 | access to array | TAINT | +| taint.cpp:112:7:112:10 | arr2 | taint.cpp:112:7:112:13 | access to array | | | taint.cpp:112:12:112:12 | i | taint.cpp:112:7:112:13 | access to array | TAINT | -| taint.cpp:113:7:113:10 | arr3 | taint.cpp:113:7:113:13 | access to array | TAINT | +| taint.cpp:113:7:113:10 | arr3 | taint.cpp:113:7:113:13 | access to array | | | taint.cpp:113:12:113:12 | 5 | taint.cpp:113:7:113:13 | access to array | TAINT | -| taint.cpp:114:7:114:10 | arr3 | taint.cpp:114:7:114:13 | access to array | TAINT | +| taint.cpp:114:7:114:10 | arr3 | taint.cpp:114:7:114:13 | access to array | | | taint.cpp:114:12:114:12 | i | taint.cpp:114:7:114:13 | access to array | TAINT | | taint.cpp:120:11:120:16 | call to source | taint.cpp:123:13:123:14 | t1 | | | taint.cpp:120:11:120:16 | call to source | taint.cpp:133:8:133:9 | t1 | | @@ -298,6 +5449,9 @@ | taint.cpp:124:13:124:14 | t2 | taint.cpp:124:12:124:14 | & ... | | | taint.cpp:125:12:125:14 | & ... | taint.cpp:131:8:131:9 | p3 | | | taint.cpp:125:13:125:14 | t3 | taint.cpp:125:12:125:14 | & ... | | +| taint.cpp:127:2:127:4 | * ... [post update] | taint.cpp:127:3:127:4 | p2 [inner post update] | | +| taint.cpp:127:2:127:4 | * ... [post update] | taint.cpp:130:8:130:9 | p2 | | +| taint.cpp:127:2:127:15 | ... = ... | taint.cpp:127:2:127:4 | * ... [post update] | | | taint.cpp:127:3:127:4 | p2 | taint.cpp:127:2:127:4 | * ... | TAINT | | taint.cpp:127:8:127:13 | call to source | taint.cpp:127:2:127:15 | ... = ... | | | taint.cpp:129:8:129:9 | p1 | taint.cpp:129:7:129:9 | * ... | TAINT | @@ -309,6 +5463,9 @@ | taint.cpp:133:7:133:9 | & ... | taint.cpp:137:8:137:9 | p3 | | | taint.cpp:133:8:133:9 | t1 | taint.cpp:133:7:133:9 | & ... | | | taint.cpp:134:8:134:9 | p3 | taint.cpp:134:7:134:9 | * ... | TAINT | +| taint.cpp:136:2:136:4 | * ... [post update] | taint.cpp:136:3:136:4 | p3 [inner post update] | | +| taint.cpp:136:2:136:4 | * ... [post update] | taint.cpp:137:8:137:9 | p3 | | +| taint.cpp:136:2:136:8 | ... = ... | taint.cpp:136:2:136:4 | * ... [post update] | | | taint.cpp:136:3:136:4 | p3 | taint.cpp:136:2:136:4 | * ... | TAINT | | taint.cpp:136:8:136:8 | 0 | taint.cpp:136:2:136:8 | ... = ... | | | taint.cpp:137:8:137:9 | p3 | taint.cpp:137:7:137:9 | * ... | TAINT | @@ -401,6 +5558,7 @@ | taint.cpp:235:15:235:15 | this | taint.cpp:236:3:236:6 | this | | | taint.cpp:236:3:236:6 | this | taint.cpp:237:3:237:6 | this | | | taint.cpp:237:3:237:6 | this | taint.cpp:238:3:238:14 | this | | +| taint.cpp:238:3:238:14 | ... = ... | taint.cpp:238:3:238:14 | v [post update] | | | taint.cpp:238:7:238:12 | call to source | taint.cpp:238:3:238:14 | ... = ... | | | taint.cpp:243:10:246:2 | [...](...){...} | taint.cpp:247:2:247:2 | c | | | taint.cpp:243:10:246:2 | {...} | taint.cpp:243:10:246:2 | [...](...){...} | | @@ -419,6 +5577,8 @@ | taint.cpp:255:19:255:19 | a | taint.cpp:256:8:256:8 | a | | | taint.cpp:255:27:255:27 | b | taint.cpp:255:27:255:27 | b | | | taint.cpp:255:27:255:27 | b | taint.cpp:257:8:257:8 | b | | +| taint.cpp:258:3:258:3 | c [post update] | taint.cpp:255:35:255:35 | c | | +| taint.cpp:258:3:258:14 | ... = ... | taint.cpp:258:3:258:3 | c [post update] | | | taint.cpp:258:7:258:12 | call to source | taint.cpp:255:35:255:35 | c | | | taint.cpp:258:7:258:12 | call to source | taint.cpp:258:3:258:14 | ... = ... | | | taint.cpp:260:10:260:10 | ref arg w | taint.cpp:261:7:261:7 | w | | @@ -443,13 +5603,19 @@ | taint.cpp:287:6:287:7 | call to id | taint.cpp:292:7:292:7 | z | | | taint.cpp:297:29:297:29 | b | taint.cpp:297:29:297:29 | b | | | taint.cpp:297:29:297:29 | b | taint.cpp:299:6:299:6 | b | | +| taint.cpp:299:2:299:2 | a [post update] | taint.cpp:297:21:297:21 | a | | +| taint.cpp:299:2:299:6 | ... = ... | taint.cpp:299:2:299:2 | a [post update] | | | taint.cpp:299:6:299:6 | b | taint.cpp:297:21:297:21 | a | | | taint.cpp:299:6:299:6 | b | taint.cpp:299:2:299:6 | ... = ... | | | taint.cpp:302:28:302:28 | b | taint.cpp:304:6:304:6 | b | | +| taint.cpp:304:2:304:2 | a [post update] | taint.cpp:302:21:302:21 | a | | +| taint.cpp:304:2:304:6 | ... = ... | taint.cpp:304:2:304:2 | a [post update] | | | taint.cpp:304:6:304:6 | b | taint.cpp:302:21:302:21 | a | | | taint.cpp:304:6:304:6 | b | taint.cpp:304:2:304:6 | ... = ... | | | taint.cpp:307:21:307:21 | a | taint.cpp:309:3:309:3 | a | | | taint.cpp:307:28:307:28 | b | taint.cpp:309:7:309:7 | b | | +| taint.cpp:309:2:309:3 | * ... [post update] | taint.cpp:309:3:309:3 | a [inner post update] | | +| taint.cpp:309:2:309:7 | ... = ... | taint.cpp:309:2:309:3 | * ... [post update] | | | taint.cpp:309:3:309:3 | a | taint.cpp:309:2:309:3 | * ... | TAINT | | taint.cpp:309:7:309:7 | b | taint.cpp:309:2:309:7 | ... = ... | | | taint.cpp:312:21:312:21 | a | taint.cpp:317:3:317:3 | a | | @@ -458,14 +5624,20 @@ | taint.cpp:316:6:316:10 | ... + ... | taint.cpp:316:2:316:10 | ... = ... | | | taint.cpp:316:6:316:10 | ... + ... | taint.cpp:317:7:317:7 | c | | | taint.cpp:316:10:316:10 | 1 | taint.cpp:316:6:316:10 | ... + ... | TAINT | +| taint.cpp:317:2:317:3 | * ... [post update] | taint.cpp:317:3:317:3 | a [inner post update] | | +| taint.cpp:317:2:317:7 | ... = ... | taint.cpp:317:2:317:3 | * ... [post update] | | | taint.cpp:317:3:317:3 | a | taint.cpp:317:2:317:3 | * ... | TAINT | | taint.cpp:317:7:317:7 | c | taint.cpp:317:2:317:7 | ... = ... | | | taint.cpp:320:23:320:23 | a | taint.cpp:322:6:322:6 | a | | | taint.cpp:320:31:320:31 | b | taint.cpp:323:6:323:6 | b | | +| taint.cpp:322:2:322:2 | a [post update] | taint.cpp:320:23:320:23 | a | | +| taint.cpp:322:2:322:10 | ... = ... | taint.cpp:322:2:322:2 | a [post update] | | | taint.cpp:322:6:322:6 | a | taint.cpp:322:6:322:10 | ... + ... | TAINT | | taint.cpp:322:6:322:10 | ... + ... | taint.cpp:320:23:320:23 | a | | | taint.cpp:322:6:322:10 | ... + ... | taint.cpp:322:2:322:10 | ... = ... | | | taint.cpp:322:10:322:10 | 1 | taint.cpp:322:6:322:10 | ... + ... | TAINT | +| taint.cpp:323:2:323:2 | b [post update] | taint.cpp:320:31:320:31 | b | | +| taint.cpp:323:2:323:10 | ... = ... | taint.cpp:323:2:323:2 | b [post update] | | | taint.cpp:323:6:323:6 | b | taint.cpp:323:6:323:10 | ... + ... | TAINT | | taint.cpp:323:6:323:10 | ... + ... | taint.cpp:320:31:320:31 | b | | | taint.cpp:323:6:323:10 | ... + ... | taint.cpp:323:2:323:10 | ... = ... | | @@ -537,17 +5709,20 @@ | taint.cpp:390:6:390:11 | call to wcsdup | taint.cpp:390:2:390:28 | ... = ... | | | taint.cpp:390:6:390:11 | call to wcsdup | taint.cpp:392:7:392:7 | b | | | taint.cpp:390:13:390:27 | hello, world | taint.cpp:390:6:390:11 | call to wcsdup | TAINT | +| taint.cpp:417:13:417:13 | 0 | taint.cpp:417:13:417:14 | call to MyClass2 | TAINT | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:420:7:420:7 | a | | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:421:7:421:7 | a | | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:422:2:422:2 | a | | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:423:7:423:7 | a | | | taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:424:7:424:7 | a | | +| taint.cpp:417:19:417:19 | 0 | taint.cpp:417:19:417:20 | call to MyClass2 | TAINT | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:426:7:426:7 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:427:7:427:7 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:428:2:428:2 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:429:7:429:7 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:430:7:430:7 | b | | | taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:431:7:431:7 | b | | +| taint.cpp:418:13:418:14 | | taint.cpp:418:13:418:15 | call to MyClass3 | TAINT | | taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:443:7:443:7 | d | | | taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:444:7:444:7 | d | | | taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:445:2:445:2 | d | | @@ -565,6 +5740,7 @@ | taint.cpp:428:2:428:2 | b [post update] | taint.cpp:429:7:429:7 | b | | | taint.cpp:428:2:428:2 | b [post update] | taint.cpp:430:7:430:7 | b | | | taint.cpp:428:2:428:2 | b [post update] | taint.cpp:431:7:431:7 | b | | +| taint.cpp:428:2:428:20 | ... = ... | taint.cpp:428:4:428:9 | member [post update] | | | taint.cpp:428:2:428:20 | ... = ... | taint.cpp:430:9:430:14 | member | | | taint.cpp:428:13:428:18 | call to source | taint.cpp:428:2:428:20 | ... = ... | | | taint.cpp:433:6:433:20 | call to MyClass2 | taint.cpp:433:6:433:20 | new | | @@ -575,6 +5751,7 @@ | taint.cpp:433:6:433:20 | new | taint.cpp:438:7:438:7 | c | | | taint.cpp:433:6:433:20 | new | taint.cpp:439:7:439:7 | c | | | taint.cpp:433:6:433:20 | new | taint.cpp:441:9:441:9 | c | | +| taint.cpp:433:19:433:19 | 0 | taint.cpp:433:6:433:20 | call to MyClass2 | TAINT | | taint.cpp:435:7:435:7 | ref arg c | taint.cpp:436:7:436:7 | c | | | taint.cpp:435:7:435:7 | ref arg c | taint.cpp:437:2:437:2 | c | | | taint.cpp:435:7:435:7 | ref arg c | taint.cpp:438:7:438:7 | c | | @@ -599,8 +5776,12 @@ | taint.cpp:452:16:452:16 | a | taint.cpp:454:10:454:10 | a | | | taint.cpp:452:24:452:24 | b | taint.cpp:455:6:455:6 | b | | | taint.cpp:454:10:454:10 | a | taint.cpp:456:6:456:6 | c | | +| taint.cpp:455:2:455:2 | a [post update] | taint.cpp:452:16:452:16 | a | | +| taint.cpp:455:2:455:6 | ... = ... | taint.cpp:455:2:455:2 | a [post update] | | | taint.cpp:455:6:455:6 | b | taint.cpp:452:16:452:16 | a | | | taint.cpp:455:6:455:6 | b | taint.cpp:455:2:455:6 | ... = ... | | +| taint.cpp:456:2:456:2 | b [post update] | taint.cpp:452:24:452:24 | b | | +| taint.cpp:456:2:456:6 | ... = ... | taint.cpp:456:2:456:2 | b [post update] | | | taint.cpp:456:6:456:6 | c | taint.cpp:452:24:452:24 | b | | | taint.cpp:456:6:456:6 | c | taint.cpp:456:2:456:6 | ... = ... | | | taint.cpp:462:6:462:11 | call to source | taint.cpp:462:2:462:13 | ... = ... | | @@ -623,3 +5804,1239 @@ | taint.cpp:483:18:483:19 | ref arg & ... | taint.cpp:483:19:483:19 | n [inner post update] | | | taint.cpp:483:19:483:19 | n | taint.cpp:483:18:483:19 | & ... | | | taint.cpp:483:28:483:34 | source1 | taint.cpp:483:11:483:15 | ref arg & ... | TAINT | +| vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | | +| vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | | +| vector.cpp:17:21:17:33 | call to vector | vector.cpp:19:14:19:14 | v | | +| vector.cpp:17:21:17:33 | call to vector | vector.cpp:23:38:23:38 | v | | +| vector.cpp:17:21:17:33 | call to vector | vector.cpp:23:55:23:55 | v | | +| vector.cpp:17:21:17:33 | call to vector | vector.cpp:27:15:27:15 | v | | +| vector.cpp:17:21:17:33 | call to vector | vector.cpp:35:1:35:1 | v | | +| vector.cpp:17:26:17:32 | source1 | vector.cpp:17:21:17:33 | call to vector | TAINT | +| vector.cpp:19:14:19:14 | (__begin) | vector.cpp:19:14:19:14 | call to operator* | TAINT | +| vector.cpp:19:14:19:14 | (__begin) | vector.cpp:19:14:19:14 | call to operator++ | | +| vector.cpp:19:14:19:14 | (__end) | vector.cpp:19:14:19:14 | call to iterator | | +| vector.cpp:19:14:19:14 | (__range) | vector.cpp:19:14:19:14 | call to begin | TAINT | +| vector.cpp:19:14:19:14 | (__range) | vector.cpp:19:14:19:14 | call to end | TAINT | +| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | | +| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | | +| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | | +| vector.cpp:19:14:19:14 | call to end | vector.cpp:19:14:19:14 | (__end) | | +| vector.cpp:19:14:19:14 | call to operator* | vector.cpp:20:8:20:8 | x | | +| vector.cpp:19:14:19:14 | ref arg (__begin) | vector.cpp:19:14:19:14 | (__begin) | | +| vector.cpp:19:14:19:14 | ref arg (__begin) | vector.cpp:19:14:19:14 | (__begin) | | +| vector.cpp:19:14:19:14 | ref arg (__begin) | vector.cpp:19:14:19:14 | (__begin) | | +| vector.cpp:19:14:19:14 | ref arg (__range) | vector.cpp:19:14:19:14 | (__range) | | +| vector.cpp:19:14:19:14 | v | vector.cpp:19:14:19:14 | (__range) | | +| vector.cpp:19:14:19:14 | v | vector.cpp:19:14:19:14 | (__range) | | +| vector.cpp:19:14:19:14 | v | vector.cpp:19:14:19:14 | call to operator* | TAINT | +| vector.cpp:23:38:23:38 | ref arg v | vector.cpp:23:55:23:55 | v | | +| vector.cpp:23:38:23:38 | ref arg v | vector.cpp:27:15:27:15 | v | | +| vector.cpp:23:38:23:38 | ref arg v | vector.cpp:35:1:35:1 | v | | +| vector.cpp:23:38:23:38 | v | vector.cpp:23:40:23:44 | call to begin | TAINT | +| vector.cpp:23:40:23:44 | call to begin | vector.cpp:23:49:23:50 | it | | +| vector.cpp:23:40:23:44 | call to begin | vector.cpp:23:66:23:67 | it | | +| vector.cpp:23:40:23:44 | call to begin | vector.cpp:24:9:24:10 | it | | +| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:23:55:23:55 | v | | +| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:27:15:27:15 | v | | +| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:35:1:35:1 | v | | +| vector.cpp:23:55:23:55 | v | vector.cpp:23:57:23:59 | call to end | TAINT | +| vector.cpp:23:66:23:67 | it | vector.cpp:23:64:23:64 | call to operator++ | | +| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:23:49:23:50 | it | | +| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:23:66:23:67 | it | | +| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:24:9:24:10 | it | | +| vector.cpp:24:9:24:10 | it | vector.cpp:24:8:24:8 | call to operator* | TAINT | +| vector.cpp:27:15:27:15 | (__begin) | vector.cpp:27:15:27:15 | call to operator* | TAINT | +| vector.cpp:27:15:27:15 | (__begin) | vector.cpp:27:15:27:15 | call to operator++ | | +| vector.cpp:27:15:27:15 | (__end) | vector.cpp:27:15:27:15 | call to iterator | | +| vector.cpp:27:15:27:15 | (__range) | vector.cpp:27:15:27:15 | call to begin | TAINT | +| vector.cpp:27:15:27:15 | (__range) | vector.cpp:27:15:27:15 | call to end | TAINT | +| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | | +| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | | +| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | | +| vector.cpp:27:15:27:15 | call to end | vector.cpp:27:15:27:15 | (__end) | | +| vector.cpp:27:15:27:15 | call to operator* | vector.cpp:28:8:28:8 | x | | +| vector.cpp:27:15:27:15 | ref arg (__begin) | vector.cpp:27:15:27:15 | (__begin) | | +| vector.cpp:27:15:27:15 | ref arg (__begin) | vector.cpp:27:15:27:15 | (__begin) | | +| vector.cpp:27:15:27:15 | ref arg (__begin) | vector.cpp:27:15:27:15 | (__begin) | | +| vector.cpp:27:15:27:15 | ref arg (__range) | vector.cpp:27:15:27:15 | (__range) | | +| vector.cpp:27:15:27:15 | v | vector.cpp:27:15:27:15 | (__range) | | +| vector.cpp:27:15:27:15 | v | vector.cpp:27:15:27:15 | (__range) | | +| vector.cpp:27:15:27:15 | v | vector.cpp:27:15:27:15 | call to operator* | TAINT | +| vector.cpp:31:33:31:45 | call to vector | vector.cpp:32:21:32:27 | const_v | | +| vector.cpp:31:33:31:45 | call to vector | vector.cpp:35:1:35:1 | const_v | | +| vector.cpp:31:38:31:44 | source1 | vector.cpp:31:33:31:45 | call to vector | TAINT | +| vector.cpp:32:21:32:21 | (__begin) | vector.cpp:32:21:32:21 | call to operator* | TAINT | +| vector.cpp:32:21:32:21 | (__begin) | vector.cpp:32:21:32:21 | call to operator++ | | +| vector.cpp:32:21:32:21 | (__range) | vector.cpp:32:21:32:21 | call to begin | TAINT | +| vector.cpp:32:21:32:21 | (__range) | vector.cpp:32:21:32:21 | call to end | TAINT | +| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | | +| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | | +| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | | +| vector.cpp:32:21:32:21 | call to end | vector.cpp:32:21:32:21 | (__end) | | +| vector.cpp:32:21:32:21 | call to operator* | vector.cpp:33:8:33:8 | x | | +| vector.cpp:32:21:32:21 | ref arg (__begin) | vector.cpp:32:21:32:21 | (__begin) | | +| vector.cpp:32:21:32:21 | ref arg (__begin) | vector.cpp:32:21:32:21 | (__begin) | | +| vector.cpp:32:21:32:21 | ref arg (__begin) | vector.cpp:32:21:32:21 | (__begin) | | +| vector.cpp:32:21:32:27 | const_v | vector.cpp:32:21:32:21 | (__range) | | +| vector.cpp:32:21:32:27 | const_v | vector.cpp:32:21:32:21 | (__range) | | +| vector.cpp:32:21:32:27 | const_v | vector.cpp:32:21:32:21 | call to operator* | TAINT | +| vector.cpp:37:29:37:29 | x | vector.cpp:42:5:42:5 | x | | +| vector.cpp:37:29:37:29 | x | vector.cpp:47:10:47:10 | x | | +| vector.cpp:37:29:37:29 | x | vector.cpp:55:10:55:10 | x | | +| vector.cpp:37:29:37:29 | x | vector.cpp:61:10:61:10 | x | | +| vector.cpp:37:29:37:29 | x | vector.cpp:63:5:63:5 | x | | +| vector.cpp:37:29:37:29 | x | vector.cpp:67:10:67:10 | x | | +| vector.cpp:37:29:37:29 | x | vector.cpp:96:8:96:8 | x | | +| vector.cpp:37:29:37:29 | x | vector.cpp:100:13:100:13 | x | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:40:2:40:3 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:41:2:41:3 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:42:2:42:3 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:43:2:43:3 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:44:7:44:8 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:45:7:45:8 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:46:7:46:8 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:47:7:47:8 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:38:22:38:24 | call to vector | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:38:30:38:32 | call to vector | vector.cpp:51:2:51:3 | v2 | | +| vector.cpp:38:30:38:32 | call to vector | vector.cpp:52:7:52:8 | v2 | | +| vector.cpp:38:30:38:32 | call to vector | vector.cpp:53:7:53:8 | v2 | | +| vector.cpp:38:30:38:32 | call to vector | vector.cpp:54:7:54:8 | v2 | | +| vector.cpp:38:30:38:32 | call to vector | vector.cpp:55:7:55:8 | v2 | | +| vector.cpp:38:30:38:32 | call to vector | vector.cpp:57:7:57:8 | v2 | | +| vector.cpp:38:30:38:32 | call to vector | vector.cpp:101:1:101:1 | v2 | | +| vector.cpp:38:38:38:40 | call to vector | vector.cpp:57:2:57:3 | v3 | | +| vector.cpp:38:38:38:40 | call to vector | vector.cpp:58:7:58:8 | v3 | | +| vector.cpp:38:38:38:40 | call to vector | vector.cpp:59:7:59:8 | v3 | | +| vector.cpp:38:38:38:40 | call to vector | vector.cpp:60:7:60:8 | v3 | | +| vector.cpp:38:38:38:40 | call to vector | vector.cpp:61:7:61:8 | v3 | | +| vector.cpp:38:38:38:40 | call to vector | vector.cpp:101:1:101:1 | v3 | | +| vector.cpp:38:46:38:48 | call to vector | vector.cpp:63:2:63:3 | v4 | | +| vector.cpp:38:46:38:48 | call to vector | vector.cpp:64:7:64:8 | v4 | | +| vector.cpp:38:46:38:48 | call to vector | vector.cpp:65:7:65:8 | v4 | | +| vector.cpp:38:46:38:48 | call to vector | vector.cpp:66:7:66:8 | v4 | | +| vector.cpp:38:46:38:48 | call to vector | vector.cpp:67:7:67:8 | v4 | | +| vector.cpp:38:46:38:48 | call to vector | vector.cpp:101:1:101:1 | v4 | | +| vector.cpp:38:54:38:56 | call to vector | vector.cpp:69:2:69:3 | v5 | | +| vector.cpp:38:54:38:56 | call to vector | vector.cpp:70:7:70:8 | v5 | | +| vector.cpp:38:54:38:56 | call to vector | vector.cpp:71:7:71:8 | v5 | | +| vector.cpp:38:54:38:56 | call to vector | vector.cpp:72:7:72:8 | v5 | | +| vector.cpp:38:54:38:56 | call to vector | vector.cpp:101:1:101:1 | v5 | | +| vector.cpp:38:62:38:64 | call to vector | vector.cpp:74:2:74:3 | v6 | | +| vector.cpp:38:62:38:64 | call to vector | vector.cpp:75:7:75:8 | v6 | | +| vector.cpp:38:62:38:64 | call to vector | vector.cpp:76:7:76:8 | v6 | | +| vector.cpp:38:62:38:64 | call to vector | vector.cpp:101:1:101:1 | v6 | | +| vector.cpp:38:70:38:72 | call to vector | vector.cpp:80:41:80:42 | v7 | | +| vector.cpp:38:70:38:72 | call to vector | vector.cpp:81:3:81:4 | v7 | | +| vector.cpp:38:70:38:72 | call to vector | vector.cpp:83:7:83:8 | v7 | | +| vector.cpp:38:70:38:72 | call to vector | vector.cpp:84:7:84:8 | v7 | | +| vector.cpp:38:70:38:72 | call to vector | vector.cpp:85:7:85:8 | v7 | | +| vector.cpp:38:70:38:72 | call to vector | vector.cpp:101:1:101:1 | v7 | | +| vector.cpp:38:78:38:80 | call to vector | vector.cpp:88:33:88:34 | v8 | | +| vector.cpp:38:78:38:80 | call to vector | vector.cpp:90:3:90:4 | v8 | | +| vector.cpp:38:78:38:80 | call to vector | vector.cpp:92:7:92:8 | v8 | | +| vector.cpp:38:78:38:80 | call to vector | vector.cpp:93:7:93:8 | v8 | | +| vector.cpp:38:78:38:80 | call to vector | vector.cpp:94:7:94:8 | v8 | | +| vector.cpp:38:78:38:80 | call to vector | vector.cpp:101:1:101:1 | v8 | | +| vector.cpp:38:86:38:88 | call to vector | vector.cpp:96:2:96:3 | v9 | | +| vector.cpp:38:86:38:88 | call to vector | vector.cpp:97:7:97:8 | v9 | | +| vector.cpp:38:86:38:88 | call to vector | vector.cpp:98:7:98:8 | v9 | | +| vector.cpp:38:86:38:88 | call to vector | vector.cpp:99:7:99:8 | v9 | | +| vector.cpp:38:86:38:88 | call to vector | vector.cpp:100:7:100:8 | v9 | | +| vector.cpp:38:86:38:88 | call to vector | vector.cpp:101:1:101:1 | v9 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:41:2:41:3 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:42:2:42:3 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:43:2:43:3 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:44:7:44:8 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:40:2:40:3 | v1 | vector.cpp:40:4:40:4 | call to operator[] | TAINT | +| vector.cpp:40:2:40:10 | ... = ... | vector.cpp:40:4:40:4 | call to operator[] [post update] | | +| vector.cpp:40:4:40:4 | call to operator[] [post update] | vector.cpp:40:2:40:3 | ref arg v1 | TAINT | +| vector.cpp:40:10:40:10 | 0 | vector.cpp:40:2:40:10 | ... = ... | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:42:2:42:3 | v1 | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:43:2:43:3 | v1 | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:44:7:44:8 | v1 | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:41:2:41:3 | v1 | vector.cpp:41:4:41:4 | call to operator[] | TAINT | +| vector.cpp:41:2:41:10 | ... = ... | vector.cpp:41:4:41:4 | call to operator[] [post update] | | +| vector.cpp:41:4:41:4 | call to operator[] [post update] | vector.cpp:41:2:41:3 | ref arg v1 | TAINT | +| vector.cpp:41:10:41:10 | 0 | vector.cpp:41:2:41:10 | ... = ... | | +| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:43:2:43:3 | v1 | | +| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:44:7:44:8 | v1 | | +| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | | +| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | | +| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | | +| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:42:2:42:3 | v1 | vector.cpp:42:4:42:4 | call to operator[] | TAINT | +| vector.cpp:42:2:42:10 | ... = ... | vector.cpp:42:4:42:4 | call to operator[] [post update] | | +| vector.cpp:42:4:42:4 | call to operator[] [post update] | vector.cpp:42:2:42:3 | ref arg v1 | TAINT | +| vector.cpp:42:10:42:10 | 0 | vector.cpp:42:2:42:10 | ... = ... | | +| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:44:7:44:8 | v1 | | +| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | | +| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | | +| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | | +| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:43:15:43:15 | 1 | vector.cpp:43:2:43:3 | ref arg v1 | TAINT | +| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | | +| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | | +| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | | +| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | | +| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | | +| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:45:7:45:8 | v1 | vector.cpp:45:9:45:9 | call to operator[] | TAINT | +| vector.cpp:46:7:46:8 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | | +| vector.cpp:46:7:46:8 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:46:7:46:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:46:7:46:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:46:7:46:8 | v1 | vector.cpp:46:9:46:9 | call to operator[] | TAINT | +| vector.cpp:47:7:47:8 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | | +| vector.cpp:47:7:47:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:47:7:47:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:47:7:47:8 | v1 | vector.cpp:47:9:47:9 | call to operator[] | TAINT | +| vector.cpp:48:7:48:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | | +| vector.cpp:48:7:48:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:48:7:48:8 | v1 | vector.cpp:48:10:48:14 | call to front | TAINT | +| vector.cpp:49:7:49:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | | +| vector.cpp:49:7:49:8 | v1 | vector.cpp:49:10:49:13 | call to back | TAINT | +| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:52:7:52:8 | v2 | | +| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:53:7:53:8 | v2 | | +| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:54:7:54:8 | v2 | | +| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:55:7:55:8 | v2 | | +| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | | +| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | | +| vector.cpp:51:2:51:3 | v2 | vector.cpp:51:4:51:4 | call to operator[] | TAINT | +| vector.cpp:51:2:51:17 | ... = ... | vector.cpp:51:4:51:4 | call to operator[] [post update] | | +| vector.cpp:51:4:51:4 | call to operator[] [post update] | vector.cpp:51:2:51:3 | ref arg v2 | TAINT | +| vector.cpp:51:10:51:15 | call to source | vector.cpp:51:2:51:17 | ... = ... | | +| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:53:7:53:8 | v2 | | +| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:54:7:54:8 | v2 | | +| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:55:7:55:8 | v2 | | +| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | | +| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | | +| vector.cpp:53:7:53:8 | ref arg v2 | vector.cpp:54:7:54:8 | v2 | | +| vector.cpp:53:7:53:8 | ref arg v2 | vector.cpp:55:7:55:8 | v2 | | +| vector.cpp:53:7:53:8 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | | +| vector.cpp:53:7:53:8 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | | +| vector.cpp:53:7:53:8 | v2 | vector.cpp:53:9:53:9 | call to operator[] | TAINT | +| vector.cpp:54:7:54:8 | ref arg v2 | vector.cpp:55:7:55:8 | v2 | | +| vector.cpp:54:7:54:8 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | | +| vector.cpp:54:7:54:8 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | | +| vector.cpp:54:7:54:8 | v2 | vector.cpp:54:9:54:9 | call to operator[] | TAINT | +| vector.cpp:55:7:55:8 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | | +| vector.cpp:55:7:55:8 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | | +| vector.cpp:55:7:55:8 | v2 | vector.cpp:55:9:55:9 | call to operator[] | TAINT | +| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:58:7:58:8 | v3 | | +| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:59:7:59:8 | v3 | | +| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:60:7:60:8 | v3 | | +| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:61:7:61:8 | v3 | | +| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | | +| vector.cpp:57:7:57:8 | v2 | vector.cpp:57:2:57:3 | ref arg v3 | TAINT | +| vector.cpp:57:7:57:8 | v2 | vector.cpp:57:5:57:5 | call to operator= | TAINT | +| vector.cpp:58:7:58:8 | ref arg v3 | vector.cpp:59:7:59:8 | v3 | | +| vector.cpp:58:7:58:8 | ref arg v3 | vector.cpp:60:7:60:8 | v3 | | +| vector.cpp:58:7:58:8 | ref arg v3 | vector.cpp:61:7:61:8 | v3 | | +| vector.cpp:58:7:58:8 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | | +| vector.cpp:59:7:59:8 | ref arg v3 | vector.cpp:60:7:60:8 | v3 | | +| vector.cpp:59:7:59:8 | ref arg v3 | vector.cpp:61:7:61:8 | v3 | | +| vector.cpp:59:7:59:8 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | | +| vector.cpp:59:7:59:8 | v3 | vector.cpp:59:9:59:9 | call to operator[] | TAINT | +| vector.cpp:60:7:60:8 | ref arg v3 | vector.cpp:61:7:61:8 | v3 | | +| vector.cpp:60:7:60:8 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | | +| vector.cpp:60:7:60:8 | v3 | vector.cpp:60:9:60:9 | call to operator[] | TAINT | +| vector.cpp:61:7:61:8 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | | +| vector.cpp:61:7:61:8 | v3 | vector.cpp:61:9:61:9 | call to operator[] | TAINT | +| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:64:7:64:8 | v4 | | +| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:65:7:65:8 | v4 | | +| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:66:7:66:8 | v4 | | +| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:67:7:67:8 | v4 | | +| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | | +| vector.cpp:63:2:63:3 | v4 | vector.cpp:63:4:63:4 | call to operator[] | TAINT | +| vector.cpp:63:2:63:17 | ... = ... | vector.cpp:63:4:63:4 | call to operator[] [post update] | | +| vector.cpp:63:4:63:4 | call to operator[] [post update] | vector.cpp:63:2:63:3 | ref arg v4 | TAINT | +| vector.cpp:63:10:63:15 | call to source | vector.cpp:63:2:63:17 | ... = ... | | +| vector.cpp:64:7:64:8 | ref arg v4 | vector.cpp:65:7:65:8 | v4 | | +| vector.cpp:64:7:64:8 | ref arg v4 | vector.cpp:66:7:66:8 | v4 | | +| vector.cpp:64:7:64:8 | ref arg v4 | vector.cpp:67:7:67:8 | v4 | | +| vector.cpp:64:7:64:8 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | | +| vector.cpp:65:7:65:8 | ref arg v4 | vector.cpp:66:7:66:8 | v4 | | +| vector.cpp:65:7:65:8 | ref arg v4 | vector.cpp:67:7:67:8 | v4 | | +| vector.cpp:65:7:65:8 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | | +| vector.cpp:65:7:65:8 | v4 | vector.cpp:65:9:65:9 | call to operator[] | TAINT | +| vector.cpp:66:7:66:8 | ref arg v4 | vector.cpp:67:7:67:8 | v4 | | +| vector.cpp:66:7:66:8 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | | +| vector.cpp:66:7:66:8 | v4 | vector.cpp:66:9:66:9 | call to operator[] | TAINT | +| vector.cpp:67:7:67:8 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | | +| vector.cpp:67:7:67:8 | v4 | vector.cpp:67:9:67:9 | call to operator[] | TAINT | +| vector.cpp:69:2:69:3 | ref arg v5 | vector.cpp:70:7:70:8 | v5 | | +| vector.cpp:69:2:69:3 | ref arg v5 | vector.cpp:71:7:71:8 | v5 | | +| vector.cpp:69:2:69:3 | ref arg v5 | vector.cpp:72:7:72:8 | v5 | | +| vector.cpp:69:2:69:3 | ref arg v5 | vector.cpp:101:1:101:1 | v5 | | +| vector.cpp:69:15:69:20 | call to source | vector.cpp:69:2:69:3 | ref arg v5 | TAINT | +| vector.cpp:70:7:70:8 | ref arg v5 | vector.cpp:71:7:71:8 | v5 | | +| vector.cpp:70:7:70:8 | ref arg v5 | vector.cpp:72:7:72:8 | v5 | | +| vector.cpp:70:7:70:8 | ref arg v5 | vector.cpp:101:1:101:1 | v5 | | +| vector.cpp:71:7:71:8 | ref arg v5 | vector.cpp:72:7:72:8 | v5 | | +| vector.cpp:71:7:71:8 | ref arg v5 | vector.cpp:101:1:101:1 | v5 | | +| vector.cpp:71:7:71:8 | v5 | vector.cpp:71:10:71:14 | call to front | TAINT | +| vector.cpp:72:7:72:8 | ref arg v5 | vector.cpp:101:1:101:1 | v5 | | +| vector.cpp:72:7:72:8 | v5 | vector.cpp:72:10:72:13 | call to back | TAINT | +| vector.cpp:74:2:74:3 | ref arg v6 | vector.cpp:75:7:75:8 | v6 | | +| vector.cpp:74:2:74:3 | ref arg v6 | vector.cpp:76:7:76:8 | v6 | | +| vector.cpp:74:2:74:3 | ref arg v6 | vector.cpp:101:1:101:1 | v6 | | +| vector.cpp:74:2:74:3 | v6 | vector.cpp:74:5:74:8 | call to data | TAINT | +| vector.cpp:74:2:74:13 | access to array [post update] | vector.cpp:74:5:74:8 | call to data [inner post update] | | +| vector.cpp:74:2:74:24 | ... = ... | vector.cpp:74:2:74:13 | access to array [post update] | | +| vector.cpp:74:5:74:8 | call to data | vector.cpp:74:2:74:13 | access to array | TAINT | +| vector.cpp:74:5:74:8 | call to data [inner post update] | vector.cpp:74:2:74:3 | ref arg v6 | TAINT | +| vector.cpp:74:12:74:12 | 2 | vector.cpp:74:2:74:13 | access to array | TAINT | +| vector.cpp:74:17:74:22 | call to source | vector.cpp:74:2:74:24 | ... = ... | | +| vector.cpp:75:7:75:8 | ref arg v6 | vector.cpp:76:7:76:8 | v6 | | +| vector.cpp:75:7:75:8 | ref arg v6 | vector.cpp:101:1:101:1 | v6 | | +| vector.cpp:76:7:76:8 | ref arg v6 | vector.cpp:101:1:101:1 | v6 | | +| vector.cpp:76:7:76:8 | v6 | vector.cpp:76:10:76:13 | call to data | TAINT | +| vector.cpp:76:10:76:13 | call to data | vector.cpp:76:7:76:18 | access to array | TAINT | +| vector.cpp:76:17:76:17 | 2 | vector.cpp:76:7:76:18 | access to array | TAINT | +| vector.cpp:80:40:80:50 | call to iterator | vector.cpp:81:13:81:14 | it | | +| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:81:3:81:4 | v7 | | +| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:83:7:83:8 | v7 | | +| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:84:7:84:8 | v7 | | +| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:85:7:85:8 | v7 | | +| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | | +| vector.cpp:80:41:80:42 | v7 | vector.cpp:80:44:80:48 | call to begin | TAINT | +| vector.cpp:80:44:80:48 | call to begin | vector.cpp:80:40:80:50 | call to iterator | TAINT | +| vector.cpp:81:3:81:4 | ref arg v7 | vector.cpp:83:7:83:8 | v7 | | +| vector.cpp:81:3:81:4 | ref arg v7 | vector.cpp:84:7:84:8 | v7 | | +| vector.cpp:81:3:81:4 | ref arg v7 | vector.cpp:85:7:85:8 | v7 | | +| vector.cpp:81:3:81:4 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | | +| vector.cpp:81:3:81:4 | v7 | vector.cpp:81:6:81:11 | call to insert | TAINT | +| vector.cpp:81:17:81:22 | call to source | vector.cpp:81:3:81:4 | ref arg v7 | TAINT | +| vector.cpp:81:17:81:22 | call to source | vector.cpp:81:6:81:11 | call to insert | TAINT | +| vector.cpp:83:7:83:8 | ref arg v7 | vector.cpp:84:7:84:8 | v7 | | +| vector.cpp:83:7:83:8 | ref arg v7 | vector.cpp:85:7:85:8 | v7 | | +| vector.cpp:83:7:83:8 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | | +| vector.cpp:84:7:84:8 | ref arg v7 | vector.cpp:85:7:85:8 | v7 | | +| vector.cpp:84:7:84:8 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | | +| vector.cpp:84:7:84:8 | v7 | vector.cpp:84:10:84:14 | call to front | TAINT | +| vector.cpp:85:7:85:8 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | | +| vector.cpp:85:7:85:8 | v7 | vector.cpp:85:10:85:13 | call to back | TAINT | +| vector.cpp:88:33:88:34 | v8 | vector.cpp:89:41:89:43 | v8c | | +| vector.cpp:89:41:89:43 | v8c | vector.cpp:89:45:89:49 | call to begin | TAINT | +| vector.cpp:89:45:89:49 | call to begin | vector.cpp:90:13:90:14 | it | | +| vector.cpp:90:3:90:4 | ref arg v8 | vector.cpp:92:7:92:8 | v8 | | +| vector.cpp:90:3:90:4 | ref arg v8 | vector.cpp:93:7:93:8 | v8 | | +| vector.cpp:90:3:90:4 | ref arg v8 | vector.cpp:94:7:94:8 | v8 | | +| vector.cpp:90:3:90:4 | ref arg v8 | vector.cpp:101:1:101:1 | v8 | | +| vector.cpp:90:3:90:4 | v8 | vector.cpp:90:6:90:11 | call to insert | TAINT | +| vector.cpp:92:7:92:8 | ref arg v8 | vector.cpp:93:7:93:8 | v8 | | +| vector.cpp:92:7:92:8 | ref arg v8 | vector.cpp:94:7:94:8 | v8 | | +| vector.cpp:92:7:92:8 | ref arg v8 | vector.cpp:101:1:101:1 | v8 | | +| vector.cpp:93:7:93:8 | ref arg v8 | vector.cpp:94:7:94:8 | v8 | | +| vector.cpp:93:7:93:8 | ref arg v8 | vector.cpp:101:1:101:1 | v8 | | +| vector.cpp:93:7:93:8 | v8 | vector.cpp:93:10:93:14 | call to front | TAINT | +| vector.cpp:94:7:94:8 | ref arg v8 | vector.cpp:101:1:101:1 | v8 | | +| vector.cpp:94:7:94:8 | v8 | vector.cpp:94:10:94:13 | call to back | TAINT | +| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:97:7:97:8 | v9 | | +| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:98:7:98:8 | v9 | | +| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:99:7:99:8 | v9 | | +| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:100:7:100:8 | v9 | | +| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | | +| vector.cpp:96:2:96:3 | v9 | vector.cpp:96:5:96:6 | call to at | TAINT | +| vector.cpp:96:2:96:20 | ... = ... | vector.cpp:96:5:96:6 | call to at [post update] | | +| vector.cpp:96:5:96:6 | call to at [post update] | vector.cpp:96:2:96:3 | ref arg v9 | TAINT | +| vector.cpp:96:13:96:18 | call to source | vector.cpp:96:2:96:20 | ... = ... | | +| vector.cpp:97:7:97:8 | ref arg v9 | vector.cpp:98:7:98:8 | v9 | | +| vector.cpp:97:7:97:8 | ref arg v9 | vector.cpp:99:7:99:8 | v9 | | +| vector.cpp:97:7:97:8 | ref arg v9 | vector.cpp:100:7:100:8 | v9 | | +| vector.cpp:97:7:97:8 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | | +| vector.cpp:98:7:98:8 | ref arg v9 | vector.cpp:99:7:99:8 | v9 | | +| vector.cpp:98:7:98:8 | ref arg v9 | vector.cpp:100:7:100:8 | v9 | | +| vector.cpp:98:7:98:8 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | | +| vector.cpp:98:7:98:8 | v9 | vector.cpp:98:10:98:11 | call to at | TAINT | +| vector.cpp:99:7:99:8 | ref arg v9 | vector.cpp:100:7:100:8 | v9 | | +| vector.cpp:99:7:99:8 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | | +| vector.cpp:99:7:99:8 | v9 | vector.cpp:99:10:99:11 | call to at | TAINT | +| vector.cpp:100:7:100:8 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | | +| vector.cpp:100:7:100:8 | v9 | vector.cpp:100:10:100:11 | call to at | TAINT | +| vector.cpp:104:22:104:24 | call to vector | vector.cpp:106:2:106:3 | v1 | | +| vector.cpp:104:22:104:24 | call to vector | vector.cpp:109:7:109:8 | v1 | | +| vector.cpp:104:22:104:24 | call to vector | vector.cpp:114:2:114:3 | v1 | | +| vector.cpp:104:22:104:24 | call to vector | vector.cpp:117:7:117:8 | v1 | | +| vector.cpp:104:22:104:24 | call to vector | vector.cpp:121:1:121:1 | v1 | | +| vector.cpp:104:30:104:32 | call to vector | vector.cpp:110:7:110:8 | v2 | | +| vector.cpp:104:30:104:32 | call to vector | vector.cpp:114:10:114:11 | v2 | | +| vector.cpp:104:30:104:32 | call to vector | vector.cpp:118:7:118:8 | v2 | | +| vector.cpp:104:30:104:32 | call to vector | vector.cpp:121:1:121:1 | v2 | | +| vector.cpp:104:38:104:40 | call to vector | vector.cpp:111:7:111:8 | v3 | | +| vector.cpp:104:38:104:40 | call to vector | vector.cpp:115:2:115:3 | v3 | | +| vector.cpp:104:38:104:40 | call to vector | vector.cpp:119:7:119:8 | v3 | | +| vector.cpp:104:38:104:40 | call to vector | vector.cpp:121:1:121:1 | v3 | | +| vector.cpp:104:46:104:48 | call to vector | vector.cpp:107:2:107:3 | v4 | | +| vector.cpp:104:46:104:48 | call to vector | vector.cpp:112:7:112:8 | v4 | | +| vector.cpp:104:46:104:48 | call to vector | vector.cpp:115:10:115:11 | v4 | | +| vector.cpp:104:46:104:48 | call to vector | vector.cpp:120:7:120:8 | v4 | | +| vector.cpp:104:46:104:48 | call to vector | vector.cpp:121:1:121:1 | v4 | | +| vector.cpp:106:2:106:3 | ref arg v1 | vector.cpp:109:7:109:8 | v1 | | +| vector.cpp:106:2:106:3 | ref arg v1 | vector.cpp:114:2:114:3 | v1 | | +| vector.cpp:106:2:106:3 | ref arg v1 | vector.cpp:117:7:117:8 | v1 | | +| vector.cpp:106:2:106:3 | ref arg v1 | vector.cpp:121:1:121:1 | v1 | | +| vector.cpp:106:15:106:20 | call to source | vector.cpp:106:2:106:3 | ref arg v1 | TAINT | +| vector.cpp:107:2:107:3 | ref arg v4 | vector.cpp:112:7:112:8 | v4 | | +| vector.cpp:107:2:107:3 | ref arg v4 | vector.cpp:115:10:115:11 | v4 | | +| vector.cpp:107:2:107:3 | ref arg v4 | vector.cpp:120:7:120:8 | v4 | | +| vector.cpp:107:2:107:3 | ref arg v4 | vector.cpp:121:1:121:1 | v4 | | +| vector.cpp:107:15:107:20 | call to source | vector.cpp:107:2:107:3 | ref arg v4 | TAINT | +| vector.cpp:109:7:109:8 | ref arg v1 | vector.cpp:114:2:114:3 | v1 | | +| vector.cpp:109:7:109:8 | ref arg v1 | vector.cpp:117:7:117:8 | v1 | | +| vector.cpp:109:7:109:8 | ref arg v1 | vector.cpp:121:1:121:1 | v1 | | +| vector.cpp:110:7:110:8 | ref arg v2 | vector.cpp:114:10:114:11 | v2 | | +| vector.cpp:110:7:110:8 | ref arg v2 | vector.cpp:118:7:118:8 | v2 | | +| vector.cpp:110:7:110:8 | ref arg v2 | vector.cpp:121:1:121:1 | v2 | | +| vector.cpp:111:7:111:8 | ref arg v3 | vector.cpp:115:2:115:3 | v3 | | +| vector.cpp:111:7:111:8 | ref arg v3 | vector.cpp:119:7:119:8 | v3 | | +| vector.cpp:111:7:111:8 | ref arg v3 | vector.cpp:121:1:121:1 | v3 | | +| vector.cpp:112:7:112:8 | ref arg v4 | vector.cpp:115:10:115:11 | v4 | | +| vector.cpp:112:7:112:8 | ref arg v4 | vector.cpp:120:7:120:8 | v4 | | +| vector.cpp:112:7:112:8 | ref arg v4 | vector.cpp:121:1:121:1 | v4 | | +| vector.cpp:114:2:114:3 | ref arg v1 | vector.cpp:117:7:117:8 | v1 | | +| vector.cpp:114:2:114:3 | ref arg v1 | vector.cpp:121:1:121:1 | v1 | | +| vector.cpp:114:2:114:3 | v1 | vector.cpp:114:10:114:11 | ref arg v2 | TAINT | +| vector.cpp:114:10:114:11 | ref arg v2 | vector.cpp:118:7:118:8 | v2 | | +| vector.cpp:114:10:114:11 | ref arg v2 | vector.cpp:121:1:121:1 | v2 | | +| vector.cpp:114:10:114:11 | v2 | vector.cpp:114:2:114:3 | ref arg v1 | TAINT | +| vector.cpp:115:2:115:3 | ref arg v3 | vector.cpp:119:7:119:8 | v3 | | +| vector.cpp:115:2:115:3 | ref arg v3 | vector.cpp:121:1:121:1 | v3 | | +| vector.cpp:115:2:115:3 | v3 | vector.cpp:115:10:115:11 | ref arg v4 | TAINT | +| vector.cpp:115:10:115:11 | ref arg v4 | vector.cpp:120:7:120:8 | v4 | | +| vector.cpp:115:10:115:11 | ref arg v4 | vector.cpp:121:1:121:1 | v4 | | +| vector.cpp:115:10:115:11 | v4 | vector.cpp:115:2:115:3 | ref arg v3 | TAINT | +| vector.cpp:117:7:117:8 | ref arg v1 | vector.cpp:121:1:121:1 | v1 | | +| vector.cpp:118:7:118:8 | ref arg v2 | vector.cpp:121:1:121:1 | v2 | | +| vector.cpp:119:7:119:8 | ref arg v3 | vector.cpp:121:1:121:1 | v3 | | +| vector.cpp:120:7:120:8 | ref arg v4 | vector.cpp:121:1:121:1 | v4 | | +| vector.cpp:124:22:124:24 | call to vector | vector.cpp:126:2:126:3 | v1 | | +| vector.cpp:124:22:124:24 | call to vector | vector.cpp:130:7:130:8 | v1 | | +| vector.cpp:124:22:124:24 | call to vector | vector.cpp:135:2:135:3 | v1 | | +| vector.cpp:124:22:124:24 | call to vector | vector.cpp:139:7:139:8 | v1 | | +| vector.cpp:124:22:124:24 | call to vector | vector.cpp:143:1:143:1 | v1 | | +| vector.cpp:124:30:124:32 | call to vector | vector.cpp:127:2:127:3 | v2 | | +| vector.cpp:124:30:124:32 | call to vector | vector.cpp:131:7:131:8 | v2 | | +| vector.cpp:124:30:124:32 | call to vector | vector.cpp:136:2:136:3 | v2 | | +| vector.cpp:124:30:124:32 | call to vector | vector.cpp:136:7:136:8 | v2 | | +| vector.cpp:124:30:124:32 | call to vector | vector.cpp:140:7:140:8 | v2 | | +| vector.cpp:124:30:124:32 | call to vector | vector.cpp:143:1:143:1 | v2 | | +| vector.cpp:124:38:124:40 | call to vector | vector.cpp:128:2:128:3 | v3 | | +| vector.cpp:124:38:124:40 | call to vector | vector.cpp:132:7:132:8 | v3 | | +| vector.cpp:124:38:124:40 | call to vector | vector.cpp:137:2:137:3 | v3 | | +| vector.cpp:124:38:124:40 | call to vector | vector.cpp:141:7:141:8 | v3 | | +| vector.cpp:124:38:124:40 | call to vector | vector.cpp:143:1:143:1 | v3 | | +| vector.cpp:124:46:124:48 | call to vector | vector.cpp:133:7:133:8 | v4 | | +| vector.cpp:124:46:124:48 | call to vector | vector.cpp:137:7:137:8 | v4 | | +| vector.cpp:124:46:124:48 | call to vector | vector.cpp:142:7:142:8 | v4 | | +| vector.cpp:124:46:124:48 | call to vector | vector.cpp:143:1:143:1 | v4 | | +| vector.cpp:126:2:126:3 | ref arg v1 | vector.cpp:130:7:130:8 | v1 | | +| vector.cpp:126:2:126:3 | ref arg v1 | vector.cpp:135:2:135:3 | v1 | | +| vector.cpp:126:2:126:3 | ref arg v1 | vector.cpp:139:7:139:8 | v1 | | +| vector.cpp:126:2:126:3 | ref arg v1 | vector.cpp:143:1:143:1 | v1 | | +| vector.cpp:126:15:126:20 | call to source | vector.cpp:126:2:126:3 | ref arg v1 | TAINT | +| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:131:7:131:8 | v2 | | +| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:136:2:136:3 | v2 | | +| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:136:7:136:8 | v2 | | +| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:140:7:140:8 | v2 | | +| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:143:1:143:1 | v2 | | +| vector.cpp:127:15:127:20 | call to source | vector.cpp:127:2:127:3 | ref arg v2 | TAINT | +| vector.cpp:128:2:128:3 | ref arg v3 | vector.cpp:132:7:132:8 | v3 | | +| vector.cpp:128:2:128:3 | ref arg v3 | vector.cpp:137:2:137:3 | v3 | | +| vector.cpp:128:2:128:3 | ref arg v3 | vector.cpp:141:7:141:8 | v3 | | +| vector.cpp:128:2:128:3 | ref arg v3 | vector.cpp:143:1:143:1 | v3 | | +| vector.cpp:128:15:128:20 | call to source | vector.cpp:128:2:128:3 | ref arg v3 | TAINT | +| vector.cpp:130:7:130:8 | ref arg v1 | vector.cpp:135:2:135:3 | v1 | | +| vector.cpp:130:7:130:8 | ref arg v1 | vector.cpp:139:7:139:8 | v1 | | +| vector.cpp:130:7:130:8 | ref arg v1 | vector.cpp:143:1:143:1 | v1 | | +| vector.cpp:131:7:131:8 | ref arg v2 | vector.cpp:136:2:136:3 | v2 | | +| vector.cpp:131:7:131:8 | ref arg v2 | vector.cpp:136:7:136:8 | v2 | | +| vector.cpp:131:7:131:8 | ref arg v2 | vector.cpp:140:7:140:8 | v2 | | +| vector.cpp:131:7:131:8 | ref arg v2 | vector.cpp:143:1:143:1 | v2 | | +| vector.cpp:132:7:132:8 | ref arg v3 | vector.cpp:137:2:137:3 | v3 | | +| vector.cpp:132:7:132:8 | ref arg v3 | vector.cpp:141:7:141:8 | v3 | | +| vector.cpp:132:7:132:8 | ref arg v3 | vector.cpp:143:1:143:1 | v3 | | +| vector.cpp:133:7:133:8 | ref arg v4 | vector.cpp:137:7:137:8 | v4 | | +| vector.cpp:133:7:133:8 | ref arg v4 | vector.cpp:142:7:142:8 | v4 | | +| vector.cpp:133:7:133:8 | ref arg v4 | vector.cpp:143:1:143:1 | v4 | | +| vector.cpp:135:2:135:3 | ref arg v1 | vector.cpp:139:7:139:8 | v1 | | +| vector.cpp:135:2:135:3 | ref arg v1 | vector.cpp:143:1:143:1 | v1 | | +| vector.cpp:136:2:136:3 | ref arg v2 | vector.cpp:140:7:140:8 | v2 | | +| vector.cpp:136:2:136:3 | ref arg v2 | vector.cpp:143:1:143:1 | v2 | | +| vector.cpp:136:7:136:8 | v2 | vector.cpp:136:2:136:3 | ref arg v2 | TAINT | +| vector.cpp:136:7:136:8 | v2 | vector.cpp:136:5:136:5 | call to operator= | TAINT | +| vector.cpp:137:2:137:3 | ref arg v3 | vector.cpp:141:7:141:8 | v3 | | +| vector.cpp:137:2:137:3 | ref arg v3 | vector.cpp:143:1:143:1 | v3 | | +| vector.cpp:137:7:137:8 | v4 | vector.cpp:137:2:137:3 | ref arg v3 | TAINT | +| vector.cpp:137:7:137:8 | v4 | vector.cpp:137:5:137:5 | call to operator= | TAINT | +| vector.cpp:139:7:139:8 | ref arg v1 | vector.cpp:143:1:143:1 | v1 | | +| vector.cpp:140:7:140:8 | ref arg v2 | vector.cpp:143:1:143:1 | v2 | | +| vector.cpp:141:7:141:8 | ref arg v3 | vector.cpp:143:1:143:1 | v3 | | +| vector.cpp:142:7:142:8 | ref arg v4 | vector.cpp:143:1:143:1 | v4 | | +| vector.cpp:150:8:150:8 | call to vector | vector.cpp:150:8:150:8 | constructor init of field vs | TAINT | +| vector.cpp:150:8:150:8 | call to ~vector | vector.cpp:150:8:150:8 | destructor field destruction of vs | TAINT | +| vector.cpp:150:8:150:8 | this | vector.cpp:150:8:150:8 | constructor init of field vs [pre-this] | | +| vector.cpp:158:19:158:22 | {...} | vector.cpp:160:8:160:9 | aa | | +| vector.cpp:158:19:158:22 | {...} | vector.cpp:161:3:161:4 | aa | | +| vector.cpp:158:19:158:22 | {...} | vector.cpp:162:8:162:9 | aa | | +| vector.cpp:158:21:158:21 | 0 | vector.cpp:158:21:158:21 | {...} | TAINT | +| vector.cpp:158:21:158:21 | {...} | vector.cpp:158:19:158:22 | {...} | TAINT | +| vector.cpp:160:8:160:9 | aa | vector.cpp:160:8:160:12 | access to array | TAINT | +| vector.cpp:160:8:160:9 | aa | vector.cpp:160:8:160:15 | access to array | | +| vector.cpp:160:8:160:12 | access to array | vector.cpp:160:8:160:15 | access to array | TAINT | +| vector.cpp:160:11:160:11 | 0 | vector.cpp:160:8:160:12 | access to array | TAINT | +| vector.cpp:160:14:160:14 | 0 | vector.cpp:160:8:160:15 | access to array | TAINT | +| vector.cpp:161:3:161:4 | aa | vector.cpp:161:3:161:7 | access to array | TAINT | +| vector.cpp:161:3:161:4 | aa | vector.cpp:161:3:161:10 | access to array | | +| vector.cpp:161:3:161:7 | access to array | vector.cpp:161:3:161:10 | access to array | TAINT | +| vector.cpp:161:3:161:10 | access to array [post update] | vector.cpp:161:3:161:4 | aa [inner post update] | | +| vector.cpp:161:3:161:10 | access to array [post update] | vector.cpp:162:8:162:9 | aa | | +| vector.cpp:161:3:161:21 | ... = ... | vector.cpp:161:3:161:10 | access to array [post update] | | +| vector.cpp:161:6:161:6 | 0 | vector.cpp:161:3:161:7 | access to array | TAINT | +| vector.cpp:161:9:161:9 | 0 | vector.cpp:161:3:161:10 | access to array | TAINT | +| vector.cpp:161:14:161:19 | call to source | vector.cpp:161:3:161:21 | ... = ... | | +| vector.cpp:162:8:162:9 | aa | vector.cpp:162:8:162:12 | access to array | TAINT | +| vector.cpp:162:8:162:9 | aa | vector.cpp:162:8:162:15 | access to array | | +| vector.cpp:162:8:162:12 | access to array | vector.cpp:162:8:162:15 | access to array | TAINT | +| vector.cpp:162:11:162:11 | 0 | vector.cpp:162:8:162:12 | access to array | TAINT | +| vector.cpp:162:14:162:14 | 0 | vector.cpp:162:8:162:15 | access to array | TAINT | +| vector.cpp:166:37:166:39 | call to vector | vector.cpp:168:3:168:4 | bb | | +| vector.cpp:166:37:166:39 | call to vector | vector.cpp:169:8:169:9 | bb | | +| vector.cpp:166:37:166:39 | call to vector | vector.cpp:170:3:170:4 | bb | | +| vector.cpp:166:37:166:39 | call to vector | vector.cpp:171:8:171:9 | bb | | +| vector.cpp:166:37:166:39 | call to vector | vector.cpp:172:2:172:2 | bb | | +| vector.cpp:168:3:168:4 | bb | vector.cpp:168:5:168:5 | call to operator[] | TAINT | +| vector.cpp:168:3:168:4 | ref arg bb | vector.cpp:169:8:169:9 | bb | | +| vector.cpp:168:3:168:4 | ref arg bb | vector.cpp:170:3:170:4 | bb | | +| vector.cpp:168:3:168:4 | ref arg bb | vector.cpp:171:8:171:9 | bb | | +| vector.cpp:168:3:168:4 | ref arg bb | vector.cpp:172:2:172:2 | bb | | +| vector.cpp:168:5:168:5 | ref arg call to operator[] | vector.cpp:168:3:168:4 | ref arg bb | TAINT | +| vector.cpp:168:19:168:19 | 0 | vector.cpp:168:5:168:5 | ref arg call to operator[] | TAINT | +| vector.cpp:169:8:169:9 | bb | vector.cpp:169:10:169:10 | call to operator[] | TAINT | +| vector.cpp:169:8:169:9 | ref arg bb | vector.cpp:170:3:170:4 | bb | | +| vector.cpp:169:8:169:9 | ref arg bb | vector.cpp:171:8:171:9 | bb | | +| vector.cpp:169:8:169:9 | ref arg bb | vector.cpp:172:2:172:2 | bb | | +| vector.cpp:169:10:169:10 | call to operator[] | vector.cpp:169:13:169:13 | call to operator[] | TAINT | +| vector.cpp:169:10:169:10 | ref arg call to operator[] | vector.cpp:169:8:169:9 | ref arg bb | TAINT | +| vector.cpp:170:3:170:4 | bb | vector.cpp:170:5:170:5 | call to operator[] | TAINT | +| vector.cpp:170:3:170:4 | ref arg bb | vector.cpp:171:8:171:9 | bb | | +| vector.cpp:170:3:170:4 | ref arg bb | vector.cpp:172:2:172:2 | bb | | +| vector.cpp:170:3:170:21 | ... = ... | vector.cpp:170:8:170:8 | call to operator[] [post update] | | +| vector.cpp:170:5:170:5 | call to operator[] | vector.cpp:170:8:170:8 | call to operator[] | TAINT | +| vector.cpp:170:5:170:5 | ref arg call to operator[] | vector.cpp:170:3:170:4 | ref arg bb | TAINT | +| vector.cpp:170:8:170:8 | call to operator[] [post update] | vector.cpp:170:5:170:5 | ref arg call to operator[] | TAINT | +| vector.cpp:170:14:170:19 | call to source | vector.cpp:170:3:170:21 | ... = ... | | +| vector.cpp:171:8:171:9 | bb | vector.cpp:171:10:171:10 | call to operator[] | TAINT | +| vector.cpp:171:8:171:9 | ref arg bb | vector.cpp:172:2:172:2 | bb | | +| vector.cpp:171:10:171:10 | call to operator[] | vector.cpp:171:13:171:13 | call to operator[] | TAINT | +| vector.cpp:171:10:171:10 | ref arg call to operator[] | vector.cpp:171:8:171:9 | ref arg bb | TAINT | +| vector.cpp:175:20:175:21 | call to vector | vector.cpp:175:20:175:21 | {...} | TAINT | +| vector.cpp:175:20:175:21 | {...} | vector.cpp:177:3:177:4 | cc | | +| vector.cpp:175:20:175:21 | {...} | vector.cpp:178:8:178:9 | cc | | +| vector.cpp:175:20:175:21 | {...} | vector.cpp:179:3:179:4 | cc | | +| vector.cpp:175:20:175:21 | {...} | vector.cpp:180:8:180:9 | cc | | +| vector.cpp:175:20:175:21 | {...} | vector.cpp:181:2:181:2 | cc | | +| vector.cpp:177:3:177:4 | cc | vector.cpp:177:3:177:7 | access to array | | +| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:177:3:177:4 | cc [inner post update] | | +| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:178:8:178:9 | cc | | +| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:179:3:179:4 | cc | | +| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:180:8:180:9 | cc | | +| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:181:2:181:2 | cc | | +| vector.cpp:177:6:177:6 | 0 | vector.cpp:177:3:177:7 | access to array | TAINT | +| vector.cpp:177:19:177:19 | 0 | vector.cpp:177:3:177:7 | ref arg access to array | TAINT | +| vector.cpp:178:8:178:9 | cc | vector.cpp:178:8:178:12 | access to array | | +| vector.cpp:178:8:178:12 | access to array | vector.cpp:178:13:178:13 | call to operator[] | TAINT | +| vector.cpp:178:8:178:12 | ref arg access to array | vector.cpp:178:8:178:9 | cc [inner post update] | | +| vector.cpp:178:8:178:12 | ref arg access to array | vector.cpp:179:3:179:4 | cc | | +| vector.cpp:178:8:178:12 | ref arg access to array | vector.cpp:180:8:180:9 | cc | | +| vector.cpp:178:8:178:12 | ref arg access to array | vector.cpp:181:2:181:2 | cc | | +| vector.cpp:178:11:178:11 | 0 | vector.cpp:178:8:178:12 | access to array | TAINT | +| vector.cpp:179:3:179:4 | cc | vector.cpp:179:3:179:7 | access to array | | +| vector.cpp:179:3:179:7 | access to array | vector.cpp:179:8:179:8 | call to operator[] | TAINT | +| vector.cpp:179:3:179:7 | ref arg access to array | vector.cpp:179:3:179:4 | cc [inner post update] | | +| vector.cpp:179:3:179:7 | ref arg access to array | vector.cpp:180:8:180:9 | cc | | +| vector.cpp:179:3:179:7 | ref arg access to array | vector.cpp:181:2:181:2 | cc | | +| vector.cpp:179:3:179:21 | ... = ... | vector.cpp:179:8:179:8 | call to operator[] [post update] | | +| vector.cpp:179:6:179:6 | 0 | vector.cpp:179:3:179:7 | access to array | TAINT | +| vector.cpp:179:8:179:8 | call to operator[] [post update] | vector.cpp:179:3:179:7 | ref arg access to array | TAINT | +| vector.cpp:179:14:179:19 | call to source | vector.cpp:179:3:179:21 | ... = ... | | +| vector.cpp:180:8:180:9 | cc | vector.cpp:180:8:180:12 | access to array | | +| vector.cpp:180:8:180:12 | access to array | vector.cpp:180:13:180:13 | call to operator[] | TAINT | +| vector.cpp:180:8:180:12 | ref arg access to array | vector.cpp:180:8:180:9 | cc [inner post update] | | +| vector.cpp:180:8:180:12 | ref arg access to array | vector.cpp:181:2:181:2 | cc | | +| vector.cpp:180:11:180:11 | 0 | vector.cpp:180:8:180:12 | access to array | TAINT | +| vector.cpp:184:23:184:24 | call to vector | vector.cpp:187:3:187:4 | dd | | +| vector.cpp:184:23:184:24 | call to vector | vector.cpp:188:8:188:9 | dd | | +| vector.cpp:184:23:184:24 | call to vector | vector.cpp:189:8:189:9 | dd | | +| vector.cpp:184:23:184:24 | call to vector | vector.cpp:190:3:190:4 | dd | | +| vector.cpp:184:23:184:24 | call to vector | vector.cpp:191:8:191:9 | dd | | +| vector.cpp:184:23:184:24 | call to vector | vector.cpp:192:8:192:9 | dd | | +| vector.cpp:184:23:184:24 | call to vector | vector.cpp:193:2:193:2 | dd | | +| vector.cpp:185:14:185:20 | {...} | vector.cpp:187:16:187:17 | mp | | +| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:188:8:188:9 | dd | | +| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:189:8:189:9 | dd | | +| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:190:3:190:4 | dd | | +| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:191:8:191:9 | dd | | +| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:192:8:192:9 | dd | | +| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:193:2:193:2 | dd | | +| vector.cpp:187:16:187:17 | mp | vector.cpp:187:3:187:4 | ref arg dd | TAINT | +| vector.cpp:188:8:188:9 | dd | vector.cpp:188:10:188:10 | call to operator[] | TAINT | +| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:189:8:189:9 | dd | | +| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:190:3:190:4 | dd | | +| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:191:8:191:9 | dd | | +| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:192:8:192:9 | dd | | +| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:193:2:193:2 | dd | | +| vector.cpp:189:8:189:9 | dd | vector.cpp:189:10:189:10 | call to operator[] | TAINT | +| vector.cpp:189:8:189:9 | ref arg dd | vector.cpp:190:3:190:4 | dd | | +| vector.cpp:189:8:189:9 | ref arg dd | vector.cpp:191:8:191:9 | dd | | +| vector.cpp:189:8:189:9 | ref arg dd | vector.cpp:192:8:192:9 | dd | | +| vector.cpp:189:8:189:9 | ref arg dd | vector.cpp:193:2:193:2 | dd | | +| vector.cpp:190:3:190:4 | dd | vector.cpp:190:5:190:5 | call to operator[] | TAINT | +| vector.cpp:190:3:190:4 | ref arg dd | vector.cpp:191:8:191:9 | dd | | +| vector.cpp:190:3:190:4 | ref arg dd | vector.cpp:192:8:192:9 | dd | | +| vector.cpp:190:3:190:4 | ref arg dd | vector.cpp:193:2:193:2 | dd | | +| vector.cpp:190:3:190:20 | ... = ... | vector.cpp:190:9:190:9 | a [post update] | | +| vector.cpp:190:5:190:5 | call to operator[] [post update] | vector.cpp:190:3:190:4 | ref arg dd | TAINT | +| vector.cpp:190:13:190:18 | call to source | vector.cpp:190:3:190:20 | ... = ... | | +| vector.cpp:191:8:191:9 | dd | vector.cpp:191:10:191:10 | call to operator[] | TAINT | +| vector.cpp:191:8:191:9 | ref arg dd | vector.cpp:192:8:192:9 | dd | | +| vector.cpp:191:8:191:9 | ref arg dd | vector.cpp:193:2:193:2 | dd | | +| vector.cpp:192:8:192:9 | dd | vector.cpp:192:10:192:10 | call to operator[] | TAINT | +| vector.cpp:192:8:192:9 | ref arg dd | vector.cpp:193:2:193:2 | dd | | +| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:198:3:198:4 | ee | | +| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:199:8:199:9 | ee | | +| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:200:3:200:4 | ee | | +| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:201:8:201:9 | ee | | +| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:202:2:202:2 | ee | | +| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:199:8:199:9 | ee | | +| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:200:3:200:4 | ee | | +| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:201:8:201:9 | ee | | +| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:202:2:202:2 | ee | | +| vector.cpp:198:19:198:19 | 0 | vector.cpp:198:6:198:7 | ref arg vs | TAINT | +| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:200:3:200:4 | ee | | +| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:201:8:201:9 | ee | | +| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:202:2:202:2 | ee | | +| vector.cpp:199:11:199:12 | vs | vector.cpp:199:13:199:13 | call to operator[] | TAINT | +| vector.cpp:200:3:200:4 | ee [post update] | vector.cpp:201:8:201:9 | ee | | +| vector.cpp:200:3:200:4 | ee [post update] | vector.cpp:202:2:202:2 | ee | | +| vector.cpp:200:3:200:21 | ... = ... | vector.cpp:200:8:200:8 | call to operator[] [post update] | | +| vector.cpp:200:6:200:7 | vs | vector.cpp:200:8:200:8 | call to operator[] | TAINT | +| vector.cpp:200:8:200:8 | call to operator[] [post update] | vector.cpp:200:6:200:7 | ref arg vs | TAINT | +| vector.cpp:200:14:200:19 | call to source | vector.cpp:200:3:200:21 | ... = ... | | +| vector.cpp:201:8:201:9 | ee [post update] | vector.cpp:202:2:202:2 | ee | | +| vector.cpp:201:11:201:12 | vs | vector.cpp:201:13:201:13 | call to operator[] | TAINT | +| vector.cpp:205:34:205:35 | call to vector | vector.cpp:209:3:209:4 | ff | | +| vector.cpp:205:34:205:35 | call to vector | vector.cpp:210:8:210:9 | ff | | +| vector.cpp:205:34:205:35 | call to vector | vector.cpp:211:3:211:4 | ff | | +| vector.cpp:205:34:205:35 | call to vector | vector.cpp:212:8:212:9 | ff | | +| vector.cpp:205:34:205:35 | call to vector | vector.cpp:213:2:213:2 | ff | | +| vector.cpp:206:21:206:23 | call to MyVectorContainer | vector.cpp:208:3:208:5 | mvc | | +| vector.cpp:206:21:206:23 | call to MyVectorContainer | vector.cpp:209:16:209:18 | mvc | | +| vector.cpp:206:21:206:23 | call to MyVectorContainer | vector.cpp:213:2:213:2 | mvc | | +| vector.cpp:208:3:208:5 | mvc [post update] | vector.cpp:209:16:209:18 | mvc | | +| vector.cpp:208:3:208:5 | mvc [post update] | vector.cpp:213:2:213:2 | mvc | | +| vector.cpp:208:20:208:20 | 0 | vector.cpp:208:7:208:8 | ref arg vs | TAINT | +| vector.cpp:209:3:209:4 | ref arg ff | vector.cpp:210:8:210:9 | ff | | +| vector.cpp:209:3:209:4 | ref arg ff | vector.cpp:211:3:211:4 | ff | | +| vector.cpp:209:3:209:4 | ref arg ff | vector.cpp:212:8:212:9 | ff | | +| vector.cpp:209:3:209:4 | ref arg ff | vector.cpp:213:2:213:2 | ff | | +| vector.cpp:209:16:209:18 | mvc | vector.cpp:209:3:209:4 | ref arg ff | TAINT | +| vector.cpp:210:8:210:9 | ff | vector.cpp:210:10:210:10 | call to operator[] | TAINT | +| vector.cpp:210:8:210:9 | ref arg ff | vector.cpp:211:3:211:4 | ff | | +| vector.cpp:210:8:210:9 | ref arg ff | vector.cpp:212:8:212:9 | ff | | +| vector.cpp:210:8:210:9 | ref arg ff | vector.cpp:213:2:213:2 | ff | | +| vector.cpp:210:10:210:10 | call to operator[] [post update] | vector.cpp:210:8:210:9 | ref arg ff | TAINT | +| vector.cpp:210:14:210:15 | vs | vector.cpp:210:16:210:16 | call to operator[] | TAINT | +| vector.cpp:211:3:211:4 | ff | vector.cpp:211:5:211:5 | call to operator[] | TAINT | +| vector.cpp:211:3:211:4 | ref arg ff | vector.cpp:212:8:212:9 | ff | | +| vector.cpp:211:3:211:4 | ref arg ff | vector.cpp:213:2:213:2 | ff | | +| vector.cpp:211:3:211:24 | ... = ... | vector.cpp:211:11:211:11 | call to operator[] [post update] | | +| vector.cpp:211:5:211:5 | call to operator[] [post update] | vector.cpp:211:3:211:4 | ref arg ff | TAINT | +| vector.cpp:211:9:211:10 | vs | vector.cpp:211:11:211:11 | call to operator[] | TAINT | +| vector.cpp:211:11:211:11 | call to operator[] [post update] | vector.cpp:211:9:211:10 | ref arg vs | TAINT | +| vector.cpp:211:17:211:22 | call to source | vector.cpp:211:3:211:24 | ... = ... | | +| vector.cpp:212:8:212:9 | ff | vector.cpp:212:10:212:10 | call to operator[] | TAINT | +| vector.cpp:212:8:212:9 | ref arg ff | vector.cpp:213:2:213:2 | ff | | +| vector.cpp:212:10:212:10 | call to operator[] [post update] | vector.cpp:212:8:212:9 | ref arg ff | TAINT | +| vector.cpp:212:14:212:15 | vs | vector.cpp:212:16:212:16 | call to operator[] | TAINT | +| vector.cpp:235:19:235:20 | call to vector | vector.cpp:237:2:237:3 | v1 | | +| vector.cpp:235:19:235:20 | call to vector | vector.cpp:241:7:241:8 | v1 | | +| vector.cpp:235:19:235:20 | call to vector | vector.cpp:249:13:249:14 | v1 | | +| vector.cpp:235:19:235:20 | call to vector | vector.cpp:249:25:249:26 | v1 | | +| vector.cpp:235:19:235:20 | call to vector | vector.cpp:277:1:277:1 | v1 | | +| vector.cpp:235:23:235:24 | call to vector | vector.cpp:238:2:238:3 | v2 | | +| vector.cpp:235:23:235:24 | call to vector | vector.cpp:242:7:242:8 | v2 | | +| vector.cpp:235:23:235:24 | call to vector | vector.cpp:277:1:277:1 | v2 | | +| vector.cpp:235:27:235:28 | call to vector | vector.cpp:239:2:239:3 | v3 | | +| vector.cpp:235:27:235:28 | call to vector | vector.cpp:243:7:243:8 | v3 | | +| vector.cpp:235:27:235:28 | call to vector | vector.cpp:250:13:250:14 | v3 | | +| vector.cpp:235:27:235:28 | call to vector | vector.cpp:250:25:250:26 | v3 | | +| vector.cpp:235:27:235:28 | call to vector | vector.cpp:251:8:251:9 | v3 | | +| vector.cpp:235:27:235:28 | call to vector | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:237:2:237:3 | ref arg v1 | vector.cpp:241:7:241:8 | v1 | | +| vector.cpp:237:2:237:3 | ref arg v1 | vector.cpp:249:13:249:14 | v1 | | +| vector.cpp:237:2:237:3 | ref arg v1 | vector.cpp:249:25:249:26 | v1 | | +| vector.cpp:237:2:237:3 | ref arg v1 | vector.cpp:277:1:277:1 | v1 | | +| vector.cpp:237:17:237:17 | 0 | vector.cpp:237:2:237:3 | ref arg v1 | TAINT | +| vector.cpp:238:2:238:3 | ref arg v2 | vector.cpp:242:7:242:8 | v2 | | +| vector.cpp:238:2:238:3 | ref arg v2 | vector.cpp:277:1:277:1 | v2 | | +| vector.cpp:238:17:238:30 | call to source | vector.cpp:238:2:238:3 | ref arg v2 | TAINT | +| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:243:7:243:8 | v3 | | +| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:250:13:250:14 | v3 | | +| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:250:25:250:26 | v3 | | +| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:251:8:251:9 | v3 | | +| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:239:15:239:20 | call to source | vector.cpp:239:2:239:3 | ref arg v3 | TAINT | +| vector.cpp:241:7:241:8 | ref arg v1 | vector.cpp:249:13:249:14 | v1 | | +| vector.cpp:241:7:241:8 | ref arg v1 | vector.cpp:249:25:249:26 | v1 | | +| vector.cpp:241:7:241:8 | ref arg v1 | vector.cpp:277:1:277:1 | v1 | | +| vector.cpp:242:7:242:8 | ref arg v2 | vector.cpp:277:1:277:1 | v2 | | +| vector.cpp:243:7:243:8 | ref arg v3 | vector.cpp:250:13:250:14 | v3 | | +| vector.cpp:243:7:243:8 | ref arg v3 | vector.cpp:250:25:250:26 | v3 | | +| vector.cpp:243:7:243:8 | ref arg v3 | vector.cpp:251:8:251:9 | v3 | | +| vector.cpp:243:7:243:8 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:246:20:246:21 | call to vector | vector.cpp:249:3:249:4 | v4 | | +| vector.cpp:246:20:246:21 | call to vector | vector.cpp:257:8:257:9 | v4 | | +| vector.cpp:246:20:246:21 | call to vector | vector.cpp:262:2:262:2 | v4 | | +| vector.cpp:246:24:246:25 | call to vector | vector.cpp:250:3:250:4 | v5 | | +| vector.cpp:246:24:246:25 | call to vector | vector.cpp:258:8:258:9 | v5 | | +| vector.cpp:246:24:246:25 | call to vector | vector.cpp:262:2:262:2 | v5 | | +| vector.cpp:246:28:246:29 | call to vector | vector.cpp:255:3:255:4 | v6 | | +| vector.cpp:246:28:246:29 | call to vector | vector.cpp:261:8:261:9 | v6 | | +| vector.cpp:246:28:246:29 | call to vector | vector.cpp:262:2:262:2 | v6 | | +| vector.cpp:249:3:249:4 | ref arg v4 | vector.cpp:257:8:257:9 | v4 | | +| vector.cpp:249:3:249:4 | ref arg v4 | vector.cpp:262:2:262:2 | v4 | | +| vector.cpp:249:13:249:14 | ref arg v1 | vector.cpp:249:25:249:26 | v1 | | +| vector.cpp:249:13:249:14 | ref arg v1 | vector.cpp:277:1:277:1 | v1 | | +| vector.cpp:249:13:249:14 | v1 | vector.cpp:249:16:249:20 | call to begin | TAINT | +| vector.cpp:249:16:249:20 | call to begin | vector.cpp:249:3:249:4 | ref arg v4 | TAINT | +| vector.cpp:249:25:249:26 | ref arg v1 | vector.cpp:277:1:277:1 | v1 | | +| vector.cpp:249:25:249:26 | v1 | vector.cpp:249:28:249:30 | call to end | TAINT | +| vector.cpp:249:28:249:30 | call to end | vector.cpp:249:3:249:4 | ref arg v4 | TAINT | +| vector.cpp:250:3:250:4 | ref arg v5 | vector.cpp:258:8:258:9 | v5 | | +| vector.cpp:250:3:250:4 | ref arg v5 | vector.cpp:262:2:262:2 | v5 | | +| vector.cpp:250:13:250:14 | ref arg v3 | vector.cpp:250:25:250:26 | v3 | | +| vector.cpp:250:13:250:14 | ref arg v3 | vector.cpp:251:8:251:9 | v3 | | +| vector.cpp:250:13:250:14 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:250:13:250:14 | v3 | vector.cpp:250:16:250:20 | call to begin | TAINT | +| vector.cpp:250:16:250:20 | call to begin | vector.cpp:250:3:250:4 | ref arg v5 | TAINT | +| vector.cpp:250:25:250:26 | ref arg v3 | vector.cpp:251:8:251:9 | v3 | | +| vector.cpp:250:25:250:26 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:250:25:250:26 | v3 | vector.cpp:250:28:250:30 | call to end | TAINT | +| vector.cpp:250:28:250:30 | call to end | vector.cpp:250:3:250:4 | ref arg v5 | TAINT | +| vector.cpp:251:8:251:9 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:251:8:251:9 | v3 | vector.cpp:251:11:251:15 | call to begin | TAINT | +| vector.cpp:251:11:251:15 | call to begin | vector.cpp:251:3:251:17 | ... = ... | | +| vector.cpp:251:11:251:15 | call to begin | vector.cpp:252:3:252:4 | i1 | | +| vector.cpp:251:11:251:15 | call to begin | vector.cpp:253:8:253:9 | i1 | | +| vector.cpp:251:11:251:15 | call to begin | vector.cpp:255:13:255:14 | i1 | | +| vector.cpp:251:11:251:15 | call to begin | vector.cpp:259:8:259:9 | i1 | | +| vector.cpp:252:3:252:4 | i1 | vector.cpp:252:5:252:5 | call to operator++ | | +| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:253:8:253:9 | i1 | | +| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:255:13:255:14 | i1 | | +| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:259:8:259:9 | i1 | | +| vector.cpp:253:8:253:9 | i1 | vector.cpp:253:3:253:9 | ... = ... | | +| vector.cpp:253:8:253:9 | i1 | vector.cpp:254:3:254:4 | i2 | | +| vector.cpp:253:8:253:9 | i1 | vector.cpp:255:17:255:18 | i2 | | +| vector.cpp:253:8:253:9 | i1 | vector.cpp:260:8:260:9 | i2 | | +| vector.cpp:254:3:254:4 | i2 | vector.cpp:254:5:254:5 | call to operator++ | | +| vector.cpp:254:3:254:4 | ref arg i2 | vector.cpp:255:17:255:18 | i2 | | +| vector.cpp:254:3:254:4 | ref arg i2 | vector.cpp:260:8:260:9 | i2 | | +| vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:261:8:261:9 | v6 | | +| vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | | +| vector.cpp:255:13:255:14 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT | +| vector.cpp:255:13:255:14 | call to iterator [post update] | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:255:13:255:14 | i1 | vector.cpp:255:13:255:14 | call to iterator | | +| vector.cpp:255:13:255:14 | i1 [post update] | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:255:17:255:18 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT | +| vector.cpp:255:17:255:18 | i2 | vector.cpp:255:17:255:18 | call to iterator | | +| vector.cpp:257:8:257:9 | ref arg v4 | vector.cpp:262:2:262:2 | v4 | | +| vector.cpp:258:8:258:9 | ref arg v5 | vector.cpp:262:2:262:2 | v5 | | +| vector.cpp:259:8:259:9 | ref arg i1 | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:261:8:261:9 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | | +| vector.cpp:265:22:265:23 | call to vector | vector.cpp:269:3:269:4 | v7 | | +| vector.cpp:265:22:265:23 | call to vector | vector.cpp:273:8:273:9 | v7 | | +| vector.cpp:265:22:265:23 | call to vector | vector.cpp:276:2:276:2 | v7 | | +| vector.cpp:266:24:266:25 | call to vector | vector.cpp:270:3:270:4 | v8 | | +| vector.cpp:266:24:266:25 | call to vector | vector.cpp:274:8:274:9 | v8 | | +| vector.cpp:266:24:266:25 | call to vector | vector.cpp:276:2:276:2 | v8 | | +| vector.cpp:267:28:267:29 | call to vector | vector.cpp:271:3:271:4 | v9 | | +| vector.cpp:267:28:267:29 | call to vector | vector.cpp:275:8:275:9 | v9 | | +| vector.cpp:267:28:267:29 | call to vector | vector.cpp:276:2:276:2 | v9 | | +| vector.cpp:269:3:269:4 | ref arg v7 | vector.cpp:273:8:273:9 | v7 | | +| vector.cpp:269:3:269:4 | ref arg v7 | vector.cpp:276:2:276:2 | v7 | | +| vector.cpp:269:18:269:31 | call to source | vector.cpp:269:3:269:4 | ref arg v7 | TAINT | +| vector.cpp:270:3:270:4 | ref arg v8 | vector.cpp:274:8:274:9 | v8 | | +| vector.cpp:270:3:270:4 | ref arg v8 | vector.cpp:276:2:276:2 | v8 | | +| vector.cpp:270:18:270:35 | call to source | vector.cpp:270:3:270:4 | ref arg v8 | TAINT | +| vector.cpp:271:3:271:4 | ref arg v9 | vector.cpp:275:8:275:9 | v9 | | +| vector.cpp:271:3:271:4 | ref arg v9 | vector.cpp:276:2:276:2 | v9 | | +| vector.cpp:271:18:271:34 | call to source | vector.cpp:271:3:271:4 | ref arg v9 | TAINT | +| vector.cpp:273:8:273:9 | ref arg v7 | vector.cpp:276:2:276:2 | v7 | | +| vector.cpp:274:8:274:9 | ref arg v8 | vector.cpp:276:2:276:2 | v8 | | +| vector.cpp:275:8:275:9 | ref arg v9 | vector.cpp:276:2:276:2 | v9 | | +| vector.cpp:282:19:282:20 | call to vector | vector.cpp:284:2:284:3 | v1 | | +| vector.cpp:282:19:282:20 | call to vector | vector.cpp:285:7:285:8 | v1 | | +| vector.cpp:282:19:282:20 | call to vector | vector.cpp:286:7:286:8 | v1 | | +| vector.cpp:282:19:282:20 | call to vector | vector.cpp:287:7:287:8 | v1 | | +| vector.cpp:282:19:282:20 | call to vector | vector.cpp:293:1:293:1 | v1 | | +| vector.cpp:282:23:282:24 | call to vector | vector.cpp:289:4:289:5 | v2 | | +| vector.cpp:282:23:282:24 | call to vector | vector.cpp:290:7:290:8 | v2 | | +| vector.cpp:282:23:282:24 | call to vector | vector.cpp:291:7:291:8 | v2 | | +| vector.cpp:282:23:282:24 | call to vector | vector.cpp:292:7:292:8 | v2 | | +| vector.cpp:282:23:282:24 | call to vector | vector.cpp:293:1:293:1 | v2 | | +| vector.cpp:284:2:284:3 | ref arg v1 | vector.cpp:285:7:285:8 | v1 | | +| vector.cpp:284:2:284:3 | ref arg v1 | vector.cpp:286:7:286:8 | v1 | | +| vector.cpp:284:2:284:3 | ref arg v1 | vector.cpp:287:7:287:8 | v1 | | +| vector.cpp:284:2:284:3 | ref arg v1 | vector.cpp:293:1:293:1 | v1 | | +| vector.cpp:284:15:284:20 | call to source | vector.cpp:284:2:284:3 | ref arg v1 | TAINT | +| vector.cpp:285:7:285:8 | ref arg v1 | vector.cpp:286:7:286:8 | v1 | | +| vector.cpp:285:7:285:8 | ref arg v1 | vector.cpp:287:7:287:8 | v1 | | +| vector.cpp:285:7:285:8 | ref arg v1 | vector.cpp:293:1:293:1 | v1 | | +| vector.cpp:286:7:286:8 | ref arg v1 | vector.cpp:287:7:287:8 | v1 | | +| vector.cpp:286:7:286:8 | ref arg v1 | vector.cpp:293:1:293:1 | v1 | | +| vector.cpp:286:7:286:8 | v1 | vector.cpp:286:10:286:13 | call to data | TAINT | +| vector.cpp:286:10:286:13 | ref arg call to data | vector.cpp:286:7:286:8 | ref arg v1 | TAINT | +| vector.cpp:287:7:287:8 | ref arg v1 | vector.cpp:293:1:293:1 | v1 | | +| vector.cpp:287:7:287:8 | v1 | vector.cpp:287:10:287:13 | call to data | TAINT | +| vector.cpp:287:10:287:13 | call to data | vector.cpp:287:7:287:18 | access to array | TAINT | +| vector.cpp:287:17:287:17 | 2 | vector.cpp:287:7:287:18 | access to array | TAINT | +| vector.cpp:289:2:289:13 | * ... [post update] | vector.cpp:289:7:289:10 | call to data [inner post update] | | +| vector.cpp:289:2:289:32 | ... = ... | vector.cpp:289:2:289:13 | * ... [post update] | | +| vector.cpp:289:4:289:5 | ref arg v2 | vector.cpp:290:7:290:8 | v2 | | +| vector.cpp:289:4:289:5 | ref arg v2 | vector.cpp:291:7:291:8 | v2 | | +| vector.cpp:289:4:289:5 | ref arg v2 | vector.cpp:292:7:292:8 | v2 | | +| vector.cpp:289:4:289:5 | ref arg v2 | vector.cpp:293:1:293:1 | v2 | | +| vector.cpp:289:4:289:5 | v2 | vector.cpp:289:7:289:10 | call to data | TAINT | +| vector.cpp:289:7:289:10 | call to data | vector.cpp:289:2:289:13 | * ... | TAINT | +| vector.cpp:289:7:289:10 | call to data [inner post update] | vector.cpp:289:4:289:5 | ref arg v2 | TAINT | +| vector.cpp:289:17:289:30 | call to source | vector.cpp:289:2:289:32 | ... = ... | | +| vector.cpp:290:7:290:8 | ref arg v2 | vector.cpp:291:7:291:8 | v2 | | +| vector.cpp:290:7:290:8 | ref arg v2 | vector.cpp:292:7:292:8 | v2 | | +| vector.cpp:290:7:290:8 | ref arg v2 | vector.cpp:293:1:293:1 | v2 | | +| vector.cpp:291:7:291:8 | ref arg v2 | vector.cpp:292:7:292:8 | v2 | | +| vector.cpp:291:7:291:8 | ref arg v2 | vector.cpp:293:1:293:1 | v2 | | +| vector.cpp:291:7:291:8 | v2 | vector.cpp:291:10:291:13 | call to data | TAINT | +| vector.cpp:291:10:291:13 | ref arg call to data | vector.cpp:291:7:291:8 | ref arg v2 | TAINT | +| vector.cpp:292:7:292:8 | ref arg v2 | vector.cpp:293:1:293:1 | v2 | | +| vector.cpp:292:7:292:8 | v2 | vector.cpp:292:10:292:13 | call to data | TAINT | +| vector.cpp:292:10:292:13 | call to data | vector.cpp:292:7:292:18 | access to array | TAINT | +| vector.cpp:292:17:292:17 | 2 | vector.cpp:292:7:292:18 | access to array | TAINT | +| vector.cpp:298:19:298:19 | call to vector | vector.cpp:305:7:305:7 | a | | +| vector.cpp:298:19:298:19 | call to vector | vector.cpp:305:16:305:16 | a | | +| vector.cpp:298:19:298:19 | call to vector | vector.cpp:306:7:306:7 | a | | +| vector.cpp:298:19:298:19 | call to vector | vector.cpp:311:25:311:25 | a | | +| vector.cpp:298:19:298:19 | call to vector | vector.cpp:311:36:311:36 | a | | +| vector.cpp:298:19:298:19 | call to vector | vector.cpp:313:1:313:1 | a | | +| vector.cpp:299:19:299:19 | call to vector | vector.cpp:305:25:305:25 | b | | +| vector.cpp:299:19:299:19 | call to vector | vector.cpp:305:36:305:36 | b | | +| vector.cpp:299:19:299:19 | call to vector | vector.cpp:313:1:313:1 | b | | +| vector.cpp:300:19:300:19 | call to vector | vector.cpp:308:7:308:7 | c | | +| vector.cpp:300:19:300:19 | call to vector | vector.cpp:308:16:308:16 | c | | +| vector.cpp:300:19:300:19 | call to vector | vector.cpp:309:7:309:7 | c | | +| vector.cpp:300:19:300:19 | call to vector | vector.cpp:313:1:313:1 | c | | +| vector.cpp:301:19:301:19 | call to vector | vector.cpp:303:2:303:2 | d | | +| vector.cpp:301:19:301:19 | call to vector | vector.cpp:308:25:308:25 | d | | +| vector.cpp:301:19:301:19 | call to vector | vector.cpp:308:36:308:36 | d | | +| vector.cpp:301:19:301:19 | call to vector | vector.cpp:311:7:311:7 | d | | +| vector.cpp:301:19:301:19 | call to vector | vector.cpp:311:16:311:16 | d | | +| vector.cpp:301:19:301:19 | call to vector | vector.cpp:312:7:312:7 | d | | +| vector.cpp:301:19:301:19 | call to vector | vector.cpp:313:1:313:1 | d | | +| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:308:25:308:25 | d | | +| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:308:36:308:36 | d | | +| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:311:7:311:7 | d | | +| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:311:16:311:16 | d | | +| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:312:7:312:7 | d | | +| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:313:1:313:1 | d | | +| vector.cpp:303:14:303:19 | call to source | vector.cpp:303:2:303:2 | ref arg d | TAINT | +| vector.cpp:305:7:305:7 | a | vector.cpp:305:9:305:14 | call to insert | TAINT | +| vector.cpp:305:7:305:7 | ref arg a | vector.cpp:306:7:306:7 | a | | +| vector.cpp:305:7:305:7 | ref arg a | vector.cpp:311:25:311:25 | a | | +| vector.cpp:305:7:305:7 | ref arg a | vector.cpp:311:36:311:36 | a | | +| vector.cpp:305:7:305:7 | ref arg a | vector.cpp:313:1:313:1 | a | | +| vector.cpp:305:16:305:16 | a | vector.cpp:305:18:305:20 | call to end | TAINT | +| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:305:7:305:7 | a | | +| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:306:7:306:7 | a | | +| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:311:25:311:25 | a | | +| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:311:36:311:36 | a | | +| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:313:1:313:1 | a | | +| vector.cpp:305:18:305:20 | call to end | vector.cpp:305:16:305:22 | call to iterator | TAINT | +| vector.cpp:305:25:305:25 | b | vector.cpp:305:27:305:31 | call to begin | TAINT | +| vector.cpp:305:25:305:25 | ref arg b | vector.cpp:305:36:305:36 | b | | +| vector.cpp:305:25:305:25 | ref arg b | vector.cpp:313:1:313:1 | b | | +| vector.cpp:305:27:305:31 | call to begin | vector.cpp:305:7:305:7 | ref arg a | TAINT | +| vector.cpp:305:27:305:31 | call to begin | vector.cpp:305:9:305:14 | call to insert | TAINT | +| vector.cpp:305:36:305:36 | b | vector.cpp:305:38:305:40 | call to end | TAINT | +| vector.cpp:305:36:305:36 | ref arg b | vector.cpp:313:1:313:1 | b | | +| vector.cpp:305:38:305:40 | call to end | vector.cpp:305:7:305:7 | ref arg a | TAINT | +| vector.cpp:305:38:305:40 | call to end | vector.cpp:305:9:305:14 | call to insert | TAINT | +| vector.cpp:306:7:306:7 | ref arg a | vector.cpp:311:25:311:25 | a | | +| vector.cpp:306:7:306:7 | ref arg a | vector.cpp:311:36:311:36 | a | | +| vector.cpp:306:7:306:7 | ref arg a | vector.cpp:313:1:313:1 | a | | +| vector.cpp:308:7:308:7 | c | vector.cpp:308:9:308:14 | call to insert | TAINT | +| vector.cpp:308:7:308:7 | ref arg c | vector.cpp:309:7:309:7 | c | | +| vector.cpp:308:7:308:7 | ref arg c | vector.cpp:313:1:313:1 | c | | +| vector.cpp:308:16:308:16 | c | vector.cpp:308:18:308:20 | call to end | TAINT | +| vector.cpp:308:16:308:16 | ref arg c | vector.cpp:308:7:308:7 | c | | +| vector.cpp:308:16:308:16 | ref arg c | vector.cpp:309:7:309:7 | c | | +| vector.cpp:308:16:308:16 | ref arg c | vector.cpp:313:1:313:1 | c | | +| vector.cpp:308:18:308:20 | call to end | vector.cpp:308:16:308:22 | call to iterator | TAINT | +| vector.cpp:308:25:308:25 | d | vector.cpp:308:27:308:31 | call to begin | TAINT | +| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:308:36:308:36 | d | | +| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:311:7:311:7 | d | | +| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:311:16:311:16 | d | | +| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:312:7:312:7 | d | | +| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:313:1:313:1 | d | | +| vector.cpp:308:27:308:31 | call to begin | vector.cpp:308:7:308:7 | ref arg c | TAINT | +| vector.cpp:308:27:308:31 | call to begin | vector.cpp:308:9:308:14 | call to insert | TAINT | +| vector.cpp:308:36:308:36 | d | vector.cpp:308:38:308:40 | call to end | TAINT | +| vector.cpp:308:36:308:36 | ref arg d | vector.cpp:311:7:311:7 | d | | +| vector.cpp:308:36:308:36 | ref arg d | vector.cpp:311:16:311:16 | d | | +| vector.cpp:308:36:308:36 | ref arg d | vector.cpp:312:7:312:7 | d | | +| vector.cpp:308:36:308:36 | ref arg d | vector.cpp:313:1:313:1 | d | | +| vector.cpp:308:38:308:40 | call to end | vector.cpp:308:7:308:7 | ref arg c | TAINT | +| vector.cpp:308:38:308:40 | call to end | vector.cpp:308:9:308:14 | call to insert | TAINT | +| vector.cpp:309:7:309:7 | ref arg c | vector.cpp:313:1:313:1 | c | | +| vector.cpp:311:7:311:7 | d | vector.cpp:311:9:311:14 | call to insert | TAINT | +| vector.cpp:311:7:311:7 | ref arg d | vector.cpp:312:7:312:7 | d | | +| vector.cpp:311:7:311:7 | ref arg d | vector.cpp:313:1:313:1 | d | | +| vector.cpp:311:16:311:16 | d | vector.cpp:311:18:311:20 | call to end | TAINT | +| vector.cpp:311:16:311:16 | ref arg d | vector.cpp:311:7:311:7 | d | | +| vector.cpp:311:16:311:16 | ref arg d | vector.cpp:312:7:312:7 | d | | +| vector.cpp:311:16:311:16 | ref arg d | vector.cpp:313:1:313:1 | d | | +| vector.cpp:311:18:311:20 | call to end | vector.cpp:311:16:311:22 | call to iterator | TAINT | +| vector.cpp:311:25:311:25 | a | vector.cpp:311:27:311:31 | call to begin | TAINT | +| vector.cpp:311:25:311:25 | ref arg a | vector.cpp:311:36:311:36 | a | | +| vector.cpp:311:25:311:25 | ref arg a | vector.cpp:313:1:313:1 | a | | +| vector.cpp:311:27:311:31 | call to begin | vector.cpp:311:7:311:7 | ref arg d | TAINT | +| vector.cpp:311:27:311:31 | call to begin | vector.cpp:311:9:311:14 | call to insert | TAINT | +| vector.cpp:311:36:311:36 | a | vector.cpp:311:38:311:40 | call to end | TAINT | +| vector.cpp:311:36:311:36 | ref arg a | vector.cpp:313:1:313:1 | a | | +| vector.cpp:311:38:311:40 | call to end | vector.cpp:311:7:311:7 | ref arg d | TAINT | +| vector.cpp:311:38:311:40 | call to end | vector.cpp:311:9:311:14 | call to insert | TAINT | +| vector.cpp:312:7:312:7 | ref arg d | vector.cpp:313:1:313:1 | d | | +| vector.cpp:316:19:316:20 | call to vector | vector.cpp:320:22:320:23 | v1 | | +| vector.cpp:316:19:316:20 | call to vector | vector.cpp:320:34:320:35 | v1 | | +| vector.cpp:316:19:316:20 | call to vector | vector.cpp:323:7:323:8 | v1 | | +| vector.cpp:316:19:316:20 | call to vector | vector.cpp:327:1:327:1 | v1 | | +| vector.cpp:317:19:317:20 | call to vector | vector.cpp:318:2:318:3 | v2 | | +| vector.cpp:317:19:317:20 | call to vector | vector.cpp:321:22:321:23 | v2 | | +| vector.cpp:317:19:317:20 | call to vector | vector.cpp:321:34:321:35 | v2 | | +| vector.cpp:317:19:317:20 | call to vector | vector.cpp:324:7:324:8 | v2 | | +| vector.cpp:317:19:317:20 | call to vector | vector.cpp:327:1:327:1 | v2 | | +| vector.cpp:318:2:318:3 | ref arg v2 | vector.cpp:321:22:321:23 | v2 | | +| vector.cpp:318:2:318:3 | ref arg v2 | vector.cpp:321:34:321:35 | v2 | | +| vector.cpp:318:2:318:3 | ref arg v2 | vector.cpp:324:7:324:8 | v2 | | +| vector.cpp:318:2:318:3 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | | +| vector.cpp:318:15:318:20 | call to source | vector.cpp:318:2:318:3 | ref arg v2 | TAINT | +| vector.cpp:320:22:320:23 | ref arg v1 | vector.cpp:320:34:320:35 | v1 | | +| vector.cpp:320:22:320:23 | ref arg v1 | vector.cpp:323:7:323:8 | v1 | | +| vector.cpp:320:22:320:23 | ref arg v1 | vector.cpp:327:1:327:1 | v1 | | +| vector.cpp:320:22:320:23 | v1 | vector.cpp:320:25:320:29 | call to begin | TAINT | +| vector.cpp:320:22:320:42 | call to vector | vector.cpp:325:7:325:8 | v3 | | +| vector.cpp:320:22:320:42 | call to vector | vector.cpp:327:1:327:1 | v3 | | +| vector.cpp:320:25:320:29 | call to begin | vector.cpp:320:22:320:42 | call to vector | TAINT | +| vector.cpp:320:34:320:35 | ref arg v1 | vector.cpp:323:7:323:8 | v1 | | +| vector.cpp:320:34:320:35 | ref arg v1 | vector.cpp:327:1:327:1 | v1 | | +| vector.cpp:320:34:320:35 | v1 | vector.cpp:320:37:320:39 | call to end | TAINT | +| vector.cpp:320:37:320:39 | call to end | vector.cpp:320:22:320:42 | call to vector | TAINT | +| vector.cpp:321:22:321:23 | ref arg v2 | vector.cpp:321:34:321:35 | v2 | | +| vector.cpp:321:22:321:23 | ref arg v2 | vector.cpp:324:7:324:8 | v2 | | +| vector.cpp:321:22:321:23 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | | +| vector.cpp:321:22:321:23 | v2 | vector.cpp:321:25:321:29 | call to begin | TAINT | +| vector.cpp:321:22:321:42 | call to vector | vector.cpp:326:7:326:8 | v4 | | +| vector.cpp:321:22:321:42 | call to vector | vector.cpp:327:1:327:1 | v4 | | +| vector.cpp:321:25:321:29 | call to begin | vector.cpp:321:22:321:42 | call to vector | TAINT | +| vector.cpp:321:34:321:35 | ref arg v2 | vector.cpp:324:7:324:8 | v2 | | +| vector.cpp:321:34:321:35 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | | +| vector.cpp:321:34:321:35 | v2 | vector.cpp:321:37:321:39 | call to end | TAINT | +| vector.cpp:321:37:321:39 | call to end | vector.cpp:321:22:321:42 | call to vector | TAINT | +| vector.cpp:323:7:323:8 | ref arg v1 | vector.cpp:327:1:327:1 | v1 | | +| vector.cpp:324:7:324:8 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | | +| vector.cpp:325:7:325:8 | ref arg v3 | vector.cpp:327:1:327:1 | v3 | | +| vector.cpp:326:7:326:8 | ref arg v4 | vector.cpp:327:1:327:1 | v4 | | +| vector.cpp:329:62:329:65 | iter | vector.cpp:329:62:329:65 | iter | | +| vector.cpp:329:62:329:65 | iter | vector.cpp:330:3:330:6 | iter | | +| vector.cpp:330:2:330:2 | call to operator* [post update] | vector.cpp:329:62:329:65 | iter | | +| vector.cpp:330:2:330:17 | ... = ... | vector.cpp:330:2:330:2 | call to operator* [post update] | | +| vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT | +| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:2 | call to operator* [post update] | TAINT | +| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:17 | ... = ... | | +| vector.cpp:333:64:333:67 | iter | vector.cpp:333:64:333:67 | iter | | +| vector.cpp:333:64:333:67 | iter | vector.cpp:334:3:334:6 | iter | | +| vector.cpp:333:74:333:74 | i | vector.cpp:334:10:334:10 | i | | +| vector.cpp:334:2:334:2 | call to operator* [post update] | vector.cpp:333:64:333:67 | iter | | +| vector.cpp:334:2:334:10 | ... = ... | vector.cpp:334:2:334:2 | call to operator* [post update] | | +| vector.cpp:334:3:334:6 | iter | vector.cpp:334:2:334:2 | call to operator* | TAINT | +| vector.cpp:334:10:334:10 | i | vector.cpp:334:2:334:2 | call to operator* [post update] | TAINT | +| vector.cpp:334:10:334:10 | i | vector.cpp:334:2:334:10 | ... = ... | | +| vector.cpp:337:38:337:38 | b | vector.cpp:372:5:372:5 | b | | +| vector.cpp:338:22:338:24 | call to vector | vector.cpp:340:34:340:35 | v1 | | +| vector.cpp:338:22:338:24 | call to vector | vector.cpp:342:7:342:8 | v1 | | +| vector.cpp:338:22:338:24 | call to vector | vector.cpp:401:1:401:1 | v1 | | +| vector.cpp:338:30:338:32 | call to vector | vector.cpp:344:38:344:39 | v2 | | +| vector.cpp:338:30:338:32 | call to vector | vector.cpp:344:56:344:57 | v2 | | +| vector.cpp:338:30:338:32 | call to vector | vector.cpp:347:7:347:8 | v2 | | +| vector.cpp:338:30:338:32 | call to vector | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:338:38:338:40 | call to vector | vector.cpp:349:15:349:16 | v3 | | +| vector.cpp:338:38:338:40 | call to vector | vector.cpp:352:7:352:8 | v3 | | +| vector.cpp:338:38:338:40 | call to vector | vector.cpp:401:1:401:1 | v3 | | +| vector.cpp:338:46:338:48 | call to vector | vector.cpp:354:38:354:39 | v4 | | +| vector.cpp:338:46:338:48 | call to vector | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:338:46:338:48 | call to vector | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:338:46:338:48 | call to vector | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:338:54:338:56 | call to vector | vector.cpp:359:34:359:35 | v5 | | +| vector.cpp:338:54:338:56 | call to vector | vector.cpp:361:7:361:8 | v5 | | +| vector.cpp:338:54:338:56 | call to vector | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:338:54:338:56 | call to vector | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:365:34:365:35 | v6 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:367:7:367:8 | v6 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:368:2:368:3 | v6 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:371:34:371:35 | v7 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:374:8:374:9 | v7 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:377:8:377:9 | v7 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:338:78:338:80 | call to vector | vector.cpp:381:34:381:35 | v8 | | +| vector.cpp:338:78:338:80 | call to vector | vector.cpp:383:7:383:8 | v8 | | +| vector.cpp:338:78:338:80 | call to vector | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:338:78:338:80 | call to vector | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:338:86:338:88 | call to vector | vector.cpp:387:34:387:35 | v9 | | +| vector.cpp:338:86:338:88 | call to vector | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:338:86:338:88 | call to vector | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:338:95:338:97 | call to vector | vector.cpp:394:35:394:37 | v10 | | +| vector.cpp:338:95:338:97 | call to vector | vector.cpp:396:7:396:9 | v10 | | +| vector.cpp:338:95:338:97 | call to vector | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:338:104:338:106 | call to vector | vector.cpp:398:35:398:37 | v11 | | +| vector.cpp:338:104:338:106 | call to vector | vector.cpp:400:7:400:9 | v11 | | +| vector.cpp:338:104:338:106 | call to vector | vector.cpp:401:1:401:1 | v11 | | +| vector.cpp:340:34:340:35 | ref arg v1 | vector.cpp:342:7:342:8 | v1 | | +| vector.cpp:340:34:340:35 | ref arg v1 | vector.cpp:401:1:401:1 | v1 | | +| vector.cpp:340:34:340:35 | v1 | vector.cpp:340:37:340:41 | call to begin | TAINT | +| vector.cpp:340:37:340:41 | call to begin | vector.cpp:341:3:341:4 | i1 | | +| vector.cpp:341:2:341:2 | call to operator* [post update] | vector.cpp:342:7:342:8 | v1 | | +| vector.cpp:341:2:341:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v1 | | +| vector.cpp:341:2:341:15 | ... = ... | vector.cpp:341:2:341:2 | call to operator* [post update] | | +| vector.cpp:341:3:341:4 | i1 | vector.cpp:341:2:341:2 | call to operator* | TAINT | +| vector.cpp:341:8:341:13 | call to source | vector.cpp:341:2:341:2 | call to operator* [post update] | TAINT | +| vector.cpp:341:8:341:13 | call to source | vector.cpp:341:2:341:15 | ... = ... | | +| vector.cpp:342:7:342:8 | ref arg v1 | vector.cpp:401:1:401:1 | v1 | | +| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:344:56:344:57 | v2 | | +| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:347:7:347:8 | v2 | | +| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:344:38:344:39 | v2 | vector.cpp:344:41:344:45 | call to begin | TAINT | +| vector.cpp:344:41:344:45 | call to begin | vector.cpp:344:50:344:51 | it | | +| vector.cpp:344:41:344:45 | call to begin | vector.cpp:344:68:344:69 | it | | +| vector.cpp:344:41:344:45 | call to begin | vector.cpp:345:4:345:5 | it | | +| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:344:56:344:57 | v2 | | +| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:347:7:347:8 | v2 | | +| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:344:56:344:57 | v2 | vector.cpp:344:59:344:61 | call to end | TAINT | +| vector.cpp:344:68:344:69 | it | vector.cpp:344:66:344:66 | call to operator++ | | +| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:344:50:344:51 | it | | +| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:344:68:344:69 | it | | +| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:345:4:345:5 | it | | +| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:344:56:344:57 | v2 | | +| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:347:7:347:8 | v2 | | +| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:345:3:345:16 | ... = ... | vector.cpp:345:3:345:3 | call to operator* [post update] | | +| vector.cpp:345:4:345:5 | it | vector.cpp:345:3:345:3 | call to operator* | TAINT | +| vector.cpp:345:9:345:14 | call to source | vector.cpp:345:3:345:3 | call to operator* [post update] | TAINT | +| vector.cpp:345:9:345:14 | call to source | vector.cpp:345:3:345:16 | ... = ... | | +| vector.cpp:347:7:347:8 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:349:15:349:15 | (__begin) | vector.cpp:349:15:349:15 | call to operator* | TAINT | +| vector.cpp:349:15:349:15 | (__begin) | vector.cpp:349:15:349:15 | call to operator++ | | +| vector.cpp:349:15:349:15 | (__end) | vector.cpp:349:15:349:15 | call to iterator | | +| vector.cpp:349:15:349:15 | (__range) | vector.cpp:349:15:349:15 | call to begin | TAINT | +| vector.cpp:349:15:349:15 | (__range) | vector.cpp:349:15:349:15 | call to end | TAINT | +| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | call to end | vector.cpp:349:15:349:15 | (__end) | | +| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | ref arg (__range) | vector.cpp:349:15:349:15 | (__range) | | +| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | (__range) | | +| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | (__range) | | +| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | call to operator* | TAINT | +| vector.cpp:350:3:350:14 | ... = ... | vector.cpp:350:3:350:3 | x [post update] | | +| vector.cpp:350:7:350:12 | call to source | vector.cpp:350:3:350:14 | ... = ... | | +| vector.cpp:352:7:352:8 | ref arg v3 | vector.cpp:401:1:401:1 | v3 | | +| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:354:38:354:39 | v4 | vector.cpp:354:41:354:45 | call to begin | TAINT | +| vector.cpp:354:41:354:45 | call to begin | vector.cpp:354:50:354:51 | it | | +| vector.cpp:354:41:354:45 | call to begin | vector.cpp:354:68:354:69 | it | | +| vector.cpp:354:41:354:45 | call to begin | vector.cpp:355:32:355:33 | it | | +| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:354:56:354:57 | v4 | vector.cpp:354:59:354:61 | call to end | TAINT | +| vector.cpp:354:68:354:69 | it | vector.cpp:354:66:354:66 | call to operator++ | | +| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:354:50:354:51 | it | | +| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:354:68:354:69 | it | | +| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:355:32:355:33 | it | | +| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:355:32:355:33 | it | vector.cpp:355:32:355:33 | call to iterator | | +| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:357:7:357:8 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:361:7:361:8 | v5 | | +| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:359:34:359:35 | v5 | vector.cpp:359:37:359:41 | call to begin | TAINT | +| vector.cpp:359:37:359:41 | call to begin | vector.cpp:360:3:360:4 | i5 | | +| vector.cpp:359:37:359:41 | call to begin | vector.cpp:362:3:362:4 | i5 | | +| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:361:7:361:8 | v5 | | +| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:360:2:360:15 | ... = ... | vector.cpp:360:2:360:2 | call to operator* [post update] | | +| vector.cpp:360:3:360:4 | i5 | vector.cpp:360:2:360:2 | call to operator* | TAINT | +| vector.cpp:360:8:360:13 | call to source | vector.cpp:360:2:360:2 | call to operator* [post update] | TAINT | +| vector.cpp:360:8:360:13 | call to source | vector.cpp:360:2:360:15 | ... = ... | | +| vector.cpp:361:7:361:8 | ref arg v5 | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:361:7:361:8 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:362:2:362:8 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | | +| vector.cpp:362:3:362:4 | i5 | vector.cpp:362:2:362:2 | call to operator* | TAINT | +| vector.cpp:362:8:362:8 | 1 | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT | +| vector.cpp:362:8:362:8 | 1 | vector.cpp:362:2:362:8 | ... = ... | | +| vector.cpp:363:7:363:8 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:367:7:367:8 | v6 | | +| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:368:2:368:3 | v6 | | +| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:365:34:365:35 | v6 | vector.cpp:365:37:365:41 | call to begin | TAINT | +| vector.cpp:365:37:365:41 | call to begin | vector.cpp:366:3:366:4 | i6 | | +| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:367:7:367:8 | v6 | | +| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:368:2:368:3 | v6 | | +| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:366:2:366:15 | ... = ... | vector.cpp:366:2:366:2 | call to operator* [post update] | | +| vector.cpp:366:3:366:4 | i6 | vector.cpp:366:2:366:2 | call to operator* | TAINT | +| vector.cpp:366:8:366:13 | call to source | vector.cpp:366:2:366:2 | call to operator* [post update] | TAINT | +| vector.cpp:366:8:366:13 | call to source | vector.cpp:366:2:366:15 | ... = ... | | +| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:368:2:368:3 | v6 | | +| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:368:2:368:3 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:368:2:368:3 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:368:7:368:26 | call to vector | vector.cpp:368:2:368:3 | ref arg v6 | TAINT | +| vector.cpp:368:7:368:26 | call to vector | vector.cpp:368:5:368:5 | call to operator= | TAINT | +| vector.cpp:369:7:369:8 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:374:8:374:9 | v7 | | +| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:377:8:377:9 | v7 | | +| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:371:34:371:35 | v7 | vector.cpp:371:37:371:41 | call to begin | TAINT | +| vector.cpp:371:37:371:41 | call to begin | vector.cpp:373:4:373:5 | i7 | | +| vector.cpp:371:37:371:41 | call to begin | vector.cpp:376:4:376:5 | i7 | | +| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:374:8:374:9 | v7 | | +| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:373:3:373:16 | ... = ... | vector.cpp:373:3:373:3 | call to operator* [post update] | | +| vector.cpp:373:4:373:5 | i7 | vector.cpp:373:3:373:3 | call to operator* | TAINT | +| vector.cpp:373:9:373:14 | call to source | vector.cpp:373:3:373:3 | call to operator* [post update] | TAINT | +| vector.cpp:373:9:373:14 | call to source | vector.cpp:373:3:373:16 | ... = ... | | +| vector.cpp:374:8:374:9 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:374:8:374:9 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:377:8:377:9 | v7 | | +| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:376:3:376:9 | ... = ... | vector.cpp:376:3:376:3 | call to operator* [post update] | | +| vector.cpp:376:4:376:5 | i7 | vector.cpp:376:3:376:3 | call to operator* | TAINT | +| vector.cpp:376:9:376:9 | 1 | vector.cpp:376:3:376:3 | call to operator* [post update] | TAINT | +| vector.cpp:376:9:376:9 | 1 | vector.cpp:376:3:376:9 | ... = ... | | +| vector.cpp:377:8:377:9 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:377:8:377:9 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:379:7:379:8 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:383:7:383:8 | v8 | | +| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:381:34:381:35 | v8 | vector.cpp:381:37:381:41 | call to begin | TAINT | +| vector.cpp:381:37:381:41 | call to begin | vector.cpp:382:3:382:4 | i8 | | +| vector.cpp:381:37:381:41 | call to begin | vector.cpp:384:3:384:4 | i8 | | +| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:383:7:383:8 | v8 | | +| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:382:2:382:15 | ... = ... | vector.cpp:382:2:382:2 | call to operator* [post update] | | +| vector.cpp:382:3:382:4 | i8 | vector.cpp:382:2:382:2 | call to operator* | TAINT | +| vector.cpp:382:8:382:13 | call to source | vector.cpp:382:2:382:2 | call to operator* [post update] | TAINT | +| vector.cpp:382:8:382:13 | call to source | vector.cpp:382:2:382:15 | ... = ... | | +| vector.cpp:383:7:383:8 | ref arg v8 | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:383:7:383:8 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:384:2:384:2 | call to operator* [post update] | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:384:2:384:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:384:2:384:8 | ... = ... | vector.cpp:384:2:384:2 | call to operator* [post update] | | +| vector.cpp:384:3:384:4 | i8 | vector.cpp:384:2:384:2 | call to operator* | TAINT | +| vector.cpp:384:8:384:8 | 1 | vector.cpp:384:2:384:2 | call to operator* [post update] | TAINT | +| vector.cpp:384:8:384:8 | 1 | vector.cpp:384:2:384:8 | ... = ... | | +| vector.cpp:385:7:385:8 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:387:34:387:35 | ref arg v9 | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:387:34:387:35 | ref arg v9 | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:387:34:387:35 | v9 | vector.cpp:387:37:387:41 | call to begin | TAINT | +| vector.cpp:387:37:387:41 | call to begin | vector.cpp:389:3:389:4 | i9 | | +| vector.cpp:387:37:387:41 | call to begin | vector.cpp:390:31:390:32 | i9 | | +| vector.cpp:389:2:389:2 | call to operator* [post update] | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:389:2:389:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:389:2:389:15 | ... = ... | vector.cpp:389:2:389:2 | call to operator* [post update] | | +| vector.cpp:389:3:389:4 | i9 | vector.cpp:389:2:389:2 | call to operator* | TAINT | +| vector.cpp:389:8:389:13 | call to source | vector.cpp:389:2:389:2 | call to operator* [post update] | TAINT | +| vector.cpp:389:8:389:13 | call to source | vector.cpp:389:2:389:15 | ... = ... | | +| vector.cpp:390:31:390:32 | call to iterator [post update] | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:390:31:390:32 | call to iterator [post update] | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:390:31:390:32 | i9 | vector.cpp:390:31:390:32 | call to iterator | | +| vector.cpp:390:31:390:32 | i9 [post update] | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:390:31:390:32 | i9 [post update] | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:392:7:392:8 | ref arg v9 | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:394:35:394:37 | ref arg v10 | vector.cpp:396:7:396:9 | v10 | | +| vector.cpp:394:35:394:37 | ref arg v10 | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:394:35:394:37 | v10 | vector.cpp:394:39:394:43 | call to begin | TAINT | +| vector.cpp:394:39:394:43 | call to begin | vector.cpp:395:33:395:35 | i10 | | +| vector.cpp:395:33:395:35 | call to iterator [post update] | vector.cpp:396:7:396:9 | v10 | | +| vector.cpp:395:33:395:35 | call to iterator [post update] | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:395:33:395:35 | i10 | vector.cpp:395:33:395:35 | call to iterator | | +| vector.cpp:395:33:395:35 | i10 [post update] | vector.cpp:396:7:396:9 | v10 | | +| vector.cpp:395:33:395:35 | i10 [post update] | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:396:7:396:9 | ref arg v10 | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:398:35:398:37 | ref arg v11 | vector.cpp:400:7:400:9 | v11 | | +| vector.cpp:398:35:398:37 | ref arg v11 | vector.cpp:401:1:401:1 | v11 | | +| vector.cpp:398:35:398:37 | v11 | vector.cpp:398:39:398:43 | call to begin | TAINT | +| vector.cpp:398:39:398:43 | call to begin | vector.cpp:399:33:399:35 | i11 | | +| vector.cpp:399:33:399:35 | call to iterator [post update] | vector.cpp:400:7:400:9 | v11 | | +| vector.cpp:399:33:399:35 | call to iterator [post update] | vector.cpp:401:1:401:1 | v11 | | +| vector.cpp:399:33:399:35 | i11 | vector.cpp:399:33:399:35 | call to iterator | | +| vector.cpp:399:33:399:35 | i11 [post update] | vector.cpp:400:7:400:9 | v11 | | +| vector.cpp:399:33:399:35 | i11 [post update] | vector.cpp:401:1:401:1 | v11 | | +| vector.cpp:400:7:400:9 | ref arg v11 | vector.cpp:401:1:401:1 | v11 | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp new file mode 100644 index 000000000000..6b4d32f074f4 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp @@ -0,0 +1,438 @@ + +#include "stl.h" + +using namespace std; + +char *source(); + +void sink(char *); +void sink(const char *); +void sink(bool); +void sink(std::pair); +void sink(std::map); +void sink(std::map::iterator); +void sink(std::unordered_map); +void sink(std::unordered_map::iterator); +void sink(std::unordered_map >); +void sink(std::unordered_map >::iterator); + +void test_pair() +{ + std::pair a, b, c; + + a.first = "123"; + sink(a.first); + sink(a.second); + sink(a); + + b.first = source(); + sink(b.first); // tainted + sink(b.second); + sink(b); // tainted [NOT DETECTED] + + c.second = source(); + sink(c.first); + sink(c.second); // tainted + sink(c); // tainted [NOT DETECTED] + + std::pair d("123", "456"); + sink(d.first); + sink(d.second); + sink(d); + + std::pair e(source(), "456"); + sink(e.first); // tainted + sink(e.second); + sink(e); // tainted [NOT DETECTED] + + std::pair f("123", source()); + sink(f.first); // [FALSE POSITIVE] + sink(f.second); // tainted + sink(f); // tainted + + std::pair g(f); + sink(g.first); // [FALSE POSITIVE] + sink(g.second); // tainted + sink(g); // tainted + + std::pair h; + h = f; + sink(h.first); // [FALSE POSITIVE] + sink(h.second); // tainted + sink(h); // tainted + + std::pair i("123", "456"); + std::pair j("123", source()); + std::pair k("123", source()); + std::pair l("123", "456"); + i.swap(j); + k.swap(l); + sink(i.first); // [FALSE POSITIVE] + sink(i.second); // tainted + sink(i); // tainted + sink(j.first); // [FALSE POSITIVE] + sink(j.second); // [FALSE POSITIVE] + sink(j); // [FALSE POSITIVE] + sink(k.first); // [FALSE POSITIVE] + sink(k.second); // [FALSE POSITIVE] + sink(k); // [FALSE POSITIVE] + sink(l.first); // [FALSE POSITIVE] + sink(l.second); // tainted + sink(l); // tainted + + sink(make_pair("123", "456")); + sink(make_pair("123", "456").first); + sink(make_pair("123", "456").second); + sink(make_pair(source(), "456")); // tainted [NOT DETECTED] + sink(make_pair(source(), "456").first); // tainted [NOT DETECTED] + sink(make_pair(source(), "456").second); + sink(make_pair("123", source())); // tainted + sink(make_pair("123", source()).first); // [FALSE POSITIVE] + sink(make_pair("123", source()).second); // tainted + + std::pair, char *> m; + m = make_pair(make_pair("123", source()), "789"); + sink(m); // tainted [NOT DETECTED] + sink(m.first); // tainted [NOT DETECTED] + sink(m.first.first); + sink(m.first.second); // tainted [NOT DETECTED] + sink(m.second); +} + +void test_map() +{ + // insert + std::map m1, m2, m3, m4, m5, m6; + + sink(m1.insert(std::make_pair("abc", "def")).first); + sink(m2.insert(std::make_pair("abc", source())).first); // tainted + sink(m3.insert(std::make_pair(source(), "def")).first); // tainted [NOT DETECTED] + sink(m4.insert(m4.begin(), std::pair("abc", source()))); // tainted + sink(m5.insert_or_assign("abc", source()).first); // tainted + sink(m6.insert_or_assign(m6.begin(), "abc", source())); // tainted + sink(m1); + sink(m2); // tainted + sink(m3); // tainted [NOT DETECTED] + sink(m4); // tainted + sink(m5); // tainted + sink(m6); // tainted + sink(m1.find("abc")); + sink(m2.find("abc")); // tainted + sink(m3.find("abc")); + sink(m4.find("abc")); // tainted + sink(m5.find("abc")); // tainted + sink(m6.find("abc")); // tainted + sink(m1.find("def")); + sink(m2.find("def")); // [FALSE POSITIVE] + sink(m3.find("def")); + sink(m4.find("def")); // [FALSE POSITIVE] + sink(m5.find("def")); // [FALSE POSITIVE] + sink(m6.find("def")); // [FALSE POSITIVE] + + // copy constructors and assignment + std::map m7(m2); + std::map m8 = m2; + std::map m9; + m9 = m2; + sink(m7); // tainted + sink(m8); // tainted + sink(m9); // tainted + sink(m7.find("abc")); // tainted + sink(m8.find("abc")); // tainted + sink(m9.find("abc")); // tainted + + // iterators + std::map::iterator i1, i2, i3; + for (i1 = m1.begin(); i1 != m1.end(); i1++) + { + sink(*i1); + sink(i1->first); + sink(i1->second); + } + for (i2 = m2.begin(); i2 != m2.end(); i2++) + { + sink(*i2); // tainted + sink(i2->first); // [FALSE POSITIVE] + sink(i2->second); // tainted + } + for (i3 = m3.begin(); i3 != m3.end(); i3++) + { + sink(*i3); // tainted [NOT DETECTED] + sink(i2->first); // tainted + sink(i2->second); // [FALSE POSITIVE] + } + + // array-like access + std::map m10, m11, m12, m13; + sink(m10["abc"] = "def"); + sink(m11["abc"] = source()); // tainted + sink(m12.at("abc") = "def"); + sink(m13.at("abc") = source()); // tainted + sink(m10["abc"]); + sink(m11["abc"]); // tainted + sink(m12["abc"]); + sink(m13["abc"]); // tainted + + // ranges + std::map m14; + m14.insert(std::make_pair("a", "a")); + m14.insert(std::make_pair("b", source())); + m14.insert(std::make_pair("c", source())); + m14.insert(std::make_pair("d", "d")); + sink(m2.lower_bound("b")); // tainted + sink(m2.upper_bound("b")); // tainted + sink(m2.equal_range("b").first); // tainted + sink(m2.equal_range("b").second); // tainted + sink(m2.upper_bound("c")); // [FALSE POSITIVE] + sink(m2.equal_range("c").second); // [FALSE POSITIVE] + + // swap + std::map m15, m16, m17, m18; + m15.insert(std::pair(source(), source())); + m18.insert(std::pair(source(), source())); + sink(m15); // tainted + sink(m16); + sink(m17); + sink(m18); // tainted + m15.swap(m16); + m17.swap(m18); + sink(m15); // [FALSE POSITIVE] + sink(m16); // tainted + sink(m17); // tainted + sink(m18); // [FALSE POSITIVE] + + // merge + std::map m19, m20, m21, m22; + m19.insert(std::pair(source(), source())); + m20.insert(std::pair("abc", "def")); + m21.insert(std::pair("abc", "def")); + m22.insert(std::pair(source(), source())); + sink(m19); // tainted + sink(m20); + sink(m21); + sink(m22); // tainted + m19.merge(m20); + m21.merge(m22); + sink(m19); // tainted + sink(m20); + sink(m21); // tainted + sink(m22); // tainted + + // erase, clear + std::map m23; + m23.insert(std::pair(source(), source())); + m23.insert(std::pair(source(), source())); + sink(m23); // tainted + sink(m23.erase(m23.begin())); // tainted + sink(m23); // tainted + m23.clear(); + sink(m23); // [FALSE POSITIVE] + + // emplace, emplace_hint + std::map m24, m25; + sink(m24.emplace("abc", "def").first); + sink(m24); + sink(m24.emplace("abc", source()).first); // tainted + sink(m24); // tainted + sink(m25.emplace_hint(m25.begin(), "abc", "def")); + sink(m25); + sink(m25.emplace_hint(m25.begin(), "abc", source())); // tainted + sink(m25); // tainted + + // try_emplace + std::map m26, m27; + sink(m26.try_emplace("abc", "def").first); + sink(m26); + sink(m26.try_emplace("abc", source()).first); // tainted + sink(m26); // tainted + sink(m27.try_emplace(m27.begin(), "abc", "def")); + sink(m27); + sink(m27.try_emplace(m27.begin(), "abc", source())); // tainted + sink(m27); // tainted +} + +void test_unordered_map() +{ + // insert + std::unordered_map m1, m2, m3, m4, m5, m6; + + sink(m1.insert(std::make_pair("abc", "def")).first); + sink(m2.insert(std::make_pair("abc", source())).first); // tainted + sink(m3.insert(std::make_pair(source(), "def")).first); // tainted [NOT DETECTED] + sink(m4.insert(m4.begin(), std::pair("abc", source()))); // tainted + sink(m5.insert_or_assign("abc", source()).first); // tainted + sink(m6.insert_or_assign(m6.begin(), "abc", source())); // tainted + sink(m1); + sink(m2); // tainted + sink(m3); // tainted [NOT DETECTED] + sink(m4); // tainted + sink(m5); // tainted + sink(m6); // tainted + sink(m1.find("abc")); + sink(m2.find("abc")); // tainted + sink(m3.find("abc")); + sink(m4.find("abc")); // tainted + sink(m5.find("abc")); // tainted + sink(m6.find("abc")); // tainted + sink(m1.find("def")); + sink(m2.find("def")); // [FALSE POSITIVE] + sink(m3.find("def")); + sink(m4.find("def")); // [FALSE POSITIVE] + sink(m5.find("def")); // [FALSE POSITIVE] + sink(m6.find("def")); // [FALSE POSITIVE] + + // copy constructors and assignment + std::unordered_map m7(m2); + std::unordered_map m8 = m2; + std::unordered_map m9; + m9 = m2; + sink(m7); // tainted + sink(m8); // tainted + sink(m9); // tainted + sink(m7.find("abc")); // tainted + sink(m8.find("abc")); // tainted + sink(m9.find("abc")); // tainted + + // iterators + std::unordered_map::iterator i1, i2, i3; + for (i1 = m1.begin(); i1 != m1.end(); i1++) + { + sink(*i1); + sink(i1->first); + sink(i1->second); + } + for (i2 = m2.begin(); i2 != m2.end(); i2++) + { + sink(*i2); // tainted + sink(i2->first); // [FALSE POSITIVE] + sink(i2->second); // tainted + } + for (i3 = m3.begin(); i3 != m3.end(); i3++) + { + sink(*i3); // tainted [NOT DETECTED] + sink(i2->first); // tainted + sink(i2->second); // [FALSE POSITIVE] + } + + // array-like access + std::unordered_map m10, m11, m12, m13; + sink(m10["abc"] = "def"); + sink(m11["abc"] = source()); // tainted + sink(m12.at("abc") = "def"); + sink(m13.at("abc") = source()); // tainted + sink(m10["abc"]); + sink(m11["abc"]); // tainted + sink(m12["abc"]); + sink(m13["abc"]); // tainted + + // ranges + std::unordered_map m14; + m14.insert(std::make_pair("a", "a")); + m14.insert(std::make_pair("b", source())); + m14.insert(std::make_pair("c", source())); + m14.insert(std::make_pair("d", "d")); + sink(m2.equal_range("b").first); // tainted + sink(m2.equal_range("b").second); // tainted + sink(m2.equal_range("c").second); // [FALSE POSITIVE] + + // swap + std::unordered_map m15, m16, m17, m18; + m15.insert(std::pair(source(), source())); + m18.insert(std::pair(source(), source())); + sink(m15); // tainted + sink(m16); + sink(m17); + sink(m18); // tainted + m15.swap(m16); + m17.swap(m18); + sink(m15); // [FALSE POSITIVE] + sink(m16); // tainted + sink(m17); // tainted + sink(m18); // [FALSE POSITIVE] + + // merge + std::unordered_map m19, m20, m21, m22; + m19.insert(std::pair(source(), source())); + m20.insert(std::pair("abc", "def")); + m21.insert(std::pair("abc", "def")); + m22.insert(std::pair(source(), source())); + sink(m19); // tainted + sink(m20); + sink(m21); + sink(m22); // tainted + m19.merge(m20); + m21.merge(m22); + sink(m19); // tainted + sink(m20); + sink(m21); // tainted + sink(m22); // tainted + + // erase, clear + std::unordered_map m23; + m23.insert(std::pair(source(), source())); + m23.insert(std::pair(source(), source())); + sink(m23); // tainted + sink(m23.erase(m23.begin())); // tainted + sink(m23); // tainted + m23.clear(); + sink(m23); // [FALSE POSITIVE] + + // emplace, emplace_hint + std::unordered_map m24, m25; + sink(m24.emplace("abc", "def").first); + sink(m24); + sink(m24.emplace("abc", source()).first); // tainted + sink(m24); // tainted + sink(m25.emplace_hint(m25.begin(), "abc", "def")); + sink(m25); + sink(m25.emplace_hint(m25.begin(), "abc", source())); // tainted + sink(m25); // tainted + + // try_emplace + std::unordered_map m26, m27, m28; + sink(m26.try_emplace("abc", "def").first); + sink(m26.try_emplace("abc", "def").second); + sink(m26); + sink(m26.try_emplace("abc", source()).first); // tainted + sink(m26.try_emplace("abc", source()).second); // [FALSE POSITIVE] + sink(m26); // tainted + sink(m27.try_emplace(m27.begin(), "abc", "def")); + sink(m27); + sink(m27.try_emplace(m27.begin(), "abc", source())); // tainted + sink(m27); // tainted + sink(m28.try_emplace(m28.begin(), "abc", "def")); + sink(m28); + sink(m28.try_emplace(m28.begin(), source(), "def")); // tainted [NOT DETECTED] + sink(m28); // tainted [NOT DETECTED] + + // additional try_emplace test cases + std::unordered_map> m29, m30, m31, m32; + sink(m29.try_emplace("abc", 1, 2)); + sink(m29); + sink(m29["abc"]); + sink(m30.try_emplace(source(), 1, 2)); // tainted [NOT DETECTED] + sink(m30); // tainted [NOT DETECTED] + sink(m30["abc"]); + sink(m31.try_emplace("abc", source(), 2)); // tainted + sink(m31); // tainted + sink(m31["abc"]); // tainted + sink(m32.try_emplace("abc", 1, source())); // tainted + sink(m32); // tainted + sink(m32["abc"]); // tainted + + // additional emplace test cases + std::unordered_map m33; + sink(m33.emplace(source(), "def").first); // tainted [NOT DETECTED] + sink(m33); // tainted [NOT DETECTED] + + std::unordered_map m34, m35; + sink(m34.emplace(std::pair("abc", "def")).first); + sink(m34); + sink(m34.emplace(std::pair("abc", source())).first); // tainted + sink(m34); // tainted + sink(m34.emplace_hint(m34.begin(), "abc", "def")); // tainted + sink(m35.emplace().first); + sink(m35); + sink(m35.emplace(std::pair(source(), "def")).first); // tainted [NOT DETECTED] + sink(m35); // tainted [NOT DETECTED] +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp new file mode 100644 index 000000000000..c0003388307a --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp @@ -0,0 +1,67 @@ + +int source(); +void sink(...) {}; + +class MyMovableClass { +public: + MyMovableClass() {} // Constructor + MyMovableClass(int _v) : v(_v) {} // ConversionConstructor + MyMovableClass(MyMovableClass &&other) noexcept { // ConversionConstructor, MoveConstructor + v = other.v; + other.v = 0; + } + MyMovableClass &operator=(MyMovableClass &&other) noexcept { // MoveAssignmentOperator + v = other.v; + other.v = 0; + return *this; + } + + int v; +}; + +MyMovableClass &&getUnTainted() { return MyMovableClass(1); } +MyMovableClass &&getTainted() { return MyMovableClass(source()); } + +void test_copyableclass() +{ + { + MyMovableClass s1(1); + MyMovableClass s2 = 1; + MyMovableClass s3; + s3 = 1; + + sink(s1); + sink(s2); + sink(s3); + } + + { + MyMovableClass s1(source()); + MyMovableClass s2 = source(); + MyMovableClass s3; + s3 = source(); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + } + + { + MyMovableClass s1 = MyMovableClass(source()); + MyMovableClass s2; + s2 = MyMovableClass(source()); + + sink(s1); // tainted + sink(s2); // tainted + } + + { + MyMovableClass s1(getUnTainted()); + MyMovableClass s2(getTainted()); + MyMovableClass s3; + + sink(s1); + sink(s2); // tainted + sink(s3 = source()); // tainted + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp new file mode 100644 index 000000000000..bcc764e35730 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp @@ -0,0 +1,238 @@ + +#include "stl.h" + +using namespace std; + +char *source(); + +void sink(char *); +void sink(std::set); +void sink(std::set::iterator); +void sink(std::unordered_set); +void sink(std::unordered_set::iterator); + +void test_set() +{ + // insert, find + std::set s1, s2, s3, s4, s5, s6; + + sink(s1.insert("abc").first); + sink(s2.insert(source()).first); // tainted + sink(s3.insert(s3.begin(), "abc")); + sink(s4.insert(s4.begin(), source())); // tainted + s5.insert(s1.begin(), s1.end()); + s6.insert(s2.begin(), s2.end()); + sink(s1); + sink(s2); // tainted + sink(s3); + sink(s4); // tainted + sink(s5); + sink(s6); // tainted + sink(s1.find("abc")); + sink(s2.find("abc")); // tainted + sink(s3.find("abc")); + sink(s4.find("abc")); // tainted + sink(s5.find("abc")); + sink(s6.find("abc")); // tainted + + // copy constructors and assignment + std::set s7(s2); + std::set s8 = s2; + std::set s9(s2.begin(), s2.end()); + std::set s10; + s10 = s2; + sink(s7); // tainted + sink(s8); // tainted + sink(s9); // tainted + sink(s10); // tainted + sink(s7.find("abc")); // tainted + sink(s8.find("abc")); // tainted + sink(s9.find("abc")); // tainted + sink(s10.find("abc")); // tainted + + // iterators + std::set::iterator i1, i2; + for (i1 = s1.begin(); i1 != s1.end(); i1++) + { + sink(*i1); + } + for (i2 = s2.begin(); i2 != s2.end(); i2++) + { + sink(*i2); // tainted + } + + // ranges + std::set s11; + s11.insert("a"); + s11.insert(source()); + s11.insert("c"); + sink(s11.lower_bound("b")); // tainted + sink(s11.upper_bound("b")); // tainted + sink(s11.equal_range("b").first); // tainted + sink(s11.equal_range("b").second); // tainted + + // swap + std::set s12, s13, s14, s15; + s12.insert(source()); + s15.insert(source()); + sink(s12); // tainted + sink(s13); + sink(s14); + sink(s15); // tainted + s12.swap(s13); + s14.swap(s15); + sink(s12); // [FALSE POSITIVE] + sink(s13); // tainted + sink(s14); // tainted + sink(s15); // [FALSE POSITIVE] + + // merge + std::set s16, s17, s18, s19; + s16.insert(source()); + s17.insert("abc"); + s18.insert("def"); + s19.insert(source()); + sink(s16); // tainted + sink(s17); + sink(s18); + sink(s19); // tainted + s16.merge(s17); + s18.merge(s19); + sink(s16); // tainted + sink(s17); + sink(s18); // tainted + sink(s19); // tainted + + // erase, clear + std::set s20; + s20.insert(source()); + s20.insert(source()); + sink(s20); // tainted + sink(s20.erase(s20.begin())); // tainted + sink(s20); // tainted + s20.clear(); + sink(s20); // [FALSE POSITIVE] + + // emplace, emplace_hint + std::set s21, s22; + sink(s21.emplace("abc").first); + sink(s21); + sink(s21.emplace(source()).first); // tainted + sink(s21); // tainted + sink(s22.emplace_hint(s22.begin(), "abc")); + sink(s22); + sink(s22.emplace_hint(s22.begin(), source())); // tainted + sink(s22); // tainted +} + +void test_unordered_set() +{ + // insert, find + std::unordered_set s1, s2, s3, s4, s5, s6; + + sink(s1.insert("abc").first); + sink(s2.insert(source()).first); // tainted + sink(s3.insert(s3.begin(), "abc")); + sink(s4.insert(s4.begin(), source())); // tainted + s5.insert(s1.begin(), s1.end()); + s6.insert(s2.begin(), s2.end()); + sink(s1); + sink(s2); // tainted + sink(s3); + sink(s4); // tainted + sink(s5); + sink(s6); // tainted + sink(s1.find("abc")); + sink(s2.find("abc")); // tainted + sink(s3.find("abc")); + sink(s4.find("abc")); // tainted + sink(s5.find("abc")); + sink(s6.find("abc")); // tainted + + // copy constructors and assignment + std::unordered_set s7(s2); + std::unordered_set s8 = s2; + std::unordered_set s9(s2.begin(), s2.end()); + std::unordered_set s10; + s10 = s2; + sink(s7); // tainted + sink(s8); // tainted + sink(s9); // tainted + sink(s10); // tainted + sink(s7.find("abc")); // tainted + sink(s8.find("abc")); // tainted + sink(s9.find("abc")); // tainted + sink(s10.find("abc")); // tainted + + // iterators + std::unordered_set::iterator i1, i2; + for (i1 = s1.begin(); i1 != s1.end(); i1++) + { + sink(*i1); + } + for (i2 = s2.begin(); i2 != s2.end(); i2++) + { + sink(*i2); // tainted + } + + // ranges + std::unordered_set s11; + s11.insert("a"); + s11.insert(source()); + s11.insert("c"); + sink(s11.equal_range("b").first); // tainted + sink(s11.equal_range("b").second); // tainted + + // swap + std::unordered_set s12, s13, s14, s15; + s12.insert(source()); + s15.insert(source()); + sink(s12); // tainted + sink(s13); + sink(s14); + sink(s15); // tainted + s12.swap(s13); + s14.swap(s15); + sink(s12); // [FALSE POSITIVE] + sink(s13); // tainted + sink(s14); // tainted + sink(s15); // [FALSE POSITIVE] + + // merge + std::unordered_set s16, s17, s18, s19; + s16.insert(source()); + s17.insert("abc"); + s18.insert("def"); + s19.insert(source()); + sink(s16); // tainted + sink(s17); + sink(s18); + sink(s19); // tainted + s16.merge(s17); + s18.merge(s19); + sink(s16); // tainted + sink(s17); + sink(s18); // tainted + sink(s19); // tainted + + // erase, clear + std::unordered_set s20; + s20.insert(source()); + s20.insert(source()); + sink(s20); // tainted + sink(s20.erase(s20.begin())); // tainted + sink(s20); // tainted + s20.clear(); + sink(s20); // [FALSE POSITIVE] + + // emplace, emplace_hint + std::unordered_set s21, s22; + sink(s21.emplace("abc").first); + sink(s21); + sink(s21.emplace(source()).first); // tainted + sink(s21); // tainted + sink(s22.emplace_hint(s22.begin(), "abc")); + sink(s22); + sink(s22.emplace_hint(s22.begin(), source())); // tainted + sink(s22); // tainted +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp new file mode 100644 index 000000000000..e6a5a1a0847b --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp @@ -0,0 +1,68 @@ +#include "stl.h" + +int source(); +void sink(int); +void sink(int*); + +template void sink(std::shared_ptr&); +template void sink(std::unique_ptr&); + +void test_make_shared() { + std::shared_ptr p = std::make_shared(source()); + sink(*p); // tainted + sink(p); // tainted +} + +void test_make_shared_array() { + std::shared_ptr p = std::make_shared(source()); + sink(*p); // not tainted + sink(p); // not tainted +} + +void test_make_unique() { + std::unique_ptr p = std::make_unique(source()); + sink(*p); // tainted + sink(p); // tainted +} + +void test_make_unique_array() { + std::unique_ptr p = std::make_unique(source()); + sink(*p); // not tainted + sink(p); // not tainted +} + +void test_reverse_taint_shared() { + std::shared_ptr p = std::make_shared(); + + *p = source(); + sink(p); // tainted [NOT DETECTED] + sink(*p); // tainted [NOT DETECTED] +} + +void test_reverse_taint_unique() { + std::unique_ptr p = std::unique_ptr(); + + *p = source(); + sink(p); // tainted [NOT DETECTED] + sink(*p); // tainted [NOT DETECTED] +} + +void test_shared_get() { + std::shared_ptr p = std::make_shared(source()); + sink(p.get()); // tainted +} + +void test_unique_get() { + std::unique_ptr p = std::make_unique(source()); + sink(p.get()); // tainted +} + +struct A { + int x, y; +}; + +void test_shared_field_member() { + std::unique_ptr p = std::make_unique(source(), 0); + sink(p->x); // tainted [NOT DETECTED] + sink(p->y); // not tainted +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp new file mode 100644 index 000000000000..60c36412a910 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp @@ -0,0 +1,55 @@ +#include "stl.h" + +using namespace std; + +void sink(int); + +class int_iterator_by_typedefs { +public: + typedef input_iterator_tag iterator_category; + typedef int value_type; + typedef size_t difference_type; + typedef int * pointer; + typedef int & reference; + + int_iterator_by_typedefs &operator++(); + int_iterator_by_typedefs operator++(int); + int operator*() const; +}; + +class int_iterator_by_trait { +public: + int_iterator_by_trait &operator++(); + int_iterator_by_trait operator++(int); + int operator*() const; +}; + +template<> +struct std::iterator_traits { + typedef input_iterator_tag iterator_category; +}; + +class non_iterator { +public: + non_iterator &operator++(); + non_iterator operator++(int); + int operator*() const; +}; + +void test_typedefs(int_iterator_by_typedefs source1) { + sink(*source1); // tainted + sink(*(source1++)); // tainted + sink(*(++source1)); // tainted +} + +void test_trait(int_iterator_by_trait source1) { + sink(*source1); // tainted + sink(*(source1++)); // tainted + sink(*(++source1)); // tainted +} + +void test_non_iterator(non_iterator source1) { + sink(*source1); + sink(*(source1++)); + sink(*(++source1)); +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h new file mode 100644 index 000000000000..d767218c1007 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h @@ -0,0 +1,589 @@ + +typedef unsigned long size_t; + +template +struct remove_const { typedef T type; }; + +template +struct remove_const { typedef T type; }; + +// `remove_const_t` removes any `const` specifier from `T` +template +using remove_const_t = typename remove_const::type; + +template +struct remove_reference { typedef T type; }; + +template +struct remove_reference { typedef T type; }; + +template +struct remove_reference { typedef T type; }; + +// `remove_reference_t` removes any `&` from `T` +template +using remove_reference_t = typename remove_reference::type; + +namespace std +{ + template constexpr T&& forward(remove_reference_t& t) noexcept; + template constexpr T&& forward(remove_reference_t&& t) noexcept; +} + +// --- iterator --- + +namespace std { + struct ptrdiff_t; + + template struct iterator_traits; + + template + struct iterator { + typedef Category iterator_category; + + iterator(); + iterator(iterator > const &other); // non-const -> const conversion constructor + + iterator &operator++(); + iterator operator++(int); + iterator &operator--(); + iterator operator--(int); + bool operator==(iterator other) const; + bool operator!=(iterator other) const; + reference_type operator*() const; + pointer_type operator->() const; + iterator operator+(int); + iterator operator-(int); + iterator &operator+=(int); + iterator &operator-=(int); + int operator-(iterator); + reference_type operator[](int); + }; + + struct input_iterator_tag {}; + struct forward_iterator_tag : public input_iterator_tag {}; + struct bidirectional_iterator_tag : public forward_iterator_tag {}; + struct random_access_iterator_tag : public bidirectional_iterator_tag {}; +} + +// --- string --- + +namespace std +{ + template struct char_traits; + + typedef size_t streamsize; + + template class allocator { + public: + allocator() throw(); + typedef size_t size_type; + }; + + template, class Allocator = allocator > + class basic_string { + public: + using value_type = charT; + using reference = value_type&; + using const_reference = const value_type&; + typedef typename Allocator::size_type size_type; + static const size_type npos = -1; + + explicit basic_string(const Allocator& a = Allocator()); + basic_string(const charT* s, const Allocator& a = Allocator()); + template basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()); + + const charT* c_str() const; + charT* data() noexcept; + size_t length() const; + + typedef std::iterator iterator; + typedef std::iterator const_iterator; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + void push_back(charT c); + + const charT& front() const; + charT& front(); + const charT& back() const; + charT& back(); + + const_reference operator[](size_type pos) const; + reference operator[](size_type pos); + const_reference at(size_type n) const; + reference at(size_type n); + template basic_string& operator+=(const T& t); + basic_string& operator+=(const charT* s); + basic_string& append(const basic_string& str); + basic_string& append(const charT* s); + basic_string& append(size_type n, charT c); + template basic_string& append(InputIterator first, InputIterator last); + basic_string& assign(const basic_string& str); + basic_string& assign(size_type n, charT c); + template basic_string& assign(InputIterator first, InputIterator last); + basic_string& insert(size_type pos, const basic_string& str); + basic_string& insert(size_type pos, size_type n, charT c); + basic_string& insert(size_type pos, const charT* s); + iterator insert(const_iterator p, size_type n, charT c); + template iterator insert(const_iterator p, InputIterator first, InputIterator last); + basic_string& replace(size_type pos1, size_type n1, const basic_string& str); + basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c); + size_type copy(charT* s, size_type n, size_type pos = 0) const; + void clear() noexcept; + basic_string substr(size_type pos = 0, size_type n = npos) const; + void swap(basic_string& s) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; + }; + + template basic_string operator+(const basic_string& lhs, const basic_string& rhs); + template basic_string operator+(const basic_string& lhs, const charT* rhs); + + typedef basic_string string; +} + +// --- istring / ostream / stringstream --- + +namespace std +{ + template > + class basic_istream /*: virtual public basic_ios - not needed for this test */ { + public: + using char_type = charT; + using int_type = int; //typename traits::int_type; + + basic_istream& operator>>(int& n); + + int_type get(); + basic_istream& get(char_type& c); + basic_istream& get(char_type* s, streamsize n); + int_type peek(); + basic_istream& read (char_type* s, streamsize n); + streamsize readsome(char_type* s, streamsize n); + basic_istream& putback(char_type c); + basic_istream& unget(); + + basic_istream& getline(char_type* s, streamsize n); + basic_istream& getline(char_type* s, streamsize n, char_type delim); + }; + + template basic_istream& operator>>(basic_istream&, charT*); + template basic_istream& operator>>(basic_istream& is, basic_string& str); + + template basic_istream& getline(basic_istream& is, basic_string& str, charT delim); + template basic_istream& getline(basic_istream& is, basic_string& str); + + template > + class basic_ostream /*: virtual public basic_ios - not needed for this test */ { + public: + typedef charT char_type; + + basic_ostream& operator<<(int n); + + basic_ostream& put(char_type c); + basic_ostream& write(const char_type* s, streamsize n); + basic_ostream& flush(); + }; + + template basic_ostream& operator<<(basic_ostream&, const charT*); + template basic_ostream& operator<<(basic_ostream& os, const basic_string& str); + + template> + class basic_iostream : public basic_istream, public basic_ostream { + public: + }; + + template, class Allocator = allocator> + class basic_stringstream : public basic_iostream { + public: + explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/); + explicit basic_stringstream( const basic_string& str/*, ios_base::openmode which = ios_base::out | ios_base::in*/); + basic_stringstream(const basic_stringstream& rhs) = delete; + basic_stringstream(basic_stringstream&& rhs); + basic_stringstream& operator=(const basic_stringstream& rhs) = delete; + basic_stringstream& operator=(basic_stringstream&& rhs); + + void swap(basic_stringstream& rhs); + + basic_string str() const; + void str(const basic_string& str); + }; + + typedef basic_istream istream; + typedef basic_ostream ostream; + extern istream cin; + extern ostream cout; + + using stringstream = basic_stringstream; +} + +// --- vector --- + +namespace std { + template> + class vector { + public: + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = unsigned int; + using iterator = std::iterator; + using const_iterator = std::iterator; + + vector() noexcept(noexcept(Allocator())) : vector(Allocator()) { } + explicit vector(const Allocator&) noexcept; + explicit vector(size_type n, const Allocator& = Allocator()); + vector(size_type n, const T& value, const Allocator& = Allocator()); + template vector(InputIterator first, InputIterator last, const Allocator& = Allocator()); + // use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or + // similar that should match a different overload (SFINAE). + ~vector(); + + vector& operator=(const vector& x); + vector& operator=(vector&& x) noexcept/*(allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value)*/; + template void assign(InputIterator first, InputIterator last); + // use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or + // similar that should match a different overload (SFINAE). + void assign(size_type n, const T& u); + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + size_type size() const noexcept; + + reference operator[](size_type n); + const_reference operator[](size_type n) const; + const_reference at(size_type n) const; + reference at(size_type n); + reference front(); + const_reference front() const; + reference back(); + const_reference back() const; + + T* data() noexcept; + const T* data() const noexcept; + + void push_back(const T& x); + void push_back(T&& x); + + iterator insert(const_iterator position, const T& x); + iterator insert(const_iterator position, T&& x); + iterator insert(const_iterator position, size_type n, const T& x); + template iterator insert(const_iterator position, InputIterator first, InputIterator last); + + void swap(vector&) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; + + void clear() noexcept; + }; +} + +// --- make_shared / make_unique --- + +namespace std { + template + class shared_ptr { + public: + shared_ptr() noexcept; + explicit shared_ptr(T*); + template shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(shared_ptr&&) noexcept; + + shared_ptr& operator=(const shared_ptr&) noexcept; + shared_ptr& operator=(shared_ptr&&) noexcept; + + T& operator*() const noexcept; + T* operator->() const noexcept; + + T* get() const noexcept; + }; + + template + class unique_ptr { + public: + constexpr unique_ptr() noexcept; + explicit unique_ptr(T*) noexcept; + unique_ptr(unique_ptr&&) noexcept; + + unique_ptr& operator=(unique_ptr&&) noexcept; + + T& operator*() const; + T* operator->() const noexcept; + + T* get() const noexcept; + }; + + template unique_ptr make_unique(Args&&...); + + template shared_ptr make_shared(Args&&...); +} + +// --- pair --- + +namespace std { + template + struct pair { + typedef T1 first_type; + typedef T2 second_type; + + T1 first; + T2 second; + pair(); + pair(const T1& x, const T2& y) : first(x), second(y) {}; + template pair(const pair &p); + + void swap(pair& p) /*noexcept(...)*/; + }; + + template constexpr pair, remove_reference_t> make_pair(T1&& x, T2&& y) { + return pair(std::forward(x), std::forward(y)); + } +} + +// --- map --- + +namespace std { + template struct less; + + template, class Allocator = allocator>> + class map { + public: + using key_type = Key; + using mapped_type = T; + using value_type = pair; + using iterator = std::iterator; + using const_iterator = std::iterator; + + map() /*: map(Compare()) { }*/; + map(const map& x); + map(map&& x); + ~map(); + + map& operator=(const map& x); + map& operator=(map&& x) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + T& operator[](const key_type& x); + T& operator[](key_type&& x); + T& at(const key_type& x); + const T& at(const key_type& x) const; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + + pair insert(const value_type& x); + pair insert(value_type&& x); + iterator insert(const_iterator position, const value_type& x); + iterator insert(const_iterator position, value_type&& x); + + template pair try_emplace(const key_type& k, Args&&... args); + template pair try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); + template pair insert_or_assign(const key_type& k, M&& obj); + template pair insert_or_assign(key_type&& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(map&) /*noexcept(/*==allocator_traits::is_always_equal::value && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(map& source); + template void merge(map&& source); + + iterator find(const key_type& x); + const_iterator find(const key_type& x) const; + + iterator lower_bound(const key_type& x); + const_iterator lower_bound(const key_type& x) const; + iterator upper_bound(const key_type& x); + const_iterator upper_bound(const key_type& x) const; + + pair equal_range(const key_type& x); + pair equal_range(const key_type& x) const; + }; + + template struct hash; + template struct equal_to; + + template, class Pred = equal_to, class Allocator = allocator>> + class unordered_map { + public: + using key_type = Key; + using mapped_type = T; + using value_type = pair; + using iterator = std::iterator; + using const_iterator = std::iterator; + + unordered_map(); + unordered_map(const unordered_map&); + unordered_map(unordered_map&&); + ~unordered_map(); + + unordered_map& operator=(const unordered_map&); + unordered_map& operator=(unordered_map&&) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + mapped_type& operator[](const key_type& k); + mapped_type& operator[](key_type&& k); + mapped_type& at(const key_type& k); + const mapped_type& at(const key_type& k) const; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + + pair insert(const value_type& obj); + pair insert(value_type&& obj); + iterator insert(const_iterator hint, const value_type& obj); + iterator insert(const_iterator hint, value_type&& obj); + + template pair try_emplace(const key_type& k, Args&&... args); + template pair try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); + template pair insert_or_assign(const key_type& k, M&& obj); + template pair insert_or_assign(key_type&& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(unordered_map&) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_swappable_v && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(unordered_map& source); + template void merge(unordered_map&& source); + + iterator find(const key_type& k); + const_iterator find(const key_type& k) const; + + pair equal_range(const key_type& k); + pair equal_range(const key_type& k) const; + }; +}; + +// --- set --- + +namespace std { + template, class Allocator = allocator> + class set { + public: + using key_type = Key; + using value_type = Key; + using size_type = size_t; + using allocator_type = Allocator; + using iterator = std::iterator; + using const_iterator = std::iterator; + + set() /*: set(Compare())*/ { } + set(const set& x); + set(set&& x); + template set(InputIterator first, InputIterator last/*, const Compare& comp = Compare(), const Allocator& = Allocator()*/); + ~set(); + + set& operator=(const set& x); + set& operator=(set&& x) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + pair insert(const value_type& x); + pair insert(value_type&& x); + iterator insert(const_iterator position, const value_type& x); + iterator insert(const_iterator position, value_type&& x); + template void insert(InputIterator first, InputIterator last); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(set&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(set& source); + template void merge(set&& source); + + iterator find(const key_type& x); + const_iterator find(const key_type& x) const; + + iterator lower_bound(const key_type& x); + const_iterator lower_bound(const key_type& x) const; + iterator upper_bound(const key_type& x); + const_iterator upper_bound(const key_type& x) const; + pair equal_range(const key_type& x); + pair equal_range(const key_type& x) const; + }; + + template, class Pred = equal_to, class Allocator = allocator> + class unordered_set { + public: + using key_type = Key; + using value_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using size_type = size_t; + using iterator = std::iterator; + using const_iterator = std::iterator; + + unordered_set(); + unordered_set(const unordered_set&); + unordered_set(unordered_set&&); + template unordered_set(InputIterator f, InputIterator l, size_type n = 0/*, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()*/); + ~unordered_set(); + + unordered_set& operator=(const unordered_set&); + unordered_set& operator=(unordered_set&&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + pair insert(const value_type& obj); + pair insert(value_type&& obj); + iterator insert(const_iterator hint, const value_type& obj); + iterator insert(const_iterator hint, value_type&& obj); + template void insert(InputIterator first, InputIterator last); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(unordered_set&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_swappable_v && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(unordered_set& source); + template void merge(unordered_set&& source); + + iterator find(const key_type& k); + const_iterator find(const key_type& k) const; + pair equal_range(const key_type& k); + pair equal_range(const key_type& k) const; + }; +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp new file mode 100644 index 000000000000..6a0bf47e4d7d --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp @@ -0,0 +1,566 @@ + +#include "stl.h" + +using namespace std; + +char *source(); + +namespace ns_char +{ + char source(); +} + +char *user_input() { + return source(); +} + +void sink(const char *s); +void sink(const std::string &s); +void sink(const char *filename, const char *mode); +void sink(char); +void sink(std::string::iterator); + +void test_string() +{ + char *a = source(); + std::string b("123"); + std::string c(source()); + + sink(a); // tainted + sink(b); + sink(c); // tainted + sink(b.c_str()); + sink(c.c_str()); // tainted +} + +void test_strings2() +{ + string path1 = user_input(); + sink(path1.c_str(), "r"); // tainted + + string path2; + path2 = user_input(); + sink(path2.c_str(), "r"); // tainted + + string path3(user_input()); + sink(path3.c_str(), "r"); // tainted +} + +void test_string3() +{ + const char *cs = source(); + + // convert char * -> std::string + std::string ss(cs); + + sink(cs); // tainted + sink(ss); // tainted +} + +void test_string4() +{ + const char *cs = source(); + + // convert char * -> std::string + std::string ss(cs); + + // convert back std::string -> char * + cs = ss.c_str(); + + sink(cs); // tainted + sink(ss); // tainted +} + +void test_string_constructors_assignments() +{ + { + std::string s1("hello"); + std::string s2 = "hello"; + std::string s3; + s3 = "hello"; + + sink(s1); + sink(s2); + sink(s3); + } + + { + std::string s1(source()); + std::string s2 = source(); + std::string s3; + s3 = source(); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + } + + { + std::string s1; + std::string s2 = s1; + std::string s3; + s3 = s1; + + sink(s1); + sink(s2); + sink(s3); + } + + { + std::string s1 = std::string(source()); + std::string s2; + s2 = std::string(source()); + + sink(s1); // tainted + sink(s2); // tainted + } +} + +void test_range_based_for_loop_string() { + std::string s(source()); + for(char c : s) { + sink(c); // tainted [NOT DETECTED by IR] + } + + for(std::string::iterator it = s.begin(); it != s.end(); ++it) { + sink(*it); // tainted [NOT DETECTED by IR] + } + + for(char& c : s) { + sink(c); // tainted [NOT DETECTED by IR] + } + + const std::string const_s(source()); + for(const char& c : const_s) { + sink(c); // tainted [NOT DETECTED by IR] + } +} + +void test_string_append() { + { + std::string s1("hello"); + std::string s2(source()); + + sink(s1 + s1); + sink(s1 + s2); // tainted + sink(s2 + s1); // tainted + sink(s2 + s2); // tainted + + sink(s1 + " world"); + sink(s1 + source()); // tainted + } + + { + std::string s3("abc"); + std::string s4(source()); + std::string s5, s6, s7, s8, s9; + + s5 = s3 + s4; + sink(s5); // tainted + + s6 = s3; + sink(s6 += s4); // tainted + sink(s6); // tainted + + s7 = s3; + sink(s7 += source()); // tainted + sink(s7 += " "); // tainted + sink(s7); // tainted + + s8 = s3; + s8.append(s4); + sink(s8); // tainted + + s9 = s3; + s9.append(source()); + s9.append(" "); + sink(s9); // tainted + } + + { + std::string s10("abc"); + char c = ns_char::source(); + + s10.append(1, c); + sink(s10); // tainted + } +} + +void test_string_assign() { + std::string s1("hello"); + std::string s2(source()); + char c = ns_char::source(); + std::string s3, s4, s5; + std::string s6(source()); + + sink(s3.assign(s1)); + sink(s3); + + sink(s4.assign(s2)); // tainted + sink(s4); // tainted + + sink(s5.assign(10, c)); // tainted + sink(s5); // tainted + + sink(s6.assign(s1)); + sink(s6); // [FALSE POSITIVE] +} + +void test_string_insert() { + std::string s1("hello"); + std::string s2(source()); + char c = ns_char::source(); + std::string s3, s4, s5, s6; + + s3 = s1; + sink(s3.insert(0, s1)); + sink(s3); + + s4 = s2; + sink(s4.insert(0, s1)); // tainted + sink(s4); // tainted + + s5 = s1; + sink(s5.insert(0, s2)); // tainted + sink(s5); // tainted + + s6 = s1; + sink(s6.insert(0, 10, c)); // tainted + sink(s6); // tainted +} + +void test_string_replace() { + std::string s1("hello"); + std::string s2(source()); + char c = ns_char::source(); + std::string s3, s4, s5, s6; + + s3 = s1; + sink(s3.replace(1, 2, s1)); + sink(s3); + + s4 = s2; + sink(s4.replace(1, 2, s1)); // tainted + sink(s4); // tainted + + s5 = s1; + sink(s5.replace(1, 2, s2)); // tainted + sink(s5); // tainted + + s6 = s1; + sink(s6.replace(1, 2, 10, c)); // tainted + sink(s6); // tainted +} + +void test_string_copy() { + char b1[1024] = {0}; + char b2[1024] = {0}; + std::string s1("hello"); + std::string s2(source()); + + s1.copy(b1, s1.length(), 0); + sink(b1); + + s2.copy(b2, s1.length(), 0); + sink(b2); // tainted +} + +void test_string_swap() { + std::string s1("hello"); + std::string s2(source()); + std::string s3("world"); + std::string s4(source()); + + sink(s1); + sink(s2); // tainted + sink(s3); + sink(s4); // tainted + + s1.swap(s2); + s4.swap(s3); + + sink(s1); // tainted + sink(s2); // [FALSE POSITIVE] + sink(s3); // tainted + sink(s4); // [FALSE POSITIVE] +} + +void test_string_clear() { + std::string s1(source()); + std::string s2(source()); + std::string s3(source()); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + + s1.clear(); + s2 = ""; + s3 = s3; + + sink(s1); // [FALSE POSITIVE] + sink(s2); + sink(s3); // tainted +} + +void test_string_data() +{ + std::string a("123"); + std::string b(source()); + + sink(a.data()); + sink(b.data()); // tainted + sink(a.length()); + sink(b.length()); +} + +void test_string_substr() +{ + std::string a("123"); + std::string b(source()); + + sink(a.substr(0, a.length())); + sink(b.substr(0, b.length())); // tainted +} + +void test_string_at() +{ + std::string a("123"); + std::string b("123"); + std::string c("123"); + + sink(a); + sink(b); + sink(c); + + a[0] = ns_char::source(); + b.at(0) = ns_char::source(); + c[0] = a[0]; + + sink(a); // tainted + sink(b); // tainted + sink(c); // tainted +} + +void test_string_data_more() +{ + std::string str("123"); + + str.data()[1] = ns_char::source(); + sink(str); // tainted + sink(str.data()); // tainted +} + +void test_string_iterators() { + // string append + { + std::string s1("hello"); + std::string s2(source()); + std::string s3("hello"); + std::string s4("world"); + + sink(s1); + sink(s1.append(s2.begin(), s2.end())); // tainted + sink(s1); // tainted + + sink(s3); + sink(s3.append(s4.begin(), s4.end())); + sink(s3); + } + + // dereference + { + std::string s1("hello"); + std::string s2(source()); + + string::iterator iter1 = s1.begin(); + + sink(*iter1); + sink(iter1[1]); + string::iterator iter2 = s2.begin(); + + sink(*iter2); // tainted + sink(iter2[1]); // tainted + } + + // arithmetic operators + { + std::string s1("hello"); + std::string s2(source()); + + string::iterator i1 = s1.begin(); + + string::iterator i2 = s2.begin(); + string::iterator i3, i4, i5, i6, i7, i8, i9, i10, i11; + + sink(*(i2+1)); //tainted + sink(*(i2-1)); // tainted + i3 = i2; + sink(*(++i3)); // tainted + i4 = i2; + sink(*(--i4)); // tainted + i5 = i2; + i5++; + sink(*i5); // tainted + i6 = i2; + i6--; + sink(*i6); // tainted + i7 = i2; + sink(*(i7+=1)); // tainted + i8 = i2; + sink(*(i8-=1)); // tainted + + i9 = s2.end(); + --i9; + sink(*i9); // tainted + + i10 = i2; + sink(*(i10++)); // tainted + sink(i10); // tainted + i11 = i2; + sink(*(i11--)); // tainted + sink(i11); // tainted + } +} + +void test_string_insert_more() +{ + std::string s1("aa"); + std::string s2("bb"); + char *cs1 = "cc"; + char *cs2 = source(); + + sink(s1.insert(0, cs1)); + sink(s1); + + sink(s2.insert(0, cs2)); // tainted + sink(s2); // tainted +} + +void test_string_iterator_methods() +{ + { + std::string a("aa"); + std::string b("bb"); + + sink(a.insert(a.begin(), 10, 'x')); + sink(a); + + sink(b.insert(b.begin(), 10, ns_char::source())); // tainted + sink(b); // tainted + } + + { + std::string c("cc"); + std::string d("dd"); + std::string s1("11"); + std::string s2(source()); + + sink(c.insert(c.end(), s1.begin(), s1.end())); + sink(c); + + sink(d.insert(d.end(), s2.begin(), s2.end())); // tainted + sink(d); // tainted + + sink(s2.insert(s2.end(), s1.begin(), s1.end())); // tainted + sink(s2); // tainted + } + + { + std::string e("ee"); + std::string f("ff"); + std::string s3("33"); + std::string s4(source()); + + sink(e.append(s3.begin(), s3.end())); + sink(e); + + sink(f.append(s4.begin(), s4.end())); // tainted + sink(f); // tainted + + sink(s4.append(s3.begin(), s3.end())); // tainted + sink(s4); // tainted + } + + { + std::string g("gg"); + std::string h("hh"); + std::string s5("55"); + std::string s6(source()); + + sink(g.assign(s5.cbegin(), s5.cend())); + sink(g); + + sink(h.assign(s6.cbegin(), s6.cend())); // tainted + sink(h); // tainted + + sink(s6.assign(s5.cbegin(), s5.cend())); + sink(s6); // [FALSE POSITIVE] + } +} + +void test_constructors_more() { + char *cs1 = "abc"; + char *cs2 = source(); + std::string s1(cs1); + std::string s2(cs2); + std::string s3(s1.begin(), s1.end()); + std::string s4(s2.begin(), s2.end()); + + sink(s1); + sink(s2); // tainted + sink(s3); + sink(s4); // tainted +} + +void test_string_front_back() { + std::string a("aa"); + + sink(a.front()); + sink(a.back()); + a.push_back(ns_char::source()); + sink(a.front()); // [FALSE POSITIVE] + sink(a.back()); // tainted +} + +void test_string_return_assign() { + { + std::string a("aa"); + std::string b("bb"); + std::string c("cc"); + std::string d("dd"); + std::string e("ee"); + std::string f("ff"); + + sink( a += (b += "bb") ); + sink( c += (d += source()) ); // tainted + sink( (e += "ee") += source() ); // tainted + sink( (f += source()) += "ff" ); // tainted + sink(a); + sink(b); + sink(c); // tainted + sink(d); // tainted + sink(e); // tainted + sink(f); // tainted + } + + { + std::string a("aa"); + std::string b("bb"); + std::string c("cc"); + std::string d("dd"); + std::string e("ee"); + std::string f("ff"); + + sink( a.assign(b.assign("bb")) ); + sink( c.assign(d.assign(source())) ); // tainted + sink( e.assign("ee").assign(source()) ); // tainted + sink( f.assign(source()).assign("ff") ); + sink(a); + sink(b); + sink(c); // tainted + sink(d); // tainted + sink(e); // tainted + sink(f); // [FALSE POSITIVE] + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp new file mode 100644 index 000000000000..e700bccb9300 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp @@ -0,0 +1,268 @@ + +#include "stl.h" + +using namespace std; + +char *source(); + +namespace ns_char +{ + char source(); +} + +void sink(int i); + +void sink(const std::string &s); + +template +void sink(const std::basic_ostream &s); + +template +void sink(const std::basic_istream &s); + +template +void sink(const std::basic_iostream &s); + +void test_stringstream_string(int amount) +{ + std::stringstream ss1, ss2, ss3, ss4, ss5, ss6, ss7, ss8, ss9, ss10, ss11, ss12, ss13; + std::string t(source()); + + sink(ss1 << "1234"); + sink(ss2 << source()); // tainted + sink(ss3 << "123" << source()); // tainted + sink(ss4 << source() << "456"); // tainted + sink(ss5 << t); // tainted + + sink(ss1); + sink(ss2); // tainted + sink(ss3); // tainted + sink(ss4); // tainted + sink(ss5); // tainted + sink(ss1.str()); + sink(ss2.str()); // tainted + sink(ss3.str()); // tainted + sink(ss4.str()); // tainted + sink(ss5.str()); // tainted + + ss6.str("abc"); + ss6.str(source()); // (overwrites) + ss7.str(source()); + ss7.str("abc"); // (overwrites) + sink(ss6); // tainted + sink(ss7); // [FALSE POSITIVE] + + sink(ss8.put('a')); + sink(ss9.put(ns_char::source())); // tainted + sink(ss10.put('a').put(ns_char::source()).put('z')); // tainted + sink(ss8); + sink(ss9); // tainted + sink(ss10); // tainted + + sink(ss11.write("begin", 5)); + sink(ss12.write(source(), 5)); // tainted + sink(ss13.write("begin", 5).write(source(), amount).write("end", 3)); // tainted + sink(ss11); + sink(ss12); // tainted + sink(ss13); // tainted +} + +void test_stringstream_int(int source) +{ + std::stringstream ss1, ss2; + int v1 = 0, v2 = 0; + + sink(ss1 << 1234); + sink(ss2 << source); // tainted + sink(ss1 >> v1); + sink(ss2 >> v2); // tainted + + sink(ss1); + sink(ss2); // tainted + sink(ss1.str()); + sink(ss2.str()); // tainted + sink(v1); + sink(v2); // tainted +} + +void test_stringstream_constructors() +{ + std::string s1 = "abc"; + std::string s2 = source(); + std::stringstream ss1(s1); + std::stringstream ss2(s2); + std::stringstream ss3 = std::stringstream("abc"); + std::stringstream ss4 = std::stringstream(source()); + std::stringstream ss5; + std::stringstream ss6; + + sink(ss5 = std::stringstream("abc")); + sink(ss6 = std::stringstream(source())); // tainted + + sink(ss1); + sink(ss2); // tainted + sink(ss3); + sink(ss4); // tainted + sink(ss5); + sink(ss6); // tainted +} + +void test_stringstream_swap() +{ + std::stringstream ss1("abc"); + std::stringstream ss2(source()); + std::stringstream ss3("abc"); + std::stringstream ss4(source()); + + ss1.swap(ss2); + ss4.swap(ss3); + + sink(ss1); // tainted + sink(ss2); // [FALSE POSITIVE] + sink(ss3); // tainted + sink(ss4); // [FALSE POSITIVE] +} + +void test_stringstream_in() +{ + std::stringstream ss1, ss2; + std::string s1, s2, s3, s4; + char b1[100] = {0}; + char b2[100] = {0}; + char b3[100] = {0}; + char b4[100] = {0}; + char b5[100] = {0}; + char b6[100] = {0}; + char b7[100] = {0}; + char b8[100] = {0}; + char b9[100] = {0}; + char b10[100] = {0}; + char c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0, c6 = 0; + + sink(ss1 << "abc"); + sink(ss2 << source()); // tainted + + sink(ss1 >> s1); + sink(ss2 >> s2); // tainted + sink(ss2 >> s3 >> s4); // tainted + sink(s1); + sink(s2); // tainted + sink(s3); // tainted + sink(s4); // tainted + + sink(ss1 >> b1); + sink(ss2 >> b2); // tainted + sink(ss2 >> b3 >> b4); // tainted + sink(b1); + sink(b2); // tainted + sink(b3); // tainted + sink(b4); // tainted + + sink(ss1.read(b5, 100)); + sink(ss2.read(b6, 100)); // tainted + sink(ss1.readsome(b7, 100)); + sink(ss2.readsome(b8, 100)); // (returns a length, not significantly tainted) + sink(ss1.get(b9, 100)); + sink(ss2.get(b10, 100)); // tainted + sink(b5); + sink(b6); // tainted + sink(b7); + sink(b8); // tainted + sink(b9); + sink(b10); // tainted + + sink(c1 = ss1.get()); + sink(c2 = ss2.get()); // tainted + sink(c3 = ss1.peek()); + sink(c4 = ss2.peek()); // tainted + sink(ss1.get(c5)); + sink(ss2.get(c6)); // tainted + sink(c1); + sink(c2); // tainted + sink(c3); + sink(c4); // tainted + sink(c5); + sink(c6); // tainted +} + +void test_stringstream_putback() +{ + std::stringstream ss; + + sink(ss.put('a')); + sink(ss.get()); + sink(ss.putback('b')); + sink(ss.get()); + sink(ss.putback(ns_char::source())); // tainted + sink(ss.get()); // tainted +} + +void test_getline() +{ + std::stringstream ss1("abc"); + std::stringstream ss2(source()); + char b1[1000] = {0}; + char b2[1000] = {0}; + char b3[1000] = {0}; + char b4[1000] = {0}; + char b5[1000] = {0}; + char b6[1000] = {0}; + char b7[1000] = {0}; + char b8[1000] = {0}; + std::string s1, s2, s3, s4, s5, s6, s7, s8; + + sink(ss1.getline(b1, 1000)); + sink(ss2.getline(b2, 1000)); // tainted + sink(ss2.getline(b3, 1000)); // tainted + sink(ss1.getline(b3, 1000)); + sink(b1); + sink(b2); // tainted + sink(b3); // [FALSE POSITIVE] + + sink(ss1.getline(b4, 1000, ' ')); + sink(ss2.getline(b5, 1000, ' ')); // tainted + sink(ss2.getline(b6, 1000, ' ')); // tainted + sink(ss1.getline(b6, 1000, ' ')); + sink(b4); + sink(b5); // tainted + sink(b6); // [FALSE POSITIVE] + + sink(ss2.getline(b7, 1000).getline(b8, 1000)); // tainted + sink(b7); // tainted + sink(b8); // tainted + + sink(getline(ss1, s1)); + sink(getline(ss2, s2)); // tainted + sink(getline(ss2, s3)); // tainted + sink(getline(ss1, s3)); + sink(s1); + sink(s2); // tainted + sink(s3); // [FALSE POSITIVE] + + sink(getline(ss1, s4, ' ')); + sink(getline(ss2, s5, ' ')); // tainted + sink(getline(ss2, s6, ' ')); // tainted + sink(getline(ss1, s6, ' ')); + sink(s4); + sink(s5); // tainted + sink(s6); // [FALSE POSITIVE] + + sink(getline(getline(ss2, s7), s8)); // tainted + sink(s7); // tainted + sink(s8); // tainted +} + +void test_chaining() +{ + std::stringstream ss1(source()); + std::stringstream ss2; + char b1[1000] = {0}; + char b2[1000] = {0}; + + sink(ss1.get(b1, 100).unget().get(b2, 100)); // tainted + sink(b1); // tainted + sink(b2); // tainted + + sink(ss2.write("abc", 3).flush().write(source(), 3).flush().write("xyz", 3)); // tainted + sink(ss2); // tainted +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp new file mode 100644 index 000000000000..727a0fff53b9 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/structlikeclass.cpp @@ -0,0 +1,64 @@ + +int source(); +void sink(...) {}; + +class StructLikeClass { +public: + StructLikeClass() {} // Constructor + StructLikeClass(int _v) : v(_v) {} // ConversionConstructor + + int v; +}; + +void test_structlikeclass() +{ + { + StructLikeClass s1(1); + StructLikeClass s2 = 1; + StructLikeClass s3(s1); + StructLikeClass s4; + s4 = 1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + StructLikeClass s1(source()); + StructLikeClass s2 = source(); + StructLikeClass s3(s1); + StructLikeClass s4; + s4 = source(); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3); // tainted + sink(s4); // tainted + } + + { + StructLikeClass s1; + StructLikeClass s2 = s1; + StructLikeClass s3(s1); + StructLikeClass s4; + s4 = s1; + + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + { + StructLikeClass s1 = StructLikeClass(source()); + StructLikeClass s2; + StructLikeClass s3; + s2 = StructLikeClass(source()); + + sink(s1); // tainted + sink(s2); // tainted + sink(s3 = source()); // tainted + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/swap.h b/cpp/ql/test/library-tests/dataflow/taint-tests/swap.h new file mode 100644 index 000000000000..6e554ac15dba --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/swap.h @@ -0,0 +1,5 @@ +namespace std +{ + template + constexpr void swap(T &a, T &b); +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp new file mode 100644 index 000000000000..7ba05b86f729 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp @@ -0,0 +1,146 @@ +#include "swap.h" +/* + * Note: This file exists in two versions (swap1.cpp and swap2.cpp). + * The only difference is that `IntWrapper` in swap1.cpp contains a single data member, and swap2.cpp + * contains two data members. + */ + +int source(); +void sink(...); + +namespace std +{ + template + T &&move(T &t) noexcept { return static_cast(t); } // simplified signature (and implementation) +} // namespace std + +namespace IntWrapper +{ + struct Class + { + int data1; + + Class() = default; + Class(Class &&that) { swap(that); } + Class(const Class &that) : data1(that.data1) {} + + Class &operator=(const Class &that) + { + auto tmp = that; + swap(tmp); + return *this; + } + + Class &operator=(Class &&that) + { + swap(that); + return *this; + } + + Class ©_assign(const Class &that) // copy assignment without the usual signature + { + auto tmp = that; + swap(tmp); + return *this; + } + + Class &move_assign(Class &&that) // move assignment without the usual signature + { + swap(that); + return *this; + } + + void swap(Class &that) noexcept + { + using std::swap; + swap(data1, that.data1); + } + }; + + // For ADL + void swap(Class &x, Class &y) + { + x.swap(y); + } +} // namespace IntWrapper + +void test_copy_assignment_operator() +{ + IntWrapper::Class x; + IntWrapper::Class y; + x.data1 = source(); + + sink(x.data1); // tainted + sink(y.data1); // clean + + y = x; + + sink(y.data1); // tainted + sink(x.data1); // tainted + + IntWrapper::Class z1, z2; + z1.data1 = source(); + sink(z1.data1); // tainted + + swap(z1, z2); + + sink(z2.data1); // tainted [FALSE NEGATIVE in IR] + sink(z1.data1); // clean [FALSE POSITIVE] +} + +void test_move_assignment_operator() +{ + IntWrapper::Class x; + IntWrapper::Class y; + x.data1 = source(); + + sink(x.data1); // tainted + sink(y.data1); // clean + + y = std::move(x); + + sink(y.data1); // tainted + sink(x.data1); // tainted +} + +void test_move_constructor() +{ + IntWrapper::Class move_from; + move_from.data1 = source(); + + sink(move_from.data1); // tainted + + IntWrapper::Class move_to(std::move(move_from)); + + sink(move_to.data1); // tainted +} + +void test_copy_assignment_method() +{ + IntWrapper::Class x; + IntWrapper::Class y; + x.data1 = source(); + + sink(x.data1); // tainted + sink(y.data1); // clean + + y.copy_assign(x); + + sink(y.data1); // tainted + sink(x.data1); // tainted +} + +void test_move_assignment_method() +{ + IntWrapper::Class x; + IntWrapper::Class y; + x.data1 = source(); + + sink(x.data1); // tainted + sink(y.data1); // clean + + y.move_assign(std::move(x)); + + sink(y.data1); // tainted + sink(x.data1); // tainted +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp new file mode 100644 index 000000000000..e5203f344f2e --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp @@ -0,0 +1,146 @@ +#include "swap.h" +/* + * Note: This file exists in two versions (swap1.cpp and swap2.cpp). + * The only difference is that `IntWrapper` in swap1.cpp contains a single data member, and swap2.cpp + * contains two data members. + */ + +int source(); +void sink(...); + +namespace std +{ + template + T &&move(T &t) noexcept { return static_cast(t); } // simplified signature (and implementation) +} // namespace std + +namespace IntWrapper +{ + struct Class + { + int data1; int data2; + + Class() = default; + Class(Class &&that) { swap(that); } + Class(const Class &that) : data1(that.data1), data2(that.data2) {} + + Class &operator=(const Class &that) + { + auto tmp = that; + swap(tmp); + return *this; + } + + Class &operator=(Class &&that) + { + swap(that); + return *this; + } + + Class ©_assign(const Class &that) // copy assignment without the usual signature + { + auto tmp = that; + swap(tmp); + return *this; + } + + Class &move_assign(Class &&that) // move assignment without the usual signature + { + swap(that); + return *this; + } + + void swap(Class &that) noexcept + { + using std::swap; + swap(data1, that.data1); swap(data2, that.data2); + } + }; + + // For ADL + void swap(Class &x, Class &y) + { + x.swap(y); + } +} // namespace IntWrapper + +void test_copy_assignment_operator() +{ + IntWrapper::Class x; + IntWrapper::Class y; + x.data1 = source(); + + sink(x.data1); // tainted + sink(y.data1); // clean + + y = x; + + sink(y.data1); // tainted + sink(x.data1); // tainted + + IntWrapper::Class z1, z2; + z1.data1 = source(); + sink(z1.data1); // tainted + + swap(z1, z2); + + sink(z2.data1); // tainted [FALSE NEGATIVE in IR] + sink(z1.data1); // clean [FALSE POSITIVE] +} + +void test_move_assignment_operator() +{ + IntWrapper::Class x; + IntWrapper::Class y; + x.data1 = source(); + + sink(x.data1); // tainted + sink(y.data1); // clean + + y = std::move(x); + + sink(y.data1); // tainted + sink(x.data1); // tainted +} + +void test_move_constructor() +{ + IntWrapper::Class move_from; + move_from.data1 = source(); + + sink(move_from.data1); // tainted + + IntWrapper::Class move_to(std::move(move_from)); + + sink(move_to.data1); // tainted +} + +void test_copy_assignment_method() +{ + IntWrapper::Class x; + IntWrapper::Class y; + x.data1 = source(); + + sink(x.data1); // tainted + sink(y.data1); // clean + + y.copy_assign(x); + + sink(y.data1); // tainted + sink(x.data1); // tainted +} + +void test_move_assignment_method() +{ + IntWrapper::Class x; + IntWrapper::Class y; + x.data1 = source(); + + sink(x.data1); // tainted + sink(y.data1); // clean + + y.move_assign(std::move(x)); + + sink(y.data1); // tainted + sink(x.data1); // tainted +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp index 3d09f075a40e..48e78c62147f 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -86,12 +86,12 @@ void class_field_test() { mc1.myMethod(); sink(mc1.a); - sink(mc1.b); // tainted [NOT DETECTED with IR] - sink(mc1.c); // tainted [NOT DETECTED with IR] - sink(mc1.d); // tainted [NOT DETECTED with IR] + sink(mc1.b); // tainted + sink(mc1.c); // tainted + sink(mc1.d); // tainted sink(mc2.a); - sink(mc2.b); // tainted [NOT DETECTED with IR] - sink(mc2.c); // tainted [NOT DETECTED with IR] + sink(mc2.b); // tainted + sink(mc2.c); // tainted sink(mc2.d); } @@ -107,9 +107,9 @@ void array_test(int i) { arr3[5] = 0; sink(arr1[5]); // tainted - sink(arr1[i]); // tainted [NOT DETECTED] - sink(arr2[5]); // tainted [NOT DETECTED] - sink(arr2[i]); // tainted [NOT DETECTED] + sink(arr1[i]); // tainted + sink(arr2[5]); // tainted + sink(arr2[i]); // tainted sink(arr3[5]); sink(arr3[i]); } @@ -127,7 +127,7 @@ void pointer_test() { *p2 = source(); sink(*p1); // tainted - sink(*p2); // tainted [NOT DETECTED] + sink(*p2); // tainted sink(*p3); p3 = &t1; @@ -197,9 +197,9 @@ void test_memcpy(int *source) { // --- std::swap --- -namespace std { - template constexpr void swap(T& a, T& b); -} +#include "swap.h" + + void test_swap() { int x, y; @@ -350,8 +350,8 @@ void test_outparams() sink(t); // tainted sink(a); // tainted sink(b); // tainted - sink(c); // tainted [NOT DETECTED] - sink(d); // tainted [NOT DETECTED] + sink(c); // tainted + sink(d); // tainted sink(e); } @@ -483,4 +483,4 @@ void test_getdelim(FILE* source1) { getdelim(&line, &n, '\n', source1); sink(line); -} \ No newline at end of file +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 312a46e979e9..deecad605dde 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -1,3 +1,25 @@ +| arrayassignment.cpp:17:7:17:10 | * ... | arrayassignment.cpp:14:9:14:14 | call to source | +| arrayassignment.cpp:33:7:33:9 | r_x | arrayassignment.cpp:29:8:29:13 | call to source | +| arrayassignment.cpp:57:10:57:12 | call to get | arrayassignment.cpp:54:9:54:14 | call to source | +| arrayassignment.cpp:67:10:67:12 | call to get | arrayassignment.cpp:64:13:64:18 | call to source | +| arrayassignment.cpp:101:7:101:18 | access to array | arrayassignment.cpp:99:17:99:22 | call to source | +| arrayassignment.cpp:135:7:135:10 | ref1 | arrayassignment.cpp:134:9:134:14 | call to source | +| arrayassignment.cpp:140:7:140:11 | * ... | arrayassignment.cpp:139:10:139:15 | call to source | +| arrayassignment.cpp:145:7:145:13 | access to array | arrayassignment.cpp:144:12:144:17 | call to source | +| copyableclass.cpp:40:8:40:9 | s1 | copyableclass.cpp:34:22:34:27 | call to source | +| copyableclass.cpp:41:8:41:9 | s2 | copyableclass.cpp:35:24:35:29 | call to source | +| copyableclass.cpp:42:8:42:9 | s3 | copyableclass.cpp:34:22:34:27 | call to source | +| copyableclass.cpp:43:8:43:9 | s4 | copyableclass.cpp:38:8:38:13 | call to source | +| copyableclass.cpp:65:8:65:9 | s1 | copyableclass.cpp:60:40:60:45 | call to source | +| copyableclass.cpp:66:8:66:9 | s2 | copyableclass.cpp:63:24:63:29 | call to source | +| copyableclass.cpp:67:11:67:11 | call to operator= | copyableclass.cpp:67:13:67:18 | call to source | +| copyableclass_declonly.cpp:40:8:40:9 | s1 | copyableclass_declonly.cpp:34:30:34:35 | call to source | +| copyableclass_declonly.cpp:41:8:41:9 | s2 | copyableclass_declonly.cpp:35:32:35:37 | call to source | +| copyableclass_declonly.cpp:42:8:42:9 | s3 | copyableclass_declonly.cpp:34:30:34:35 | call to source | +| copyableclass_declonly.cpp:43:8:43:9 | s4 | copyableclass_declonly.cpp:38:8:38:13 | call to source | +| copyableclass_declonly.cpp:65:8:65:9 | s1 | copyableclass_declonly.cpp:60:56:60:61 | call to source | +| copyableclass_declonly.cpp:66:8:66:9 | s2 | copyableclass_declonly.cpp:63:32:63:37 | call to source | +| copyableclass_declonly.cpp:67:11:67:11 | call to operator= | copyableclass_declonly.cpp:67:13:67:18 | call to source | | format.cpp:57:8:57:13 | buffer | format.cpp:56:36:56:49 | call to source | | format.cpp:62:8:62:13 | buffer | format.cpp:61:30:61:43 | call to source | | format.cpp:67:8:67:13 | buffer | format.cpp:66:52:66:65 | call to source | @@ -8,14 +30,489 @@ | format.cpp:100:8:100:13 | buffer | format.cpp:99:30:99:43 | call to source | | format.cpp:105:8:105:13 | buffer | format.cpp:104:31:104:45 | call to source | | format.cpp:110:8:110:14 | wbuffer | format.cpp:109:38:109:52 | call to source | +| format.cpp:115:8:115:13 | buffer | format.cpp:114:37:114:50 | call to source | | format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source | | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | -| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | -| stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source | -| stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source | -| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | -| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | -| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source | +| map.cpp:29:9:29:13 | first | map.cpp:28:12:28:17 | call to source | +| map.cpp:35:9:35:14 | second | map.cpp:33:13:33:18 | call to source | +| map.cpp:44:9:44:13 | first | map.cpp:43:30:43:35 | call to source | +| map.cpp:50:9:50:14 | second | map.cpp:48:37:48:42 | call to source | +| map.cpp:51:7:51:7 | f | map.cpp:48:37:48:42 | call to source | +| map.cpp:55:9:55:14 | second | map.cpp:48:37:48:42 | call to source | +| map.cpp:56:7:56:7 | g | map.cpp:48:37:48:42 | call to source | +| map.cpp:61:9:61:14 | second | map.cpp:48:37:48:42 | call to source | +| map.cpp:62:7:62:7 | h | map.cpp:48:37:48:42 | call to source | +| map.cpp:72:7:72:7 | i | map.cpp:65:37:65:42 | call to source | +| map.cpp:74:9:74:14 | second | map.cpp:65:37:65:42 | call to source | +| map.cpp:75:7:75:7 | j | map.cpp:65:37:65:42 | call to source | +| map.cpp:77:9:77:14 | second | map.cpp:66:37:66:42 | call to source | +| map.cpp:78:7:78:7 | k | map.cpp:66:37:66:42 | call to source | +| map.cpp:81:7:81:7 | l | map.cpp:66:37:66:42 | call to source | +| map.cpp:89:7:89:32 | call to pair | map.cpp:89:24:89:29 | call to source | +| map.cpp:110:10:110:15 | call to insert | map.cpp:110:62:110:67 | call to source | +| map.cpp:112:10:112:25 | call to insert_or_assign | map.cpp:112:46:112:51 | call to source | +| map.cpp:114:7:114:8 | call to map | map.cpp:108:39:108:44 | call to source | +| map.cpp:116:7:116:8 | call to map | map.cpp:110:62:110:67 | call to source | +| map.cpp:117:7:117:8 | call to map | map.cpp:111:34:111:39 | call to source | +| map.cpp:118:7:118:8 | call to map | map.cpp:112:46:112:51 | call to source | +| map.cpp:120:10:120:13 | call to find | map.cpp:108:39:108:44 | call to source | +| map.cpp:122:10:122:13 | call to find | map.cpp:110:62:110:67 | call to source | +| map.cpp:123:10:123:13 | call to find | map.cpp:111:34:111:39 | call to source | +| map.cpp:124:10:124:13 | call to find | map.cpp:112:46:112:51 | call to source | +| map.cpp:126:10:126:13 | call to find | map.cpp:108:39:108:44 | call to source | +| map.cpp:128:10:128:13 | call to find | map.cpp:110:62:110:67 | call to source | +| map.cpp:129:10:129:13 | call to find | map.cpp:111:34:111:39 | call to source | +| map.cpp:130:10:130:13 | call to find | map.cpp:112:46:112:51 | call to source | +| map.cpp:137:7:137:8 | call to map | map.cpp:108:39:108:44 | call to source | +| map.cpp:138:7:138:8 | call to map | map.cpp:108:39:108:44 | call to source | +| map.cpp:139:7:139:8 | call to map | map.cpp:108:39:108:44 | call to source | +| map.cpp:140:10:140:13 | call to find | map.cpp:108:39:108:44 | call to source | +| map.cpp:141:10:141:13 | call to find | map.cpp:108:39:108:44 | call to source | +| map.cpp:142:10:142:13 | call to find | map.cpp:108:39:108:44 | call to source | +| map.cpp:154:8:154:10 | call to pair | map.cpp:108:39:108:44 | call to source | +| map.cpp:168:7:168:27 | ... = ... | map.cpp:168:20:168:25 | call to source | +| map.cpp:170:7:170:30 | ... = ... | map.cpp:170:23:170:28 | call to source | +| map.cpp:172:10:172:10 | call to operator[] | map.cpp:168:20:168:25 | call to source | +| map.cpp:174:10:174:10 | call to operator[] | map.cpp:170:23:170:28 | call to source | +| map.cpp:182:10:182:20 | call to lower_bound | map.cpp:108:39:108:44 | call to source | +| map.cpp:183:10:183:20 | call to upper_bound | map.cpp:108:39:108:44 | call to source | +| map.cpp:186:10:186:20 | call to upper_bound | map.cpp:108:39:108:44 | call to source | +| map.cpp:193:7:193:9 | call to map | map.cpp:191:49:191:54 | call to source | +| map.cpp:196:7:196:9 | call to map | map.cpp:192:49:192:54 | call to source | +| map.cpp:199:7:199:9 | call to map | map.cpp:191:49:191:54 | call to source | +| map.cpp:200:7:200:9 | call to map | map.cpp:191:49:191:54 | call to source | +| map.cpp:201:7:201:9 | call to map | map.cpp:192:49:192:54 | call to source | +| map.cpp:202:7:202:9 | call to map | map.cpp:192:49:192:54 | call to source | +| map.cpp:210:7:210:9 | call to map | map.cpp:206:49:206:54 | call to source | +| map.cpp:213:7:213:9 | call to map | map.cpp:209:49:209:54 | call to source | +| map.cpp:216:7:216:9 | call to map | map.cpp:206:49:206:54 | call to source | +| map.cpp:218:7:218:9 | call to map | map.cpp:209:49:209:54 | call to source | +| map.cpp:219:7:219:9 | call to map | map.cpp:209:49:209:54 | call to source | +| map.cpp:225:7:225:9 | call to map | map.cpp:223:49:223:54 | call to source | +| map.cpp:225:7:225:9 | call to map | map.cpp:224:49:224:54 | call to source | +| map.cpp:226:11:226:15 | call to erase | map.cpp:223:49:223:54 | call to source | +| map.cpp:226:11:226:15 | call to erase | map.cpp:224:49:224:54 | call to source | +| map.cpp:227:7:227:9 | call to map | map.cpp:223:49:223:54 | call to source | +| map.cpp:227:7:227:9 | call to map | map.cpp:224:49:224:54 | call to source | +| map.cpp:229:7:229:9 | call to map | map.cpp:223:49:223:54 | call to source | +| map.cpp:229:7:229:9 | call to map | map.cpp:224:49:224:54 | call to source | +| map.cpp:236:7:236:9 | call to map | map.cpp:235:26:235:31 | call to source | +| map.cpp:239:11:239:22 | call to emplace_hint | map.cpp:239:44:239:49 | call to source | +| map.cpp:240:7:240:9 | call to map | map.cpp:239:44:239:49 | call to source | +| map.cpp:247:7:247:9 | call to map | map.cpp:246:30:246:35 | call to source | +| map.cpp:250:11:250:21 | call to try_emplace | map.cpp:250:43:250:48 | call to source | +| map.cpp:251:7:251:9 | call to map | map.cpp:250:43:250:48 | call to source | +| map.cpp:262:10:262:15 | call to insert | map.cpp:262:62:262:67 | call to source | +| map.cpp:264:10:264:25 | call to insert_or_assign | map.cpp:264:46:264:51 | call to source | +| map.cpp:266:7:266:8 | call to unordered_map | map.cpp:260:39:260:44 | call to source | +| map.cpp:268:7:268:8 | call to unordered_map | map.cpp:262:62:262:67 | call to source | +| map.cpp:269:7:269:8 | call to unordered_map | map.cpp:263:34:263:39 | call to source | +| map.cpp:270:7:270:8 | call to unordered_map | map.cpp:264:46:264:51 | call to source | +| map.cpp:272:10:272:13 | call to find | map.cpp:260:39:260:44 | call to source | +| map.cpp:274:10:274:13 | call to find | map.cpp:262:62:262:67 | call to source | +| map.cpp:275:10:275:13 | call to find | map.cpp:263:34:263:39 | call to source | +| map.cpp:276:10:276:13 | call to find | map.cpp:264:46:264:51 | call to source | +| map.cpp:278:10:278:13 | call to find | map.cpp:260:39:260:44 | call to source | +| map.cpp:280:10:280:13 | call to find | map.cpp:262:62:262:67 | call to source | +| map.cpp:281:10:281:13 | call to find | map.cpp:263:34:263:39 | call to source | +| map.cpp:282:10:282:13 | call to find | map.cpp:264:46:264:51 | call to source | +| map.cpp:289:7:289:8 | call to unordered_map | map.cpp:260:39:260:44 | call to source | +| map.cpp:290:7:290:8 | call to unordered_map | map.cpp:260:39:260:44 | call to source | +| map.cpp:291:7:291:8 | call to unordered_map | map.cpp:260:39:260:44 | call to source | +| map.cpp:292:10:292:13 | call to find | map.cpp:260:39:260:44 | call to source | +| map.cpp:293:10:293:13 | call to find | map.cpp:260:39:260:44 | call to source | +| map.cpp:294:10:294:13 | call to find | map.cpp:260:39:260:44 | call to source | +| map.cpp:306:8:306:10 | call to pair | map.cpp:260:39:260:44 | call to source | +| map.cpp:320:7:320:27 | ... = ... | map.cpp:320:20:320:25 | call to source | +| map.cpp:322:7:322:30 | ... = ... | map.cpp:322:23:322:28 | call to source | +| map.cpp:324:10:324:10 | call to operator[] | map.cpp:320:20:320:25 | call to source | +| map.cpp:326:10:326:10 | call to operator[] | map.cpp:322:23:322:28 | call to source | +| map.cpp:342:7:342:9 | call to unordered_map | map.cpp:340:49:340:54 | call to source | +| map.cpp:345:7:345:9 | call to unordered_map | map.cpp:341:49:341:54 | call to source | +| map.cpp:348:7:348:9 | call to unordered_map | map.cpp:340:49:340:54 | call to source | +| map.cpp:349:7:349:9 | call to unordered_map | map.cpp:340:49:340:54 | call to source | +| map.cpp:350:7:350:9 | call to unordered_map | map.cpp:341:49:341:54 | call to source | +| map.cpp:351:7:351:9 | call to unordered_map | map.cpp:341:49:341:54 | call to source | +| map.cpp:359:7:359:9 | call to unordered_map | map.cpp:355:49:355:54 | call to source | +| map.cpp:362:7:362:9 | call to unordered_map | map.cpp:358:49:358:54 | call to source | +| map.cpp:365:7:365:9 | call to unordered_map | map.cpp:355:49:355:54 | call to source | +| map.cpp:367:7:367:9 | call to unordered_map | map.cpp:358:49:358:54 | call to source | +| map.cpp:368:7:368:9 | call to unordered_map | map.cpp:358:49:358:54 | call to source | +| map.cpp:374:7:374:9 | call to unordered_map | map.cpp:372:49:372:54 | call to source | +| map.cpp:374:7:374:9 | call to unordered_map | map.cpp:373:49:373:54 | call to source | +| map.cpp:375:11:375:15 | call to erase | map.cpp:372:49:372:54 | call to source | +| map.cpp:375:11:375:15 | call to erase | map.cpp:373:49:373:54 | call to source | +| map.cpp:376:7:376:9 | call to unordered_map | map.cpp:372:49:372:54 | call to source | +| map.cpp:376:7:376:9 | call to unordered_map | map.cpp:373:49:373:54 | call to source | +| map.cpp:378:7:378:9 | call to unordered_map | map.cpp:372:49:372:54 | call to source | +| map.cpp:378:7:378:9 | call to unordered_map | map.cpp:373:49:373:54 | call to source | +| map.cpp:385:7:385:9 | call to unordered_map | map.cpp:384:26:384:31 | call to source | +| map.cpp:388:11:388:22 | call to emplace_hint | map.cpp:388:44:388:49 | call to source | +| map.cpp:389:7:389:9 | call to unordered_map | map.cpp:388:44:388:49 | call to source | +| map.cpp:398:7:398:9 | call to unordered_map | map.cpp:396:30:396:35 | call to source | +| map.cpp:398:7:398:9 | call to unordered_map | map.cpp:397:30:397:35 | call to source | +| map.cpp:401:11:401:21 | call to try_emplace | map.cpp:401:43:401:48 | call to source | +| map.cpp:402:7:402:9 | call to unordered_map | map.cpp:401:43:401:48 | call to source | +| map.cpp:416:7:416:41 | call to pair | map.cpp:416:30:416:35 | call to source | +| map.cpp:417:7:417:9 | call to unordered_map | map.cpp:416:30:416:35 | call to source | +| map.cpp:418:7:418:16 | call to pair | map.cpp:416:30:416:35 | call to source | +| map.cpp:419:7:419:41 | call to pair | map.cpp:419:33:419:38 | call to source | +| map.cpp:420:7:420:9 | call to unordered_map | map.cpp:419:33:419:38 | call to source | +| map.cpp:421:7:421:16 | call to pair | map.cpp:419:33:419:38 | call to source | +| map.cpp:432:7:432:9 | call to unordered_map | map.cpp:431:52:431:57 | call to source | +| map.cpp:433:11:433:22 | call to emplace_hint | map.cpp:431:52:431:57 | call to source | +| movableclass.cpp:44:8:44:9 | s1 | movableclass.cpp:39:21:39:26 | call to source | +| movableclass.cpp:45:8:45:9 | s2 | movableclass.cpp:40:23:40:28 | call to source | +| movableclass.cpp:46:8:46:9 | s3 | movableclass.cpp:42:8:42:13 | call to source | +| movableclass.cpp:54:8:54:9 | s1 | movableclass.cpp:50:38:50:43 | call to source | +| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source | +| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source | +| movableclass.cpp:65:11:65:11 | call to operator= | movableclass.cpp:65:13:65:18 | call to source | +| set.cpp:22:10:22:15 | call to insert | set.cpp:22:29:22:34 | call to source | +| set.cpp:26:7:26:8 | call to set | set.cpp:20:17:20:22 | call to source | +| set.cpp:28:7:28:8 | call to set | set.cpp:22:29:22:34 | call to source | +| set.cpp:30:7:30:8 | call to set | set.cpp:20:17:20:22 | call to source | +| set.cpp:32:10:32:13 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:34:10:34:13 | call to find | set.cpp:22:29:22:34 | call to source | +| set.cpp:36:10:36:13 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:44:7:44:8 | call to set | set.cpp:20:17:20:22 | call to source | +| set.cpp:45:7:45:8 | call to set | set.cpp:20:17:20:22 | call to source | +| set.cpp:46:7:46:8 | call to set | set.cpp:20:17:20:22 | call to source | +| set.cpp:47:7:47:9 | call to set | set.cpp:20:17:20:22 | call to source | +| set.cpp:48:10:48:13 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:49:10:49:13 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:50:10:50:13 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:51:11:51:14 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:61:8:61:8 | call to operator* | set.cpp:20:17:20:22 | call to source | +| set.cpp:69:11:69:21 | call to lower_bound | set.cpp:67:13:67:18 | call to source | +| set.cpp:70:11:70:21 | call to upper_bound | set.cpp:67:13:67:18 | call to source | +| set.cpp:78:7:78:9 | call to set | set.cpp:76:13:76:18 | call to source | +| set.cpp:81:7:81:9 | call to set | set.cpp:77:13:77:18 | call to source | +| set.cpp:84:7:84:9 | call to set | set.cpp:76:13:76:18 | call to source | +| set.cpp:85:7:85:9 | call to set | set.cpp:76:13:76:18 | call to source | +| set.cpp:86:7:86:9 | call to set | set.cpp:77:13:77:18 | call to source | +| set.cpp:87:7:87:9 | call to set | set.cpp:77:13:77:18 | call to source | +| set.cpp:95:7:95:9 | call to set | set.cpp:91:13:91:18 | call to source | +| set.cpp:98:7:98:9 | call to set | set.cpp:94:13:94:18 | call to source | +| set.cpp:101:7:101:9 | call to set | set.cpp:91:13:91:18 | call to source | +| set.cpp:103:7:103:9 | call to set | set.cpp:94:13:94:18 | call to source | +| set.cpp:104:7:104:9 | call to set | set.cpp:94:13:94:18 | call to source | +| set.cpp:110:7:110:9 | call to set | set.cpp:108:13:108:18 | call to source | +| set.cpp:110:7:110:9 | call to set | set.cpp:109:13:109:18 | call to source | +| set.cpp:111:11:111:15 | call to erase | set.cpp:108:13:108:18 | call to source | +| set.cpp:111:11:111:15 | call to erase | set.cpp:109:13:109:18 | call to source | +| set.cpp:112:7:112:9 | call to set | set.cpp:108:13:108:18 | call to source | +| set.cpp:112:7:112:9 | call to set | set.cpp:109:13:109:18 | call to source | +| set.cpp:114:7:114:9 | call to set | set.cpp:108:13:108:18 | call to source | +| set.cpp:114:7:114:9 | call to set | set.cpp:109:13:109:18 | call to source | +| set.cpp:121:7:121:9 | call to set | set.cpp:120:19:120:24 | call to source | +| set.cpp:124:11:124:22 | call to emplace_hint | set.cpp:124:37:124:42 | call to source | +| set.cpp:125:7:125:9 | call to set | set.cpp:124:37:124:42 | call to source | +| set.cpp:136:10:136:15 | call to insert | set.cpp:136:29:136:34 | call to source | +| set.cpp:140:7:140:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source | +| set.cpp:142:7:142:8 | call to unordered_set | set.cpp:136:29:136:34 | call to source | +| set.cpp:144:7:144:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source | +| set.cpp:146:10:146:13 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:148:10:148:13 | call to find | set.cpp:136:29:136:34 | call to source | +| set.cpp:150:10:150:13 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:158:7:158:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source | +| set.cpp:159:7:159:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source | +| set.cpp:160:7:160:8 | call to unordered_set | set.cpp:134:17:134:22 | call to source | +| set.cpp:161:7:161:9 | call to unordered_set | set.cpp:134:17:134:22 | call to source | +| set.cpp:162:10:162:13 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:163:10:163:13 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:164:10:164:13 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:165:11:165:14 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:175:8:175:8 | call to operator* | set.cpp:134:17:134:22 | call to source | +| set.cpp:190:7:190:9 | call to unordered_set | set.cpp:188:13:188:18 | call to source | +| set.cpp:193:7:193:9 | call to unordered_set | set.cpp:189:13:189:18 | call to source | +| set.cpp:196:7:196:9 | call to unordered_set | set.cpp:188:13:188:18 | call to source | +| set.cpp:197:7:197:9 | call to unordered_set | set.cpp:188:13:188:18 | call to source | +| set.cpp:198:7:198:9 | call to unordered_set | set.cpp:189:13:189:18 | call to source | +| set.cpp:199:7:199:9 | call to unordered_set | set.cpp:189:13:189:18 | call to source | +| set.cpp:207:7:207:9 | call to unordered_set | set.cpp:203:13:203:18 | call to source | +| set.cpp:210:7:210:9 | call to unordered_set | set.cpp:206:13:206:18 | call to source | +| set.cpp:213:7:213:9 | call to unordered_set | set.cpp:203:13:203:18 | call to source | +| set.cpp:215:7:215:9 | call to unordered_set | set.cpp:206:13:206:18 | call to source | +| set.cpp:216:7:216:9 | call to unordered_set | set.cpp:206:13:206:18 | call to source | +| set.cpp:222:7:222:9 | call to unordered_set | set.cpp:220:13:220:18 | call to source | +| set.cpp:222:7:222:9 | call to unordered_set | set.cpp:221:13:221:18 | call to source | +| set.cpp:223:11:223:15 | call to erase | set.cpp:220:13:220:18 | call to source | +| set.cpp:223:11:223:15 | call to erase | set.cpp:221:13:221:18 | call to source | +| set.cpp:224:7:224:9 | call to unordered_set | set.cpp:220:13:220:18 | call to source | +| set.cpp:224:7:224:9 | call to unordered_set | set.cpp:221:13:221:18 | call to source | +| set.cpp:226:7:226:9 | call to unordered_set | set.cpp:220:13:220:18 | call to source | +| set.cpp:226:7:226:9 | call to unordered_set | set.cpp:221:13:221:18 | call to source | +| set.cpp:233:7:233:9 | call to unordered_set | set.cpp:232:19:232:24 | call to source | +| set.cpp:236:11:236:22 | call to emplace_hint | set.cpp:236:37:236:42 | call to source | +| set.cpp:237:7:237:9 | call to unordered_set | set.cpp:236:37:236:42 | call to source | +| smart_pointer.cpp:12:10:12:10 | call to operator* | smart_pointer.cpp:11:52:11:57 | call to source | +| smart_pointer.cpp:13:10:13:10 | p | smart_pointer.cpp:11:52:11:57 | call to source | +| smart_pointer.cpp:24:10:24:10 | call to operator* | smart_pointer.cpp:23:52:23:57 | call to source | +| smart_pointer.cpp:25:10:25:10 | p | smart_pointer.cpp:23:52:23:57 | call to source | +| smart_pointer.cpp:52:12:52:14 | call to get | smart_pointer.cpp:51:52:51:57 | call to source | +| smart_pointer.cpp:57:12:57:14 | call to get | smart_pointer.cpp:56:52:56:57 | call to source | +| standalone_iterators.cpp:40:10:40:10 | call to operator* | standalone_iterators.cpp:39:45:39:51 | source1 | +| standalone_iterators.cpp:41:10:41:10 | call to operator* | standalone_iterators.cpp:39:45:39:51 | source1 | +| standalone_iterators.cpp:42:10:42:10 | call to operator* | standalone_iterators.cpp:39:45:39:51 | source1 | +| standalone_iterators.cpp:46:10:46:10 | call to operator* | standalone_iterators.cpp:45:39:45:45 | source1 | +| standalone_iterators.cpp:47:10:47:10 | call to operator* | standalone_iterators.cpp:45:39:45:45 | source1 | +| standalone_iterators.cpp:48:10:48:10 | call to operator* | standalone_iterators.cpp:45:39:45:45 | source1 | +| string.cpp:29:7:29:7 | a | string.cpp:25:12:25:17 | call to source | +| string.cpp:31:7:31:7 | c | string.cpp:27:16:27:21 | call to source | +| string.cpp:33:9:33:13 | call to c_str | string.cpp:27:16:27:21 | call to source | +| string.cpp:39:13:39:17 | call to c_str | string.cpp:14:10:14:15 | call to source | +| string.cpp:43:13:43:17 | call to c_str | string.cpp:14:10:14:15 | call to source | +| string.cpp:46:13:46:17 | call to c_str | string.cpp:14:10:14:15 | call to source | +| string.cpp:56:7:56:8 | cs | string.cpp:51:19:51:24 | call to source | +| string.cpp:57:7:57:8 | ss | string.cpp:51:19:51:24 | call to source | +| string.cpp:70:7:70:8 | cs | string.cpp:62:19:62:24 | call to source | +| string.cpp:71:7:71:8 | ss | string.cpp:62:19:62:24 | call to source | +| string.cpp:93:8:93:9 | s1 | string.cpp:88:18:88:23 | call to source | +| string.cpp:94:8:94:9 | s2 | string.cpp:89:20:89:25 | call to source | +| string.cpp:95:8:95:9 | s3 | string.cpp:91:8:91:13 | call to source | +| string.cpp:114:8:114:9 | s1 | string.cpp:110:32:110:37 | call to source | +| string.cpp:115:8:115:9 | s2 | string.cpp:112:20:112:25 | call to source | +| string.cpp:122:8:122:8 | c | string.cpp:120:16:120:21 | call to source | +| string.cpp:126:8:126:8 | call to operator* | string.cpp:120:16:120:21 | call to source | +| string.cpp:130:8:130:8 | c | string.cpp:120:16:120:21 | call to source | +| string.cpp:135:8:135:8 | c | string.cpp:133:28:133:33 | call to source | +| string.cpp:145:11:145:11 | call to operator+ | string.cpp:142:18:142:23 | call to source | +| string.cpp:146:11:146:11 | call to operator+ | string.cpp:142:18:142:23 | call to source | +| string.cpp:147:11:147:11 | call to operator+ | string.cpp:142:18:142:23 | call to source | +| string.cpp:150:11:150:11 | call to operator+ | string.cpp:150:13:150:18 | call to source | +| string.cpp:159:8:159:9 | s5 | string.cpp:155:18:155:23 | call to source | +| string.cpp:162:11:162:11 | call to operator+= | string.cpp:155:18:155:23 | call to source | +| string.cpp:163:8:163:9 | s6 | string.cpp:155:18:155:23 | call to source | +| string.cpp:166:11:166:11 | call to operator+= | string.cpp:166:14:166:19 | call to source | +| string.cpp:167:11:167:11 | call to operator+= | string.cpp:166:14:166:19 | call to source | +| string.cpp:168:8:168:9 | s7 | string.cpp:166:14:166:19 | call to source | +| string.cpp:172:8:172:9 | s8 | string.cpp:155:18:155:23 | call to source | +| string.cpp:177:8:177:9 | s9 | string.cpp:175:13:175:18 | call to source | +| string.cpp:185:8:185:10 | s10 | string.cpp:182:12:182:26 | call to source | +| string.cpp:199:10:199:15 | call to assign | string.cpp:191:17:191:22 | call to source | +| string.cpp:200:7:200:8 | s4 | string.cpp:191:17:191:22 | call to source | +| string.cpp:202:10:202:15 | call to assign | string.cpp:192:11:192:25 | call to source | +| string.cpp:203:7:203:8 | s5 | string.cpp:192:11:192:25 | call to source | +| string.cpp:206:7:206:8 | s6 | string.cpp:194:17:194:22 | call to source | +| string.cpp:220:10:220:15 | call to insert | string.cpp:211:17:211:22 | call to source | +| string.cpp:221:7:221:8 | s4 | string.cpp:211:17:211:22 | call to source | +| string.cpp:224:10:224:15 | call to insert | string.cpp:211:17:211:22 | call to source | +| string.cpp:225:7:225:8 | s5 | string.cpp:211:17:211:22 | call to source | +| string.cpp:228:10:228:15 | call to insert | string.cpp:212:11:212:25 | call to source | +| string.cpp:229:7:229:8 | s6 | string.cpp:212:11:212:25 | call to source | +| string.cpp:243:10:243:16 | call to replace | string.cpp:234:17:234:22 | call to source | +| string.cpp:244:7:244:8 | s4 | string.cpp:234:17:234:22 | call to source | +| string.cpp:247:10:247:16 | call to replace | string.cpp:234:17:234:22 | call to source | +| string.cpp:248:7:248:8 | s5 | string.cpp:234:17:234:22 | call to source | +| string.cpp:251:10:251:16 | call to replace | string.cpp:235:11:235:25 | call to source | +| string.cpp:252:7:252:8 | s6 | string.cpp:235:11:235:25 | call to source | +| string.cpp:265:7:265:8 | b2 | string.cpp:259:17:259:22 | call to source | +| string.cpp:275:7:275:8 | s2 | string.cpp:270:17:270:22 | call to source | +| string.cpp:277:7:277:8 | s4 | string.cpp:272:17:272:22 | call to source | +| string.cpp:282:7:282:8 | s1 | string.cpp:270:17:270:22 | call to source | +| string.cpp:283:7:283:8 | s2 | string.cpp:270:17:270:22 | call to source | +| string.cpp:284:7:284:8 | s3 | string.cpp:272:17:272:22 | call to source | +| string.cpp:285:7:285:8 | s4 | string.cpp:272:17:272:22 | call to source | +| string.cpp:293:7:293:8 | s1 | string.cpp:289:17:289:22 | call to source | +| string.cpp:294:7:294:8 | s2 | string.cpp:290:17:290:22 | call to source | +| string.cpp:295:7:295:8 | s3 | string.cpp:291:17:291:22 | call to source | +| string.cpp:301:7:301:8 | s1 | string.cpp:289:17:289:22 | call to source | +| string.cpp:303:7:303:8 | s3 | string.cpp:291:17:291:22 | call to source | +| string.cpp:312:9:312:12 | call to data | string.cpp:309:16:309:21 | call to source | +| string.cpp:323:9:323:14 | call to substr | string.cpp:320:16:320:21 | call to source | +| string.cpp:340:7:340:7 | a | string.cpp:336:9:336:23 | call to source | +| string.cpp:341:7:341:7 | b | string.cpp:337:12:337:26 | call to source | +| string.cpp:342:7:342:7 | c | string.cpp:336:9:336:23 | call to source | +| string.cpp:350:7:350:9 | str | string.cpp:349:18:349:32 | call to source | +| string.cpp:351:11:351:14 | call to data | string.cpp:349:18:349:32 | call to source | +| string.cpp:363:11:363:16 | call to append | string.cpp:358:18:358:23 | call to source | +| string.cpp:364:8:364:9 | s1 | string.cpp:358:18:358:23 | call to source | +| string.cpp:382:8:382:8 | call to operator* | string.cpp:374:18:374:23 | call to source | +| string.cpp:383:13:383:13 | call to operator[] | string.cpp:374:18:374:23 | call to source | +| string.cpp:396:8:396:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:397:8:397:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:399:8:399:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:401:8:401:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:404:8:404:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:407:8:407:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:409:8:409:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:411:8:411:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:415:8:415:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:418:8:418:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:419:8:419:10 | call to iterator | string.cpp:389:18:389:23 | call to source | +| string.cpp:421:8:421:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:422:8:422:10 | call to iterator | string.cpp:389:18:389:23 | call to source | +| string.cpp:436:10:436:15 | call to insert | string.cpp:431:14:431:19 | call to source | +| string.cpp:437:7:437:8 | s2 | string.cpp:431:14:431:19 | call to source | +| string.cpp:449:10:449:15 | call to insert | string.cpp:449:32:449:46 | call to source | +| string.cpp:450:8:450:8 | b | string.cpp:449:32:449:46 | call to source | +| string.cpp:462:10:462:15 | call to insert | string.cpp:457:18:457:23 | call to source | +| string.cpp:463:8:463:8 | d | string.cpp:457:18:457:23 | call to source | +| string.cpp:465:11:465:16 | call to insert | string.cpp:457:18:457:23 | call to source | +| string.cpp:466:8:466:9 | s2 | string.cpp:457:18:457:23 | call to source | +| string.cpp:478:10:478:15 | call to append | string.cpp:473:18:473:23 | call to source | +| string.cpp:479:8:479:8 | f | string.cpp:473:18:473:23 | call to source | +| string.cpp:481:11:481:16 | call to append | string.cpp:473:18:473:23 | call to source | +| string.cpp:482:8:482:9 | s4 | string.cpp:473:18:473:23 | call to source | +| string.cpp:494:10:494:15 | call to assign | string.cpp:489:18:489:23 | call to source | +| string.cpp:495:8:495:8 | h | string.cpp:489:18:489:23 | call to source | +| string.cpp:498:8:498:9 | s6 | string.cpp:489:18:489:23 | call to source | +| string.cpp:511:7:511:8 | s2 | string.cpp:504:14:504:19 | call to source | +| string.cpp:513:7:513:8 | s4 | string.cpp:504:14:504:19 | call to source | +| string.cpp:522:9:522:13 | call to front | string.cpp:521:14:521:28 | call to source | +| string.cpp:523:9:523:12 | call to back | string.cpp:521:14:521:28 | call to source | +| string.cpp:536:11:536:11 | call to operator+= | string.cpp:536:20:536:25 | call to source | +| string.cpp:537:21:537:21 | call to operator+= | string.cpp:537:24:537:29 | call to source | +| string.cpp:538:25:538:25 | call to operator+= | string.cpp:538:15:538:20 | call to source | +| string.cpp:541:8:541:8 | c | string.cpp:536:20:536:25 | call to source | +| string.cpp:542:8:542:8 | d | string.cpp:536:20:536:25 | call to source | +| string.cpp:543:8:543:8 | e | string.cpp:537:24:537:29 | call to source | +| string.cpp:544:8:544:8 | f | string.cpp:538:15:538:20 | call to source | +| string.cpp:556:11:556:16 | call to assign | string.cpp:556:27:556:32 | call to source | +| string.cpp:557:24:557:29 | call to assign | string.cpp:557:31:557:36 | call to source | +| string.cpp:561:8:561:8 | c | string.cpp:556:27:556:32 | call to source | +| string.cpp:562:8:562:8 | d | string.cpp:556:27:556:32 | call to source | +| string.cpp:563:8:563:8 | e | string.cpp:557:31:557:36 | call to source | +| string.cpp:564:8:564:8 | f | string.cpp:558:18:558:23 | call to source | +| stringstream.cpp:32:11:32:11 | call to operator<< | stringstream.cpp:32:14:32:19 | call to source | +| stringstream.cpp:33:20:33:20 | call to operator<< | stringstream.cpp:33:23:33:28 | call to source | +| stringstream.cpp:34:23:34:23 | call to operator<< | stringstream.cpp:34:14:34:19 | call to source | +| stringstream.cpp:35:11:35:11 | call to operator<< | stringstream.cpp:29:16:29:21 | call to source | +| stringstream.cpp:38:7:38:9 | ss2 | stringstream.cpp:32:14:32:19 | call to source | +| stringstream.cpp:39:7:39:9 | ss3 | stringstream.cpp:33:23:33:28 | call to source | +| stringstream.cpp:40:7:40:9 | ss4 | stringstream.cpp:34:14:34:19 | call to source | +| stringstream.cpp:41:7:41:9 | ss5 | stringstream.cpp:29:16:29:21 | call to source | +| stringstream.cpp:43:11:43:13 | call to str | stringstream.cpp:32:14:32:19 | call to source | +| stringstream.cpp:44:11:44:13 | call to str | stringstream.cpp:33:23:33:28 | call to source | +| stringstream.cpp:45:11:45:13 | call to str | stringstream.cpp:34:14:34:19 | call to source | +| stringstream.cpp:46:11:46:13 | call to str | stringstream.cpp:29:16:29:21 | call to source | +| stringstream.cpp:52:7:52:9 | ss6 | stringstream.cpp:49:10:49:15 | call to source | +| stringstream.cpp:53:7:53:9 | ss7 | stringstream.cpp:50:10:50:15 | call to source | +| stringstream.cpp:56:11:56:13 | call to put | stringstream.cpp:56:15:56:29 | call to source | +| stringstream.cpp:57:44:57:46 | call to put | stringstream.cpp:57:25:57:39 | call to source | +| stringstream.cpp:59:7:59:9 | ss9 | stringstream.cpp:56:15:56:29 | call to source | +| stringstream.cpp:60:7:60:10 | ss10 | stringstream.cpp:57:25:57:39 | call to source | +| stringstream.cpp:63:12:63:16 | call to write | stringstream.cpp:63:18:63:23 | call to source | +| stringstream.cpp:64:54:64:58 | call to write | stringstream.cpp:64:36:64:41 | call to source | +| stringstream.cpp:66:7:66:10 | ss12 | stringstream.cpp:63:18:63:23 | call to source | +| stringstream.cpp:67:7:67:10 | ss13 | stringstream.cpp:64:36:64:41 | call to source | +| stringstream.cpp:76:11:76:11 | call to operator<< | stringstream.cpp:70:32:70:37 | source | +| stringstream.cpp:78:11:78:11 | call to operator>> | stringstream.cpp:70:32:70:37 | source | +| stringstream.cpp:81:7:81:9 | ss2 | stringstream.cpp:70:32:70:37 | source | +| stringstream.cpp:83:11:83:13 | call to str | stringstream.cpp:70:32:70:37 | source | +| stringstream.cpp:85:7:85:8 | v2 | stringstream.cpp:70:32:70:37 | source | +| stringstream.cpp:100:11:100:11 | call to operator= | stringstream.cpp:100:31:100:36 | call to source | +| stringstream.cpp:103:7:103:9 | ss2 | stringstream.cpp:91:19:91:24 | call to source | +| stringstream.cpp:105:7:105:9 | ss4 | stringstream.cpp:95:44:95:49 | call to source | +| stringstream.cpp:107:7:107:9 | ss6 | stringstream.cpp:100:31:100:36 | call to source | +| stringstream.cpp:120:7:120:9 | ss1 | stringstream.cpp:113:24:113:29 | call to source | +| stringstream.cpp:121:7:121:9 | ss2 | stringstream.cpp:113:24:113:29 | call to source | +| stringstream.cpp:122:7:122:9 | ss3 | stringstream.cpp:115:24:115:29 | call to source | +| stringstream.cpp:123:7:123:9 | ss4 | stringstream.cpp:115:24:115:29 | call to source | +| stringstream.cpp:143:11:143:11 | call to operator<< | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:146:11:146:11 | call to operator>> | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:147:17:147:17 | call to operator>> | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:149:7:149:8 | s2 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:150:7:150:8 | s3 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:151:7:151:8 | s4 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:154:11:154:11 | call to operator>> | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:155:17:155:17 | call to operator>> | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:157:7:157:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:158:7:158:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:159:7:159:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:162:11:162:14 | call to read | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:166:11:166:13 | call to get | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:168:7:168:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:170:7:170:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:172:7:172:9 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:175:7:175:20 | ... = ... | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:177:7:177:21 | ... = ... | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:179:11:179:13 | call to get | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:181:7:181:8 | c2 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:183:7:183:8 | c4 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:185:7:185:8 | c6 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:196:10:196:16 | call to putback | stringstream.cpp:196:18:196:32 | call to source | +| stringstream.cpp:197:10:197:12 | call to get | stringstream.cpp:196:18:196:32 | call to source | +| stringstream.cpp:215:11:215:17 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:216:11:216:17 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:219:7:219:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:220:7:220:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:223:11:223:17 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:224:11:224:17 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:227:7:227:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:228:7:228:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:230:29:230:35 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:231:7:231:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:232:7:232:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:235:7:235:13 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:236:7:236:13 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:239:7:239:8 | s2 | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:240:7:240:8 | s3 | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:243:7:243:13 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:244:7:244:13 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:247:7:247:8 | s5 | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:248:7:248:8 | s6 | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:250:7:250:13 | call to getline | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:251:7:251:8 | s7 | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:252:7:252:8 | s8 | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:262:32:262:34 | call to get | stringstream.cpp:257:24:257:29 | call to source | +| stringstream.cpp:263:7:263:8 | call to basic_string | stringstream.cpp:257:24:257:29 | call to source | +| stringstream.cpp:264:7:264:8 | call to basic_string | stringstream.cpp:257:24:257:29 | call to source | +| stringstream.cpp:266:62:266:66 | call to write | stringstream.cpp:266:41:266:46 | call to source | +| stringstream.cpp:267:7:267:9 | ss2 | stringstream.cpp:266:41:266:46 | call to source | +| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source | +| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source | +| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source | +| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source | +| structlikeclass.cpp:60:8:60:9 | s1 | structlikeclass.cpp:55:40:55:45 | call to source | +| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source | +| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source | +| swap1.cpp:73:12:73:16 | data1 | swap1.cpp:71:15:71:20 | call to source | +| swap1.cpp:78:12:78:16 | data1 | swap1.cpp:69:23:69:23 | x | +| swap1.cpp:78:12:78:16 | data1 | swap1.cpp:71:15:71:20 | call to source | +| swap1.cpp:79:12:79:16 | data1 | swap1.cpp:71:15:71:20 | call to source | +| swap1.cpp:83:13:83:17 | data1 | swap1.cpp:82:16:82:21 | call to source | +| swap1.cpp:87:13:87:17 | data1 | swap1.cpp:82:16:82:21 | call to source | +| swap1.cpp:88:13:88:17 | data1 | swap1.cpp:81:27:81:28 | z2 | +| swap1.cpp:88:13:88:17 | data1 | swap1.cpp:82:16:82:21 | call to source | +| swap1.cpp:97:12:97:16 | data1 | swap1.cpp:95:15:95:20 | call to source | +| swap1.cpp:102:12:102:16 | data1 | swap1.cpp:93:23:93:23 | x | +| swap1.cpp:102:12:102:16 | data1 | swap1.cpp:95:15:95:20 | call to source | +| swap1.cpp:103:12:103:16 | data1 | swap1.cpp:95:15:95:20 | call to source | +| swap1.cpp:111:20:111:24 | data1 | swap1.cpp:109:23:109:28 | call to source | +| swap1.cpp:115:18:115:22 | data1 | swap1.cpp:108:23:108:31 | move_from | +| swap1.cpp:115:18:115:22 | data1 | swap1.cpp:109:23:109:28 | call to source | +| swap1.cpp:124:12:124:16 | data1 | swap1.cpp:122:15:122:20 | call to source | +| swap1.cpp:129:12:129:16 | data1 | swap1.cpp:120:23:120:23 | x | +| swap1.cpp:129:12:129:16 | data1 | swap1.cpp:122:15:122:20 | call to source | +| swap1.cpp:130:12:130:16 | data1 | swap1.cpp:122:15:122:20 | call to source | +| swap1.cpp:139:12:139:16 | data1 | swap1.cpp:137:15:137:20 | call to source | +| swap1.cpp:144:12:144:16 | data1 | swap1.cpp:135:23:135:23 | x | +| swap1.cpp:144:12:144:16 | data1 | swap1.cpp:137:15:137:20 | call to source | +| swap1.cpp:145:12:145:16 | data1 | swap1.cpp:137:15:137:20 | call to source | +| swap2.cpp:73:12:73:16 | data1 | swap2.cpp:71:15:71:20 | call to source | +| swap2.cpp:78:12:78:16 | data1 | swap2.cpp:69:23:69:23 | x | +| swap2.cpp:78:12:78:16 | data1 | swap2.cpp:71:15:71:20 | call to source | +| swap2.cpp:79:12:79:16 | data1 | swap2.cpp:71:15:71:20 | call to source | +| swap2.cpp:83:13:83:17 | data1 | swap2.cpp:82:16:82:21 | call to source | +| swap2.cpp:88:13:88:17 | data1 | swap2.cpp:81:27:81:28 | z2 | +| swap2.cpp:88:13:88:17 | data1 | swap2.cpp:82:16:82:21 | call to source | +| swap2.cpp:97:12:97:16 | data1 | swap2.cpp:95:15:95:20 | call to source | +| swap2.cpp:102:12:102:16 | data1 | swap2.cpp:93:23:93:23 | x | +| swap2.cpp:102:12:102:16 | data1 | swap2.cpp:95:15:95:20 | call to source | +| swap2.cpp:103:12:103:16 | data1 | swap2.cpp:95:15:95:20 | call to source | +| swap2.cpp:111:20:111:24 | data1 | swap2.cpp:109:23:109:28 | call to source | +| swap2.cpp:115:18:115:22 | data1 | swap2.cpp:108:23:108:31 | move_from | +| swap2.cpp:115:18:115:22 | data1 | swap2.cpp:109:23:109:28 | call to source | +| swap2.cpp:124:12:124:16 | data1 | swap2.cpp:122:15:122:20 | call to source | +| swap2.cpp:129:12:129:16 | data1 | swap2.cpp:120:23:120:23 | x | +| swap2.cpp:129:12:129:16 | data1 | swap2.cpp:122:15:122:20 | call to source | +| swap2.cpp:130:12:130:16 | data1 | swap2.cpp:122:15:122:20 | call to source | +| swap2.cpp:139:12:139:16 | data1 | swap2.cpp:137:15:137:20 | call to source | +| swap2.cpp:144:12:144:16 | data1 | swap2.cpp:135:23:135:23 | x | +| swap2.cpp:144:12:144:16 | data1 | swap2.cpp:137:15:137:20 | call to source | +| swap2.cpp:145:12:145:16 | data1 | swap2.cpp:137:15:137:20 | call to source | | taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 | | taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source | | taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source | @@ -27,7 +524,12 @@ | taint.cpp:91:11:91:11 | d | taint.cpp:77:7:77:12 | call to source | | taint.cpp:93:11:93:11 | b | taint.cpp:71:22:71:27 | call to source | | taint.cpp:94:11:94:11 | c | taint.cpp:72:7:72:12 | call to source | +| taint.cpp:109:7:109:13 | access to array | taint.cpp:105:12:105:17 | call to source | +| taint.cpp:110:7:110:13 | access to array | taint.cpp:105:12:105:17 | call to source | +| taint.cpp:111:7:111:13 | access to array | taint.cpp:106:12:106:17 | call to source | +| taint.cpp:112:7:112:13 | access to array | taint.cpp:106:12:106:17 | call to source | | taint.cpp:129:7:129:9 | * ... | taint.cpp:120:11:120:16 | call to source | +| taint.cpp:130:7:130:9 | * ... | taint.cpp:127:8:127:13 | call to source | | taint.cpp:134:7:134:9 | * ... | taint.cpp:120:11:120:16 | call to source | | taint.cpp:137:7:137:9 | * ... | taint.cpp:120:11:120:16 | call to source | | taint.cpp:151:7:151:12 | call to select | taint.cpp:151:20:151:25 | call to source | @@ -55,6 +557,8 @@ | taint.cpp:350:7:350:7 | t | taint.cpp:330:6:330:11 | call to source | | taint.cpp:351:7:351:7 | a | taint.cpp:330:6:330:11 | call to source | | taint.cpp:352:7:352:7 | b | taint.cpp:330:6:330:11 | call to source | +| taint.cpp:353:7:353:7 | c | taint.cpp:330:6:330:11 | call to source | +| taint.cpp:354:7:354:7 | d | taint.cpp:330:6:330:11 | call to source | | taint.cpp:372:7:372:7 | a | taint.cpp:365:24:365:29 | source | | taint.cpp:374:7:374:7 | c | taint.cpp:365:24:365:29 | source | | taint.cpp:382:7:382:7 | a | taint.cpp:377:23:377:28 | source | @@ -70,3 +574,82 @@ | taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source | | taint.cpp:471:7:471:7 | y | taint.cpp:462:6:462:11 | call to source | | taint.cpp:485:7:485:10 | line | taint.cpp:480:26:480:32 | source1 | +| vector.cpp:20:8:20:8 | x | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:24:8:24:8 | call to operator* | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:28:8:28:8 | x | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:33:8:33:8 | x | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:52:7:52:8 | v2 | vector.cpp:51:10:51:15 | call to source | +| vector.cpp:53:9:53:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source | +| vector.cpp:54:9:54:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source | +| vector.cpp:55:9:55:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source | +| vector.cpp:58:7:58:8 | v3 | vector.cpp:51:10:51:15 | call to source | +| vector.cpp:59:9:59:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source | +| vector.cpp:60:9:60:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source | +| vector.cpp:61:9:61:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source | +| vector.cpp:64:7:64:8 | v4 | vector.cpp:63:10:63:15 | call to source | +| vector.cpp:65:9:65:9 | call to operator[] | vector.cpp:63:10:63:15 | call to source | +| vector.cpp:66:9:66:9 | call to operator[] | vector.cpp:63:10:63:15 | call to source | +| vector.cpp:67:9:67:9 | call to operator[] | vector.cpp:63:10:63:15 | call to source | +| vector.cpp:70:7:70:8 | v5 | vector.cpp:69:15:69:20 | call to source | +| vector.cpp:71:10:71:14 | call to front | vector.cpp:69:15:69:20 | call to source | +| vector.cpp:72:10:72:13 | call to back | vector.cpp:69:15:69:20 | call to source | +| vector.cpp:75:7:75:8 | v6 | vector.cpp:74:17:74:22 | call to source | +| vector.cpp:76:7:76:18 | access to array | vector.cpp:74:17:74:22 | call to source | +| vector.cpp:83:7:83:8 | v7 | vector.cpp:81:17:81:22 | call to source | +| vector.cpp:84:10:84:14 | call to front | vector.cpp:81:17:81:22 | call to source | +| vector.cpp:85:10:85:13 | call to back | vector.cpp:81:17:81:22 | call to source | +| vector.cpp:97:7:97:8 | v9 | vector.cpp:96:13:96:18 | call to source | +| vector.cpp:98:10:98:11 | call to at | vector.cpp:96:13:96:18 | call to source | +| vector.cpp:99:10:99:11 | call to at | vector.cpp:96:13:96:18 | call to source | +| vector.cpp:100:10:100:11 | call to at | vector.cpp:96:13:96:18 | call to source | +| vector.cpp:109:7:109:8 | v1 | vector.cpp:106:15:106:20 | call to source | +| vector.cpp:112:7:112:8 | v4 | vector.cpp:107:15:107:20 | call to source | +| vector.cpp:117:7:117:8 | v1 | vector.cpp:106:15:106:20 | call to source | +| vector.cpp:118:7:118:8 | v2 | vector.cpp:106:15:106:20 | call to source | +| vector.cpp:119:7:119:8 | v3 | vector.cpp:107:15:107:20 | call to source | +| vector.cpp:120:7:120:8 | v4 | vector.cpp:107:15:107:20 | call to source | +| vector.cpp:130:7:130:8 | v1 | vector.cpp:126:15:126:20 | call to source | +| vector.cpp:131:7:131:8 | v2 | vector.cpp:127:15:127:20 | call to source | +| vector.cpp:132:7:132:8 | v3 | vector.cpp:128:15:128:20 | call to source | +| vector.cpp:139:7:139:8 | v1 | vector.cpp:126:15:126:20 | call to source | +| vector.cpp:140:7:140:8 | v2 | vector.cpp:127:15:127:20 | call to source | +| vector.cpp:141:7:141:8 | v3 | vector.cpp:128:15:128:20 | call to source | +| vector.cpp:162:8:162:15 | access to array | vector.cpp:161:14:161:19 | call to source | +| vector.cpp:171:13:171:13 | call to operator[] | vector.cpp:170:14:170:19 | call to source | +| vector.cpp:180:13:180:13 | call to operator[] | vector.cpp:179:14:179:19 | call to source | +| vector.cpp:201:13:201:13 | call to operator[] | vector.cpp:200:14:200:19 | call to source | +| vector.cpp:242:7:242:8 | v2 | vector.cpp:238:17:238:30 | call to source | +| vector.cpp:243:7:243:8 | v3 | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:258:8:258:9 | v5 | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:259:8:259:9 | i1 | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:260:8:260:9 | i2 | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:261:8:261:9 | v6 | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:273:8:273:9 | v7 | vector.cpp:269:18:269:31 | call to source | +| vector.cpp:274:8:274:9 | v8 | vector.cpp:270:18:270:35 | call to source | +| vector.cpp:275:8:275:9 | v9 | vector.cpp:271:18:271:34 | call to source | +| vector.cpp:285:7:285:8 | v1 | vector.cpp:284:15:284:20 | call to source | +| vector.cpp:286:10:286:13 | call to data | vector.cpp:284:15:284:20 | call to source | +| vector.cpp:287:7:287:18 | access to array | vector.cpp:284:15:284:20 | call to source | +| vector.cpp:290:7:290:8 | v2 | vector.cpp:289:17:289:30 | call to source | +| vector.cpp:291:10:291:13 | call to data | vector.cpp:289:17:289:30 | call to source | +| vector.cpp:292:7:292:18 | access to array | vector.cpp:289:17:289:30 | call to source | +| vector.cpp:308:9:308:14 | call to insert | vector.cpp:303:14:303:19 | call to source | +| vector.cpp:309:7:309:7 | c | vector.cpp:303:14:303:19 | call to source | +| vector.cpp:311:9:311:14 | call to insert | vector.cpp:303:14:303:19 | call to source | +| vector.cpp:312:7:312:7 | d | vector.cpp:303:14:303:19 | call to source | +| vector.cpp:324:7:324:8 | v2 | vector.cpp:318:15:318:20 | call to source | +| vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source | +| vector.cpp:342:7:342:8 | v1 | vector.cpp:341:8:341:13 | call to source | +| vector.cpp:347:7:347:8 | v2 | vector.cpp:345:9:345:14 | call to source | +| vector.cpp:357:7:357:8 | v4 | vector.cpp:330:10:330:15 | call to source | +| vector.cpp:361:7:361:8 | v5 | vector.cpp:360:8:360:13 | call to source | +| vector.cpp:363:7:363:8 | v5 | vector.cpp:360:8:360:13 | call to source | +| vector.cpp:367:7:367:8 | v6 | vector.cpp:366:8:366:13 | call to source | +| vector.cpp:369:7:369:8 | v6 | vector.cpp:366:8:366:13 | call to source | +| vector.cpp:374:8:374:9 | v7 | vector.cpp:373:9:373:14 | call to source | +| vector.cpp:379:7:379:8 | v7 | vector.cpp:373:9:373:14 | call to source | +| vector.cpp:383:7:383:8 | v8 | vector.cpp:382:8:382:13 | call to source | +| vector.cpp:385:7:385:8 | v8 | vector.cpp:382:8:382:13 | call to source | +| vector.cpp:392:7:392:8 | v9 | vector.cpp:330:10:330:15 | call to source | +| vector.cpp:392:7:392:8 | v9 | vector.cpp:389:8:389:13 | call to source | +| vector.cpp:400:7:400:9 | v11 | vector.cpp:399:38:399:43 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 0426082e01bf..9da6b44c7808 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -1,47 +1,381 @@ -| format.cpp:57:8:57:13 | format.cpp:56:36:56:49 | AST only | -| format.cpp:62:8:62:13 | format.cpp:61:30:61:43 | AST only | -| format.cpp:67:8:67:13 | format.cpp:66:52:66:65 | AST only | -| format.cpp:72:8:72:13 | format.cpp:71:42:71:55 | AST only | -| format.cpp:83:8:83:13 | format.cpp:82:36:82:41 | AST only | -| format.cpp:88:8:88:13 | format.cpp:87:38:87:43 | AST only | -| format.cpp:94:8:94:13 | format.cpp:93:36:93:49 | AST only | -| format.cpp:100:8:100:13 | format.cpp:99:30:99:43 | AST only | -| format.cpp:105:8:105:13 | format.cpp:104:31:104:45 | AST only | -| format.cpp:110:8:110:14 | format.cpp:109:38:109:52 | AST only | -| stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only | -| stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only | -| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only | -| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only | -| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only | +| arrayassignment.cpp:16:7:16:7 | arrayassignment.cpp:14:9:14:14 | IR only | +| arrayassignment.cpp:18:7:18:11 | arrayassignment.cpp:14:9:14:14 | IR only | +| arrayassignment.cpp:19:7:19:9 | arrayassignment.cpp:14:9:14:14 | IR only | +| arrayassignment.cpp:31:7:31:7 | arrayassignment.cpp:29:8:29:13 | IR only | +| arrayassignment.cpp:32:7:32:10 | arrayassignment.cpp:29:8:29:13 | IR only | +| arrayassignment.cpp:34:7:34:10 | arrayassignment.cpp:29:8:29:13 | IR only | +| arrayassignment.cpp:56:7:56:8 | arrayassignment.cpp:54:9:54:14 | IR only | +| arrayassignment.cpp:57:10:57:12 | arrayassignment.cpp:54:9:54:14 | AST only | +| arrayassignment.cpp:57:10:57:15 | arrayassignment.cpp:54:9:54:14 | IR only | +| arrayassignment.cpp:66:7:66:8 | arrayassignment.cpp:64:13:64:18 | IR only | +| arrayassignment.cpp:67:10:67:12 | arrayassignment.cpp:64:13:64:18 | AST only | +| arrayassignment.cpp:67:10:67:15 | arrayassignment.cpp:64:13:64:18 | IR only | +| arrayassignment.cpp:136:7:136:13 | arrayassignment.cpp:134:9:134:14 | IR only | +| arrayassignment.cpp:141:7:141:13 | arrayassignment.cpp:139:10:139:15 | IR only | +| arrayassignment.cpp:146:7:146:13 | arrayassignment.cpp:144:12:144:17 | IR only | +| copyableclass.cpp:67:11:67:11 | copyableclass.cpp:67:13:67:18 | AST only | +| copyableclass.cpp:67:11:67:21 | copyableclass.cpp:67:13:67:18 | IR only | +| copyableclass_declonly.cpp:42:8:42:9 | copyableclass_declonly.cpp:34:30:34:35 | AST only | +| copyableclass_declonly.cpp:67:11:67:11 | copyableclass_declonly.cpp:67:13:67:18 | AST only | +| map.cpp:49:9:49:13 | map.cpp:48:37:48:42 | IR only | +| map.cpp:54:9:54:13 | map.cpp:48:37:48:42 | IR only | +| map.cpp:60:9:60:13 | map.cpp:48:37:48:42 | IR only | +| map.cpp:70:9:70:13 | map.cpp:65:37:65:42 | IR only | +| map.cpp:71:9:71:14 | map.cpp:65:37:65:42 | IR only | +| map.cpp:73:9:73:13 | map.cpp:65:37:65:42 | IR only | +| map.cpp:76:9:76:13 | map.cpp:66:37:66:42 | IR only | +| map.cpp:79:9:79:13 | map.cpp:66:37:66:42 | IR only | +| map.cpp:80:9:80:14 | map.cpp:66:37:66:42 | IR only | +| map.cpp:90:34:90:38 | map.cpp:90:24:90:29 | IR only | +| map.cpp:91:34:91:39 | map.cpp:91:24:91:29 | IR only | +| map.cpp:108:7:108:54 | map.cpp:108:39:108:44 | IR only | +| map.cpp:111:7:111:48 | map.cpp:111:34:111:39 | IR only | +| map.cpp:114:7:114:8 | map.cpp:108:39:108:44 | AST only | +| map.cpp:116:7:116:8 | map.cpp:110:62:110:67 | AST only | +| map.cpp:117:7:117:8 | map.cpp:111:34:111:39 | AST only | +| map.cpp:118:7:118:8 | map.cpp:112:46:112:51 | AST only | +| map.cpp:123:10:123:13 | map.cpp:111:34:111:39 | AST only | +| map.cpp:124:10:124:13 | map.cpp:112:46:112:51 | AST only | +| map.cpp:129:10:129:13 | map.cpp:111:34:111:39 | AST only | +| map.cpp:130:10:130:13 | map.cpp:112:46:112:51 | AST only | +| map.cpp:137:7:137:8 | map.cpp:108:39:108:44 | AST only | +| map.cpp:138:7:138:8 | map.cpp:108:39:108:44 | AST only | +| map.cpp:139:7:139:8 | map.cpp:108:39:108:44 | AST only | +| map.cpp:140:10:140:13 | map.cpp:108:39:108:44 | AST only | +| map.cpp:141:10:141:13 | map.cpp:108:39:108:44 | AST only | +| map.cpp:155:12:155:16 | map.cpp:108:39:108:44 | IR only | +| map.cpp:156:12:156:17 | map.cpp:108:39:108:44 | IR only | +| map.cpp:161:12:161:16 | map.cpp:108:39:108:44 | IR only | +| map.cpp:162:12:162:17 | map.cpp:108:39:108:44 | IR only | +| map.cpp:172:10:172:10 | map.cpp:168:20:168:25 | AST only | +| map.cpp:174:10:174:10 | map.cpp:170:23:170:28 | AST only | +| map.cpp:184:7:184:31 | map.cpp:108:39:108:44 | IR only | +| map.cpp:185:7:185:32 | map.cpp:108:39:108:44 | IR only | +| map.cpp:187:7:187:32 | map.cpp:108:39:108:44 | IR only | +| map.cpp:193:7:193:9 | map.cpp:191:49:191:54 | AST only | +| map.cpp:196:7:196:9 | map.cpp:192:49:192:54 | AST only | +| map.cpp:199:7:199:9 | map.cpp:191:49:191:54 | AST only | +| map.cpp:200:7:200:9 | map.cpp:191:49:191:54 | AST only | +| map.cpp:201:7:201:9 | map.cpp:192:49:192:54 | AST only | +| map.cpp:202:7:202:9 | map.cpp:192:49:192:54 | AST only | +| map.cpp:210:7:210:9 | map.cpp:206:49:206:54 | AST only | +| map.cpp:213:7:213:9 | map.cpp:209:49:209:54 | AST only | +| map.cpp:216:7:216:9 | map.cpp:206:49:206:54 | AST only | +| map.cpp:218:7:218:9 | map.cpp:209:49:209:54 | AST only | +| map.cpp:219:7:219:9 | map.cpp:209:49:209:54 | AST only | +| map.cpp:225:7:225:9 | map.cpp:223:49:223:54 | AST only | +| map.cpp:225:7:225:9 | map.cpp:224:49:224:54 | AST only | +| map.cpp:227:7:227:9 | map.cpp:223:49:223:54 | AST only | +| map.cpp:227:7:227:9 | map.cpp:224:49:224:54 | AST only | +| map.cpp:229:7:229:9 | map.cpp:223:49:223:54 | AST only | +| map.cpp:229:7:229:9 | map.cpp:224:49:224:54 | AST only | +| map.cpp:235:7:235:40 | map.cpp:235:26:235:31 | IR only | +| map.cpp:236:7:236:9 | map.cpp:235:26:235:31 | AST only | +| map.cpp:240:7:240:9 | map.cpp:239:44:239:49 | AST only | +| map.cpp:246:7:246:44 | map.cpp:246:30:246:35 | IR only | +| map.cpp:247:7:247:9 | map.cpp:246:30:246:35 | AST only | +| map.cpp:251:7:251:9 | map.cpp:250:43:250:48 | AST only | +| map.cpp:260:7:260:54 | map.cpp:260:39:260:44 | IR only | +| map.cpp:263:7:263:48 | map.cpp:263:34:263:39 | IR only | +| map.cpp:266:7:266:8 | map.cpp:260:39:260:44 | AST only | +| map.cpp:268:7:268:8 | map.cpp:262:62:262:67 | AST only | +| map.cpp:269:7:269:8 | map.cpp:263:34:263:39 | AST only | +| map.cpp:270:7:270:8 | map.cpp:264:46:264:51 | AST only | +| map.cpp:275:10:275:13 | map.cpp:263:34:263:39 | AST only | +| map.cpp:276:10:276:13 | map.cpp:264:46:264:51 | AST only | +| map.cpp:281:10:281:13 | map.cpp:263:34:263:39 | AST only | +| map.cpp:282:10:282:13 | map.cpp:264:46:264:51 | AST only | +| map.cpp:289:7:289:8 | map.cpp:260:39:260:44 | AST only | +| map.cpp:290:7:290:8 | map.cpp:260:39:260:44 | AST only | +| map.cpp:291:7:291:8 | map.cpp:260:39:260:44 | AST only | +| map.cpp:292:10:292:13 | map.cpp:260:39:260:44 | AST only | +| map.cpp:293:10:293:13 | map.cpp:260:39:260:44 | AST only | +| map.cpp:307:12:307:16 | map.cpp:260:39:260:44 | IR only | +| map.cpp:308:12:308:17 | map.cpp:260:39:260:44 | IR only | +| map.cpp:313:12:313:16 | map.cpp:260:39:260:44 | IR only | +| map.cpp:314:12:314:17 | map.cpp:260:39:260:44 | IR only | +| map.cpp:324:10:324:10 | map.cpp:320:20:320:25 | AST only | +| map.cpp:326:10:326:10 | map.cpp:322:23:322:28 | AST only | +| map.cpp:334:7:334:31 | map.cpp:260:39:260:44 | IR only | +| map.cpp:335:7:335:32 | map.cpp:260:39:260:44 | IR only | +| map.cpp:336:7:336:32 | map.cpp:260:39:260:44 | IR only | +| map.cpp:342:7:342:9 | map.cpp:340:49:340:54 | AST only | +| map.cpp:345:7:345:9 | map.cpp:341:49:341:54 | AST only | +| map.cpp:348:7:348:9 | map.cpp:340:49:340:54 | AST only | +| map.cpp:349:7:349:9 | map.cpp:340:49:340:54 | AST only | +| map.cpp:350:7:350:9 | map.cpp:341:49:341:54 | AST only | +| map.cpp:351:7:351:9 | map.cpp:341:49:341:54 | AST only | +| map.cpp:359:7:359:9 | map.cpp:355:49:355:54 | AST only | +| map.cpp:362:7:362:9 | map.cpp:358:49:358:54 | AST only | +| map.cpp:365:7:365:9 | map.cpp:355:49:355:54 | AST only | +| map.cpp:367:7:367:9 | map.cpp:358:49:358:54 | AST only | +| map.cpp:368:7:368:9 | map.cpp:358:49:358:54 | AST only | +| map.cpp:374:7:374:9 | map.cpp:372:49:372:54 | AST only | +| map.cpp:374:7:374:9 | map.cpp:373:49:373:54 | AST only | +| map.cpp:376:7:376:9 | map.cpp:372:49:372:54 | AST only | +| map.cpp:376:7:376:9 | map.cpp:373:49:373:54 | AST only | +| map.cpp:378:7:378:9 | map.cpp:372:49:372:54 | AST only | +| map.cpp:378:7:378:9 | map.cpp:373:49:373:54 | AST only | +| map.cpp:384:7:384:40 | map.cpp:384:26:384:31 | IR only | +| map.cpp:385:7:385:9 | map.cpp:384:26:384:31 | AST only | +| map.cpp:389:7:389:9 | map.cpp:388:44:388:49 | AST only | +| map.cpp:396:7:396:44 | map.cpp:396:30:396:35 | IR only | +| map.cpp:397:40:397:45 | map.cpp:397:30:397:35 | IR only | +| map.cpp:398:7:398:9 | map.cpp:396:30:396:35 | AST only | +| map.cpp:398:7:398:9 | map.cpp:397:30:397:35 | AST only | +| map.cpp:402:7:402:9 | map.cpp:401:43:401:48 | AST only | +| map.cpp:417:7:417:9 | map.cpp:416:30:416:35 | AST only | +| map.cpp:418:7:418:16 | map.cpp:416:30:416:35 | AST only | +| map.cpp:420:7:420:9 | map.cpp:419:33:419:38 | AST only | +| map.cpp:421:7:421:16 | map.cpp:419:33:419:38 | AST only | +| map.cpp:431:7:431:67 | map.cpp:431:52:431:57 | IR only | +| map.cpp:432:7:432:9 | map.cpp:431:52:431:57 | AST only | +| movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only | +| movableclass.cpp:65:11:65:21 | movableclass.cpp:65:13:65:18 | IR only | +| set.cpp:20:7:20:31 | set.cpp:20:17:20:22 | IR only | +| set.cpp:26:7:26:8 | set.cpp:20:17:20:22 | AST only | +| set.cpp:28:7:28:8 | set.cpp:22:29:22:34 | AST only | +| set.cpp:30:7:30:8 | set.cpp:20:17:20:22 | AST only | +| set.cpp:44:7:44:8 | set.cpp:20:17:20:22 | AST only | +| set.cpp:45:7:45:8 | set.cpp:20:17:20:22 | AST only | +| set.cpp:46:7:46:8 | set.cpp:20:17:20:22 | AST only | +| set.cpp:47:7:47:9 | set.cpp:20:17:20:22 | AST only | +| set.cpp:48:10:48:13 | set.cpp:20:17:20:22 | AST only | +| set.cpp:49:10:49:13 | set.cpp:20:17:20:22 | AST only | +| set.cpp:61:8:61:11 | set.cpp:20:17:20:22 | IR only | +| set.cpp:71:7:71:32 | set.cpp:67:13:67:18 | IR only | +| set.cpp:72:7:72:33 | set.cpp:67:13:67:18 | IR only | +| set.cpp:78:7:78:9 | set.cpp:76:13:76:18 | AST only | +| set.cpp:81:7:81:9 | set.cpp:77:13:77:18 | AST only | +| set.cpp:84:7:84:9 | set.cpp:76:13:76:18 | AST only | +| set.cpp:85:7:85:9 | set.cpp:76:13:76:18 | AST only | +| set.cpp:86:7:86:9 | set.cpp:77:13:77:18 | AST only | +| set.cpp:87:7:87:9 | set.cpp:77:13:77:18 | AST only | +| set.cpp:95:7:95:9 | set.cpp:91:13:91:18 | AST only | +| set.cpp:98:7:98:9 | set.cpp:94:13:94:18 | AST only | +| set.cpp:101:7:101:9 | set.cpp:91:13:91:18 | AST only | +| set.cpp:103:7:103:9 | set.cpp:94:13:94:18 | AST only | +| set.cpp:104:7:104:9 | set.cpp:94:13:94:18 | AST only | +| set.cpp:110:7:110:9 | set.cpp:108:13:108:18 | AST only | +| set.cpp:110:7:110:9 | set.cpp:109:13:109:18 | AST only | +| set.cpp:112:7:112:9 | set.cpp:108:13:108:18 | AST only | +| set.cpp:112:7:112:9 | set.cpp:109:13:109:18 | AST only | +| set.cpp:114:7:114:9 | set.cpp:108:13:108:18 | AST only | +| set.cpp:114:7:114:9 | set.cpp:109:13:109:18 | AST only | +| set.cpp:120:7:120:33 | set.cpp:120:19:120:24 | IR only | +| set.cpp:121:7:121:9 | set.cpp:120:19:120:24 | AST only | +| set.cpp:125:7:125:9 | set.cpp:124:37:124:42 | AST only | +| set.cpp:134:7:134:31 | set.cpp:134:17:134:22 | IR only | +| set.cpp:140:7:140:8 | set.cpp:134:17:134:22 | AST only | +| set.cpp:142:7:142:8 | set.cpp:136:29:136:34 | AST only | +| set.cpp:144:7:144:8 | set.cpp:134:17:134:22 | AST only | +| set.cpp:158:7:158:8 | set.cpp:134:17:134:22 | AST only | +| set.cpp:159:7:159:8 | set.cpp:134:17:134:22 | AST only | +| set.cpp:160:7:160:8 | set.cpp:134:17:134:22 | AST only | +| set.cpp:161:7:161:9 | set.cpp:134:17:134:22 | AST only | +| set.cpp:162:10:162:13 | set.cpp:134:17:134:22 | AST only | +| set.cpp:163:10:163:13 | set.cpp:134:17:134:22 | AST only | +| set.cpp:175:8:175:11 | set.cpp:134:17:134:22 | IR only | +| set.cpp:183:7:183:32 | set.cpp:181:13:181:18 | IR only | +| set.cpp:184:7:184:33 | set.cpp:181:13:181:18 | IR only | +| set.cpp:190:7:190:9 | set.cpp:188:13:188:18 | AST only | +| set.cpp:193:7:193:9 | set.cpp:189:13:189:18 | AST only | +| set.cpp:196:7:196:9 | set.cpp:188:13:188:18 | AST only | +| set.cpp:197:7:197:9 | set.cpp:188:13:188:18 | AST only | +| set.cpp:198:7:198:9 | set.cpp:189:13:189:18 | AST only | +| set.cpp:199:7:199:9 | set.cpp:189:13:189:18 | AST only | +| set.cpp:207:7:207:9 | set.cpp:203:13:203:18 | AST only | +| set.cpp:210:7:210:9 | set.cpp:206:13:206:18 | AST only | +| set.cpp:213:7:213:9 | set.cpp:203:13:203:18 | AST only | +| set.cpp:215:7:215:9 | set.cpp:206:13:206:18 | AST only | +| set.cpp:216:7:216:9 | set.cpp:206:13:206:18 | AST only | +| set.cpp:222:7:222:9 | set.cpp:220:13:220:18 | AST only | +| set.cpp:222:7:222:9 | set.cpp:221:13:221:18 | AST only | +| set.cpp:224:7:224:9 | set.cpp:220:13:220:18 | AST only | +| set.cpp:224:7:224:9 | set.cpp:221:13:221:18 | AST only | +| set.cpp:226:7:226:9 | set.cpp:220:13:220:18 | AST only | +| set.cpp:226:7:226:9 | set.cpp:221:13:221:18 | AST only | +| set.cpp:232:7:232:33 | set.cpp:232:19:232:24 | IR only | +| set.cpp:233:7:233:9 | set.cpp:232:19:232:24 | AST only | +| set.cpp:237:7:237:9 | set.cpp:236:37:236:42 | AST only | +| smart_pointer.cpp:12:10:12:10 | smart_pointer.cpp:11:52:11:57 | AST only | +| smart_pointer.cpp:24:10:24:10 | smart_pointer.cpp:23:52:23:57 | AST only | +| standalone_iterators.cpp:41:10:41:10 | standalone_iterators.cpp:39:45:39:51 | AST only | +| standalone_iterators.cpp:42:10:42:10 | standalone_iterators.cpp:39:45:39:51 | AST only | +| standalone_iterators.cpp:47:10:47:10 | standalone_iterators.cpp:45:39:45:45 | AST only | +| standalone_iterators.cpp:48:10:48:10 | standalone_iterators.cpp:45:39:45:45 | AST only | +| string.cpp:33:9:33:13 | string.cpp:27:16:27:21 | AST only | +| string.cpp:39:13:39:17 | string.cpp:14:10:14:15 | AST only | +| string.cpp:43:13:43:17 | string.cpp:14:10:14:15 | AST only | +| string.cpp:46:13:46:17 | string.cpp:14:10:14:15 | AST only | +| string.cpp:70:7:70:8 | string.cpp:62:19:62:24 | AST only | +| string.cpp:126:8:126:11 | string.cpp:120:16:120:21 | IR only | +| string.cpp:162:11:162:11 | string.cpp:155:18:155:23 | AST only | +| string.cpp:166:11:166:11 | string.cpp:166:14:166:19 | AST only | +| string.cpp:167:11:167:11 | string.cpp:166:14:166:19 | AST only | +| string.cpp:199:10:199:15 | string.cpp:191:17:191:22 | AST only | +| string.cpp:202:10:202:15 | string.cpp:192:11:192:25 | AST only | +| string.cpp:220:10:220:15 | string.cpp:211:17:211:22 | AST only | +| string.cpp:224:10:224:15 | string.cpp:211:17:211:22 | AST only | +| string.cpp:228:10:228:15 | string.cpp:212:11:212:25 | AST only | +| string.cpp:243:10:243:16 | string.cpp:234:17:234:22 | AST only | +| string.cpp:247:10:247:16 | string.cpp:234:17:234:22 | AST only | +| string.cpp:251:10:251:16 | string.cpp:235:11:235:25 | AST only | +| string.cpp:312:9:312:12 | string.cpp:309:16:309:21 | AST only | +| string.cpp:340:7:340:7 | string.cpp:336:9:336:23 | AST only | +| string.cpp:341:7:341:7 | string.cpp:337:12:337:26 | AST only | +| string.cpp:342:7:342:7 | string.cpp:336:9:336:23 | AST only | +| string.cpp:350:7:350:9 | string.cpp:349:18:349:32 | AST only | +| string.cpp:351:11:351:14 | string.cpp:349:18:349:32 | AST only | +| string.cpp:363:11:363:16 | string.cpp:358:18:358:23 | AST only | +| string.cpp:382:8:382:14 | string.cpp:374:18:374:23 | IR only | +| string.cpp:383:13:383:15 | string.cpp:374:18:374:23 | IR only | +| string.cpp:396:8:396:8 | string.cpp:389:18:389:23 | AST only | +| string.cpp:397:8:397:8 | string.cpp:389:18:389:23 | AST only | +| string.cpp:399:8:399:8 | string.cpp:389:18:389:23 | AST only | +| string.cpp:401:8:401:8 | string.cpp:389:18:389:23 | AST only | +| string.cpp:404:8:404:11 | string.cpp:389:18:389:23 | IR only | +| string.cpp:407:8:407:11 | string.cpp:389:18:389:23 | IR only | +| string.cpp:409:8:409:8 | string.cpp:389:18:389:23 | AST only | +| string.cpp:411:8:411:8 | string.cpp:389:18:389:23 | AST only | +| string.cpp:415:8:415:11 | string.cpp:389:18:389:23 | IR only | +| string.cpp:418:8:418:8 | string.cpp:389:18:389:23 | AST only | +| string.cpp:419:8:419:10 | string.cpp:389:18:389:23 | AST only | +| string.cpp:421:8:421:8 | string.cpp:389:18:389:23 | AST only | +| string.cpp:422:8:422:10 | string.cpp:389:18:389:23 | AST only | +| string.cpp:436:10:436:15 | string.cpp:431:14:431:19 | AST only | +| string.cpp:449:10:449:15 | string.cpp:449:32:449:46 | AST only | +| string.cpp:462:10:462:15 | string.cpp:457:18:457:23 | AST only | +| string.cpp:465:11:465:16 | string.cpp:457:18:457:23 | AST only | +| string.cpp:478:10:478:15 | string.cpp:473:18:473:23 | AST only | +| string.cpp:481:11:481:16 | string.cpp:473:18:473:23 | AST only | +| string.cpp:494:10:494:15 | string.cpp:489:18:489:23 | AST only | +| string.cpp:522:9:522:13 | string.cpp:521:14:521:28 | AST only | +| string.cpp:523:9:523:12 | string.cpp:521:14:521:28 | AST only | +| string.cpp:536:11:536:11 | string.cpp:536:20:536:25 | AST only | +| string.cpp:537:21:537:21 | string.cpp:537:24:537:29 | AST only | +| string.cpp:538:25:538:25 | string.cpp:538:15:538:20 | AST only | +| string.cpp:541:8:541:8 | string.cpp:536:20:536:25 | AST only | +| string.cpp:543:8:543:8 | string.cpp:537:24:537:29 | AST only | +| string.cpp:556:11:556:16 | string.cpp:556:27:556:32 | AST only | +| string.cpp:557:24:557:29 | string.cpp:557:31:557:36 | AST only | +| string.cpp:561:8:561:8 | string.cpp:556:27:556:32 | AST only | +| string.cpp:563:8:563:8 | string.cpp:557:31:557:36 | AST only | +| stringstream.cpp:32:11:32:22 | stringstream.cpp:32:14:32:19 | IR only | +| stringstream.cpp:33:20:33:31 | stringstream.cpp:33:23:33:28 | IR only | +| stringstream.cpp:34:23:34:31 | stringstream.cpp:34:14:34:19 | IR only | +| stringstream.cpp:35:11:35:11 | stringstream.cpp:29:16:29:21 | AST only | +| stringstream.cpp:39:7:39:9 | stringstream.cpp:33:23:33:28 | AST only | +| stringstream.cpp:41:7:41:9 | stringstream.cpp:29:16:29:21 | AST only | +| stringstream.cpp:44:11:44:13 | stringstream.cpp:33:23:33:28 | AST only | +| stringstream.cpp:46:11:46:13 | stringstream.cpp:29:16:29:21 | AST only | +| stringstream.cpp:56:11:56:13 | stringstream.cpp:56:15:56:29 | AST only | +| stringstream.cpp:57:44:57:46 | stringstream.cpp:57:25:57:39 | AST only | +| stringstream.cpp:60:7:60:10 | stringstream.cpp:57:25:57:39 | AST only | +| stringstream.cpp:63:12:63:16 | stringstream.cpp:63:18:63:23 | AST only | +| stringstream.cpp:64:54:64:58 | stringstream.cpp:64:36:64:41 | AST only | +| stringstream.cpp:67:7:67:10 | stringstream.cpp:64:36:64:41 | AST only | +| stringstream.cpp:76:11:76:11 | stringstream.cpp:70:32:70:37 | AST only | +| stringstream.cpp:78:11:78:11 | stringstream.cpp:70:32:70:37 | AST only | +| stringstream.cpp:100:11:100:11 | stringstream.cpp:100:31:100:36 | AST only | +| stringstream.cpp:143:11:143:22 | stringstream.cpp:143:14:143:19 | IR only | +| stringstream.cpp:146:11:146:11 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:147:17:147:17 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:151:7:151:8 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:154:11:154:11 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:155:17:155:17 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:159:7:159:8 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:162:11:162:14 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:166:11:166:13 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:179:11:179:13 | stringstream.cpp:143:14:143:19 | AST only | +| stringstream.cpp:196:10:196:16 | stringstream.cpp:196:18:196:32 | AST only | +| stringstream.cpp:215:11:215:17 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:216:11:216:17 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:223:11:223:17 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:224:11:224:17 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:230:29:230:35 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:232:7:232:8 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:235:7:235:13 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:236:7:236:13 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:243:7:243:13 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:244:7:244:13 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:250:7:250:13 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:252:7:252:8 | stringstream.cpp:203:24:203:29 | AST only | +| stringstream.cpp:262:32:262:34 | stringstream.cpp:257:24:257:29 | AST only | +| stringstream.cpp:264:7:264:8 | stringstream.cpp:257:24:257:29 | AST only | +| stringstream.cpp:266:62:266:66 | stringstream.cpp:266:41:266:46 | AST only | +| stringstream.cpp:267:7:267:9 | stringstream.cpp:266:41:266:46 | AST only | +| swap1.cpp:78:12:78:16 | swap1.cpp:69:23:69:23 | AST only | +| swap1.cpp:87:13:87:17 | swap1.cpp:82:16:82:21 | AST only | +| swap1.cpp:88:13:88:17 | swap1.cpp:81:27:81:28 | AST only | +| swap1.cpp:102:12:102:16 | swap1.cpp:93:23:93:23 | AST only | +| swap1.cpp:115:18:115:22 | swap1.cpp:108:23:108:31 | AST only | +| swap1.cpp:129:12:129:16 | swap1.cpp:120:23:120:23 | AST only | +| swap1.cpp:144:12:144:16 | swap1.cpp:135:23:135:23 | AST only | +| swap2.cpp:78:12:78:16 | swap2.cpp:69:23:69:23 | AST only | +| swap2.cpp:88:13:88:17 | swap2.cpp:81:27:81:28 | AST only | +| swap2.cpp:102:12:102:16 | swap2.cpp:93:23:93:23 | AST only | +| swap2.cpp:115:18:115:22 | swap2.cpp:108:23:108:31 | AST only | +| swap2.cpp:129:12:129:16 | swap2.cpp:120:23:120:23 | AST only | +| swap2.cpp:144:12:144:16 | swap2.cpp:135:23:135:23 | AST only | | taint.cpp:41:7:41:13 | taint.cpp:35:12:35:17 | AST only | | taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only | | taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only | -| taint.cpp:89:11:89:11 | taint.cpp:71:22:71:27 | AST only | -| taint.cpp:90:11:90:11 | taint.cpp:72:7:72:12 | AST only | -| taint.cpp:91:11:91:11 | taint.cpp:77:7:77:12 | AST only | -| taint.cpp:93:11:93:11 | taint.cpp:71:22:71:27 | AST only | -| taint.cpp:94:11:94:11 | taint.cpp:72:7:72:12 | AST only | -| taint.cpp:109:7:109:13 | taint.cpp:105:12:105:17 | IR only | -| taint.cpp:110:7:110:13 | taint.cpp:105:12:105:17 | IR only | -| taint.cpp:111:7:111:13 | taint.cpp:106:12:106:17 | IR only | -| taint.cpp:112:7:112:13 | taint.cpp:106:12:106:17 | IR only | -| taint.cpp:130:7:130:9 | taint.cpp:127:8:127:13 | IR only | | taint.cpp:137:7:137:9 | taint.cpp:120:11:120:16 | AST only | -| taint.cpp:173:8:173:13 | taint.cpp:164:19:164:24 | AST only | | taint.cpp:195:7:195:7 | taint.cpp:192:23:192:28 | AST only | | taint.cpp:195:7:195:7 | taint.cpp:193:6:193:6 | AST only | | taint.cpp:236:3:236:6 | taint.cpp:223:10:223:15 | AST only | -| taint.cpp:261:7:261:7 | taint.cpp:258:7:258:12 | AST only | -| taint.cpp:351:7:351:7 | taint.cpp:330:6:330:11 | AST only | -| taint.cpp:352:7:352:7 | taint.cpp:330:6:330:11 | AST only | | taint.cpp:372:7:372:7 | taint.cpp:365:24:365:29 | AST only | | taint.cpp:374:7:374:7 | taint.cpp:365:24:365:29 | AST only | | taint.cpp:391:7:391:7 | taint.cpp:385:27:385:32 | AST only | -| taint.cpp:423:7:423:7 | taint.cpp:422:14:422:19 | AST only | -| taint.cpp:424:9:424:17 | taint.cpp:422:14:422:19 | AST only | | taint.cpp:429:7:429:7 | taint.cpp:428:13:428:18 | IR only | -| taint.cpp:438:7:438:7 | taint.cpp:437:15:437:20 | AST only | -| taint.cpp:439:10:439:18 | taint.cpp:437:15:437:20 | AST only | -| taint.cpp:446:7:446:7 | taint.cpp:445:14:445:28 | AST only | +| taint.cpp:431:9:431:17 | taint.cpp:428:13:428:18 | IR only | | taint.cpp:447:9:447:17 | taint.cpp:445:14:445:28 | AST only | -| taint.cpp:471:7:471:7 | taint.cpp:462:6:462:11 | AST only | +| vector.cpp:24:8:24:11 | vector.cpp:16:43:16:49 | IR only | +| vector.cpp:52:7:52:8 | vector.cpp:51:10:51:15 | AST only | +| vector.cpp:53:9:53:9 | vector.cpp:51:10:51:15 | AST only | +| vector.cpp:54:9:54:9 | vector.cpp:51:10:51:15 | AST only | +| vector.cpp:55:9:55:9 | vector.cpp:51:10:51:15 | AST only | +| vector.cpp:58:7:58:8 | vector.cpp:51:10:51:15 | AST only | +| vector.cpp:59:9:59:9 | vector.cpp:51:10:51:15 | AST only | +| vector.cpp:60:9:60:9 | vector.cpp:51:10:51:15 | AST only | +| vector.cpp:61:9:61:9 | vector.cpp:51:10:51:15 | AST only | +| vector.cpp:64:7:64:8 | vector.cpp:63:10:63:15 | AST only | +| vector.cpp:65:9:65:9 | vector.cpp:63:10:63:15 | AST only | +| vector.cpp:66:9:66:9 | vector.cpp:63:10:63:15 | AST only | +| vector.cpp:67:9:67:9 | vector.cpp:63:10:63:15 | AST only | +| vector.cpp:71:10:71:14 | vector.cpp:69:15:69:20 | AST only | +| vector.cpp:72:10:72:13 | vector.cpp:69:15:69:20 | AST only | +| vector.cpp:75:7:75:8 | vector.cpp:74:17:74:22 | AST only | +| vector.cpp:76:7:76:18 | vector.cpp:74:17:74:22 | AST only | +| vector.cpp:84:10:84:14 | vector.cpp:81:17:81:22 | AST only | +| vector.cpp:85:10:85:13 | vector.cpp:81:17:81:22 | AST only | +| vector.cpp:97:7:97:8 | vector.cpp:96:13:96:18 | AST only | +| vector.cpp:98:10:98:11 | vector.cpp:96:13:96:18 | AST only | +| vector.cpp:99:10:99:11 | vector.cpp:96:13:96:18 | AST only | +| vector.cpp:100:10:100:11 | vector.cpp:96:13:96:18 | AST only | +| vector.cpp:171:13:171:13 | vector.cpp:170:14:170:19 | AST only | +| vector.cpp:180:13:180:13 | vector.cpp:179:14:179:19 | AST only | +| vector.cpp:201:13:201:13 | vector.cpp:200:14:200:19 | AST only | +| vector.cpp:261:8:261:9 | vector.cpp:239:15:239:20 | AST only | +| vector.cpp:286:10:286:13 | vector.cpp:284:15:284:20 | AST only | +| vector.cpp:287:7:287:18 | vector.cpp:284:15:284:20 | AST only | +| vector.cpp:290:7:290:8 | vector.cpp:289:17:289:30 | AST only | +| vector.cpp:291:10:291:13 | vector.cpp:289:17:289:30 | AST only | +| vector.cpp:292:7:292:18 | vector.cpp:289:17:289:30 | AST only | +| vector.cpp:308:9:308:14 | vector.cpp:303:14:303:19 | AST only | +| vector.cpp:311:9:311:14 | vector.cpp:303:14:303:19 | AST only | +| vector.cpp:342:7:342:8 | vector.cpp:341:8:341:13 | AST only | +| vector.cpp:347:7:347:8 | vector.cpp:345:9:345:14 | AST only | +| vector.cpp:357:7:357:8 | vector.cpp:330:10:330:15 | AST only | +| vector.cpp:361:7:361:8 | vector.cpp:360:8:360:13 | AST only | +| vector.cpp:363:7:363:8 | vector.cpp:360:8:360:13 | AST only | +| vector.cpp:367:7:367:8 | vector.cpp:366:8:366:13 | AST only | +| vector.cpp:369:7:369:8 | vector.cpp:366:8:366:13 | AST only | +| vector.cpp:374:8:374:9 | vector.cpp:373:9:373:14 | AST only | +| vector.cpp:379:7:379:8 | vector.cpp:373:9:373:14 | AST only | +| vector.cpp:383:7:383:8 | vector.cpp:382:8:382:13 | AST only | +| vector.cpp:385:7:385:8 | vector.cpp:382:8:382:13 | AST only | +| vector.cpp:392:7:392:8 | vector.cpp:330:10:330:15 | AST only | +| vector.cpp:392:7:392:8 | vector.cpp:389:8:389:13 | AST only | +| vector.cpp:400:7:400:9 | vector.cpp:399:38:399:43 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected index bb52de0d7c51..29e6f4f7635b 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected @@ -1,11 +1,348 @@ -| format.cpp:157:7:157:22 | (int)... | format.cpp:147:12:147:25 | call to source | +| arrayassignment.cpp:16:7:16:7 | x | arrayassignment.cpp:14:9:14:14 | call to source | +| arrayassignment.cpp:17:7:17:10 | * ... | arrayassignment.cpp:14:9:14:14 | call to source | +| arrayassignment.cpp:18:7:18:11 | * ... | arrayassignment.cpp:14:9:14:14 | call to source | +| arrayassignment.cpp:19:7:19:9 | (reference dereference) | arrayassignment.cpp:14:9:14:14 | call to source | +| arrayassignment.cpp:31:7:31:7 | x | arrayassignment.cpp:29:8:29:13 | call to source | +| arrayassignment.cpp:32:7:32:10 | * ... | arrayassignment.cpp:29:8:29:13 | call to source | +| arrayassignment.cpp:33:7:33:9 | (reference dereference) | arrayassignment.cpp:29:8:29:13 | call to source | +| arrayassignment.cpp:34:7:34:10 | (reference dereference) | arrayassignment.cpp:29:8:29:13 | call to source | +| arrayassignment.cpp:56:7:56:8 | mi | arrayassignment.cpp:54:9:54:14 | call to source | +| arrayassignment.cpp:57:10:57:15 | (reference dereference) | arrayassignment.cpp:54:9:54:14 | call to source | +| arrayassignment.cpp:66:7:66:8 | mi | arrayassignment.cpp:64:13:64:18 | call to source | +| arrayassignment.cpp:67:10:67:15 | (reference dereference) | arrayassignment.cpp:64:13:64:18 | call to source | +| arrayassignment.cpp:101:7:101:18 | access to array | arrayassignment.cpp:99:17:99:22 | call to source | +| arrayassignment.cpp:135:7:135:10 | (reference dereference) | arrayassignment.cpp:134:9:134:14 | call to source | +| arrayassignment.cpp:136:7:136:13 | access to array | arrayassignment.cpp:134:9:134:14 | call to source | +| arrayassignment.cpp:140:7:140:11 | * ... | arrayassignment.cpp:139:10:139:15 | call to source | +| arrayassignment.cpp:141:7:141:13 | access to array | arrayassignment.cpp:139:10:139:15 | call to source | +| arrayassignment.cpp:145:7:145:13 | access to array | arrayassignment.cpp:144:12:144:17 | call to source | +| arrayassignment.cpp:146:7:146:13 | access to array | arrayassignment.cpp:144:12:144:17 | call to source | +| copyableclass.cpp:40:8:40:9 | s1 | copyableclass.cpp:34:22:34:27 | call to source | +| copyableclass.cpp:41:8:41:9 | s2 | copyableclass.cpp:35:24:35:29 | call to source | +| copyableclass.cpp:42:8:42:9 | s3 | copyableclass.cpp:34:22:34:27 | call to source | +| copyableclass.cpp:43:8:43:9 | s4 | copyableclass.cpp:38:8:38:13 | call to source | +| copyableclass.cpp:65:8:65:9 | s1 | copyableclass.cpp:60:40:60:45 | call to source | +| copyableclass.cpp:66:8:66:9 | s2 | copyableclass.cpp:63:24:63:29 | call to source | +| copyableclass.cpp:67:11:67:21 | (reference dereference) | copyableclass.cpp:67:13:67:18 | call to source | +| copyableclass_declonly.cpp:40:8:40:9 | s1 | copyableclass_declonly.cpp:34:30:34:35 | call to source | +| copyableclass_declonly.cpp:41:8:41:9 | s2 | copyableclass_declonly.cpp:35:32:35:37 | call to source | +| copyableclass_declonly.cpp:43:8:43:9 | s4 | copyableclass_declonly.cpp:38:8:38:13 | call to source | +| copyableclass_declonly.cpp:65:8:65:9 | s1 | copyableclass_declonly.cpp:60:56:60:61 | call to source | +| copyableclass_declonly.cpp:66:8:66:9 | s2 | copyableclass_declonly.cpp:63:32:63:37 | call to source | +| format.cpp:57:8:57:13 | Argument 0 indirection | format.cpp:56:36:56:49 | call to source | +| format.cpp:62:8:62:13 | Argument 0 indirection | format.cpp:61:30:61:43 | call to source | +| format.cpp:67:8:67:13 | Argument 0 indirection | format.cpp:66:52:66:65 | call to source | +| format.cpp:72:8:72:13 | Argument 0 indirection | format.cpp:71:42:71:55 | call to source | +| format.cpp:83:8:83:13 | Argument 0 indirection | format.cpp:82:36:82:41 | call to source | +| format.cpp:88:8:88:13 | Argument 0 indirection | format.cpp:87:38:87:43 | call to source | +| format.cpp:94:8:94:13 | Argument 0 indirection | format.cpp:93:36:93:49 | call to source | +| format.cpp:100:8:100:13 | Argument 0 indirection | format.cpp:99:30:99:43 | call to source | +| format.cpp:105:8:105:13 | Argument 0 indirection | format.cpp:104:31:104:45 | call to source | +| format.cpp:110:8:110:14 | Argument 0 indirection | format.cpp:109:38:109:52 | call to source | +| format.cpp:115:8:115:13 | Argument 0 indirection | format.cpp:114:37:114:50 | call to source | | format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source | | format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source | -| stl.cpp:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source | -| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | +| map.cpp:29:9:29:13 | first | map.cpp:28:12:28:17 | call to source | +| map.cpp:35:9:35:14 | second | map.cpp:33:13:33:18 | call to source | +| map.cpp:44:9:44:13 | first | map.cpp:43:30:43:35 | call to source | +| map.cpp:49:9:49:13 | first | map.cpp:48:37:48:42 | call to source | +| map.cpp:50:9:50:14 | second | map.cpp:48:37:48:42 | call to source | +| map.cpp:51:7:51:7 | f | map.cpp:48:37:48:42 | call to source | +| map.cpp:54:9:54:13 | first | map.cpp:48:37:48:42 | call to source | +| map.cpp:55:9:55:14 | second | map.cpp:48:37:48:42 | call to source | +| map.cpp:56:7:56:7 | g | map.cpp:48:37:48:42 | call to source | +| map.cpp:60:9:60:13 | first | map.cpp:48:37:48:42 | call to source | +| map.cpp:61:9:61:14 | second | map.cpp:48:37:48:42 | call to source | +| map.cpp:62:7:62:7 | h | map.cpp:48:37:48:42 | call to source | +| map.cpp:70:9:70:13 | first | map.cpp:65:37:65:42 | call to source | +| map.cpp:71:9:71:14 | second | map.cpp:65:37:65:42 | call to source | +| map.cpp:72:7:72:7 | i | map.cpp:65:37:65:42 | call to source | +| map.cpp:73:9:73:13 | first | map.cpp:65:37:65:42 | call to source | +| map.cpp:74:9:74:14 | second | map.cpp:65:37:65:42 | call to source | +| map.cpp:75:7:75:7 | j | map.cpp:65:37:65:42 | call to source | +| map.cpp:76:9:76:13 | first | map.cpp:66:37:66:42 | call to source | +| map.cpp:77:9:77:14 | second | map.cpp:66:37:66:42 | call to source | +| map.cpp:78:7:78:7 | k | map.cpp:66:37:66:42 | call to source | +| map.cpp:79:9:79:13 | first | map.cpp:66:37:66:42 | call to source | +| map.cpp:80:9:80:14 | second | map.cpp:66:37:66:42 | call to source | +| map.cpp:81:7:81:7 | l | map.cpp:66:37:66:42 | call to source | +| map.cpp:89:7:89:32 | call to pair | map.cpp:89:24:89:29 | call to source | +| map.cpp:90:34:90:38 | first | map.cpp:90:24:90:29 | call to source | +| map.cpp:91:34:91:39 | second | map.cpp:91:24:91:29 | call to source | +| map.cpp:108:7:108:54 | call to iterator | map.cpp:108:39:108:44 | call to source | +| map.cpp:110:10:110:15 | call to insert | map.cpp:110:62:110:67 | call to source | +| map.cpp:111:7:111:48 | call to iterator | map.cpp:111:34:111:39 | call to source | +| map.cpp:112:10:112:25 | call to insert_or_assign | map.cpp:112:46:112:51 | call to source | +| map.cpp:120:10:120:13 | call to find | map.cpp:108:39:108:44 | call to source | +| map.cpp:122:10:122:13 | call to find | map.cpp:110:62:110:67 | call to source | +| map.cpp:126:10:126:13 | call to find | map.cpp:108:39:108:44 | call to source | +| map.cpp:128:10:128:13 | call to find | map.cpp:110:62:110:67 | call to source | +| map.cpp:142:10:142:13 | call to find | map.cpp:108:39:108:44 | call to source | +| map.cpp:154:8:154:10 | call to pair | map.cpp:108:39:108:44 | call to source | +| map.cpp:155:12:155:16 | first | map.cpp:108:39:108:44 | call to source | +| map.cpp:156:12:156:17 | second | map.cpp:108:39:108:44 | call to source | +| map.cpp:161:12:161:16 | first | map.cpp:108:39:108:44 | call to source | +| map.cpp:162:12:162:17 | second | map.cpp:108:39:108:44 | call to source | +| map.cpp:168:7:168:27 | ... = ... | map.cpp:168:20:168:25 | call to source | +| map.cpp:170:7:170:30 | ... = ... | map.cpp:170:23:170:28 | call to source | +| map.cpp:182:10:182:20 | call to lower_bound | map.cpp:108:39:108:44 | call to source | +| map.cpp:183:10:183:20 | call to upper_bound | map.cpp:108:39:108:44 | call to source | +| map.cpp:184:7:184:31 | call to iterator | map.cpp:108:39:108:44 | call to source | +| map.cpp:185:7:185:32 | call to iterator | map.cpp:108:39:108:44 | call to source | +| map.cpp:186:10:186:20 | call to upper_bound | map.cpp:108:39:108:44 | call to source | +| map.cpp:187:7:187:32 | call to iterator | map.cpp:108:39:108:44 | call to source | +| map.cpp:226:11:226:15 | call to erase | map.cpp:223:49:223:54 | call to source | +| map.cpp:226:11:226:15 | call to erase | map.cpp:224:49:224:54 | call to source | +| map.cpp:235:7:235:40 | call to iterator | map.cpp:235:26:235:31 | call to source | +| map.cpp:239:11:239:22 | call to emplace_hint | map.cpp:239:44:239:49 | call to source | +| map.cpp:246:7:246:44 | call to iterator | map.cpp:246:30:246:35 | call to source | +| map.cpp:250:11:250:21 | call to try_emplace | map.cpp:250:43:250:48 | call to source | +| map.cpp:260:7:260:54 | call to iterator | map.cpp:260:39:260:44 | call to source | +| map.cpp:262:10:262:15 | call to insert | map.cpp:262:62:262:67 | call to source | +| map.cpp:263:7:263:48 | call to iterator | map.cpp:263:34:263:39 | call to source | +| map.cpp:264:10:264:25 | call to insert_or_assign | map.cpp:264:46:264:51 | call to source | +| map.cpp:272:10:272:13 | call to find | map.cpp:260:39:260:44 | call to source | +| map.cpp:274:10:274:13 | call to find | map.cpp:262:62:262:67 | call to source | +| map.cpp:278:10:278:13 | call to find | map.cpp:260:39:260:44 | call to source | +| map.cpp:280:10:280:13 | call to find | map.cpp:262:62:262:67 | call to source | +| map.cpp:294:10:294:13 | call to find | map.cpp:260:39:260:44 | call to source | +| map.cpp:306:8:306:10 | call to pair | map.cpp:260:39:260:44 | call to source | +| map.cpp:307:12:307:16 | first | map.cpp:260:39:260:44 | call to source | +| map.cpp:308:12:308:17 | second | map.cpp:260:39:260:44 | call to source | +| map.cpp:313:12:313:16 | first | map.cpp:260:39:260:44 | call to source | +| map.cpp:314:12:314:17 | second | map.cpp:260:39:260:44 | call to source | +| map.cpp:320:7:320:27 | ... = ... | map.cpp:320:20:320:25 | call to source | +| map.cpp:322:7:322:30 | ... = ... | map.cpp:322:23:322:28 | call to source | +| map.cpp:334:7:334:31 | call to iterator | map.cpp:260:39:260:44 | call to source | +| map.cpp:335:7:335:32 | call to iterator | map.cpp:260:39:260:44 | call to source | +| map.cpp:336:7:336:32 | call to iterator | map.cpp:260:39:260:44 | call to source | +| map.cpp:375:11:375:15 | call to erase | map.cpp:372:49:372:54 | call to source | +| map.cpp:375:11:375:15 | call to erase | map.cpp:373:49:373:54 | call to source | +| map.cpp:384:7:384:40 | call to iterator | map.cpp:384:26:384:31 | call to source | +| map.cpp:388:11:388:22 | call to emplace_hint | map.cpp:388:44:388:49 | call to source | +| map.cpp:396:7:396:44 | call to iterator | map.cpp:396:30:396:35 | call to source | +| map.cpp:397:40:397:45 | second | map.cpp:397:30:397:35 | call to source | +| map.cpp:401:11:401:21 | call to try_emplace | map.cpp:401:43:401:48 | call to source | +| map.cpp:416:7:416:41 | call to pair | map.cpp:416:30:416:35 | call to source | +| map.cpp:419:7:419:41 | call to pair | map.cpp:419:33:419:38 | call to source | +| map.cpp:431:7:431:67 | call to iterator | map.cpp:431:52:431:57 | call to source | +| map.cpp:433:11:433:22 | call to emplace_hint | map.cpp:431:52:431:57 | call to source | +| movableclass.cpp:44:8:44:9 | s1 | movableclass.cpp:39:21:39:26 | call to source | +| movableclass.cpp:45:8:45:9 | s2 | movableclass.cpp:40:23:40:28 | call to source | +| movableclass.cpp:46:8:46:9 | s3 | movableclass.cpp:42:8:42:13 | call to source | +| movableclass.cpp:54:8:54:9 | s1 | movableclass.cpp:50:38:50:43 | call to source | +| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source | +| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source | +| movableclass.cpp:65:11:65:21 | (reference dereference) | movableclass.cpp:65:13:65:18 | call to source | +| set.cpp:20:7:20:31 | call to iterator | set.cpp:20:17:20:22 | call to source | +| set.cpp:22:10:22:15 | call to insert | set.cpp:22:29:22:34 | call to source | +| set.cpp:32:10:32:13 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:34:10:34:13 | call to find | set.cpp:22:29:22:34 | call to source | +| set.cpp:36:10:36:13 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:50:10:50:13 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:51:11:51:14 | call to find | set.cpp:20:17:20:22 | call to source | +| set.cpp:61:8:61:8 | call to operator* | set.cpp:20:17:20:22 | call to source | +| set.cpp:61:8:61:11 | (reference dereference) | set.cpp:20:17:20:22 | call to source | +| set.cpp:69:11:69:21 | call to lower_bound | set.cpp:67:13:67:18 | call to source | +| set.cpp:70:11:70:21 | call to upper_bound | set.cpp:67:13:67:18 | call to source | +| set.cpp:71:7:71:32 | call to iterator | set.cpp:67:13:67:18 | call to source | +| set.cpp:72:7:72:33 | call to iterator | set.cpp:67:13:67:18 | call to source | +| set.cpp:111:11:111:15 | call to erase | set.cpp:108:13:108:18 | call to source | +| set.cpp:111:11:111:15 | call to erase | set.cpp:109:13:109:18 | call to source | +| set.cpp:120:7:120:33 | call to iterator | set.cpp:120:19:120:24 | call to source | +| set.cpp:124:11:124:22 | call to emplace_hint | set.cpp:124:37:124:42 | call to source | +| set.cpp:134:7:134:31 | call to iterator | set.cpp:134:17:134:22 | call to source | +| set.cpp:136:10:136:15 | call to insert | set.cpp:136:29:136:34 | call to source | +| set.cpp:146:10:146:13 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:148:10:148:13 | call to find | set.cpp:136:29:136:34 | call to source | +| set.cpp:150:10:150:13 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:164:10:164:13 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:165:11:165:14 | call to find | set.cpp:134:17:134:22 | call to source | +| set.cpp:175:8:175:8 | call to operator* | set.cpp:134:17:134:22 | call to source | +| set.cpp:175:8:175:11 | (reference dereference) | set.cpp:134:17:134:22 | call to source | +| set.cpp:183:7:183:32 | call to iterator | set.cpp:181:13:181:18 | call to source | +| set.cpp:184:7:184:33 | call to iterator | set.cpp:181:13:181:18 | call to source | +| set.cpp:223:11:223:15 | call to erase | set.cpp:220:13:220:18 | call to source | +| set.cpp:223:11:223:15 | call to erase | set.cpp:221:13:221:18 | call to source | +| set.cpp:232:7:232:33 | call to iterator | set.cpp:232:19:232:24 | call to source | +| set.cpp:236:11:236:22 | call to emplace_hint | set.cpp:236:37:236:42 | call to source | +| smart_pointer.cpp:13:10:13:10 | Argument 0 indirection | smart_pointer.cpp:11:52:11:57 | call to source | +| smart_pointer.cpp:25:10:25:10 | Argument 0 indirection | smart_pointer.cpp:23:52:23:57 | call to source | +| smart_pointer.cpp:52:12:52:14 | call to get | smart_pointer.cpp:51:52:51:57 | call to source | +| smart_pointer.cpp:57:12:57:14 | call to get | smart_pointer.cpp:56:52:56:57 | call to source | +| standalone_iterators.cpp:40:10:40:10 | call to operator* | standalone_iterators.cpp:39:45:39:51 | source1 | +| standalone_iterators.cpp:46:10:46:10 | call to operator* | standalone_iterators.cpp:45:39:45:45 | source1 | +| string.cpp:29:7:29:7 | a | string.cpp:25:12:25:17 | call to source | +| string.cpp:31:7:31:7 | Argument 0 indirection | string.cpp:27:16:27:21 | call to source | +| string.cpp:56:7:56:8 | cs | string.cpp:51:19:51:24 | call to source | +| string.cpp:57:7:57:8 | Argument 0 indirection | string.cpp:51:19:51:24 | call to source | +| string.cpp:71:7:71:8 | Argument 0 indirection | string.cpp:62:19:62:24 | call to source | +| string.cpp:93:8:93:9 | Argument 0 indirection | string.cpp:88:18:88:23 | call to source | +| string.cpp:94:8:94:9 | Argument 0 indirection | string.cpp:89:20:89:25 | call to source | +| string.cpp:95:8:95:9 | Argument 0 indirection | string.cpp:91:8:91:13 | call to source | +| string.cpp:114:8:114:9 | Argument 0 indirection | string.cpp:110:32:110:37 | call to source | +| string.cpp:115:8:115:9 | Argument 0 indirection | string.cpp:112:20:112:25 | call to source | +| string.cpp:122:8:122:8 | c | string.cpp:120:16:120:21 | call to source | +| string.cpp:126:8:126:8 | call to operator* | string.cpp:120:16:120:21 | call to source | +| string.cpp:126:8:126:11 | (reference dereference) | string.cpp:120:16:120:21 | call to source | +| string.cpp:130:8:130:8 | (reference dereference) | string.cpp:120:16:120:21 | call to source | +| string.cpp:130:8:130:8 | c | string.cpp:120:16:120:21 | call to source | +| string.cpp:135:8:135:8 | (reference dereference) | string.cpp:133:28:133:33 | call to source | +| string.cpp:135:8:135:8 | c | string.cpp:133:28:133:33 | call to source | +| string.cpp:145:11:145:11 | call to operator+ | string.cpp:142:18:142:23 | call to source | +| string.cpp:146:11:146:11 | call to operator+ | string.cpp:142:18:142:23 | call to source | +| string.cpp:147:11:147:11 | call to operator+ | string.cpp:142:18:142:23 | call to source | +| string.cpp:150:11:150:11 | call to operator+ | string.cpp:150:13:150:18 | call to source | +| string.cpp:159:8:159:9 | Argument 0 indirection | string.cpp:155:18:155:23 | call to source | +| string.cpp:163:8:163:9 | Argument 0 indirection | string.cpp:155:18:155:23 | call to source | +| string.cpp:168:8:168:9 | Argument 0 indirection | string.cpp:166:14:166:19 | call to source | +| string.cpp:172:8:172:9 | Argument 0 indirection | string.cpp:155:18:155:23 | call to source | +| string.cpp:177:8:177:9 | Argument 0 indirection | string.cpp:175:13:175:18 | call to source | +| string.cpp:185:8:185:10 | Argument 0 indirection | string.cpp:182:12:182:26 | call to source | +| string.cpp:200:7:200:8 | Argument 0 indirection | string.cpp:191:17:191:22 | call to source | +| string.cpp:203:7:203:8 | Argument 0 indirection | string.cpp:192:11:192:25 | call to source | +| string.cpp:206:7:206:8 | Argument 0 indirection | string.cpp:194:17:194:22 | call to source | +| string.cpp:221:7:221:8 | Argument 0 indirection | string.cpp:211:17:211:22 | call to source | +| string.cpp:225:7:225:8 | Argument 0 indirection | string.cpp:211:17:211:22 | call to source | +| string.cpp:229:7:229:8 | Argument 0 indirection | string.cpp:212:11:212:25 | call to source | +| string.cpp:244:7:244:8 | Argument 0 indirection | string.cpp:234:17:234:22 | call to source | +| string.cpp:248:7:248:8 | Argument 0 indirection | string.cpp:234:17:234:22 | call to source | +| string.cpp:252:7:252:8 | Argument 0 indirection | string.cpp:235:11:235:25 | call to source | +| string.cpp:265:7:265:8 | Argument 0 indirection | string.cpp:259:17:259:22 | call to source | +| string.cpp:275:7:275:8 | Argument 0 indirection | string.cpp:270:17:270:22 | call to source | +| string.cpp:277:7:277:8 | Argument 0 indirection | string.cpp:272:17:272:22 | call to source | +| string.cpp:282:7:282:8 | Argument 0 indirection | string.cpp:270:17:270:22 | call to source | +| string.cpp:283:7:283:8 | Argument 0 indirection | string.cpp:270:17:270:22 | call to source | +| string.cpp:284:7:284:8 | Argument 0 indirection | string.cpp:272:17:272:22 | call to source | +| string.cpp:285:7:285:8 | Argument 0 indirection | string.cpp:272:17:272:22 | call to source | +| string.cpp:293:7:293:8 | Argument 0 indirection | string.cpp:289:17:289:22 | call to source | +| string.cpp:294:7:294:8 | Argument 0 indirection | string.cpp:290:17:290:22 | call to source | +| string.cpp:295:7:295:8 | Argument 0 indirection | string.cpp:291:17:291:22 | call to source | +| string.cpp:301:7:301:8 | Argument 0 indirection | string.cpp:289:17:289:22 | call to source | +| string.cpp:303:7:303:8 | Argument 0 indirection | string.cpp:291:17:291:22 | call to source | +| string.cpp:323:9:323:14 | call to substr | string.cpp:320:16:320:21 | call to source | +| string.cpp:364:8:364:9 | Argument 0 indirection | string.cpp:358:18:358:23 | call to source | +| string.cpp:382:8:382:8 | call to operator* | string.cpp:374:18:374:23 | call to source | +| string.cpp:382:8:382:14 | (reference dereference) | string.cpp:374:18:374:23 | call to source | +| string.cpp:383:13:383:13 | call to operator[] | string.cpp:374:18:374:23 | call to source | +| string.cpp:383:13:383:15 | (reference dereference) | string.cpp:374:18:374:23 | call to source | +| string.cpp:404:8:404:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:404:8:404:11 | (reference dereference) | string.cpp:389:18:389:23 | call to source | +| string.cpp:407:8:407:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:407:8:407:11 | (reference dereference) | string.cpp:389:18:389:23 | call to source | +| string.cpp:415:8:415:8 | call to operator* | string.cpp:389:18:389:23 | call to source | +| string.cpp:415:8:415:11 | (reference dereference) | string.cpp:389:18:389:23 | call to source | +| string.cpp:437:7:437:8 | Argument 0 indirection | string.cpp:431:14:431:19 | call to source | +| string.cpp:450:8:450:8 | Argument 0 indirection | string.cpp:449:32:449:46 | call to source | +| string.cpp:463:8:463:8 | Argument 0 indirection | string.cpp:457:18:457:23 | call to source | +| string.cpp:466:8:466:9 | Argument 0 indirection | string.cpp:457:18:457:23 | call to source | +| string.cpp:479:8:479:8 | Argument 0 indirection | string.cpp:473:18:473:23 | call to source | +| string.cpp:482:8:482:9 | Argument 0 indirection | string.cpp:473:18:473:23 | call to source | +| string.cpp:495:8:495:8 | Argument 0 indirection | string.cpp:489:18:489:23 | call to source | +| string.cpp:498:8:498:9 | Argument 0 indirection | string.cpp:489:18:489:23 | call to source | +| string.cpp:511:7:511:8 | Argument 0 indirection | string.cpp:504:14:504:19 | call to source | +| string.cpp:513:7:513:8 | Argument 0 indirection | string.cpp:504:14:504:19 | call to source | +| string.cpp:542:8:542:8 | Argument 0 indirection | string.cpp:536:20:536:25 | call to source | +| string.cpp:544:8:544:8 | Argument 0 indirection | string.cpp:538:15:538:20 | call to source | +| string.cpp:562:8:562:8 | Argument 0 indirection | string.cpp:556:27:556:32 | call to source | +| string.cpp:564:8:564:8 | Argument 0 indirection | string.cpp:558:18:558:23 | call to source | +| stringstream.cpp:32:11:32:11 | call to operator<< | stringstream.cpp:32:14:32:19 | call to source | +| stringstream.cpp:32:11:32:22 | (reference dereference) | stringstream.cpp:32:14:32:19 | call to source | +| stringstream.cpp:33:20:33:20 | call to operator<< | stringstream.cpp:33:23:33:28 | call to source | +| stringstream.cpp:33:20:33:31 | (reference dereference) | stringstream.cpp:33:23:33:28 | call to source | +| stringstream.cpp:34:23:34:23 | call to operator<< | stringstream.cpp:34:14:34:19 | call to source | +| stringstream.cpp:34:23:34:31 | (reference dereference) | stringstream.cpp:34:14:34:19 | call to source | +| stringstream.cpp:38:7:38:9 | Argument 0 indirection | stringstream.cpp:32:14:32:19 | call to source | +| stringstream.cpp:40:7:40:9 | Argument 0 indirection | stringstream.cpp:34:14:34:19 | call to source | +| stringstream.cpp:43:11:43:13 | call to str | stringstream.cpp:32:14:32:19 | call to source | +| stringstream.cpp:45:11:45:13 | call to str | stringstream.cpp:34:14:34:19 | call to source | +| stringstream.cpp:52:7:52:9 | Argument 0 indirection | stringstream.cpp:49:10:49:15 | call to source | +| stringstream.cpp:53:7:53:9 | Argument 0 indirection | stringstream.cpp:50:10:50:15 | call to source | +| stringstream.cpp:59:7:59:9 | Argument 0 indirection | stringstream.cpp:56:15:56:29 | call to source | +| stringstream.cpp:66:7:66:10 | Argument 0 indirection | stringstream.cpp:63:18:63:23 | call to source | +| stringstream.cpp:81:7:81:9 | Argument 0 indirection | stringstream.cpp:70:32:70:37 | source | +| stringstream.cpp:83:11:83:13 | call to str | stringstream.cpp:70:32:70:37 | source | +| stringstream.cpp:85:7:85:8 | v2 | stringstream.cpp:70:32:70:37 | source | +| stringstream.cpp:103:7:103:9 | Argument 0 indirection | stringstream.cpp:91:19:91:24 | call to source | +| stringstream.cpp:105:7:105:9 | Argument 0 indirection | stringstream.cpp:95:44:95:49 | call to source | +| stringstream.cpp:107:7:107:9 | Argument 0 indirection | stringstream.cpp:100:31:100:36 | call to source | +| stringstream.cpp:120:7:120:9 | Argument 0 indirection | stringstream.cpp:113:24:113:29 | call to source | +| stringstream.cpp:121:7:121:9 | Argument 0 indirection | stringstream.cpp:113:24:113:29 | call to source | +| stringstream.cpp:122:7:122:9 | Argument 0 indirection | stringstream.cpp:115:24:115:29 | call to source | +| stringstream.cpp:123:7:123:9 | Argument 0 indirection | stringstream.cpp:115:24:115:29 | call to source | +| stringstream.cpp:143:11:143:11 | call to operator<< | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:143:11:143:22 | (reference dereference) | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:149:7:149:8 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:150:7:150:8 | Argument 0 indirection | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:157:7:157:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:158:7:158:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:168:7:168:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:170:7:170:8 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:172:7:172:9 | call to basic_string | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:175:7:175:20 | ... = ... | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:177:7:177:21 | ... = ... | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:181:7:181:8 | c2 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:183:7:183:8 | c4 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:185:7:185:8 | c6 | stringstream.cpp:143:14:143:19 | call to source | +| stringstream.cpp:197:10:197:12 | call to get | stringstream.cpp:196:18:196:32 | call to source | +| stringstream.cpp:219:7:219:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:220:7:220:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:227:7:227:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:228:7:228:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:231:7:231:8 | call to basic_string | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:239:7:239:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:240:7:240:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:247:7:247:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:248:7:248:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:251:7:251:8 | Argument 0 indirection | stringstream.cpp:203:24:203:29 | call to source | +| stringstream.cpp:263:7:263:8 | call to basic_string | stringstream.cpp:257:24:257:29 | call to source | +| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source | +| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source | +| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source | +| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source | +| structlikeclass.cpp:60:8:60:9 | s1 | structlikeclass.cpp:55:40:55:45 | call to source | +| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source | +| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source | +| swap1.cpp:73:12:73:16 | data1 | swap1.cpp:71:15:71:20 | call to source | +| swap1.cpp:78:12:78:16 | data1 | swap1.cpp:71:15:71:20 | call to source | +| swap1.cpp:79:12:79:16 | data1 | swap1.cpp:71:15:71:20 | call to source | +| swap1.cpp:83:13:83:17 | data1 | swap1.cpp:82:16:82:21 | call to source | +| swap1.cpp:88:13:88:17 | data1 | swap1.cpp:82:16:82:21 | call to source | +| swap1.cpp:97:12:97:16 | data1 | swap1.cpp:95:15:95:20 | call to source | +| swap1.cpp:102:12:102:16 | data1 | swap1.cpp:95:15:95:20 | call to source | +| swap1.cpp:103:12:103:16 | data1 | swap1.cpp:95:15:95:20 | call to source | +| swap1.cpp:111:20:111:24 | data1 | swap1.cpp:109:23:109:28 | call to source | +| swap1.cpp:115:18:115:22 | data1 | swap1.cpp:109:23:109:28 | call to source | +| swap1.cpp:124:12:124:16 | data1 | swap1.cpp:122:15:122:20 | call to source | +| swap1.cpp:129:12:129:16 | data1 | swap1.cpp:122:15:122:20 | call to source | +| swap1.cpp:130:12:130:16 | data1 | swap1.cpp:122:15:122:20 | call to source | +| swap1.cpp:139:12:139:16 | data1 | swap1.cpp:137:15:137:20 | call to source | +| swap1.cpp:144:12:144:16 | data1 | swap1.cpp:137:15:137:20 | call to source | +| swap1.cpp:145:12:145:16 | data1 | swap1.cpp:137:15:137:20 | call to source | +| swap2.cpp:73:12:73:16 | data1 | swap2.cpp:71:15:71:20 | call to source | +| swap2.cpp:78:12:78:16 | data1 | swap2.cpp:71:15:71:20 | call to source | +| swap2.cpp:79:12:79:16 | data1 | swap2.cpp:71:15:71:20 | call to source | +| swap2.cpp:83:13:83:17 | data1 | swap2.cpp:82:16:82:21 | call to source | +| swap2.cpp:88:13:88:17 | data1 | swap2.cpp:82:16:82:21 | call to source | +| swap2.cpp:97:12:97:16 | data1 | swap2.cpp:95:15:95:20 | call to source | +| swap2.cpp:102:12:102:16 | data1 | swap2.cpp:95:15:95:20 | call to source | +| swap2.cpp:103:12:103:16 | data1 | swap2.cpp:95:15:95:20 | call to source | +| swap2.cpp:111:20:111:24 | data1 | swap2.cpp:109:23:109:28 | call to source | +| swap2.cpp:115:18:115:22 | data1 | swap2.cpp:109:23:109:28 | call to source | +| swap2.cpp:124:12:124:16 | data1 | swap2.cpp:122:15:122:20 | call to source | +| swap2.cpp:129:12:129:16 | data1 | swap2.cpp:122:15:122:20 | call to source | +| swap2.cpp:130:12:130:16 | data1 | swap2.cpp:122:15:122:20 | call to source | +| swap2.cpp:139:12:139:16 | data1 | swap2.cpp:137:15:137:20 | call to source | +| swap2.cpp:144:12:144:16 | data1 | swap2.cpp:137:15:137:20 | call to source | +| swap2.cpp:145:12:145:16 | data1 | swap2.cpp:137:15:137:20 | call to source | | taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 | | taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source | | taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source | +| taint.cpp:89:11:89:11 | b | taint.cpp:71:22:71:27 | call to source | +| taint.cpp:90:11:90:11 | c | taint.cpp:72:7:72:12 | call to source | +| taint.cpp:91:11:91:11 | d | taint.cpp:77:7:77:12 | call to source | +| taint.cpp:93:11:93:11 | b | taint.cpp:71:22:71:27 | call to source | +| taint.cpp:94:11:94:11 | c | taint.cpp:72:7:72:12 | call to source | | taint.cpp:109:7:109:13 | access to array | taint.cpp:105:12:105:17 | call to source | | taint.cpp:110:7:110:13 | access to array | taint.cpp:105:12:105:17 | call to source | | taint.cpp:111:7:111:13 | access to array | taint.cpp:106:12:106:17 | call to source | @@ -16,6 +353,7 @@ | taint.cpp:151:7:151:12 | call to select | taint.cpp:151:20:151:25 | call to source | | taint.cpp:167:8:167:13 | call to source | taint.cpp:167:8:167:13 | call to source | | taint.cpp:168:8:168:14 | tainted | taint.cpp:164:19:164:24 | call to source | +| taint.cpp:173:8:173:13 | Argument 0 indirection | taint.cpp:164:19:164:24 | call to source | | taint.cpp:181:8:181:9 | * ... | taint.cpp:185:11:185:16 | call to source | | taint.cpp:210:7:210:7 | x | taint.cpp:207:6:207:11 | call to source | | taint.cpp:215:7:215:7 | x | taint.cpp:207:6:207:11 | call to source | @@ -25,15 +363,56 @@ | taint.cpp:244:3:244:6 | t | taint.cpp:223:10:223:15 | call to source | | taint.cpp:250:8:250:8 | a | taint.cpp:223:10:223:15 | call to source | | taint.cpp:256:8:256:8 | (reference dereference) | taint.cpp:223:10:223:15 | call to source | +| taint.cpp:261:7:261:7 | w | taint.cpp:258:7:258:12 | call to source | | taint.cpp:280:7:280:7 | t | taint.cpp:275:6:275:11 | call to source | | taint.cpp:289:7:289:7 | t | taint.cpp:275:6:275:11 | call to source | | taint.cpp:290:7:290:7 | x | taint.cpp:275:6:275:11 | call to source | | taint.cpp:291:7:291:7 | y | taint.cpp:275:6:275:11 | call to source | | taint.cpp:337:7:337:7 | t | taint.cpp:330:6:330:11 | call to source | | taint.cpp:350:7:350:7 | t | taint.cpp:330:6:330:11 | call to source | +| taint.cpp:351:7:351:7 | a | taint.cpp:330:6:330:11 | call to source | +| taint.cpp:352:7:352:7 | b | taint.cpp:330:6:330:11 | call to source | +| taint.cpp:353:7:353:7 | c | taint.cpp:330:6:330:11 | call to source | +| taint.cpp:354:7:354:7 | d | taint.cpp:330:6:330:11 | call to source | | taint.cpp:382:7:382:7 | a | taint.cpp:377:23:377:28 | source | | taint.cpp:429:7:429:7 | b | taint.cpp:428:13:428:18 | call to source | | taint.cpp:430:9:430:14 | member | taint.cpp:428:13:428:18 | call to source | | taint.cpp:465:7:465:7 | x | taint.cpp:462:6:462:11 | call to source | | taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source | +| taint.cpp:471:7:471:7 | y | taint.cpp:462:6:462:11 | call to source | | taint.cpp:485:7:485:10 | line | taint.cpp:480:26:480:32 | source1 | +| vector.cpp:20:8:20:8 | x | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:24:8:24:8 | call to operator* | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:24:8:24:11 | (reference dereference) | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:28:8:28:8 | (reference dereference) | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:28:8:28:8 | x | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:33:8:33:8 | (reference dereference) | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:33:8:33:8 | x | vector.cpp:16:43:16:49 | source1 | +| vector.cpp:70:7:70:8 | Argument 0 indirection | vector.cpp:69:15:69:20 | call to source | +| vector.cpp:83:7:83:8 | Argument 0 indirection | vector.cpp:81:17:81:22 | call to source | +| vector.cpp:109:7:109:8 | Argument 0 indirection | vector.cpp:106:15:106:20 | call to source | +| vector.cpp:112:7:112:8 | Argument 0 indirection | vector.cpp:107:15:107:20 | call to source | +| vector.cpp:117:7:117:8 | Argument 0 indirection | vector.cpp:106:15:106:20 | call to source | +| vector.cpp:118:7:118:8 | Argument 0 indirection | vector.cpp:106:15:106:20 | call to source | +| vector.cpp:119:7:119:8 | Argument 0 indirection | vector.cpp:107:15:107:20 | call to source | +| vector.cpp:120:7:120:8 | Argument 0 indirection | vector.cpp:107:15:107:20 | call to source | +| vector.cpp:130:7:130:8 | Argument 0 indirection | vector.cpp:126:15:126:20 | call to source | +| vector.cpp:131:7:131:8 | Argument 0 indirection | vector.cpp:127:15:127:20 | call to source | +| vector.cpp:132:7:132:8 | Argument 0 indirection | vector.cpp:128:15:128:20 | call to source | +| vector.cpp:139:7:139:8 | Argument 0 indirection | vector.cpp:126:15:126:20 | call to source | +| vector.cpp:140:7:140:8 | Argument 0 indirection | vector.cpp:127:15:127:20 | call to source | +| vector.cpp:141:7:141:8 | Argument 0 indirection | vector.cpp:128:15:128:20 | call to source | +| vector.cpp:162:8:162:15 | access to array | vector.cpp:161:14:161:19 | call to source | +| vector.cpp:242:7:242:8 | Argument 0 indirection | vector.cpp:238:17:238:30 | call to source | +| vector.cpp:243:7:243:8 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:258:8:258:9 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:259:8:259:9 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:260:8:260:9 | Argument 0 indirection | vector.cpp:239:15:239:20 | call to source | +| vector.cpp:273:8:273:9 | Argument 0 indirection | vector.cpp:269:18:269:31 | call to source | +| vector.cpp:274:8:274:9 | Argument 0 indirection | vector.cpp:270:18:270:35 | call to source | +| vector.cpp:275:8:275:9 | Argument 0 indirection | vector.cpp:271:18:271:34 | call to source | +| vector.cpp:285:7:285:8 | Argument 0 indirection | vector.cpp:284:15:284:20 | call to source | +| vector.cpp:309:7:309:7 | Argument 0 indirection | vector.cpp:303:14:303:19 | call to source | +| vector.cpp:312:7:312:7 | Argument 0 indirection | vector.cpp:303:14:303:19 | call to source | +| vector.cpp:324:7:324:8 | Argument 0 indirection | vector.cpp:318:15:318:20 | call to source | +| vector.cpp:326:7:326:8 | Argument 0 indirection | vector.cpp:318:15:318:20 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp new file mode 100644 index 000000000000..b5dad8119989 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -0,0 +1,401 @@ + +#include "stl.h" + +using namespace std; + +int source(); + +namespace ns_int +{ + int source(); +} + +void sink(int); +void sink(std::vector &); + +void test_range_based_for_loop_vector(int source1) { + std::vector v(100, source1); + + for(int x : v) { + sink(x); // tainted [NOT DETECTED by IR] + } + + for(std::vector::iterator it = v.begin(); it != v.end(); ++it) { + sink(*it); // tainted [NOT DETECTED by IR] + } + + for(int& x : v) { + sink(x); // tainted [NOT DETECTED by IR] + } + + const std::vector const_v(100, source1); + for(const int& x : const_v) { + sink(x); // tainted [NOT DETECTED by IR] + } +} + +void test_element_taint(int x) { + std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10); + + v1[0] = 0; + v1[1] = 0; + v1[x] = 0; + v1.push_back(1); + sink(v1); + sink(v1[0]); + sink(v1[1]); + sink(v1[x]); + sink(v1.front()); + sink(v1.back()); + + v2[0] = source(); + sink(v2); // tainted + sink(v2[0]); // tainted + sink(v2[1]); // [FALSE POSITIVE] + sink(v2[x]); // potentially tainted + + v3 = v2; + sink(v3); // tainted + sink(v3[0]); // tainted + sink(v3[1]); // [FALSE POSITIVE] + sink(v3[x]); // potentially tainted + + v4[x] = source(); + sink(v4); // tainted + sink(v4[0]); // potentially tainted + sink(v4[1]); // potentially tainted + sink(v4[x]); // tainted + + v5.push_back(source()); + sink(v5); // tainted + sink(v5.front()); // [FALSE POSITIVE] + sink(v5.back()); // tainted + + v6.data()[2] = source(); + sink(v6); // tainted + sink(v6.data()[2]); // tainted + + + { + std::vector::const_iterator it = v7.begin(); + v7.insert(it, source()); + } + sink(v7); // tainted + sink(v7.front()); // tainted + sink(v7.back()); // [FALSE POSITIVE] + + { + const std::vector &v8c = v8; + std::vector::const_iterator it = v8c.begin(); + v8.insert(it, 10, ns_int::source()); + } + sink(v8); // tainted [NOT DETECTED] + sink(v8.front()); // tainted [NOT DETECTED] + sink(v8.back()); + + v9.at(x) = source(); + sink(v9); // tainted + sink(v9.at(0)); // potentially tainted + sink(v9.at(1)); // potentially tainted + sink(v9.at(x)); // tainted +} + +void test_vector_swap() { + std::vector v1(10), v2(10), v3(10), v4(10); + + v1.push_back(source()); + v4.push_back(source()); + + sink(v1); // tainted + sink(v2); + sink(v3); + sink(v4); // tainted + + v1.swap(v2); + v3.swap(v4); + + sink(v1); // [FALSE POSITIVE] + sink(v2); // tainted + sink(v3); // tainted + sink(v4); // [FALSE POSITIVE] +} + +void test_vector_clear() { + std::vector v1(10), v2(10), v3(10), v4(10); + + v1.push_back(source()); + v2.push_back(source()); + v3.push_back(source()); + + sink(v1); // tainted + sink(v2); // tainted + sink(v3); // tainted + sink(v4); + + v1.clear(); + v2 = v2; + v3 = v4; + + sink(v1); // [FALSE POSITIVE] + sink(v2); // tainted + sink(v3); // [FALSE POSITIVE] + sink(v4); +} + +struct MyPair +{ + int a, b; +}; + +struct MyVectorContainer +{ + std::vector vs; +}; + +void test_nested_vectors() +{ + { + int aa[10][20] = {0}; + + sink(aa[0][0]); + aa[0][0] = source(); + sink(aa[0][0]); // tainted + } + + { + std::vector > bb(30); + + bb[0].push_back(0); + sink(bb[0][0]); + bb[0][0] = source(); + sink(bb[0][0]); // tainted + } + + { + std::vector cc[40]; + + cc[0].push_back(0); + sink(cc[0][0]); + cc[0][0] = source(); + sink(cc[0][0]); // tainted + } + + { + std::vector dd; + MyPair mp = {0, 0}; + + dd.push_back(mp); + sink(dd[0].a); + sink(dd[0].b); + dd[0].a = source(); + sink(dd[0].a); // tainted [NOT DETECTED] + sink(dd[0].b); + } + + { + MyVectorContainer ee; + + ee.vs.push_back(0); + sink(ee.vs[0]); + ee.vs[0] = source(); + sink(ee.vs[0]); // tainted + } + + { + std::vector ff; + MyVectorContainer mvc; + + mvc.vs.push_back(0); + ff.push_back(mvc); + sink(ff[0].vs[0]); + ff[0].vs[0] = source(); + sink(ff[0].vs[0]); // tainted [NOT DETECTED] + } +} + +void sink(std::vector::iterator &); + +typedef int myInt; +typedef float myFloat; + +namespace ns_myFloat +{ + myFloat source(); +} + +namespace ns_ci_ptr +{ + const int *source(); +} + +void sink(std::vector &); +void sink(std::vector &); + +void test_vector_assign() { + std::vector v1, v2, v3; + + v1.assign(100, 0); + v2.assign(100, ns_int::source()); + v3.push_back(source()); + + sink(v1); + sink(v2); // tainted + sink(v3); // tainted + + { + std::vector v4, v5, v6; + std::vector::iterator i1, i2; + + v4.assign(v1.begin(), v1.end()); + v5.assign(v3.begin(), v3.end()); + i1 = v3.begin(); + i1++; + i2 = i1; + i2++; + v6.assign(i1, i2); + + sink(v4); + sink(v5); // tainted + sink(i1); // tainted + sink(i2); // tainted + sink(v6); // tainted + } + + { + std::vector v7; + std::vector v8; + std::vector v9; + + v7.assign(100, ns_int::source()); + v8.assign(100, ns_myFloat::source()); + v9.assign(100, ns_ci_ptr::source()); + + sink(v7); // tainted + sink(v8); // tainted + sink(v9); // tainted + } +} + +void sink(int *); + +void test_data_more() { + std::vector v1, v2; + + v1.push_back(source()); + sink(v1); // tainted + sink(v1.data()); // tainted + sink(v1.data()[2]); // tainted + + *(v2.data()) = ns_int::source(); + sink(v2); // tainted + sink(v2.data()); // tainted + sink(v2.data()[2]); // tainted +} + +void sink(std::vector::iterator); + +void test_vector_insert() { + std::vector a; + std::vector b; + std::vector c; + std::vector d; + + d.push_back(source()); + + sink(a.insert(a.end(), b.begin(), b.end())); + sink(a); + + sink(c.insert(c.end(), d.begin(), d.end())); // tainted + sink(c); // tainted + + sink(d.insert(d.end(), a.begin(), a.end())); // tainted + sink(d); // tainted +} + +void test_constructors_more() { + std::vector v1; + std::vector v2; + v2.push_back(source()); + + std::vector v3(v1.begin(), v1.end()); + std::vector v4(v2.begin(), v2.end()); + + sink(v1); + sink(v2); // tainted + sink(v3); + sink(v4); // tainted +} + +void taint_vector_output_iterator(std::vector::iterator iter) { + *iter = source(); +} + +void vector_iterator_assign_wrapper(std::vector::iterator iter, int i) { + *iter = i; +} + +void test_vector_output_iterator(int b) { + std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10), v10(10), v11(10); + + std::vector::iterator i1 = v1.begin(); + *i1 = source(); + sink(v1); // tainted [NOT DETECTED by IR] + + for(std::vector::iterator it = v2.begin(); it != v2.end(); ++it) { + *it = source(); + } + sink(v2); // tainted [NOT DETECTED by IR] + + for(int& x : v3) { + x = source(); + } + sink(v3); // tainted [NOT DETECTED] + + for(std::vector::iterator it = v4.begin(); it != v4.end(); ++it) { + taint_vector_output_iterator(it); + } + sink(v4); // tainted [NOT DETECTED by IR] + + std::vector::iterator i5 = v5.begin(); + *i5 = source(); + sink(v5); // tainted [NOT DETECTED by IR] + *i5 = 1; + sink(v5); // tainted [NOT DETECTED by IR] + + std::vector::iterator i6 = v6.begin(); + *i6 = source(); + sink(v6); // tainted [NOT DETECTED by IR] + v6 = std::vector(10); + sink(v6); // [FALSE POSITIVE in AST] + + std::vector::iterator i7 = v7.begin(); + if(b) { + *i7 = source(); + sink(v7); // tainted [NOT DETECTED by IR] + } else { + *i7 = 1; + sink(v7); + } + sink(v7); // tainted [NOT DETECTED by IR] + + std::vector::iterator i8 = v8.begin(); + *i8 = source(); + sink(v8); // tainted [NOT DETECTED by IR] + *i8 = 1; + sink(v8); + + std::vector::iterator i9 = v9.begin(); + + *i9 = source(); + taint_vector_output_iterator(i9); + + sink(v9); + + std::vector::iterator i10 = v10.begin(); + vector_iterator_assign_wrapper(i10, 10); + sink(v10); + + std::vector::iterator i11 = v11.begin(); + vector_iterator_assign_wrapper(i11, source()); + sink(v11); // tainted [NOT DETECTED by IR] +} diff --git a/cpp/ql/test/library-tests/declaration/IsMember.expected b/cpp/ql/test/library-tests/declaration/IsMember.expected index 174b8745c854..80fcd74689ce 100644 --- a/cpp/ql/test/library-tests/declaration/IsMember.expected +++ b/cpp/ql/test/library-tests/declaration/IsMember.expected @@ -3,6 +3,7 @@ | declaration.cpp:32:7:32:7 | operator= | | declaration.cpp:32:7:32:7 | operator= | | declaration.cpp:34:7:34:14 | myField0 | +| declaration.cpp:36:9:36:9 | MyNestedClass | | declaration.cpp:36:9:36:9 | operator= | | declaration.cpp:36:9:36:9 | operator= | | declaration.cpp:36:9:36:21 | MyNestedClass | @@ -28,6 +29,7 @@ | declaration.cpp:100:7:100:7 | operator= | | declaration.cpp:100:7:100:7 | operator= | | declaration.cpp:102:7:102:14 | myField1 | +| declaration.cpp:104:9:104:9 | MyNestedClass | | declaration.cpp:104:9:104:9 | operator= | | declaration.cpp:104:9:104:9 | operator= | | declaration.cpp:104:9:104:21 | MyNestedClass | diff --git a/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected b/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected index 47c5f075a412..6018624fd043 100644 --- a/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected +++ b/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected @@ -27,6 +27,7 @@ | test.cpp:20:6:20:20 | definition of tmplProtoAndDef | | test.cpp:20:24:20:24 | declaration of t | | test.cpp:20:24:20:24 | definition of t | +| test.cpp:22:7:22:7 | declaration of Cl | | test.cpp:22:7:22:7 | declaration of operator= | | test.cpp:22:7:22:7 | declaration of operator= | | test.cpp:22:7:22:8 | definition of Cl | @@ -75,6 +76,8 @@ | test.cpp:67:7:67:7 | declaration of operator= | | test.cpp:67:7:67:7 | declaration of operator= | | test.cpp:67:7:67:7 | declaration of operator= | +| test.cpp:67:7:67:7 | definition of tmplInstantiatedClass | +| test.cpp:67:7:67:7 | definition of tmplInstantiatedClass | | test.cpp:67:7:67:27 | definition of tmplInstantiatedClass | | test.cpp:68:7:68:7 | definition of t | | test.cpp:68:7:68:7 | definition of t | diff --git a/cpp/ql/test/library-tests/fun_decl/test.expected b/cpp/ql/test/library-tests/fun_decl/test.expected index cd72c33fe674..dfa716838ee4 100644 --- a/cpp/ql/test/library-tests/fun_decl/test.expected +++ b/cpp/ql/test/library-tests/fun_decl/test.expected @@ -1,4 +1,5 @@ | compile1.cpp:3:6:3:10 | func1 | +| compile2.cpp:3:7:3:7 | classA | | compile2.cpp:3:7:3:7 | operator= | | compile2.cpp:5:2:5:8 | ~classA | | compile2.cpp:8:9:8:19 | create_an_a | diff --git a/cpp/ql/test/library-tests/functions/functions/Functions1.expected b/cpp/ql/test/library-tests/functions/functions/Functions1.expected index f491fa10b6ed..cebf0359fdcf 100644 --- a/cpp/ql/test/library-tests/functions/functions/Functions1.expected +++ b/cpp/ql/test/library-tests/functions/functions/Functions1.expected @@ -1,3 +1,4 @@ +| ODASA-5186.cpp:4:8:4:8 | MyClass | MyClass | | declaration:ODASA-5186.cpp:4:8:4:8, definition:ODASA-5186.cpp:4:8:4:8 | | ODASA-5186.cpp:4:8:4:8 | operator= | operator= | MyClass && p#0 | declaration:ODASA-5186.cpp:4:8:4:8 | | ODASA-5186.cpp:4:8:4:8 | operator= | operator= | const MyClass & p#0 | declaration:ODASA-5186.cpp:4:8:4:8 | | ODASA-5186.cpp:5:8:5:8 | operator== | operator== | const MyClass & other | declaration:ODASA-5186.cpp:5:8:5:8, definition:ODASA-5186.cpp:5:8:5:8 | @@ -13,6 +14,7 @@ | functions.cpp:8:7:8:8 | af | af | | declaration:functions.cpp:8:7:8:8, definition:functions.cpp:8:7:8:8 | | functions.cpp:11:7:11:8 | ag | ag | | declaration:functions.cpp:11:7:11:8 | | functions.cpp:14:6:14:6 | g | g | | TopLevelFunction, declaration:functions.cpp:14:6:14:6, declaration:functions.cpp:5:6:5:6, definition:functions.cpp:14:6:14:6, isTopLevel | +| functions.cpp:19:7:19:7 | Name | Name | | declaration:functions.cpp:19:7:19:7 | | functions.cpp:19:7:19:7 | operator= | operator= | Name && p#0 | declaration:functions.cpp:19:7:19:7 | | functions.cpp:19:7:19:7 | operator= | operator= | const Name & p#0 | declaration:functions.cpp:19:7:19:7 | | functions.cpp:23:7:23:7 | Table | Table | const Table & p#0 | declaration:functions.cpp:23:7:23:7 | diff --git a/cpp/ql/test/library-tests/functions/functions/Functions2.expected b/cpp/ql/test/library-tests/functions/functions/Functions2.expected index 8242831d5f6a..4040d024ebb5 100644 --- a/cpp/ql/test/library-tests/functions/functions/Functions2.expected +++ b/cpp/ql/test/library-tests/functions/functions/Functions2.expected @@ -1,4 +1,5 @@ | ODASA-5186.cpp:4:8:4:14 | MyClass | Class | ODASA-5186.cpp:5:8:5:17 | operator== | | +| ODASA-5186.cpp:4:8:4:14 | MyClass | Struct | ODASA-5186.cpp:4:8:4:8 | MyClass | Constructor, NoArgConstructor, getAConstructor() | | ODASA-5186.cpp:4:8:4:14 | MyClass | Struct | ODASA-5186.cpp:4:8:4:8 | operator= | | | ODASA-5186.cpp:4:8:4:14 | MyClass | Struct | ODASA-5186.cpp:4:8:4:8 | operator= | | | ODASA-5186.cpp:4:8:4:14 | MyClass | Struct | ODASA-5186.cpp:5:8:5:8 | operator== | | @@ -10,6 +11,7 @@ | functions.cpp:7:8:7:8 | A | Struct | functions.cpp:7:8:7:8 | operator= | | | functions.cpp:7:8:7:8 | A | Struct | functions.cpp:8:7:8:8 | af | | | functions.cpp:7:8:7:8 | A | Struct | functions.cpp:11:7:11:8 | ag | | +| functions.cpp:19:7:19:10 | Name | Class | functions.cpp:19:7:19:7 | Name | Constructor, NoArgConstructor, getAConstructor() | | functions.cpp:19:7:19:10 | Name | Class | functions.cpp:19:7:19:7 | operator= | | | functions.cpp:19:7:19:10 | Name | Class | functions.cpp:19:7:19:7 | operator= | | | functions.cpp:23:7:23:11 | Table | Class | functions.cpp:23:7:23:7 | Table | Constructor, CopyConstructor, getAConstructor() | @@ -20,7 +22,7 @@ | functions.cpp:23:7:23:11 | Table | Class | functions.cpp:30:8:30:13 | insert | | | functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:33:7:33:7 | operator= | | | functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:36:2:36:8 | MyClass | Constructor, NoArgConstructor, getAConstructor() | -| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:37:2:37:8 | MyClass | Constructor, ConversionConstructor, getAConstructor() | +| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:37:2:37:8 | MyClass | Constructor, getAConstructor() | | functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:38:2:38:8 | MyClass | Constructor, CopyConstructor, getAConstructor() | -| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:39:2:39:8 | MyClass | Constructor, ConversionConstructor, MoveConstructor, getAConstructor() | +| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:39:2:39:8 | MyClass | Constructor, MoveConstructor, getAConstructor() | | functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:40:2:40:13 | operator int | ConversionOperator | diff --git a/cpp/ql/test/library-tests/functions/functions/Functions2.ql b/cpp/ql/test/library-tests/functions/functions/Functions2.ql index 6cf558e54a90..506b47891197 100644 --- a/cpp/ql/test/library-tests/functions/functions/Functions2.ql +++ b/cpp/ql/test/library-tests/functions/functions/Functions2.ql @@ -19,9 +19,6 @@ string describe(Class c, MemberFunction f) { f instanceof Destructor and result = "Destructor" or - f instanceof ConversionConstructor and - result = "ConversionConstructor" - or f instanceof CopyConstructor and result = "CopyConstructor" or diff --git a/cpp/ql/test/library-tests/identifiers/qualified_names/unnamed.expected b/cpp/ql/test/library-tests/identifiers/qualified_names/unnamed.expected index 04c877da4ca4..78df738f9c77 100644 --- a/cpp/ql/test/library-tests/identifiers/qualified_names/unnamed.expected +++ b/cpp/ql/test/library-tests/identifiers/qualified_names/unnamed.expected @@ -1,6 +1,7 @@ | qualifiedNames.cpp:3:25:3:27 | p#0 | | qualifiedNames.cpp:4:25:4:28 | p#0 | | qualifiedNames.cpp:18:18:18:31 | Nested's friend | +| qualifiedNames.cpp:26:9:26:9 | LocalClass | | qualifiedNames.cpp:26:9:26:9 | operator= | | qualifiedNames.cpp:26:9:26:9 | operator= | | qualifiedNames.cpp:26:9:26:18 | LocalClass | diff --git a/cpp/ql/test/library-tests/instantiations/test.expected b/cpp/ql/test/library-tests/instantiations/test.expected index 3e23680b5fbb..7460a9fa9dcc 100644 --- a/cpp/ql/test/library-tests/instantiations/test.expected +++ b/cpp/ql/test/library-tests/instantiations/test.expected @@ -2,6 +2,7 @@ | file://:0:0:0:0 | operator new | | file://:0:0:0:0 | operator= | | file://:0:0:0:0 | operator= | +| test.cpp:2:7:2:7 | A | | test.cpp:2:7:2:7 | operator= | | test.cpp:2:19:2:20 | ~A | | test.cpp:4:7:4:7 | X | diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index 2e204cde8562..e58a36e747dc 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -71,7 +71,7 @@ bad_asts.cpp: # 9| params: # 9| 0: [Parameter] y # 9| Type = [IntType] int -# 9| body: [Block] { ... } +# 9| body: [BlockStmt] { ... } # 10| 0: [ReturnStmt] return ... # 10| 0: [AddExpr] ... + ... # 10| Type = [IntType] int @@ -96,7 +96,7 @@ bad_asts.cpp: # 9| params: # 9| 0: [Parameter] y # 9| Type = [IntType] int -# 9| body: [Block] { ... } +# 9| body: [BlockStmt] { ... } # 10| 0: [ReturnStmt] return ... # 10| 0: [AddExpr] ... + ... # 10| Type = [IntType] int @@ -119,7 +119,7 @@ bad_asts.cpp: # 10| ValueCategory = prvalue(load) # 14| [TopLevelFunction] void Bad::CallBadMemberFunction() # 14| params: -# 14| body: [Block] { ... } +# 14| body: [BlockStmt] { ... } # 15| 0: [DeclStmt] declaration # 15| 0: [VariableDeclarationEntry] definition of s # 15| Type = [Struct] S @@ -164,7 +164,7 @@ bad_asts.cpp: # 19| 0: [Literal] Unknown literal # 19| Type = [IntType] int # 19| ValueCategory = prvalue -# 19| body: [Block] { ... } +# 19| body: [BlockStmt] { ... } # 19| 0: [ReturnStmt] return ... # 19| [MoveConstructor] void Bad::Point::Point(Bad::Point&&) # 19| params: @@ -173,13 +173,13 @@ bad_asts.cpp: # 22| [Constructor] void Bad::Point::Point() # 22| params: # 22| initializations: -# 22| body: [Block] { ... } +# 22| body: [BlockStmt] { ... } # 23| 0: [ReturnStmt] return ... # 26| [TopLevelFunction] void Bad::CallCopyConstructor(Bad::Point const&) # 26| params: # 26| 0: [Parameter] a # 26| Type = [LValueReferenceType] const Point & -# 26| body: [Block] { ... } +# 26| body: [BlockStmt] { ... } # 27| 0: [DeclStmt] declaration # 27| 0: [VariableDeclarationEntry] definition of b # 27| Type = [Struct] Point @@ -197,7 +197,7 @@ bad_asts.cpp: # 28| 1: [ReturnStmt] return ... # 30| [TopLevelFunction] void Bad::errorExpr() # 30| params: -# 30| body: [Block] { ... } +# 30| body: [BlockStmt] { ... } # 31| 0: [DeclStmt] declaration # 31| 0: [VariableDeclarationEntry] definition of intref # 31| Type = [LValueReferenceType] int & @@ -226,7 +226,7 @@ bad_asts.cpp: clang.cpp: # 5| [TopLevelFunction] int* globalIntAddress() # 5| params: -# 5| body: [Block] { ... } +# 5| body: [BlockStmt] { ... } # 6| 0: [ReturnStmt] return ... # 6| 0: [BuiltInOperationBuiltInAddressOf] __builtin_addressof ... # 6| Type = [IntPointerType] int * @@ -237,7 +237,7 @@ clang.cpp: complex.c: # 1| [TopLevelFunction] void complex_literals() # 1| params: -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [DeclStmt] declaration # 2| 0: [VariableDeclarationEntry] definition of cf # 2| Type = [ArithmeticType] _Complex float @@ -354,7 +354,7 @@ complex.c: # 12| 9: [ReturnStmt] return ... # 14| [TopLevelFunction] void complex_arithmetic() # 14| params: -# 14| body: [Block] { ... } +# 14| body: [BlockStmt] { ... } # 15| 0: [DeclStmt] declaration # 15| 0: [VariableDeclarationEntry] definition of f1 # 15| Type = [FloatType] float @@ -741,7 +741,7 @@ complex.c: # 56| 29: [ReturnStmt] return ... # 58| [TopLevelFunction] void complex_conversions() # 58| params: -# 58| body: [Block] { ... } +# 58| body: [BlockStmt] { ... } # 59| 0: [DeclStmt] declaration # 59| 0: [VariableDeclarationEntry] definition of f # 59| Type = [FloatType] float @@ -1718,7 +1718,7 @@ complex.c: ir.cpp: # 1| [TopLevelFunction] void Constants() # 1| params: -# 1| body: [Block] { ... } +# 1| body: [BlockStmt] { ... } # 2| 0: [DeclStmt] declaration # 2| 0: [VariableDeclarationEntry] definition of c_i # 2| Type = [PlainCharType] char @@ -2035,7 +2035,7 @@ ir.cpp: # 41| 28: [ReturnStmt] return ... # 43| [TopLevelFunction] void Foo() # 43| params: -# 43| body: [Block] { ... } +# 43| body: [BlockStmt] { ... } # 44| 0: [DeclStmt] declaration # 44| 0: [VariableDeclarationEntry] definition of x # 44| Type = [IntType] int @@ -2116,7 +2116,7 @@ ir.cpp: # 50| Type = [IntType] int # 50| 1: [Parameter] y # 50| Type = [IntType] int -# 50| body: [Block] { ... } +# 50| body: [BlockStmt] { ... } # 51| 0: [DeclStmt] declaration # 51| 0: [VariableDeclarationEntry] definition of z # 51| Type = [IntType] int @@ -2457,7 +2457,7 @@ ir.cpp: # 87| Type = [IntType] int # 87| 1: [Parameter] y # 87| Type = [IntType] int -# 87| body: [Block] { ... } +# 87| body: [BlockStmt] { ... } # 88| 0: [DeclStmt] declaration # 88| 0: [VariableDeclarationEntry] definition of b # 88| Type = [BoolType] bool @@ -2562,7 +2562,7 @@ ir.cpp: # 98| params: # 98| 0: [Parameter] x # 98| Type = [IntType] int -# 98| body: [Block] { ... } +# 98| body: [BlockStmt] { ... } # 99| 0: [DeclStmt] declaration # 99| 0: [VariableDeclarationEntry] definition of y # 99| Type = [IntType] int @@ -2623,7 +2623,7 @@ ir.cpp: # 107| params: # 107| 0: [Parameter] x # 107| Type = [IntType] int -# 107| body: [Block] { ... } +# 107| body: [BlockStmt] { ... } # 108| 0: [DeclStmt] declaration # 108| 0: [VariableDeclarationEntry] definition of p # 108| Type = [IntPointerType] int * @@ -2672,7 +2672,7 @@ ir.cpp: # 114| Type = [DoubleType] double # 114| 1: [Parameter] y # 114| Type = [DoubleType] double -# 114| body: [Block] { ... } +# 114| body: [BlockStmt] { ... } # 115| 0: [DeclStmt] declaration # 115| 0: [VariableDeclarationEntry] definition of z # 115| Type = [DoubleType] double @@ -2823,7 +2823,7 @@ ir.cpp: # 133| Type = [DoubleType] double # 133| 1: [Parameter] y # 133| Type = [DoubleType] double -# 133| body: [Block] { ... } +# 133| body: [BlockStmt] { ... } # 134| 0: [DeclStmt] declaration # 134| 0: [VariableDeclarationEntry] definition of b # 134| Type = [BoolType] bool @@ -2928,7 +2928,7 @@ ir.cpp: # 144| params: # 144| 0: [Parameter] x # 144| Type = [FloatType] float -# 144| body: [Block] { ... } +# 144| body: [BlockStmt] { ... } # 145| 0: [DeclStmt] declaration # 145| 0: [VariableDeclarationEntry] definition of y # 145| Type = [FloatType] float @@ -2991,7 +2991,7 @@ ir.cpp: # 153| Type = [IntPointerType] int * # 153| 1: [Parameter] i # 153| Type = [IntType] int -# 153| body: [Block] { ... } +# 153| body: [BlockStmt] { ... } # 154| 0: [DeclStmt] declaration # 154| 0: [VariableDeclarationEntry] definition of q # 154| Type = [IntPointerType] int * @@ -3134,7 +3134,7 @@ ir.cpp: # 171| Type = [IntPointerType] int * # 171| 1: [Parameter] i # 171| Type = [IntType] int -# 171| body: [Block] { ... } +# 171| body: [BlockStmt] { ... } # 172| 0: [DeclStmt] declaration # 172| 0: [VariableDeclarationEntry] definition of x # 172| Type = [IntType] int @@ -3286,7 +3286,7 @@ ir.cpp: # 187| params: # 187| 0: [Parameter] i # 187| Type = [IntType] int -# 187| body: [Block] { ... } +# 187| body: [BlockStmt] { ... } # 188| 0: [DeclStmt] declaration # 188| 0: [VariableDeclarationEntry] definition of c # 188| Type = [PlainCharType] char @@ -3339,7 +3339,7 @@ ir.cpp: # 193| Type = [IntPointerType] int * # 193| 1: [Parameter] q # 193| Type = [IntPointerType] int * -# 193| body: [Block] { ... } +# 193| body: [BlockStmt] { ... } # 194| 0: [DeclStmt] declaration # 194| 0: [VariableDeclarationEntry] definition of b # 194| Type = [BoolType] bool @@ -3444,7 +3444,7 @@ ir.cpp: # 204| params: # 204| 0: [Parameter] p # 204| Type = [IntPointerType] int * -# 204| body: [Block] { ... } +# 204| body: [BlockStmt] { ... } # 205| 0: [DeclStmt] declaration # 205| 0: [VariableDeclarationEntry] definition of q # 205| Type = [IntPointerType] int * @@ -3503,7 +3503,7 @@ ir.cpp: # 211| 5: [ReturnStmt] return ... # 213| [TopLevelFunction] void CompoundAssignment() # 213| params: -# 213| body: [Block] { ... } +# 213| body: [BlockStmt] { ... } # 215| 0: [DeclStmt] declaration # 215| 0: [VariableDeclarationEntry] definition of x # 215| Type = [IntType] int @@ -3584,7 +3584,7 @@ ir.cpp: # 228| 7: [ReturnStmt] return ... # 230| [TopLevelFunction] void UninitializedVariables() # 230| params: -# 230| body: [Block] { ... } +# 230| body: [BlockStmt] { ... } # 231| 0: [DeclStmt] declaration # 231| 0: [VariableDeclarationEntry] definition of x # 231| Type = [IntType] int @@ -3602,7 +3602,7 @@ ir.cpp: # 235| Type = [IntType] int # 235| 1: [Parameter] y # 235| Type = [IntType] int -# 235| body: [Block] { ... } +# 235| body: [BlockStmt] { ... } # 236| 0: [ReturnStmt] return ... # 236| 0: [RemExpr] ... % ... # 236| Type = [IntType] int @@ -3621,17 +3621,17 @@ ir.cpp: # 239| Type = [IntType] int # 239| 2: [Parameter] y # 239| Type = [IntType] int -# 239| body: [Block] { ... } +# 239| body: [BlockStmt] { ... } # 240| 0: [IfStmt] if (...) ... # 240| 0: [VariableAccess] b # 240| Type = [BoolType] bool # 240| ValueCategory = prvalue(load) -# 240| 1: [Block] { ... } +# 240| 1: [BlockStmt] { ... } # 243| 1: [IfStmt] if (...) ... # 243| 0: [VariableAccess] b # 243| Type = [BoolType] bool # 243| ValueCategory = prvalue(load) -# 243| 1: [Block] { ... } +# 243| 1: [BlockStmt] { ... } # 244| 0: [ExprStmt] ExprStmt # 244| 0: [AssignExpr] ... = ... # 244| Type = [IntType] int @@ -3680,7 +3680,7 @@ ir.cpp: # 253| params: # 253| 0: [Parameter] n # 253| Type = [IntType] int -# 253| body: [Block] { ... } +# 253| body: [BlockStmt] { ... } # 254| 0: [WhileStmt] while (...) ... # 254| 0: [GTExpr] ... > ... # 254| Type = [BoolType] bool @@ -3692,7 +3692,7 @@ ir.cpp: # 254| Type = [IntType] int # 254| Value = [Literal] 0 # 254| ValueCategory = prvalue -# 254| 1: [Block] { ... } +# 254| 1: [BlockStmt] { ... } # 255| 0: [ExprStmt] ExprStmt # 255| 0: [AssignSubExpr] ... -= ... # 255| Type = [IntType] int @@ -3709,7 +3709,7 @@ ir.cpp: # 259| params: # 259| 0: [Parameter] n # 259| Type = [IntType] int -# 259| body: [Block] { ... } +# 259| body: [BlockStmt] { ... } # 260| 0: [DoStmt] do (...) ... # 262| 0: [GTExpr] ... > ... # 262| Type = [BoolType] bool @@ -3721,7 +3721,7 @@ ir.cpp: # 262| Type = [IntType] int # 262| Value = [Literal] 0 # 262| ValueCategory = prvalue -# 260| 1: [Block] { ... } +# 260| 1: [BlockStmt] { ... } # 261| 0: [ExprStmt] ExprStmt # 261| 0: [AssignSubExpr] ... -= ... # 261| Type = [IntType] int @@ -3736,16 +3736,16 @@ ir.cpp: # 263| 1: [ReturnStmt] return ... # 265| [TopLevelFunction] void For_Empty() # 265| params: -# 265| body: [Block] { ... } +# 265| body: [BlockStmt] { ... } # 266| 0: [DeclStmt] declaration # 266| 0: [VariableDeclarationEntry] definition of j # 266| Type = [IntType] int # 267| 1: [ForStmt] for(...;...;...) ... -# 267| 3: [Block] { ... } +# 267| 3: [BlockStmt] { ... } # 268| 0: [EmptyStmt] ; # 272| [TopLevelFunction] void For_Init() # 272| params: -# 272| body: [Block] { ... } +# 272| body: [BlockStmt] { ... } # 273| 0: [ForStmt] for(...;...;...) ... # 273| 0: [DeclStmt] declaration # 273| 0: [VariableDeclarationEntry] definition of i @@ -3755,11 +3755,11 @@ ir.cpp: # 273| Type = [IntType] int # 273| Value = [Literal] 0 # 273| ValueCategory = prvalue -# 273| 3: [Block] { ... } +# 273| 3: [BlockStmt] { ... } # 274| 0: [EmptyStmt] ; # 278| [TopLevelFunction] void For_Condition() # 278| params: -# 278| body: [Block] { ... } +# 278| body: [BlockStmt] { ... } # 279| 0: [DeclStmt] declaration # 279| 0: [VariableDeclarationEntry] definition of i # 279| Type = [IntType] int @@ -3779,12 +3779,12 @@ ir.cpp: # 280| Type = [IntType] int # 280| Value = [Literal] 10 # 280| ValueCategory = prvalue -# 280| 3: [Block] { ... } +# 280| 3: [BlockStmt] { ... } # 281| 0: [EmptyStmt] ; # 283| 2: [ReturnStmt] return ... # 285| [TopLevelFunction] void For_Update() # 285| params: -# 285| body: [Block] { ... } +# 285| body: [BlockStmt] { ... } # 286| 0: [DeclStmt] declaration # 286| 0: [VariableDeclarationEntry] definition of i # 286| Type = [IntType] int @@ -3804,11 +3804,11 @@ ir.cpp: # 287| Type = [IntType] int # 287| Value = [Literal] 1 # 287| ValueCategory = prvalue -# 287| 3: [Block] { ... } +# 287| 3: [BlockStmt] { ... } # 288| 0: [EmptyStmt] ; # 292| [TopLevelFunction] void For_InitCondition() # 292| params: -# 292| body: [Block] { ... } +# 292| body: [BlockStmt] { ... } # 293| 0: [ForStmt] for(...;...;...) ... # 293| 0: [DeclStmt] declaration # 293| 0: [VariableDeclarationEntry] definition of i @@ -3828,12 +3828,12 @@ ir.cpp: # 293| Type = [IntType] int # 293| Value = [Literal] 10 # 293| ValueCategory = prvalue -# 293| 3: [Block] { ... } +# 293| 3: [BlockStmt] { ... } # 294| 0: [EmptyStmt] ; # 296| 1: [ReturnStmt] return ... # 298| [TopLevelFunction] void For_InitUpdate() # 298| params: -# 298| body: [Block] { ... } +# 298| body: [BlockStmt] { ... } # 299| 0: [ForStmt] for(...;...;...) ... # 299| 0: [DeclStmt] declaration # 299| 0: [VariableDeclarationEntry] definition of i @@ -3853,11 +3853,11 @@ ir.cpp: # 299| Type = [IntType] int # 299| Value = [Literal] 1 # 299| ValueCategory = prvalue -# 299| 3: [Block] { ... } +# 299| 3: [BlockStmt] { ... } # 300| 0: [EmptyStmt] ; # 304| [TopLevelFunction] void For_ConditionUpdate() # 304| params: -# 304| body: [Block] { ... } +# 304| body: [BlockStmt] { ... } # 305| 0: [DeclStmt] declaration # 305| 0: [VariableDeclarationEntry] definition of i # 305| Type = [IntType] int @@ -3887,12 +3887,12 @@ ir.cpp: # 306| Type = [IntType] int # 306| Value = [Literal] 1 # 306| ValueCategory = prvalue -# 306| 3: [Block] { ... } +# 306| 3: [BlockStmt] { ... } # 307| 0: [EmptyStmt] ; # 309| 2: [ReturnStmt] return ... # 311| [TopLevelFunction] void For_InitConditionUpdate() # 311| params: -# 311| body: [Block] { ... } +# 311| body: [BlockStmt] { ... } # 312| 0: [ForStmt] for(...;...;...) ... # 312| 0: [DeclStmt] declaration # 312| 0: [VariableDeclarationEntry] definition of i @@ -3922,12 +3922,12 @@ ir.cpp: # 312| Type = [IntType] int # 312| Value = [Literal] 1 # 312| ValueCategory = prvalue -# 312| 3: [Block] { ... } +# 312| 3: [BlockStmt] { ... } # 313| 0: [EmptyStmt] ; # 315| 1: [ReturnStmt] return ... # 317| [TopLevelFunction] void For_Break() # 317| params: -# 317| body: [Block] { ... } +# 317| body: [BlockStmt] { ... } # 318| 0: [ForStmt] for(...;...;...) ... # 318| 0: [DeclStmt] declaration # 318| 0: [VariableDeclarationEntry] definition of i @@ -3957,7 +3957,7 @@ ir.cpp: # 318| Type = [IntType] int # 318| Value = [Literal] 1 # 318| ValueCategory = prvalue -# 318| 3: [Block] { ... } +# 318| 3: [BlockStmt] { ... } # 319| 0: [IfStmt] if (...) ... # 319| 0: [EQExpr] ... == ... # 319| Type = [BoolType] bool @@ -3969,13 +3969,13 @@ ir.cpp: # 319| Type = [IntType] int # 319| Value = [Literal] 5 # 319| ValueCategory = prvalue -# 319| 1: [Block] { ... } +# 319| 1: [BlockStmt] { ... } # 320| 0: [BreakStmt] break; # 322| 1: [LabelStmt] label ...: # 323| 2: [ReturnStmt] return ... # 325| [TopLevelFunction] void For_Continue_Update() # 325| params: -# 325| body: [Block] { ... } +# 325| body: [BlockStmt] { ... } # 326| 0: [ForStmt] for(...;...;...) ... # 326| 0: [DeclStmt] declaration # 326| 0: [VariableDeclarationEntry] definition of i @@ -4005,7 +4005,7 @@ ir.cpp: # 326| Type = [IntType] int # 326| Value = [Literal] 1 # 326| ValueCategory = prvalue -# 326| 3: [Block] { ... } +# 326| 3: [BlockStmt] { ... } # 327| 0: [IfStmt] if (...) ... # 327| 0: [EQExpr] ... == ... # 327| Type = [BoolType] bool @@ -4017,13 +4017,13 @@ ir.cpp: # 327| Type = [IntType] int # 327| Value = [Literal] 5 # 327| ValueCategory = prvalue -# 327| 1: [Block] { ... } +# 327| 1: [BlockStmt] { ... } # 328| 0: [ContinueStmt] continue; # 326| 1: [LabelStmt] label ...: # 331| 1: [ReturnStmt] return ... # 333| [TopLevelFunction] void For_Continue_NoUpdate() # 333| params: -# 333| body: [Block] { ... } +# 333| body: [BlockStmt] { ... } # 334| 0: [ForStmt] for(...;...;...) ... # 334| 0: [DeclStmt] declaration # 334| 0: [VariableDeclarationEntry] definition of i @@ -4043,7 +4043,7 @@ ir.cpp: # 334| Type = [IntType] int # 334| Value = [Literal] 10 # 334| ValueCategory = prvalue -# 334| 3: [Block] { ... } +# 334| 3: [BlockStmt] { ... } # 335| 0: [IfStmt] if (...) ... # 335| 0: [EQExpr] ... == ... # 335| Type = [BoolType] bool @@ -4055,7 +4055,7 @@ ir.cpp: # 335| Type = [IntType] int # 335| Value = [Literal] 5 # 335| ValueCategory = prvalue -# 335| 1: [Block] { ... } +# 335| 1: [BlockStmt] { ... } # 336| 0: [ContinueStmt] continue; # 334| 1: [LabelStmt] label ...: # 339| 1: [ReturnStmt] return ... @@ -4063,7 +4063,7 @@ ir.cpp: # 341| params: # 341| 0: [Parameter] p # 341| Type = [IntPointerType] int * -# 341| body: [Block] { ... } +# 341| body: [BlockStmt] { ... } # 342| 0: [ExprStmt] ExprStmt # 342| 0: [AssignExpr] ... = ... # 342| Type = [IntType] int @@ -4087,7 +4087,7 @@ ir.cpp: # 343| ValueCategory = prvalue(load) # 348| [TopLevelFunction] int* AddressOf() # 348| params: -# 348| body: [Block] { ... } +# 348| body: [BlockStmt] { ... } # 349| 0: [ReturnStmt] return ... # 349| 0: [AddressOfExpr] & ... # 349| Type = [IntPointerType] int * @@ -4099,7 +4099,7 @@ ir.cpp: # 352| params: # 352| 0: [Parameter] n # 352| Type = [IntType] int -# 352| body: [Block] { ... } +# 352| body: [BlockStmt] { ... } # 353| 0: [WhileStmt] while (...) ... # 353| 0: [GTExpr] ... > ... # 353| Type = [BoolType] bool @@ -4111,7 +4111,7 @@ ir.cpp: # 353| Type = [IntType] int # 353| Value = [Literal] 0 # 353| ValueCategory = prvalue -# 353| 1: [Block] { ... } +# 353| 1: [BlockStmt] { ... } # 354| 0: [IfStmt] if (...) ... # 354| 0: [EQExpr] ... == ... # 354| Type = [BoolType] bool @@ -4141,7 +4141,7 @@ ir.cpp: # 360| params: # 360| 0: [Parameter] n # 360| Type = [IntType] int -# 360| body: [Block] { ... } +# 360| body: [BlockStmt] { ... } # 361| 0: [DoStmt] do (...) ... # 366| 0: [GTExpr] ... > ... # 366| Type = [BoolType] bool @@ -4153,7 +4153,7 @@ ir.cpp: # 366| Type = [IntType] int # 366| Value = [Literal] 0 # 366| ValueCategory = prvalue -# 361| 1: [Block] { ... } +# 361| 1: [BlockStmt] { ... } # 362| 0: [IfStmt] if (...) ... # 362| 0: [EQExpr] ... == ... # 362| Type = [BoolType] bool @@ -4165,7 +4165,7 @@ ir.cpp: # 362| Type = [IntType] int # 362| Value = [Literal] 1 # 362| ValueCategory = prvalue -# 362| 1: [Block] { ... } +# 362| 1: [BlockStmt] { ... } # 363| 0: [ContinueStmt] continue; # 365| 1: [ExprStmt] ExprStmt # 365| 0: [AssignSubExpr] ... -= ... @@ -4190,7 +4190,7 @@ ir.cpp: # 370| Type = [IntType] int # 372| [TopLevelFunction] void Call() # 372| params: -# 372| body: [Block] { ... } +# 372| body: [BlockStmt] { ... } # 373| 0: [ExprStmt] ExprStmt # 373| 0: [FunctionCall] call to VoidFunc # 373| Type = [VoidType] void @@ -4202,7 +4202,7 @@ ir.cpp: # 376| Type = [IntType] int # 376| 1: [Parameter] y # 376| Type = [IntType] int -# 376| body: [Block] { ... } +# 376| body: [BlockStmt] { ... } # 377| 0: [ReturnStmt] return ... # 377| 0: [FunctionCall] call to Add # 377| Type = [IntType] int @@ -4219,7 +4219,7 @@ ir.cpp: # 380| Type = [IntType] int # 380| 1: [Parameter] y # 380| Type = [IntType] int -# 380| body: [Block] { ... } +# 380| body: [BlockStmt] { ... } # 381| 0: [ReturnStmt] return ... # 381| 0: [CommaExpr] ... , ... # 381| Type = [IntType] int @@ -4240,7 +4240,7 @@ ir.cpp: # 384| params: # 384| 0: [Parameter] x # 384| Type = [IntType] int -# 384| body: [Block] { ... } +# 384| body: [BlockStmt] { ... } # 385| 0: [DeclStmt] declaration # 385| 0: [VariableDeclarationEntry] definition of y # 385| Type = [IntType] int @@ -4248,7 +4248,7 @@ ir.cpp: # 386| 0: [VariableAccess] x # 386| Type = [IntType] int # 386| ValueCategory = prvalue(load) -# 386| 1: [Block] { ... } +# 386| 1: [BlockStmt] { ... } # 387| 0: [ExprStmt] ExprStmt # 387| 0: [AssignExpr] ... = ... # 387| Type = [IntType] int @@ -4386,14 +4386,14 @@ ir.cpp: # 422| params: # 422| 0: [Parameter] pt # 422| Type = [Struct] Point -# 422| body: [Block] { ... } +# 422| body: [BlockStmt] { ... } # 423| 0: [ReturnStmt] return ... # 423| 0: [VariableAccess] pt # 423| Type = [Struct] Point # 423| ValueCategory = prvalue(load) # 426| [TopLevelFunction] void FieldAccess() # 426| params: -# 426| body: [Block] { ... } +# 426| body: [BlockStmt] { ... } # 427| 0: [DeclStmt] declaration # 427| 0: [VariableDeclarationEntry] definition of pt # 427| Type = [Struct] Point @@ -4447,7 +4447,7 @@ ir.cpp: # 433| Type = [BoolType] bool # 433| 1: [Parameter] b # 433| Type = [BoolType] bool -# 433| body: [Block] { ... } +# 433| body: [BlockStmt] { ... } # 434| 0: [DeclStmt] declaration # 434| 0: [VariableDeclarationEntry] definition of x # 434| Type = [IntType] int @@ -4461,7 +4461,7 @@ ir.cpp: # 435| 1: [VariableAccess] b # 435| Type = [BoolType] bool # 435| ValueCategory = prvalue(load) -# 435| 1: [Block] { ... } +# 435| 1: [BlockStmt] { ... } # 436| 0: [ExprStmt] ExprStmt # 436| 0: [AssignExpr] ... = ... # 436| Type = [IntType] int @@ -4483,7 +4483,7 @@ ir.cpp: # 439| 1: [VariableAccess] b # 439| Type = [BoolType] bool # 439| ValueCategory = prvalue(load) -# 439| 1: [Block] { ... } +# 439| 1: [BlockStmt] { ... } # 440| 0: [ExprStmt] ExprStmt # 440| 0: [AssignExpr] ... = ... # 440| Type = [IntType] int @@ -4495,7 +4495,7 @@ ir.cpp: # 440| Type = [IntType] int # 440| Value = [Literal] 1 # 440| ValueCategory = prvalue -# 442| 2: [Block] { ... } +# 442| 2: [BlockStmt] { ... } # 443| 0: [ExprStmt] ExprStmt # 443| 0: [AssignExpr] ... = ... # 443| Type = [IntType] int @@ -4514,7 +4514,7 @@ ir.cpp: # 447| Type = [BoolType] bool # 447| 1: [Parameter] b # 447| Type = [BoolType] bool -# 447| body: [Block] { ... } +# 447| body: [BlockStmt] { ... } # 448| 0: [DeclStmt] declaration # 448| 0: [VariableDeclarationEntry] definition of x # 448| Type = [IntType] int @@ -4528,7 +4528,7 @@ ir.cpp: # 449| 1: [VariableAccess] b # 449| Type = [BoolType] bool # 449| ValueCategory = prvalue(load) -# 449| 1: [Block] { ... } +# 449| 1: [BlockStmt] { ... } # 450| 0: [ExprStmt] ExprStmt # 450| 0: [AssignExpr] ... = ... # 450| Type = [IntType] int @@ -4550,7 +4550,7 @@ ir.cpp: # 453| 1: [VariableAccess] b # 453| Type = [BoolType] bool # 453| ValueCategory = prvalue(load) -# 453| 1: [Block] { ... } +# 453| 1: [BlockStmt] { ... } # 454| 0: [ExprStmt] ExprStmt # 454| 0: [AssignExpr] ... = ... # 454| Type = [IntType] int @@ -4562,7 +4562,7 @@ ir.cpp: # 454| Type = [IntType] int # 454| Value = [Literal] 1 # 454| ValueCategory = prvalue -# 456| 2: [Block] { ... } +# 456| 2: [BlockStmt] { ... } # 457| 0: [ExprStmt] ExprStmt # 457| 0: [AssignExpr] ... = ... # 457| Type = [IntType] int @@ -4581,7 +4581,7 @@ ir.cpp: # 461| Type = [BoolType] bool # 461| 1: [Parameter] b # 461| Type = [BoolType] bool -# 461| body: [Block] { ... } +# 461| body: [BlockStmt] { ... } # 462| 0: [DeclStmt] declaration # 462| 0: [VariableDeclarationEntry] definition of x # 462| Type = [IntType] int @@ -4592,7 +4592,7 @@ ir.cpp: # 463| 0: [VariableAccess] a # 463| Type = [BoolType] bool # 463| ValueCategory = prvalue(load) -# 463| 1: [Block] { ... } +# 463| 1: [BlockStmt] { ... } # 464| 0: [ExprStmt] ExprStmt # 464| 0: [AssignExpr] ... = ... # 464| Type = [IntType] int @@ -4620,7 +4620,7 @@ ir.cpp: # 467| 1: [VariableAccess] b # 467| Type = [BoolType] bool # 467| ValueCategory = prvalue(load) -# 467| 1: [Block] { ... } +# 467| 1: [BlockStmt] { ... } # 468| 0: [ExprStmt] ExprStmt # 468| 0: [AssignExpr] ... = ... # 468| Type = [IntType] int @@ -4632,7 +4632,7 @@ ir.cpp: # 468| Type = [IntType] int # 468| Value = [Literal] 2 # 468| ValueCategory = prvalue -# 470| 2: [Block] { ... } +# 470| 2: [BlockStmt] { ... } # 471| 0: [ExprStmt] ExprStmt # 471| 0: [AssignExpr] ... = ... # 471| Type = [IntType] int @@ -4651,7 +4651,7 @@ ir.cpp: # 475| Type = [BoolType] bool # 475| 1: [Parameter] b # 475| Type = [BoolType] bool -# 475| body: [Block] { ... } +# 475| body: [BlockStmt] { ... } # 476| 0: [DeclStmt] declaration # 476| 0: [VariableDeclarationEntry] definition of x # 476| Type = [BoolType] bool @@ -4718,7 +4718,7 @@ ir.cpp: # 482| Type = [IntType] int # 482| 2: [Parameter] y # 482| Type = [IntType] int -# 482| body: [Block] { ... } +# 482| body: [BlockStmt] { ... } # 483| 0: [DeclStmt] declaration # 483| 0: [VariableDeclarationEntry] definition of z # 483| Type = [IntType] int @@ -4740,7 +4740,7 @@ ir.cpp: # 486| params: # 486| 0: [Parameter] a # 486| Type = [BoolType] bool -# 486| body: [Block] { ... } +# 486| body: [BlockStmt] { ... } # 487| 0: [DeclStmt] declaration # 487| 0: [VariableDeclarationEntry] definition of x # 487| Type = [IntType] int @@ -4775,7 +4775,7 @@ ir.cpp: # 492| params: # 492| 0: [Parameter] a # 492| Type = [BoolType] bool -# 492| body: [Block] { ... } +# 492| body: [BlockStmt] { ... } # 493| 0: [ExprStmt] ExprStmt # 493| 0: [ConditionalExpr] ... ? ... : ... # 493| Type = [VoidType] void @@ -4792,7 +4792,7 @@ ir.cpp: # 494| 1: [ReturnStmt] return ... # 496| [TopLevelFunction] void Nullptr() # 496| params: -# 496| body: [Block] { ... } +# 496| body: [BlockStmt] { ... } # 497| 0: [DeclStmt] declaration # 497| 0: [VariableDeclarationEntry] definition of p # 497| Type = [IntPointerType] int * @@ -4858,7 +4858,7 @@ ir.cpp: # 503| Type = [IntType] int # 503| 1: [Parameter] f # 503| Type = [FloatType] float -# 503| body: [Block] { ... } +# 503| body: [BlockStmt] { ... } # 504| 0: [DeclStmt] declaration # 504| 0: [VariableDeclarationEntry] definition of pt1 # 504| Type = [Struct] Point @@ -4916,7 +4916,7 @@ ir.cpp: # 512| Type = [IntType] int # 512| 1: [Parameter] f # 512| Type = [FloatType] float -# 512| body: [Block] { ... } +# 512| body: [BlockStmt] { ... } # 513| 0: [DeclStmt] declaration # 513| 0: [VariableDeclarationEntry] definition of r1 # 513| Type = [Struct] Rect @@ -5003,7 +5003,7 @@ ir.cpp: # 519| Type = [IntType] int # 519| 1: [Parameter] f # 519| Type = [FloatType] float -# 519| body: [Block] { ... } +# 519| body: [BlockStmt] { ... } # 520| 0: [DeclStmt] declaration # 520| 0: [VariableDeclarationEntry] definition of a1 # 520| Type = [ArrayType] int[3] @@ -5057,7 +5057,7 @@ ir.cpp: # 530| Type = [IntType] int # 530| 1: [Parameter] f # 530| Type = [FloatType] float -# 530| body: [Block] { ... } +# 530| body: [BlockStmt] { ... } # 531| 0: [DeclStmt] declaration # 531| 0: [VariableDeclarationEntry] definition of u1 # 531| Type = [Union] U @@ -5079,7 +5079,7 @@ ir.cpp: # 535| Type = [IntType] int # 535| 1: [Parameter] y # 535| Type = [IntType] int -# 535| body: [Block] { ... } +# 535| body: [BlockStmt] { ... } # 536| 0: [IfStmt] if (...) ... # 536| 0: [LTExpr] ... < ... # 536| Type = [BoolType] bool @@ -5090,7 +5090,7 @@ ir.cpp: # 536| 1: [VariableAccess] y # 536| Type = [IntType] int # 536| ValueCategory = prvalue(load) -# 536| 1: [Block] { ... } +# 536| 1: [BlockStmt] { ... } # 537| 0: [ReturnStmt] return ... # 540| 1: [ExprStmt] ExprStmt # 540| 0: [AssignExpr] ... = ... @@ -5109,7 +5109,7 @@ ir.cpp: # 543| Type = [IntType] int # 543| 1: [Parameter] y # 543| Type = [IntType] int -# 543| body: [Block] { ... } +# 543| body: [BlockStmt] { ... } # 544| 0: [IfStmt] if (...) ... # 544| 0: [LTExpr] ... < ... # 544| Type = [BoolType] bool @@ -5120,7 +5120,7 @@ ir.cpp: # 544| 1: [VariableAccess] y # 544| Type = [IntType] int # 544| ValueCategory = prvalue(load) -# 544| 1: [Block] { ... } +# 544| 1: [BlockStmt] { ... } # 545| 0: [ReturnStmt] return ... # 545| 0: [VariableAccess] x # 545| Type = [IntType] int @@ -5139,7 +5139,7 @@ ir.cpp: # 551| params: # 551| 0: [Parameter] pfn # 551| Type = [FunctionPointerType] ..(*)(..) -# 551| body: [Block] { ... } +# 551| body: [BlockStmt] { ... } # 552| 0: [ReturnStmt] return ... # 552| 0: [VariableCall] call to expression # 552| Type = [IntType] int @@ -5155,7 +5155,7 @@ ir.cpp: # 560| params: # 560| 0: [Parameter] e # 560| Type = [CTypedefType] E -# 560| body: [Block] { ... } +# 560| body: [BlockStmt] { ... } # 561| 0: [SwitchStmt] switch (...) ... # 561| 0: [CStyleCast] (int)... # 561| Conversion = [IntegralConversion] integral conversion @@ -5164,7 +5164,7 @@ ir.cpp: # 561| expr: [VariableAccess] e # 561| Type = [CTypedefType] E # 561| ValueCategory = prvalue(load) -# 561| 1: [Block] { ... } +# 561| 1: [BlockStmt] { ... } # 562| 0: [SwitchCase] case ...: # 562| 0: [CStyleCast] (int)... # 562| Conversion = [IntegralConversion] integral conversion @@ -5207,7 +5207,7 @@ ir.cpp: # 567| ValueCategory = prvalue # 571| [TopLevelFunction] void InitArray() # 571| params: -# 571| body: [Block] { ... } +# 571| body: [BlockStmt] { ... } # 572| 0: [DeclStmt] declaration # 572| 0: [VariableDeclarationEntry] definition of a_pad # 572| Type = [ArrayType] char[32] @@ -5306,7 +5306,7 @@ ir.cpp: # 582| Type = [PointerType] const char * # 584| [TopLevelFunction] void VarArgs() # 584| params: -# 584| body: [Block] { ... } +# 584| body: [BlockStmt] { ... } # 585| 0: [ExprStmt] ExprStmt # 585| 0: [FunctionCall] call to VarArgFunction # 585| Type = [VoidType] void @@ -5336,7 +5336,7 @@ ir.cpp: # 588| Type = [IntType] int # 590| [TopLevelFunction] void SetFuncPtr() # 590| params: -# 590| body: [Block] { ... } +# 590| body: [BlockStmt] { ... } # 591| 0: [DeclStmt] declaration # 591| 0: [VariableDeclarationEntry] definition of pfn # 591| Type = [FunctionPointerType] ..(*)(..) @@ -5421,7 +5421,7 @@ ir.cpp: # 613| params: # 615| [TopLevelFunction] void DeclareObject() # 615| params: -# 615| body: [Block] { ... } +# 615| body: [BlockStmt] { ... } # 616| 0: [DeclStmt] declaration # 616| 0: [VariableDeclarationEntry] definition of s1 # 616| Type = [Struct] String @@ -5473,7 +5473,7 @@ ir.cpp: # 622| Type = [PointerType] String * # 622| 2: [Parameter] s # 622| Type = [Struct] String -# 622| body: [Block] { ... } +# 622| body: [BlockStmt] { ... } # 623| 0: [ExprStmt] ExprStmt # 623| 0: [FunctionCall] call to c_str # 623| Type = [PointerType] const char * @@ -5529,7 +5529,7 @@ ir.cpp: #-----| Type = [RValueReferenceType] C && # 628| [Destructor] void C::~C() # 628| params: -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ReturnStmt] return ... # 628| destructions: # 628| 0: [DestructorFieldDestruction] destructor field destruction of m_f @@ -5554,7 +5554,7 @@ ir.cpp: # 630| params: # 630| 0: [Parameter] x # 630| Type = [IntType] int -# 630| body: [Block] { ... } +# 630| body: [BlockStmt] { ... } # 631| 0: [ReturnStmt] return ... # 631| 0: [VariableAccess] x # 631| Type = [IntType] int @@ -5563,7 +5563,7 @@ ir.cpp: # 634| params: # 634| 0: [Parameter] x # 634| Type = [IntType] int -# 634| body: [Block] { ... } +# 634| body: [BlockStmt] { ... } # 635| 0: [ReturnStmt] return ... # 635| 0: [VariableAccess] x # 635| Type = [IntType] int @@ -5572,14 +5572,14 @@ ir.cpp: # 638| params: # 638| 0: [Parameter] x # 638| Type = [IntType] int -# 638| body: [Block] { ... } +# 638| body: [BlockStmt] { ... } # 639| 0: [ReturnStmt] return ... # 639| 0: [VariableAccess] x # 639| Type = [IntType] int # 639| ValueCategory = prvalue(load) # 642| [MemberFunction] void C::FieldAccess() # 642| params: -# 642| body: [Block] { ... } +# 642| body: [BlockStmt] { ... } # 643| 0: [ExprStmt] ExprStmt # 643| 0: [AssignExpr] ... = ... # 643| Type = [IntType] int @@ -5679,7 +5679,7 @@ ir.cpp: # 650| 7: [ReturnStmt] return ... # 652| [MemberFunction] void C::MethodCalls() # 652| params: -# 652| body: [Block] { ... } +# 652| body: [BlockStmt] { ... } # 653| 0: [ExprStmt] ExprStmt # 653| 0: [FunctionCall] call to InstanceMemberFunction # 653| Type = [IntType] int @@ -5768,13 +5768,13 @@ ir.cpp: # 662| Type = [ArrayType] const char[5] # 662| Value = [StringLiteral] "test" # 662| ValueCategory = lvalue -# 663| body: [Block] { ... } +# 663| body: [BlockStmt] { ... } # 664| 0: [ReturnStmt] return ... # 675| [TopLevelFunction] int DerefReference(int&) # 675| params: # 675| 0: [Parameter] r # 675| Type = [LValueReferenceType] int & -# 675| body: [Block] { ... } +# 675| body: [BlockStmt] { ... } # 676| 0: [ReturnStmt] return ... # 676| 0: [ReferenceDereferenceExpr] (reference dereference) # 676| Type = [IntType] int @@ -5784,7 +5784,7 @@ ir.cpp: # 676| ValueCategory = prvalue(load) # 679| [TopLevelFunction] int& TakeReference() # 679| params: -# 679| body: [Block] { ... } +# 679| body: [BlockStmt] { ... } # 680| 0: [ReturnStmt] return ... # 680| 0: [ReferenceToExpr] (reference to) # 680| Type = [LValueReferenceType] int & @@ -5798,7 +5798,7 @@ ir.cpp: # 685| params: # 685| 0: [Parameter] x # 685| Type = [IntType] int -# 685| body: [Block] { ... } +# 685| body: [BlockStmt] { ... } # 686| 0: [DeclStmt] declaration # 686| 0: [VariableDeclarationEntry] definition of r # 686| Type = [LValueReferenceType] int & @@ -5842,7 +5842,7 @@ ir.cpp: # 689| 3: [ReturnStmt] return ... # 691| [TopLevelFunction] void ArrayReferences() # 691| params: -# 691| body: [Block] { ... } +# 691| body: [BlockStmt] { ... } # 692| 0: [DeclStmt] declaration # 692| 0: [VariableDeclarationEntry] definition of a # 692| Type = [ArrayType] int[10] @@ -5879,7 +5879,7 @@ ir.cpp: # 695| 3: [ReturnStmt] return ... # 697| [TopLevelFunction] void FunctionReferences() # 697| params: -# 697| body: [Block] { ... } +# 697| body: [BlockStmt] { ... } # 698| 0: [DeclStmt] declaration # 698| 0: [VariableDeclarationEntry] definition of rfn # 698| Type = [FunctionReferenceType] ..(&)(..) @@ -5921,7 +5921,7 @@ ir.cpp: # 704| Type = [TemplateParameter] T # 704| 1: [Parameter] y # 704| Type = [TemplateParameter] T -# 704| body: [Block] { ... } +# 704| body: [BlockStmt] { ... } # 705| 0: [ReturnStmt] return ... # 705| 0: [ConditionalExpr] ... ? ... : ... # 705| Type = [UnknownType] unknown @@ -5954,7 +5954,7 @@ ir.cpp: # 704| Type = [IntType] int # 704| 1: [Parameter] y # 704| Type = [IntType] int -# 704| body: [Block] { ... } +# 704| body: [BlockStmt] { ... } # 705| 0: [ReturnStmt] return ... # 705| 0: [ConditionalExpr] ... ? ... : ... # 705| Type = [IntType] int @@ -5983,7 +5983,7 @@ ir.cpp: # 708| Type = [IntType] int # 708| 1: [Parameter] y # 708| Type = [IntType] int -# 708| body: [Block] { ... } +# 708| body: [BlockStmt] { ... } # 709| 0: [ReturnStmt] return ... # 709| 0: [FunctionCall] call to min # 709| Type = [IntType] int @@ -6008,7 +6008,7 @@ ir.cpp: # 715| Type = [TemplateParameter] U # 715| 1: [Parameter] y # 715| Type = [TemplateParameter] V -# 715| body: [Block] { ... } +# 715| body: [BlockStmt] { ... } # 716| 0: [ReturnStmt] return ... # 716| 0: [Literal] 0 # 716| Type = [TemplateParameter] T @@ -6026,7 +6026,7 @@ ir.cpp: # 715| Type = [VoidPointerType] void * # 715| 1: [Parameter] y # 715| Type = [PlainCharType] char -# 715| body: [Block] { ... } +# 715| body: [BlockStmt] { ... } # 716| 0: [ReturnStmt] return ... # 716| 0: [Literal] 0 # 716| Type = [LongType] long @@ -6034,7 +6034,7 @@ ir.cpp: # 716| ValueCategory = prvalue # 720| [TopLevelFunction] double CallNestedTemplateFunc() # 720| params: -# 720| body: [Block] { ... } +# 720| body: [BlockStmt] { ... } # 721| 0: [ReturnStmt] return ... # 721| 0: [CStyleCast] (double)... # 721| Conversion = [IntegralToFloatingPointConversion] integral to floating point conversion @@ -6060,9 +6060,9 @@ ir.cpp: # 724| params: # 724| 0: [Parameter] b # 724| Type = [BoolType] bool -# 724| body: [Block] { ... } +# 724| body: [BlockStmt] { ... } # 725| 0: [TryStmt] try { ... } -# 725| 0: [Block] { ... } +# 725| 0: [BlockStmt] { ... } # 726| 0: [DeclStmt] declaration # 726| 0: [VariableDeclarationEntry] definition of x # 726| Type = [IntType] int @@ -6075,7 +6075,7 @@ ir.cpp: # 727| 0: [VariableAccess] b # 727| Type = [BoolType] bool # 727| ValueCategory = prvalue(load) -# 727| 1: [Block] { ... } +# 727| 1: [BlockStmt] { ... } # 728| 0: [ExprStmt] ExprStmt # 728| 0: [ThrowExpr] throw ... # 728| Type = [PointerType] const char * @@ -6098,7 +6098,7 @@ ir.cpp: # 730| Type = [IntType] int # 730| Value = [Literal] 2 # 730| ValueCategory = prvalue -# 730| 1: [Block] { ... } +# 730| 1: [BlockStmt] { ... } # 731| 0: [ExprStmt] ExprStmt # 731| 0: [AssignExpr] ... = ... # 731| Type = [IntType] int @@ -6165,7 +6165,7 @@ ir.cpp: # 745| params: #-----| 0: [Parameter] p#0 #-----| Type = [LValueReferenceType] const Base & -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ExprStmt] ExprStmt #-----| 0: [ReferenceDereferenceExpr] (reference dereference) #-----| Type = [Struct] String @@ -6215,7 +6215,7 @@ ir.cpp: # 745| 0: [ConstructorCall] call to String # 745| Type = [VoidType] void # 745| ValueCategory = prvalue -# 745| body: [Block] { ... } +# 745| body: [BlockStmt] { ... } # 745| 0: [ReturnStmt] return ... # 748| [Constructor] void Base::Base() # 748| params: @@ -6226,11 +6226,11 @@ ir.cpp: # 748| 0: [ConstructorCall] call to String # 748| Type = [VoidType] void # 748| ValueCategory = prvalue -# 748| body: [Block] { ... } +# 748| body: [BlockStmt] { ... } # 749| 0: [ReturnStmt] return ... # 750| [Destructor] void Base::~Base() # 750| params: -# 750| body: [Block] { ... } +# 750| body: [BlockStmt] { ... } # 751| 0: [ReturnStmt] return ... # 750| destructions: # 751| 0: [DestructorFieldDestruction] destructor field destruction of base_s @@ -6246,7 +6246,7 @@ ir.cpp: # 754| params: #-----| 0: [Parameter] p#0 #-----| Type = [LValueReferenceType] const Middle & -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ExprStmt] ExprStmt #-----| 0: [ReferenceDereferenceExpr] (reference dereference) #-----| Type = [Struct,VirtualBaseClass] Base @@ -6334,11 +6334,11 @@ ir.cpp: # 757| 0: [ConstructorCall] call to String # 757| Type = [VoidType] void # 757| ValueCategory = prvalue -# 757| body: [Block] { ... } +# 757| body: [BlockStmt] { ... } # 758| 0: [ReturnStmt] return ... # 759| [Destructor] void Middle::~Middle() # 759| params: -# 759| body: [Block] { ... } +# 759| body: [BlockStmt] { ... } # 760| 0: [ReturnStmt] return ... # 759| destructions: # 760| 0: [DestructorFieldDestruction] destructor field destruction of middle_s @@ -6357,7 +6357,7 @@ ir.cpp: # 763| params: #-----| 0: [Parameter] p#0 #-----| Type = [LValueReferenceType] const Derived & -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ExprStmt] ExprStmt #-----| 0: [ReferenceDereferenceExpr] (reference dereference) #-----| Type = [Struct] Middle @@ -6445,11 +6445,11 @@ ir.cpp: # 766| 0: [ConstructorCall] call to String # 766| Type = [VoidType] void # 766| ValueCategory = prvalue -# 766| body: [Block] { ... } +# 766| body: [BlockStmt] { ... } # 767| 0: [ReturnStmt] return ... # 768| [Destructor] void Derived::~Derived() # 768| params: -# 768| body: [Block] { ... } +# 768| body: [BlockStmt] { ... } # 769| 0: [ReturnStmt] return ... # 768| destructions: # 769| 0: [DestructorFieldDestruction] destructor field destruction of derived_s @@ -6484,11 +6484,11 @@ ir.cpp: # 775| 0: [ConstructorCall] call to String # 775| Type = [VoidType] void # 775| ValueCategory = prvalue -# 775| body: [Block] { ... } +# 775| body: [BlockStmt] { ... } # 776| 0: [ReturnStmt] return ... # 777| [Destructor] void MiddleVB1::~MiddleVB1() # 777| params: -# 777| body: [Block] { ... } +# 777| body: [BlockStmt] { ... } # 778| 0: [ReturnStmt] return ... # 777| destructions: # 778| 0: [DestructorFieldDestruction] destructor field destruction of middlevb1_s @@ -6523,11 +6523,11 @@ ir.cpp: # 784| 0: [ConstructorCall] call to String # 784| Type = [VoidType] void # 784| ValueCategory = prvalue -# 784| body: [Block] { ... } +# 784| body: [BlockStmt] { ... } # 785| 0: [ReturnStmt] return ... # 786| [Destructor] void MiddleVB2::~MiddleVB2() # 786| params: -# 786| body: [Block] { ... } +# 786| body: [BlockStmt] { ... } # 787| 0: [ReturnStmt] return ... # 786| destructions: # 787| 0: [DestructorFieldDestruction] destructor field destruction of middlevb2_s @@ -6568,11 +6568,11 @@ ir.cpp: # 793| 0: [ConstructorCall] call to String # 793| Type = [VoidType] void # 793| ValueCategory = prvalue -# 793| body: [Block] { ... } +# 793| body: [BlockStmt] { ... } # 794| 0: [ReturnStmt] return ... # 795| [Destructor] void DerivedVB::~DerivedVB() # 795| params: -# 795| body: [Block] { ... } +# 795| body: [BlockStmt] { ... } # 796| 0: [ReturnStmt] return ... # 795| destructions: # 796| 0: [DestructorFieldDestruction] destructor field destruction of derivedvb_s @@ -6595,7 +6595,7 @@ ir.cpp: # 796| ValueCategory = prvalue # 799| [TopLevelFunction] void HierarchyConversions() # 799| params: -# 799| body: [Block] { ... } +# 799| body: [BlockStmt] { ... } # 800| 0: [DeclStmt] declaration # 800| 0: [VariableDeclarationEntry] definition of b # 800| Type = [Struct,VirtualBaseClass] Base @@ -7201,7 +7201,7 @@ ir.cpp: # 842| [Constructor] void PolymorphicBase::PolymorphicBase() # 842| params: # 842| initializations: -# 842| body: [Block] { ... } +# 842| body: [BlockStmt] { ... } # 842| 0: [ReturnStmt] return ... # 842| [CopyConstructor] void PolymorphicBase::PolymorphicBase(PolymorphicBase const&) # 842| params: @@ -7223,7 +7223,7 @@ ir.cpp: # 846| 0: [ConstructorDirectInit] call to PolymorphicBase # 846| Type = [VoidType] void # 846| ValueCategory = prvalue -# 846| body: [Block] { ... } +# 846| body: [BlockStmt] { ... } # 846| 0: [ReturnStmt] return ... # 846| [CopyConstructor] void PolymorphicDerived::PolymorphicDerived(PolymorphicDerived const&) # 846| params: @@ -7235,7 +7235,7 @@ ir.cpp: #-----| Type = [RValueReferenceType] PolymorphicDerived && # 846| [Destructor,VirtualFunction] void PolymorphicDerived::~PolymorphicDerived() # 846| params: -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ReturnStmt] return ... # 846| destructions: # 846| 0: [DestructorDirectDestruction] call to ~PolymorphicBase @@ -7243,7 +7243,7 @@ ir.cpp: # 846| ValueCategory = prvalue # 849| [TopLevelFunction] void DynamicCast() # 849| params: -# 849| body: [Block] { ... } +# 849| body: [BlockStmt] { ... } # 850| 0: [DeclStmt] declaration # 850| 0: [VariableDeclarationEntry] definition of b # 850| Type = [Struct] PolymorphicBase @@ -7370,11 +7370,11 @@ ir.cpp: # 868| Type = [ArrayType] const char[1] # 868| Value = [StringLiteral] "" # 868| ValueCategory = lvalue -# 868| body: [Block] { ... } +# 868| body: [BlockStmt] { ... } # 869| 0: [ReturnStmt] return ... # 871| [TopLevelFunction] void ArrayConversions() # 871| params: -# 871| body: [Block] { ... } +# 871| body: [BlockStmt] { ... } # 872| 0: [DeclStmt] declaration # 872| 0: [VariableDeclarationEntry] definition of a # 872| Type = [ArrayType] char[5] @@ -7513,7 +7513,7 @@ ir.cpp: # 883| Type = [FunctionPointerType] ..(*)(..) # 883| 1: [Parameter] p # 883| Type = [VoidPointerType] void * -# 883| body: [Block] { ... } +# 883| body: [BlockStmt] { ... } # 884| 0: [ExprStmt] ExprStmt # 884| 0: [AssignExpr] ... = ... # 884| Type = [VoidPointerType] void * @@ -7549,7 +7549,7 @@ ir.cpp: # 888| Type = [IntType] int # 888| 1: [Parameter] args # 888| Type = [ArrayType] __va_list_tag[1] -# 888| body: [Block] { ... } +# 888| body: [BlockStmt] { ... } # 889| 0: [DeclStmt] declaration # 889| 0: [VariableDeclarationEntry] definition of args2 # 889| Type = [ArrayType] __va_list_tag[1] @@ -7605,7 +7605,7 @@ ir.cpp: # 896| params: # 896| 0: [Parameter] x # 896| Type = [IntType] int -# 896| body: [Block] { ... } +# 896| body: [BlockStmt] { ... } # 897| 0: [DeclStmt] declaration # 897| 0: [VariableDeclarationEntry] definition of args # 897| Type = [ArrayType] __va_list_tag[1] @@ -7709,7 +7709,7 @@ ir.cpp: # 909| params: # 909| 0: [Parameter] x # 909| Type = [IntType] int -# 909| body: [Block] { ... } +# 909| body: [BlockStmt] { ... } # 910| 0: [ExprStmt] ExprStmt # 910| 0: [CStyleCast] (void)... # 910| Conversion = [VoidConversion] conversion to void @@ -7723,7 +7723,7 @@ ir.cpp: # 913| params: # 913| 0: [Parameter] x # 913| Type = [IntType] int -# 913| body: [Block] { ... } +# 913| body: [BlockStmt] { ... } # 914| 0: [DeclStmt] declaration # 914| 0: [VariableDeclarationEntry] definition of a # 914| Type = [BoolType] bool @@ -7876,7 +7876,7 @@ ir.cpp: # 946| Type = [DoubleType] double # 949| [TopLevelFunction] void OperatorNew() # 949| params: -# 949| body: [Block] { ... } +# 949| body: [BlockStmt] { ... } # 950| 0: [ExprStmt] ExprStmt # 950| 0: [NewExpr] new # 950| Type = [IntPointerType] int * @@ -7969,7 +7969,7 @@ ir.cpp: # 959| params: # 959| 0: [Parameter] n # 959| Type = [IntType] int -# 959| body: [Block] { ... } +# 959| body: [BlockStmt] { ... } # 960| 0: [ExprStmt] ExprStmt # 960| 0: [NewArrayExpr] new[] # 960| Type = [IntPointerType] int * @@ -8078,7 +8078,7 @@ ir.cpp: # 968| 8: [ReturnStmt] return ... # 970| [TopLevelFunction] int designatedInit() # 970| params: -# 970| body: [Block] { ... } +# 970| body: [BlockStmt] { ... } # 971| 0: [DeclStmt] declaration # 971| 0: [VariableDeclarationEntry] definition of a1 # 971| Type = [ArrayType] int[1000] @@ -8114,7 +8114,7 @@ ir.cpp: # 975| Type = [IntType] int # 975| 1: [Parameter] y # 975| Type = [IntType] int -# 975| body: [Block] { ... } +# 975| body: [BlockStmt] { ... } # 976| 0: [IfStmt] if (...) ... # 976| 0: [ConditionDeclExpr] (condition decl) # 976| Type = [BoolType] bool @@ -8122,7 +8122,7 @@ ir.cpp: # 976| 0: [VariableAccess] b # 976| Type = [BoolType] bool # 976| ValueCategory = prvalue(load) -# 976| 1: [Block] { ... } +# 976| 1: [BlockStmt] { ... } # 977| 0: [ExprStmt] ExprStmt # 977| 0: [AssignExpr] ... = ... # 977| Type = [IntType] int @@ -8145,7 +8145,7 @@ ir.cpp: # 979| expr: [VariableAccess] z # 979| Type = [IntType] int # 979| ValueCategory = prvalue(load) -# 979| 1: [Block] { ... } +# 979| 1: [BlockStmt] { ... } # 980| 0: [ExprStmt] ExprStmt # 980| 0: [AssignExpr] ... = ... # 980| Type = [IntType] int @@ -8168,7 +8168,7 @@ ir.cpp: # 982| expr: [VariableAccess] p # 982| Type = [IntPointerType] int * # 982| ValueCategory = prvalue(load) -# 982| 1: [Block] { ... } +# 982| 1: [BlockStmt] { ... } # 983| 0: [ExprStmt] ExprStmt # 983| 0: [AssignExpr] ... = ... # 983| Type = [IntType] int @@ -8190,7 +8190,7 @@ ir.cpp: # 987| Type = [IntType] int # 987| 1: [Parameter] y # 987| Type = [IntType] int -# 987| body: [Block] { ... } +# 987| body: [BlockStmt] { ... } # 988| 0: [WhileStmt] while (...) ... # 988| 0: [ConditionDeclExpr] (condition decl) # 988| Type = [BoolType] bool @@ -8198,7 +8198,7 @@ ir.cpp: # 988| 0: [VariableAccess] b # 988| Type = [BoolType] bool # 988| ValueCategory = prvalue(load) -# 988| 1: [Block] { ... } +# 988| 1: [BlockStmt] { ... } # 990| 1: [WhileStmt] while (...) ... # 990| 0: [ConditionDeclExpr] (condition decl) # 990| Type = [BoolType] bool @@ -8210,7 +8210,7 @@ ir.cpp: # 990| expr: [VariableAccess] z # 990| Type = [IntType] int # 990| ValueCategory = prvalue(load) -# 990| 1: [Block] { ... } +# 990| 1: [BlockStmt] { ... } # 992| 2: [WhileStmt] while (...) ... # 992| 0: [ConditionDeclExpr] (condition decl) # 992| Type = [BoolType] bool @@ -8222,7 +8222,7 @@ ir.cpp: # 992| expr: [VariableAccess] p # 992| Type = [IntPointerType] int * # 992| ValueCategory = prvalue(load) -# 992| 1: [Block] { ... } +# 992| 1: [BlockStmt] { ... } # 994| 3: [ReturnStmt] return ... # 996| [TopLevelFunction] int PointerDecay(int[], int(float)) # 996| params: @@ -8230,7 +8230,7 @@ ir.cpp: # 996| Type = [ArrayType] int[] # 996| 1: [Parameter] fn # 996| Type = [RoutineType] ..()(..) -# 996| body: [Block] { ... } +# 996| body: [BlockStmt] { ... } # 997| 0: [ReturnStmt] return ... # 997| 0: [AddExpr] ... + ... # 997| Type = [IntType] int @@ -8268,7 +8268,7 @@ ir.cpp: # 1000| Type = [IntType] int # 1000| 2: [Parameter] z # 1000| Type = [IntType] int -# 1000| body: [Block] { ... } +# 1000| body: [BlockStmt] { ... } # 1001| 0: [DeclStmt] declaration # 1001| 0: [VariableDeclarationEntry] definition of x # 1001| Type = [IntType] int @@ -8282,7 +8282,7 @@ ir.cpp: # 1011| ValueCategory = prvalue # 1015| [TopLevelFunction] void OperatorDelete() # 1015| params: -# 1015| body: [Block] { ... } +# 1015| body: [BlockStmt] { ... } # 1016| 0: [ExprStmt] ExprStmt # 1016| 0: [DeleteExpr] delete # 1016| Type = [VoidType] void @@ -8360,7 +8360,7 @@ ir.cpp: # 1021| 5: [ReturnStmt] return ... # 1024| [TopLevelFunction] void OperatorDeleteArray() # 1024| params: -# 1024| body: [Block] { ... } +# 1024| body: [BlockStmt] { ... } # 1025| 0: [ExprStmt] ExprStmt # 1025| 0: [DeleteArrayExpr] delete[] # 1025| Type = [VoidType] void @@ -8446,7 +8446,7 @@ ir.cpp: #-----| Type = [RValueReferenceType] EmptyStruct && # 1034| [TopLevelFunction] void EmptyStructInit() # 1034| params: -# 1034| body: [Block] { ... } +# 1034| body: [BlockStmt] { ... } # 1035| 0: [DeclStmt] declaration # 1035| 0: [VariableDeclarationEntry] definition of s # 1035| Type = [Struct] EmptyStruct @@ -8473,11 +8473,11 @@ ir.cpp: # 1038| params: # 1038| [ConstMemberFunction] void (lambda [] type at line 1038, col. 12)::operator()() const # 1038| params: -# 1038| body: [Block] { ... } +# 1038| body: [BlockStmt] { ... } # 1038| 0: [ReturnStmt] return ... # 1038| [ConstMemberFunction,ConversionOperator] void(* (lambda [] type at line 1038, col. 12)::operator void (*)()() const)() # 1038| params: -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } # 1038| 0: [ReturnStmt] return ... # 1038| 0: [FunctionAccess] _FUN # 1038| Type = [FunctionPointerType] ..(*)(..) @@ -8488,7 +8488,7 @@ ir.cpp: # 1040| Type = [IntType] int # 1040| 1: [Parameter] s # 1040| Type = [LValueReferenceType] const String & -# 1040| body: [Block] { ... } +# 1040| body: [BlockStmt] { ... } # 1041| 0: [DeclStmt] declaration # 1041| 0: [VariableDeclarationEntry] definition of lambda_empty # 1041| Type = [Closure,LocalClass] decltype([...](...){...}) @@ -8807,7 +8807,7 @@ ir.cpp: # 1041| params: # 1041| 0: [Parameter] f # 1041| Type = [FloatType] float -# 1041| body: [Block] { ... } +# 1041| body: [BlockStmt] { ... } # 1041| 0: [ReturnStmt] return ... # 1041| 0: [CharLiteral] 65 # 1041| Type = [PlainCharType] char @@ -8815,7 +8815,7 @@ ir.cpp: # 1041| ValueCategory = prvalue # 1041| [ConstMemberFunction,ConversionOperator] char(* (void Lambda(int, String const&))::(lambda [] type at line 1041, col. 23)::operator char (*)(float)() const)(float) # 1041| params: -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } # 1041| 0: [ReturnStmt] return ... # 1041| 0: [FunctionAccess] _FUN # 1041| Type = [FunctionPointerType] ..(*)(..) @@ -8838,7 +8838,7 @@ ir.cpp: # 1043| params: # 1043| 0: [Parameter] f # 1043| Type = [FloatType] float -# 1043| body: [Block] { ... } +# 1043| body: [BlockStmt] { ... } # 1043| 0: [ReturnStmt] return ... # 1043| 0: [ArrayExpr] access to array # 1043| Type = [PlainCharType] char @@ -8880,7 +8880,7 @@ ir.cpp: # 1045| params: # 1045| [Destructor] void (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::~() # 1045| params: -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ReturnStmt] return ... # 1045| destructions: # 1045| 0: [DestructorFieldDestruction] destructor field destruction of s @@ -8896,7 +8896,7 @@ ir.cpp: # 1045| params: # 1045| 0: [Parameter] f # 1045| Type = [FloatType] float -# 1045| body: [Block] { ... } +# 1045| body: [BlockStmt] { ... } # 1045| 0: [ReturnStmt] return ... # 1045| 0: [ArrayExpr] access to array # 1045| Type = [PlainCharType] char @@ -8934,7 +8934,7 @@ ir.cpp: # 1047| params: # 1047| 0: [Parameter] f # 1047| Type = [FloatType] float -# 1047| body: [Block] { ... } +# 1047| body: [BlockStmt] { ... } # 1047| 0: [ReturnStmt] return ... # 1047| 0: [ArrayExpr] access to array # 1047| Type = [PlainCharType] char @@ -8971,7 +8971,7 @@ ir.cpp: # 1049| params: # 1049| [Destructor] void (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::~() # 1049| params: -#-----| body: [Block] { ... } +#-----| body: [BlockStmt] { ... } #-----| 0: [ReturnStmt] return ... # 1049| destructions: # 1049| 0: [DestructorFieldDestruction] destructor field destruction of s @@ -8987,7 +8987,7 @@ ir.cpp: # 1049| params: # 1049| 0: [Parameter] f # 1049| Type = [FloatType] float -# 1049| body: [Block] { ... } +# 1049| body: [BlockStmt] { ... } # 1049| 0: [ReturnStmt] return ... # 1049| 0: [ArrayExpr] access to array # 1049| Type = [PlainCharType] char @@ -9023,7 +9023,7 @@ ir.cpp: # 1051| params: # 1051| 0: [Parameter] f # 1051| Type = [FloatType] float -# 1051| body: [Block] { ... } +# 1051| body: [BlockStmt] { ... } # 1051| 0: [ReturnStmt] return ... # 1051| 0: [ArrayExpr] access to array # 1051| Type = [PlainCharType] char @@ -9064,7 +9064,7 @@ ir.cpp: # 1054| params: # 1054| 0: [Parameter] f # 1054| Type = [FloatType] float -# 1054| body: [Block] { ... } +# 1054| body: [BlockStmt] { ... } # 1054| 0: [ReturnStmt] return ... # 1054| 0: [ArrayExpr] access to array # 1054| Type = [PlainCharType] char @@ -9164,7 +9164,7 @@ ir.cpp: # 1077| params: # 1077| 0: [Parameter] v # 1077| Type = [LValueReferenceType] const vector & -# 1077| body: [Block] { ... } +# 1077| body: [BlockStmt] { ... } # 1078| 0: [RangeBasedForStmt] for(...:...) ... # 1078| 0: [DeclStmt] declaration # 1078| 1: [DeclStmt] declaration @@ -9191,7 +9191,7 @@ ir.cpp: # 1078| Type = [NestedStruct] iterator # 1078| ValueCategory = lvalue # 1078| 4: [DeclStmt] declaration -# 1078| 5: [Block] { ... } +# 1078| 5: [BlockStmt] { ... } # 1079| 0: [IfStmt] if (...) ... # 1079| 0: [GTExpr] ... > ... # 1079| Type = [BoolType] bool @@ -9203,7 +9203,7 @@ ir.cpp: # 1079| Type = [IntType] int # 1079| Value = [Literal] 0 # 1079| ValueCategory = prvalue -# 1079| 1: [Block] { ... } +# 1079| 1: [BlockStmt] { ... } # 1080| 0: [ContinueStmt] continue; # 1078| 1: [LabelStmt] label ...: # 1084| 1: [RangeBasedForStmt] for(...:...) ... @@ -9232,7 +9232,7 @@ ir.cpp: # 1084| Type = [NestedStruct] iterator # 1084| ValueCategory = lvalue # 1084| 4: [DeclStmt] declaration -# 1084| 5: [Block] { ... } +# 1084| 5: [BlockStmt] { ... } # 1085| 0: [IfStmt] if (...) ... # 1085| 0: [LTExpr] ... < ... # 1085| Type = [BoolType] bool @@ -9247,7 +9247,7 @@ ir.cpp: # 1085| Type = [IntType] int # 1085| Value = [Literal] 5 # 1085| ValueCategory = prvalue -# 1085| 1: [Block] { ... } +# 1085| 1: [BlockStmt] { ... } # 1086| 0: [BreakStmt] break; # 1088| 2: [LabelStmt] label ...: # 1089| 3: [ReturnStmt] return ... @@ -9255,7 +9255,7 @@ ir.cpp: # 1108| params: # 1108| 0: [Parameter] x # 1108| Type = [IntType] int -# 1108| body: [Block] { ... } +# 1108| body: [BlockStmt] { ... } # 1109| 0: [AsmStmt] asm statement # 1110| 1: [ReturnStmt] return ... # 1110| 0: [VariableAccess] x @@ -9271,7 +9271,7 @@ ir.cpp: # 1113| Type = [LValueReferenceType] unsigned int & # 1113| 3: [Parameter] d # 1113| Type = [IntType] unsigned int -# 1114| body: [Block] { ... } +# 1114| body: [BlockStmt] { ... } # 1115| 0: [AsmStmt] asm statement # 1118| 0: [ReferenceDereferenceExpr] (reference dereference) # 1118| Type = [IntType] unsigned int @@ -9294,7 +9294,7 @@ ir.cpp: # 1120| 1: [ReturnStmt] return ... # 1122| [TopLevelFunction] void ExternDeclarations() # 1122| params: -# 1123| body: [Block] { ... } +# 1123| body: [BlockStmt] { ... } # 1124| 0: [DeclStmt] declaration # 1124| 0: [VariableDeclarationEntry] declaration of g # 1124| Type = [IntType] int @@ -9331,7 +9331,7 @@ ir.cpp: # 1127| Type = [FloatType] float # 1137| [TopLevelFunction] void ExternDeclarationsInMacro() # 1137| params: -# 1138| body: [Block] { ... } +# 1138| body: [BlockStmt] { ... } # 1139| 0: [DeclStmt] declaration # 1139| 0: [VariableDeclarationEntry] declaration of g # 1139| Type = [IntType] int @@ -9360,7 +9360,7 @@ ir.cpp: # 1139| 0: [VariableAccess] i # 1139| Type = [IntType] int # 1139| ValueCategory = lvalue -# 1139| 3: [Block] { ... } +# 1139| 3: [BlockStmt] { ... } # 1139| 0: [DeclStmt] declaration # 1139| 0: [VariableDeclarationEntry] declaration of g # 1139| Type = [IntType] int @@ -9370,9 +9370,9 @@ ir.cpp: # 1142| params: # 1142| 0: [Parameter] b # 1142| Type = [BoolType] bool -# 1142| body: [Block] { ... } +# 1142| body: [BlockStmt] { ... } # 1143| 0: [TryStmt] try { ... } -# 1143| 0: [Block] { ... } +# 1143| 0: [BlockStmt] { ... } # 1144| 0: [DeclStmt] declaration # 1144| 0: [VariableDeclarationEntry] definition of x # 1144| Type = [IntType] int @@ -9385,7 +9385,7 @@ ir.cpp: # 1145| 0: [VariableAccess] b # 1145| Type = [BoolType] bool # 1145| ValueCategory = prvalue(load) -# 1145| 1: [Block] { ... } +# 1145| 1: [BlockStmt] { ... } # 1146| 0: [ExprStmt] ExprStmt # 1146| 0: [ThrowExpr] throw ... # 1146| Type = [PointerType] const char * @@ -9408,7 +9408,7 @@ ir.cpp: # 1148| Type = [IntType] int # 1148| Value = [Literal] 2 # 1148| ValueCategory = prvalue -# 1148| 1: [Block] { ... } +# 1148| 1: [BlockStmt] { ... } # 1149| 0: [ExprStmt] ExprStmt # 1149| 0: [AssignExpr] ... = ... # 1149| Type = [IntType] int @@ -9469,7 +9469,7 @@ ir.cpp: # 1162| params: # 1162| 0: [Parameter] i # 1162| Type = [IntType] int -# 1162| body: [Block] { ... } +# 1162| body: [BlockStmt] { ... } # 1163| 0: [DeclStmt] declaration # 1163| 0: [VariableDeclarationEntry] definition of vi4 # 1163| Type = [SpecifiedType] __attribute((vector_size(16UL))) int @@ -9588,7 +9588,7 @@ ir.cpp: # 1172| params: # 1172| 0: [Parameter] x # 1172| Type = [IntType] int -# 1172| body: [Block] { ... } +# 1172| body: [BlockStmt] { ... } # 1173| 0: [DeclStmt] declaration # 1173| 0: [VariableDeclarationEntry] definition of y # 1173| Type = [IntType] int @@ -9631,7 +9631,7 @@ ir.cpp: # 1175| ValueCategory = prvalue(load) # 1178| [TopLevelFunction] String ReturnObjectImpl() # 1178| params: -# 1178| body: [Block] { ... } +# 1178| body: [BlockStmt] { ... } # 1179| 0: [ReturnStmt] return ... # 1179| 0: [ConstructorCall] call to String # 1179| Type = [Struct] String @@ -9647,7 +9647,7 @@ ir.cpp: # 1182| params: # 1182| 0: [Parameter] x # 1182| Type = [IntType] int -# 1182| body: [Block] { ... } +# 1182| body: [BlockStmt] { ... } # 1183| 0: [DeclStmt] declaration # 1183| 0: [VariableDeclarationEntry] definition of y # 1183| Type = [IntType] int @@ -9660,7 +9660,7 @@ ir.cpp: # 1184| 0: [VariableAccess] x # 1184| Type = [IntType] int # 1184| ValueCategory = prvalue(load) -# 1184| 1: [Block] { ... } +# 1184| 1: [BlockStmt] { ... } # 1185| 0: [SwitchCase] case ...: # 1185| 0: [Literal] 1 # 1185| Type = [IntType] int @@ -9689,7 +9689,7 @@ ir.cpp: # 1191| params: # 1191| 0: [Parameter] x # 1191| Type = [IntType] int -# 1191| body: [Block] { ... } +# 1191| body: [BlockStmt] { ... } # 1192| 0: [DeclStmt] declaration # 1192| 0: [VariableDeclarationEntry] definition of y # 1192| Type = [IntType] int @@ -9702,7 +9702,7 @@ ir.cpp: # 1193| 0: [VariableAccess] x # 1193| Type = [IntType] int # 1193| ValueCategory = prvalue(load) -# 1193| 1: [Block] { ... } +# 1193| 1: [BlockStmt] { ... } # 1194| 0: [SwitchCase] case ...: # 1194| 0: [Literal] 1 # 1194| Type = [IntType] int @@ -9747,7 +9747,7 @@ ir.cpp: # 1202| params: # 1202| 0: [Parameter] x # 1202| Type = [IntType] int -# 1202| body: [Block] { ... } +# 1202| body: [BlockStmt] { ... } # 1203| 0: [DeclStmt] declaration # 1203| 0: [VariableDeclarationEntry] definition of y # 1203| Type = [IntType] int @@ -9760,7 +9760,7 @@ ir.cpp: # 1204| 0: [VariableAccess] x # 1204| Type = [IntType] int # 1204| ValueCategory = prvalue(load) -# 1204| 1: [Block] { ... } +# 1204| 1: [BlockStmt] { ... } # 1205| 0: [SwitchCase] case ...: # 1205| 0: [Literal] 1 # 1205| Type = [IntType] int @@ -9807,7 +9807,7 @@ ir.cpp: # 1214| params: # 1214| 0: [Parameter] x # 1214| Type = [IntType] int -# 1214| body: [Block] { ... } +# 1214| body: [BlockStmt] { ... } # 1215| 0: [DeclStmt] declaration # 1215| 0: [VariableDeclarationEntry] definition of y # 1215| Type = [IntType] int @@ -9820,7 +9820,7 @@ ir.cpp: # 1216| 0: [VariableAccess] x # 1216| Type = [IntType] int # 1216| ValueCategory = prvalue(load) -# 1216| 1: [Block] { ... } +# 1216| 1: [BlockStmt] { ... } # 1217| 0: [SwitchCase] case ...: # 1217| 0: [Literal] 1 # 1217| Type = [IntType] int @@ -9880,7 +9880,7 @@ ir.cpp: # 1231| params: # 1231| 0: [Parameter] x # 1231| Type = [IntType] int -# 1231| body: [Block] { ... } +# 1231| body: [BlockStmt] { ... } # 1232| 0: [DeclStmt] declaration # 1232| 0: [VariableDeclarationEntry] definition of a # 1232| Type = [IntType] int @@ -9944,7 +9944,7 @@ ir.cpp: # 1240| params: # 1240| 0: [Parameter] dynamic # 1240| Type = [PointerType] const char * -# 1240| body: [Block] { ... } +# 1240| body: [BlockStmt] { ... } # 1241| 0: [DeclStmt] declaration # 1241| 0: [VariableDeclarationEntry] definition of a # 1241| Type = [Struct] String @@ -9995,7 +9995,7 @@ ir.cpp: # 1251| Type = [CharPointerType] char * # 1251| 1: [Parameter] s2 # 1251| Type = [CharPointerType] char * -# 1251| body: [Block] { ... } +# 1251| body: [BlockStmt] { ... } # 1252| 0: [DeclStmt] declaration # 1252| 0: [VariableDeclarationEntry] definition of buffer # 1252| Type = [ArrayType] char[1024] @@ -10061,7 +10061,7 @@ ir.cpp: # 1261| Type = [PointerType] A * # 1261| 1: [Parameter] x # 1261| Type = [IntType] int -# 1261| body: [Block] { ... } +# 1261| body: [BlockStmt] { ... } # 1262| 0: [ExprStmt] ExprStmt # 1262| 0: [AssignExpr] ... = ... # 1262| Type = [IntType] int @@ -10086,7 +10086,7 @@ ir.cpp: # 1270| Type = [IntType] int # 1270| 1: [Parameter] a_arg # 1270| Type = [PointerType] A * -# 1270| body: [Block] { ... } +# 1270| body: [BlockStmt] { ... } # 1271| 0: [DeclStmt] declaration # 1271| 0: [VariableDeclarationEntry] definition of c # 1271| Type = [Class] C @@ -10237,12 +10237,12 @@ ir.cpp: # 1289| Type = [BoolType] bool # 1289| 1: [Parameter] x # 1289| Type = [IntType] int -# 1289| body: [Block] { ... } +# 1289| body: [BlockStmt] { ... } # 1290| 0: [IfStmt] if (...) ... # 1290| 0: [VariableAccess] b # 1290| Type = [BoolType] bool # 1290| ValueCategory = prvalue(load) -# 1290| 1: [Block] { ... } +# 1290| 1: [BlockStmt] { ... } # 1291| 0: [ReturnStmt] return ... # 1291| 0: [VariableAccess] x # 1291| Type = [IntType] int @@ -10254,7 +10254,7 @@ ir.cpp: # 1295| Type = [IntType] int # 1295| 1: [Parameter] y # 1295| Type = [IntType] int -# 1295| body: [Block] { ... } +# 1295| body: [BlockStmt] { ... } # 1296| 0: [ReturnStmt] return ... # 1296| 0: [FunctionCall] call to IntegerOps # 1296| Type = [VoidType] void @@ -10273,7 +10273,7 @@ ir.cpp: # 1299| Type = [IntType] int # 1299| 2: [Parameter] y # 1299| Type = [LongType] long -# 1299| body: [Block] { ... } +# 1299| body: [BlockStmt] { ... } # 1300| 0: [DeclStmt] declaration # 1300| 0: [VariableDeclarationEntry] definition of z # 1300| Type = [IntType] int @@ -10463,7 +10463,7 @@ ir.cpp: # 1314| Type = [IntType] int # 1314| 1: [Parameter] y # 1314| Type = [IntType] int -# 1314| body: [Block] { ... } +# 1314| body: [BlockStmt] { ... } # 1315| 0: [ReturnStmt] return ... # 1315| 0: [ConditionalExpr] ... ? ... : ... # 1315| Type = [IntType] int @@ -10493,7 +10493,7 @@ ir.cpp: # 1320| params: # 1320| 0: [Parameter] p # 1320| Type = [IntPointerType] int * -# 1321| body: [Block] { ... } +# 1321| body: [BlockStmt] { ... } # 1322| 0: [ExprStmt] ExprStmt # 1322| 0: [NewExpr] new # 1322| Type = [IntPointerType] int * @@ -10538,11 +10538,11 @@ perf-regression.cpp: # 6| 0: [ArrayAggregateLiteral] {...} # 6| Type = [ArrayType] char[1073741824] # 6| ValueCategory = prvalue -# 6| body: [Block] { ... } +# 6| body: [BlockStmt] { ... } # 6| 0: [ReturnStmt] return ... # 9| [TopLevelFunction] int main() # 9| params: -# 9| body: [Block] { ... } +# 9| body: [BlockStmt] { ... } # 10| 0: [DeclStmt] declaration # 10| 0: [VariableDeclarationEntry] definition of big # 10| Type = [PointerType] Big * @@ -10579,7 +10579,7 @@ struct_init.cpp: # 16| params: # 16| 0: [Parameter] info # 16| Type = [PointerType] Info * -# 16| body: [Block] { ... } +# 16| body: [BlockStmt] { ... } # 17| 0: [ExprStmt] ExprStmt # 17| 0: [AssignExpr] ... = ... # 17| Type = [PointerType] Info * @@ -10593,7 +10593,7 @@ struct_init.cpp: # 18| 1: [ReturnStmt] return ... # 20| [TopLevelFunction] void declare_static_infos() # 20| params: -# 20| body: [Block] { ... } +# 20| body: [BlockStmt] { ... } # 21| 0: [DeclStmt] declaration # 21| 0: [VariableDeclarationEntry] definition of static_infos # 21| Type = [ArrayType] Info[] @@ -10643,7 +10643,7 @@ struct_init.cpp: # 26| 2: [ReturnStmt] return ... # 28| [TopLevelFunction] void declare_local_infos() # 28| params: -# 28| body: [Block] { ... } +# 28| body: [BlockStmt] { ... } # 29| 0: [DeclStmt] declaration # 29| 0: [VariableDeclarationEntry] definition of local_infos # 29| Type = [ArrayType] Info[] @@ -10695,7 +10695,7 @@ struct_init.cpp: # 36| params: # 36| 0: [Parameter] name1 # 36| Type = [PointerType] const char * -# 36| body: [Block] { ... } +# 36| body: [BlockStmt] { ... } # 37| 0: [DeclStmt] declaration # 37| 0: [VariableDeclarationEntry] definition of static_infos # 37| Type = [ArrayType] Info[] diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected index 3a1a30265b2a..64172ad18738 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected @@ -1,8 +1,8 @@ missingOperand -| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | +| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | unexpectedOperand duplicateOperand missingPhiOperand @@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected index 3a1a30265b2a..64172ad18738 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected @@ -1,8 +1,8 @@ missingOperand -| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | +| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | unexpectedOperand duplicateOperand missingPhiOperand @@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected index 3a1a30265b2a..64172ad18738 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected @@ -1,8 +1,8 @@ missingOperand -| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | +| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | unexpectedOperand duplicateOperand missingPhiOperand @@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 6af78798ce04..a2be838ae483 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -21,10 +21,11 @@ bad_asts.cpp: # 10| r10_9(int) = Load : &:r10_8, ~m? # 10| r10_10(int) = Add : r10_7, r10_9 # 10| mu10_11(int) = Store : &:r10_1, r10_10 -# 9| r9_10(glval) = VariableAddress[#return] : -# 9| v9_11(void) = ReturnValue : &:r9_10, ~m? -# 9| v9_12(void) = AliasedUse : ~m? -# 9| v9_13(void) = ExitFunction : +# 9| v9_10(void) = ReturnIndirection[#this] : &:r9_6, ~m? +# 9| r9_11(glval) = VariableAddress[#return] : +# 9| v9_12(void) = ReturnValue : &:r9_11, ~m? +# 9| v9_13(void) = AliasedUse : ~m? +# 9| v9_14(void) = ExitFunction : # 14| void Bad::CallBadMemberFunction() # 14| Block 0 @@ -58,9 +59,10 @@ bad_asts.cpp: # 22| r22_6(glval) = Load : &:r22_4, ~m? # 22| mu22_7(Point) = InitializeIndirection[#this] : &:r22_6 # 23| v23_1(void) = NoOp : -# 22| v22_8(void) = ReturnVoid : -# 22| v22_9(void) = AliasedUse : ~m? -# 22| v22_10(void) = ExitFunction : +# 22| v22_8(void) = ReturnIndirection[#this] : &:r22_6, ~m? +# 22| v22_9(void) = ReturnVoid : +# 22| v22_10(void) = AliasedUse : ~m? +# 22| v22_11(void) = ExitFunction : # 26| void Bad::CallCopyConstructor(Bad::Point const&) # 26| Block 0 @@ -3448,9 +3450,10 @@ ir.cpp: # 628| r628_13(glval) = FunctionAddress[~String] : # 628| v628_14(void) = Call : func:r628_13, this:r628_12 # 628| mu628_15(unknown) = ^CallSideEffect : ~m? -# 628| v628_16(void) = ReturnVoid : -# 628| v628_17(void) = AliasedUse : ~m? -# 628| v628_18(void) = ExitFunction : +# 628| v628_16(void) = ReturnIndirection[#this] : &:r628_6, ~m? +# 628| v628_17(void) = ReturnVoid : +# 628| v628_18(void) = AliasedUse : ~m? +# 628| v628_19(void) = ExitFunction : # 630| int C::StaticMemberFunction(int) # 630| Block 0 @@ -3483,10 +3486,11 @@ ir.cpp: # 635| r635_2(glval) = VariableAddress[x] : # 635| r635_3(int) = Load : &:r635_2, ~m? # 635| mu635_4(int) = Store : &:r635_1, r635_3 -# 634| r634_10(glval) = VariableAddress[#return] : -# 634| v634_11(void) = ReturnValue : &:r634_10, ~m? -# 634| v634_12(void) = AliasedUse : ~m? -# 634| v634_13(void) = ExitFunction : +# 634| v634_10(void) = ReturnIndirection[#this] : &:r634_6, ~m? +# 634| r634_11(glval) = VariableAddress[#return] : +# 634| v634_12(void) = ReturnValue : &:r634_11, ~m? +# 634| v634_13(void) = AliasedUse : ~m? +# 634| v634_14(void) = ExitFunction : # 638| int C::VirtualMemberFunction(int) # 638| Block 0 @@ -3503,10 +3507,11 @@ ir.cpp: # 639| r639_2(glval) = VariableAddress[x] : # 639| r639_3(int) = Load : &:r639_2, ~m? # 639| mu639_4(int) = Store : &:r639_1, r639_3 -# 638| r638_10(glval) = VariableAddress[#return] : -# 638| v638_11(void) = ReturnValue : &:r638_10, ~m? -# 638| v638_12(void) = AliasedUse : ~m? -# 638| v638_13(void) = ExitFunction : +# 638| v638_10(void) = ReturnIndirection[#this] : &:r638_6, ~m? +# 638| r638_11(glval) = VariableAddress[#return] : +# 638| v638_12(void) = ReturnValue : &:r638_11, ~m? +# 638| v638_13(void) = AliasedUse : ~m? +# 638| v638_14(void) = ExitFunction : # 642| void C::FieldAccess() # 642| Block 0 @@ -3555,9 +3560,10 @@ ir.cpp: # 649| r649_5(glval) = VariableAddress[x] : # 649| mu649_6(int) = Store : &:r649_5, r649_4 # 650| v650_1(void) = NoOp : -# 642| v642_8(void) = ReturnVoid : -# 642| v642_9(void) = AliasedUse : ~m? -# 642| v642_10(void) = ExitFunction : +# 642| v642_8(void) = ReturnIndirection[#this] : &:r642_6, ~m? +# 642| v642_9(void) = ReturnVoid : +# 642| v642_10(void) = AliasedUse : ~m? +# 642| v642_11(void) = ExitFunction : # 652| void C::MethodCalls() # 652| Block 0 @@ -3594,9 +3600,10 @@ ir.cpp: # 655| v655_7(void) = ^BufferReadSideEffect[-1] : &:r655_2, ~m? # 655| mu655_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r655_2 # 656| v656_1(void) = NoOp : -# 652| v652_8(void) = ReturnVoid : -# 652| v652_9(void) = AliasedUse : ~m? -# 652| v652_10(void) = ExitFunction : +# 652| v652_8(void) = ReturnIndirection[#this] : &:r652_6, ~m? +# 652| v652_9(void) = ReturnVoid : +# 652| v652_10(void) = AliasedUse : ~m? +# 652| v652_11(void) = ExitFunction : # 658| void C::C() # 658| Block 0 @@ -3631,9 +3638,10 @@ ir.cpp: # 662| v662_8(void) = ^BufferReadSideEffect[0] : &:r662_4, ~m? # 662| mu662_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r662_4 # 664| v664_1(void) = NoOp : -# 658| v658_8(void) = ReturnVoid : -# 658| v658_9(void) = AliasedUse : ~m? -# 658| v658_10(void) = ExitFunction : +# 658| v658_8(void) = ReturnIndirection[#this] : &:r658_6, ~m? +# 658| v658_9(void) = ReturnVoid : +# 658| v658_10(void) = AliasedUse : ~m? +# 658| v658_11(void) = ExitFunction : # 675| int DerefReference(int&) # 675| Block 0 @@ -4014,11 +4022,12 @@ ir.cpp: #-----| r0_13(glval) = CopyValue : r0_12 #-----| r0_14(Base &) = CopyValue : r0_13 #-----| mu0_15(Base &) = Store : &:r0_10, r0_14 +# 745| v745_20(void) = ReturnIndirection[#this] : &:r745_6, ~m? #-----| v0_16(void) = ReturnIndirection[p#0] : &:r0_3, ~m? -# 745| r745_20(glval) = VariableAddress[#return] : -# 745| v745_21(void) = ReturnValue : &:r745_20, ~m? -# 745| v745_22(void) = AliasedUse : ~m? -# 745| v745_23(void) = ExitFunction : +# 745| r745_21(glval) = VariableAddress[#return] : +# 745| v745_22(void) = ReturnValue : &:r745_21, ~m? +# 745| v745_23(void) = AliasedUse : ~m? +# 745| v745_24(void) = ExitFunction : # 745| void Base::Base(Base const&) # 745| Block 0 @@ -4039,10 +4048,11 @@ ir.cpp: # 745| mu745_11(unknown) = ^CallSideEffect : ~m? # 745| mu745_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_8 # 745| v745_13(void) = NoOp : +# 745| v745_14(void) = ReturnIndirection[#this] : &:r745_6, ~m? #-----| v0_5(void) = ReturnIndirection[p#0] : &:r0_3, ~m? -# 745| v745_14(void) = ReturnVoid : -# 745| v745_15(void) = AliasedUse : ~m? -# 745| v745_16(void) = ExitFunction : +# 745| v745_15(void) = ReturnVoid : +# 745| v745_16(void) = AliasedUse : ~m? +# 745| v745_17(void) = ExitFunction : # 748| void Base::Base() # 748| Block 0 @@ -4059,9 +4069,10 @@ ir.cpp: # 748| mu748_11(unknown) = ^CallSideEffect : ~m? # 748| mu748_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r748_8 # 749| v749_1(void) = NoOp : -# 748| v748_13(void) = ReturnVoid : -# 748| v748_14(void) = AliasedUse : ~m? -# 748| v748_15(void) = ExitFunction : +# 748| v748_13(void) = ReturnIndirection[#this] : &:r748_6, ~m? +# 748| v748_14(void) = ReturnVoid : +# 748| v748_15(void) = AliasedUse : ~m? +# 748| v748_16(void) = ExitFunction : # 750| void Base::~Base() # 750| Block 0 @@ -4077,9 +4088,10 @@ ir.cpp: # 751| r751_3(glval) = FunctionAddress[~String] : # 751| v751_4(void) = Call : func:r751_3, this:r751_2 # 751| mu751_5(unknown) = ^CallSideEffect : ~m? -# 750| v750_8(void) = ReturnVoid : -# 750| v750_9(void) = AliasedUse : ~m? -# 750| v750_10(void) = ExitFunction : +# 750| v750_8(void) = ReturnIndirection[#this] : &:r750_6, ~m? +# 750| v750_9(void) = ReturnVoid : +# 750| v750_10(void) = AliasedUse : ~m? +# 750| v750_11(void) = ExitFunction : # 754| Middle& Middle::operator=(Middle const&) # 754| Block 0 @@ -4135,11 +4147,12 @@ ir.cpp: #-----| r0_22(glval) = CopyValue : r0_21 #-----| r0_23(Middle &) = CopyValue : r0_22 #-----| mu0_24(Middle &) = Store : &:r0_19, r0_23 +# 754| v754_29(void) = ReturnIndirection[#this] : &:r754_6, ~m? #-----| v0_25(void) = ReturnIndirection[p#0] : &:r0_3, ~m? -# 754| r754_29(glval) = VariableAddress[#return] : -# 754| v754_30(void) = ReturnValue : &:r754_29, ~m? -# 754| v754_31(void) = AliasedUse : ~m? -# 754| v754_32(void) = ExitFunction : +# 754| r754_30(glval) = VariableAddress[#return] : +# 754| v754_31(void) = ReturnValue : &:r754_30, ~m? +# 754| v754_32(void) = AliasedUse : ~m? +# 754| v754_33(void) = ExitFunction : # 757| void Middle::Middle() # 757| Block 0 @@ -4161,9 +4174,10 @@ ir.cpp: # 757| mu757_16(unknown) = ^CallSideEffect : ~m? # 757| mu757_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r757_13 # 758| v758_1(void) = NoOp : -# 757| v757_18(void) = ReturnVoid : -# 757| v757_19(void) = AliasedUse : ~m? -# 757| v757_20(void) = ExitFunction : +# 757| v757_18(void) = ReturnIndirection[#this] : &:r757_6, ~m? +# 757| v757_19(void) = ReturnVoid : +# 757| v757_20(void) = AliasedUse : ~m? +# 757| v757_21(void) = ExitFunction : # 759| void Middle::~Middle() # 759| Block 0 @@ -4183,9 +4197,10 @@ ir.cpp: # 760| r760_7(glval) = FunctionAddress[~Base] : # 760| v760_8(void) = Call : func:r760_7, this:r760_6 # 760| mu760_9(unknown) = ^CallSideEffect : ~m? -# 759| v759_8(void) = ReturnVoid : -# 759| v759_9(void) = AliasedUse : ~m? -# 759| v759_10(void) = ExitFunction : +# 759| v759_8(void) = ReturnIndirection[#this] : &:r759_6, ~m? +# 759| v759_9(void) = ReturnVoid : +# 759| v759_10(void) = AliasedUse : ~m? +# 759| v759_11(void) = ExitFunction : # 763| Derived& Derived::operator=(Derived const&) # 763| Block 0 @@ -4241,11 +4256,12 @@ ir.cpp: #-----| r0_22(glval) = CopyValue : r0_21 #-----| r0_23(Derived &) = CopyValue : r0_22 #-----| mu0_24(Derived &) = Store : &:r0_19, r0_23 +# 763| v763_29(void) = ReturnIndirection[#this] : &:r763_6, ~m? #-----| v0_25(void) = ReturnIndirection[p#0] : &:r0_3, ~m? -# 763| r763_29(glval) = VariableAddress[#return] : -# 763| v763_30(void) = ReturnValue : &:r763_29, ~m? -# 763| v763_31(void) = AliasedUse : ~m? -# 763| v763_32(void) = ExitFunction : +# 763| r763_30(glval) = VariableAddress[#return] : +# 763| v763_31(void) = ReturnValue : &:r763_30, ~m? +# 763| v763_32(void) = AliasedUse : ~m? +# 763| v763_33(void) = ExitFunction : # 766| void Derived::Derived() # 766| Block 0 @@ -4267,9 +4283,10 @@ ir.cpp: # 766| mu766_16(unknown) = ^CallSideEffect : ~m? # 766| mu766_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r766_13 # 767| v767_1(void) = NoOp : -# 766| v766_18(void) = ReturnVoid : -# 766| v766_19(void) = AliasedUse : ~m? -# 766| v766_20(void) = ExitFunction : +# 766| v766_18(void) = ReturnIndirection[#this] : &:r766_6, ~m? +# 766| v766_19(void) = ReturnVoid : +# 766| v766_20(void) = AliasedUse : ~m? +# 766| v766_21(void) = ExitFunction : # 768| void Derived::~Derived() # 768| Block 0 @@ -4289,9 +4306,10 @@ ir.cpp: # 769| r769_7(glval) = FunctionAddress[~Middle] : # 769| v769_8(void) = Call : func:r769_7, this:r769_6 # 769| mu769_9(unknown) = ^CallSideEffect : ~m? -# 768| v768_8(void) = ReturnVoid : -# 768| v768_9(void) = AliasedUse : ~m? -# 768| v768_10(void) = ExitFunction : +# 768| v768_8(void) = ReturnIndirection[#this] : &:r768_6, ~m? +# 768| v768_9(void) = ReturnVoid : +# 768| v768_10(void) = AliasedUse : ~m? +# 768| v768_11(void) = ExitFunction : # 775| void MiddleVB1::MiddleVB1() # 775| Block 0 @@ -4313,9 +4331,10 @@ ir.cpp: # 775| mu775_16(unknown) = ^CallSideEffect : ~m? # 775| mu775_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r775_13 # 776| v776_1(void) = NoOp : -# 775| v775_18(void) = ReturnVoid : -# 775| v775_19(void) = AliasedUse : ~m? -# 775| v775_20(void) = ExitFunction : +# 775| v775_18(void) = ReturnIndirection[#this] : &:r775_6, ~m? +# 775| v775_19(void) = ReturnVoid : +# 775| v775_20(void) = AliasedUse : ~m? +# 775| v775_21(void) = ExitFunction : # 777| void MiddleVB1::~MiddleVB1() # 777| Block 0 @@ -4335,9 +4354,10 @@ ir.cpp: # 778| r778_7(glval) = FunctionAddress[~Base] : # 778| v778_8(void) = Call : func:r778_7, this:r778_6 # 778| mu778_9(unknown) = ^CallSideEffect : ~m? -# 777| v777_8(void) = ReturnVoid : -# 777| v777_9(void) = AliasedUse : ~m? -# 777| v777_10(void) = ExitFunction : +# 777| v777_8(void) = ReturnIndirection[#this] : &:r777_6, ~m? +# 777| v777_9(void) = ReturnVoid : +# 777| v777_10(void) = AliasedUse : ~m? +# 777| v777_11(void) = ExitFunction : # 784| void MiddleVB2::MiddleVB2() # 784| Block 0 @@ -4359,9 +4379,10 @@ ir.cpp: # 784| mu784_16(unknown) = ^CallSideEffect : ~m? # 784| mu784_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r784_13 # 785| v785_1(void) = NoOp : -# 784| v784_18(void) = ReturnVoid : -# 784| v784_19(void) = AliasedUse : ~m? -# 784| v784_20(void) = ExitFunction : +# 784| v784_18(void) = ReturnIndirection[#this] : &:r784_6, ~m? +# 784| v784_19(void) = ReturnVoid : +# 784| v784_20(void) = AliasedUse : ~m? +# 784| v784_21(void) = ExitFunction : # 786| void MiddleVB2::~MiddleVB2() # 786| Block 0 @@ -4381,9 +4402,10 @@ ir.cpp: # 787| r787_7(glval) = FunctionAddress[~Base] : # 787| v787_8(void) = Call : func:r787_7, this:r787_6 # 787| mu787_9(unknown) = ^CallSideEffect : ~m? -# 786| v786_8(void) = ReturnVoid : -# 786| v786_9(void) = AliasedUse : ~m? -# 786| v786_10(void) = ExitFunction : +# 786| v786_8(void) = ReturnIndirection[#this] : &:r786_6, ~m? +# 786| v786_9(void) = ReturnVoid : +# 786| v786_10(void) = AliasedUse : ~m? +# 786| v786_11(void) = ExitFunction : # 793| void DerivedVB::DerivedVB() # 793| Block 0 @@ -4415,9 +4437,10 @@ ir.cpp: # 793| mu793_26(unknown) = ^CallSideEffect : ~m? # 793| mu793_27(String) = ^IndirectMayWriteSideEffect[-1] : &:r793_23 # 794| v794_1(void) = NoOp : -# 793| v793_28(void) = ReturnVoid : -# 793| v793_29(void) = AliasedUse : ~m? -# 793| v793_30(void) = ExitFunction : +# 793| v793_28(void) = ReturnIndirection[#this] : &:r793_6, ~m? +# 793| v793_29(void) = ReturnVoid : +# 793| v793_30(void) = AliasedUse : ~m? +# 793| v793_31(void) = ExitFunction : # 795| void DerivedVB::~DerivedVB() # 795| Block 0 @@ -4445,9 +4468,10 @@ ir.cpp: # 796| r796_15(glval) = FunctionAddress[~Base] : # 796| v796_16(void) = Call : func:r796_15, this:r796_14 # 796| mu796_17(unknown) = ^CallSideEffect : ~m? -# 795| v795_8(void) = ReturnVoid : -# 795| v795_9(void) = AliasedUse : ~m? -# 795| v795_10(void) = ExitFunction : +# 795| v795_8(void) = ReturnIndirection[#this] : &:r795_6, ~m? +# 795| v795_9(void) = ReturnVoid : +# 795| v795_10(void) = AliasedUse : ~m? +# 795| v795_11(void) = ExitFunction : # 799| void HierarchyConversions() # 799| Block 0 @@ -4751,9 +4775,10 @@ ir.cpp: # 842| r842_6(glval) = Load : &:r842_4, ~m? # 842| mu842_7(PolymorphicBase) = InitializeIndirection[#this] : &:r842_6 # 842| v842_8(void) = NoOp : -# 842| v842_9(void) = ReturnVoid : -# 842| v842_10(void) = AliasedUse : ~m? -# 842| v842_11(void) = ExitFunction : +# 842| v842_9(void) = ReturnIndirection[#this] : &:r842_6, ~m? +# 842| v842_10(void) = ReturnVoid : +# 842| v842_11(void) = AliasedUse : ~m? +# 842| v842_12(void) = ExitFunction : # 846| void PolymorphicDerived::PolymorphicDerived() # 846| Block 0 @@ -4770,9 +4795,10 @@ ir.cpp: # 846| mu846_11(unknown) = ^CallSideEffect : ~m? # 846| mu846_12(PolymorphicBase) = ^IndirectMayWriteSideEffect[-1] : &:r846_8 # 846| v846_13(void) = NoOp : -# 846| v846_14(void) = ReturnVoid : -# 846| v846_15(void) = AliasedUse : ~m? -# 846| v846_16(void) = ExitFunction : +# 846| v846_14(void) = ReturnIndirection[#this] : &:r846_6, ~m? +# 846| v846_15(void) = ReturnVoid : +# 846| v846_16(void) = AliasedUse : ~m? +# 846| v846_17(void) = ExitFunction : # 846| void PolymorphicDerived::~PolymorphicDerived() # 846| Block 0 @@ -4788,9 +4814,10 @@ ir.cpp: # 846| r846_9(glval) = FunctionAddress[~PolymorphicBase] : # 846| v846_10(void) = Call : func:r846_9, this:r846_8 # 846| mu846_11(unknown) = ^CallSideEffect : ~m? -# 846| v846_12(void) = ReturnVoid : -# 846| v846_13(void) = AliasedUse : ~m? -# 846| v846_14(void) = ExitFunction : +# 846| v846_12(void) = ReturnIndirection[#this] : &:r846_6, ~m? +# 846| v846_13(void) = ReturnVoid : +# 846| v846_14(void) = AliasedUse : ~m? +# 846| v846_15(void) = ExitFunction : # 849| void DynamicCast() # 849| Block 0 @@ -4840,12 +4867,12 @@ ir.cpp: # 863| r863_1(glval) = VariableAddress[pv] : # 863| r863_2(glval) = VariableAddress[pb] : # 863| r863_3(PolymorphicBase *) = Load : &:r863_2, ~m? -# 863| r863_4(void *) = DynamicCastToVoid : r863_3 +# 863| r863_4(void *) = CompleteObjectAddress : r863_3 # 863| mu863_5(void *) = Store : &:r863_1, r863_4 # 864| r864_1(glval) = VariableAddress[pcv] : # 864| r864_2(glval) = VariableAddress[pd] : # 864| r864_3(PolymorphicDerived *) = Load : &:r864_2, ~m? -# 864| r864_4(void *) = DynamicCastToVoid : r864_3 +# 864| r864_4(void *) = CompleteObjectAddress : r864_3 # 864| mu864_5(void *) = Store : &:r864_1, r864_4 # 865| v865_1(void) = NoOp : # 849| v849_4(void) = ReturnVoid : @@ -4870,9 +4897,10 @@ ir.cpp: # 868| v868_7(void) = ^BufferReadSideEffect[0] : &:r868_3, ~m? # 868| mu868_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r868_3 # 869| v869_1(void) = NoOp : -# 867| v867_8(void) = ReturnVoid : -# 867| v867_9(void) = AliasedUse : ~m? -# 867| v867_10(void) = ExitFunction : +# 867| v867_8(void) = ReturnIndirection[#this] : &:r867_6, ~m? +# 867| v867_9(void) = ReturnVoid : +# 867| v867_10(void) = AliasedUse : ~m? +# 867| v867_11(void) = ExitFunction : # 871| void ArrayConversions() # 871| Block 0 @@ -5617,9 +5645,10 @@ ir.cpp: # 1038| r1038_6(glval) = Load : &:r1038_4, ~m? # 1038| mu1038_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1038_6 # 1038| v1038_8(void) = NoOp : -# 1038| v1038_9(void) = ReturnVoid : -# 1038| v1038_10(void) = AliasedUse : ~m? -# 1038| v1038_11(void) = ExitFunction : +# 1038| v1038_9(void) = ReturnIndirection[#this] : &:r1038_6, ~m? +# 1038| v1038_10(void) = ReturnVoid : +# 1038| v1038_11(void) = AliasedUse : ~m? +# 1038| v1038_12(void) = ExitFunction : # 1038| void(* (lambda [] type at line 1038, col. 12)::operator void (*)()() const)() # 1038| Block 0 @@ -5633,10 +5662,11 @@ ir.cpp: # 1038| r1038_8(glval<..(*)(..)>) = VariableAddress[#return] : # 1038| r1038_9(..(*)(..)) = FunctionAddress[_FUN] : # 1038| mu1038_10(..(*)(..)) = Store : &:r1038_8, r1038_9 -# 1038| r1038_11(glval<..(*)(..)>) = VariableAddress[#return] : -# 1038| v1038_12(void) = ReturnValue : &:r1038_11, ~m? -# 1038| v1038_13(void) = AliasedUse : ~m? -# 1038| v1038_14(void) = ExitFunction : +# 1038| v1038_11(void) = ReturnIndirection[#this] : &:r1038_6, ~m? +# 1038| r1038_12(glval<..(*)(..)>) = VariableAddress[#return] : +# 1038| v1038_13(void) = ReturnValue : &:r1038_12, ~m? +# 1038| v1038_14(void) = AliasedUse : ~m? +# 1038| v1038_15(void) = ExitFunction : # 1040| void Lambda(int, String const&) # 1040| Block 0 @@ -5819,10 +5849,11 @@ ir.cpp: # 1041| r1041_10(glval) = VariableAddress[#return] : # 1041| r1041_11(char) = Constant[65] : # 1041| mu1041_12(char) = Store : &:r1041_10, r1041_11 -# 1041| r1041_13(glval) = VariableAddress[#return] : -# 1041| v1041_14(void) = ReturnValue : &:r1041_13, ~m? -# 1041| v1041_15(void) = AliasedUse : ~m? -# 1041| v1041_16(void) = ExitFunction : +# 1041| v1041_13(void) = ReturnIndirection[#this] : &:r1041_6, ~m? +# 1041| r1041_14(glval) = VariableAddress[#return] : +# 1041| v1041_15(void) = ReturnValue : &:r1041_14, ~m? +# 1041| v1041_16(void) = AliasedUse : ~m? +# 1041| v1041_17(void) = ExitFunction : # 1041| char(* (void Lambda(int, String const&))::(lambda [] type at line 1041, col. 23)::operator char (*)(float)() const)(float) # 1041| Block 0 @@ -5836,10 +5867,11 @@ ir.cpp: # 1041| r1041_8(glval<..(*)(..)>) = VariableAddress[#return] : # 1041| r1041_9(..(*)(..)) = FunctionAddress[_FUN] : # 1041| mu1041_10(..(*)(..)) = Store : &:r1041_8, r1041_9 -# 1041| r1041_11(glval<..(*)(..)>) = VariableAddress[#return] : -# 1041| v1041_12(void) = ReturnValue : &:r1041_11, ~m? -# 1041| v1041_13(void) = AliasedUse : ~m? -# 1041| v1041_14(void) = ExitFunction : +# 1041| v1041_11(void) = ReturnIndirection[#this] : &:r1041_6, ~m? +# 1041| r1041_12(glval<..(*)(..)>) = VariableAddress[#return] : +# 1041| v1041_13(void) = ReturnValue : &:r1041_12, ~m? +# 1041| v1041_14(void) = AliasedUse : ~m? +# 1041| v1041_15(void) = ExitFunction : # 1043| char (void Lambda(int, String const&))::(lambda [] type at line 1043, col. 21)::operator()(float) const # 1043| Block 0 @@ -5871,10 +5903,11 @@ ir.cpp: # 1043| r1043_26(glval) = PointerAdd[1] : r1043_17, r1043_25 # 1043| r1043_27(char) = Load : &:r1043_26, ~m? # 1043| mu1043_28(char) = Store : &:r1043_10, r1043_27 -# 1043| r1043_29(glval) = VariableAddress[#return] : -# 1043| v1043_30(void) = ReturnValue : &:r1043_29, ~m? -# 1043| v1043_31(void) = AliasedUse : ~m? -# 1043| v1043_32(void) = ExitFunction : +# 1043| v1043_29(void) = ReturnIndirection[#this] : &:r1043_6, ~m? +# 1043| r1043_30(glval) = VariableAddress[#return] : +# 1043| v1043_31(void) = ReturnValue : &:r1043_30, ~m? +# 1043| v1043_32(void) = AliasedUse : ~m? +# 1043| v1043_33(void) = ExitFunction : # 1045| void (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::~() # 1045| Block 0 @@ -5890,9 +5923,10 @@ ir.cpp: # 1045| r1045_9(glval) = FunctionAddress[~String] : # 1045| v1045_10(void) = Call : func:r1045_9, this:r1045_8 # 1045| mu1045_11(unknown) = ^CallSideEffect : ~m? -# 1045| v1045_12(void) = ReturnVoid : -# 1045| v1045_13(void) = AliasedUse : ~m? -# 1045| v1045_14(void) = ExitFunction : +# 1045| v1045_12(void) = ReturnIndirection[#this] : &:r1045_6, ~m? +# 1045| v1045_13(void) = ReturnVoid : +# 1045| v1045_14(void) = AliasedUse : ~m? +# 1045| v1045_15(void) = ExitFunction : # 1045| char (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::operator()(float) const # 1045| Block 0 @@ -5921,10 +5955,11 @@ ir.cpp: # 1045| r1045_23(glval) = PointerAdd[1] : r1045_15, r1045_22 # 1045| r1045_24(char) = Load : &:r1045_23, ~m? # 1045| mu1045_25(char) = Store : &:r1045_10, r1045_24 -# 1045| r1045_26(glval) = VariableAddress[#return] : -# 1045| v1045_27(void) = ReturnValue : &:r1045_26, ~m? -# 1045| v1045_28(void) = AliasedUse : ~m? -# 1045| v1045_29(void) = ExitFunction : +# 1045| v1045_26(void) = ReturnIndirection[#this] : &:r1045_6, ~m? +# 1045| r1045_27(glval) = VariableAddress[#return] : +# 1045| v1045_28(void) = ReturnValue : &:r1045_27, ~m? +# 1045| v1045_29(void) = AliasedUse : ~m? +# 1045| v1045_30(void) = ExitFunction : # 1047| char (void Lambda(int, String const&))::(lambda [] type at line 1047, col. 30)::operator()(float) const # 1047| Block 0 @@ -5952,10 +5987,11 @@ ir.cpp: # 1047| r1047_22(glval) = PointerAdd[1] : r1047_17, r1047_21 # 1047| r1047_23(char) = Load : &:r1047_22, ~m? # 1047| mu1047_24(char) = Store : &:r1047_10, r1047_23 -# 1047| r1047_25(glval) = VariableAddress[#return] : -# 1047| v1047_26(void) = ReturnValue : &:r1047_25, ~m? -# 1047| v1047_27(void) = AliasedUse : ~m? -# 1047| v1047_28(void) = ExitFunction : +# 1047| v1047_25(void) = ReturnIndirection[#this] : &:r1047_6, ~m? +# 1047| r1047_26(glval) = VariableAddress[#return] : +# 1047| v1047_27(void) = ReturnValue : &:r1047_26, ~m? +# 1047| v1047_28(void) = AliasedUse : ~m? +# 1047| v1047_29(void) = ExitFunction : # 1049| void (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::~() # 1049| Block 0 @@ -5971,9 +6007,10 @@ ir.cpp: # 1049| r1049_9(glval) = FunctionAddress[~String] : # 1049| v1049_10(void) = Call : func:r1049_9, this:r1049_8 # 1049| mu1049_11(unknown) = ^CallSideEffect : ~m? -# 1049| v1049_12(void) = ReturnVoid : -# 1049| v1049_13(void) = AliasedUse : ~m? -# 1049| v1049_14(void) = ExitFunction : +# 1049| v1049_12(void) = ReturnIndirection[#this] : &:r1049_6, ~m? +# 1049| v1049_13(void) = ReturnVoid : +# 1049| v1049_14(void) = AliasedUse : ~m? +# 1049| v1049_15(void) = ExitFunction : # 1049| char (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::operator()(float) const # 1049| Block 0 @@ -5999,10 +6036,11 @@ ir.cpp: # 1049| r1049_20(glval) = PointerAdd[1] : r1049_15, r1049_19 # 1049| r1049_21(char) = Load : &:r1049_20, ~m? # 1049| mu1049_22(char) = Store : &:r1049_10, r1049_21 -# 1049| r1049_23(glval) = VariableAddress[#return] : -# 1049| v1049_24(void) = ReturnValue : &:r1049_23, ~m? -# 1049| v1049_25(void) = AliasedUse : ~m? -# 1049| v1049_26(void) = ExitFunction : +# 1049| v1049_23(void) = ReturnIndirection[#this] : &:r1049_6, ~m? +# 1049| r1049_24(glval) = VariableAddress[#return] : +# 1049| v1049_25(void) = ReturnValue : &:r1049_24, ~m? +# 1049| v1049_26(void) = AliasedUse : ~m? +# 1049| v1049_27(void) = ExitFunction : # 1051| char (void Lambda(int, String const&))::(lambda [] type at line 1051, col. 32)::operator()(float) const # 1051| Block 0 @@ -6033,10 +6071,11 @@ ir.cpp: # 1051| r1051_25(glval) = PointerAdd[1] : r1051_17, r1051_24 # 1051| r1051_26(char) = Load : &:r1051_25, ~m? # 1051| mu1051_27(char) = Store : &:r1051_10, r1051_26 -# 1051| r1051_28(glval) = VariableAddress[#return] : -# 1051| v1051_29(void) = ReturnValue : &:r1051_28, ~m? -# 1051| v1051_30(void) = AliasedUse : ~m? -# 1051| v1051_31(void) = ExitFunction : +# 1051| v1051_28(void) = ReturnIndirection[#this] : &:r1051_6, ~m? +# 1051| r1051_29(glval) = VariableAddress[#return] : +# 1051| v1051_30(void) = ReturnValue : &:r1051_29, ~m? +# 1051| v1051_31(void) = AliasedUse : ~m? +# 1051| v1051_32(void) = ExitFunction : # 1054| char (void Lambda(int, String const&))::(lambda [] type at line 1054, col. 23)::operator()(float) const # 1054| Block 0 @@ -6078,10 +6117,11 @@ ir.cpp: # 1054| r1054_36(glval) = PointerAdd[1] : r1054_17, r1054_35 # 1054| r1054_37(char) = Load : &:r1054_36, ~m? # 1054| mu1054_38(char) = Store : &:r1054_10, r1054_37 -# 1054| r1054_39(glval) = VariableAddress[#return] : -# 1054| v1054_40(void) = ReturnValue : &:r1054_39, ~m? -# 1054| v1054_41(void) = AliasedUse : ~m? -# 1054| v1054_42(void) = ExitFunction : +# 1054| v1054_39(void) = ReturnIndirection[#this] : &:r1054_6, ~m? +# 1054| r1054_40(glval) = VariableAddress[#return] : +# 1054| v1054_41(void) = ReturnValue : &:r1054_40, ~m? +# 1054| v1054_42(void) = AliasedUse : ~m? +# 1054| v1054_43(void) = ExitFunction : # 1077| void RangeBasedFor(vector const&) # 1077| Block 0 @@ -7409,9 +7449,10 @@ perf-regression.cpp: # 6| r6_11(unknown[1073741824]) = Constant[0] : # 6| mu6_12(unknown[1073741824]) = Store : &:r6_10, r6_11 # 6| v6_13(void) = NoOp : -# 6| v6_14(void) = ReturnVoid : -# 6| v6_15(void) = AliasedUse : ~m? -# 6| v6_16(void) = ExitFunction : +# 6| v6_14(void) = ReturnIndirection[#this] : &:r6_6, ~m? +# 6| v6_15(void) = ReturnVoid : +# 6| v6_16(void) = AliasedUse : ~m? +# 6| v6_17(void) = ExitFunction : # 9| int main() # 9| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected index 3a1a30265b2a..64172ad18738 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected @@ -1,8 +1,8 @@ missingOperand -| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | +| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | unexpectedOperand duplicateOperand missingPhiOperand @@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected index 3a1a30265b2a..64172ad18738 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected @@ -1,8 +1,8 @@ missingOperand -| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | +| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | unexpectedOperand duplicateOperand missingPhiOperand @@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected index e2db1e65034c..1c41692bcaad 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected @@ -20,6 +20,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected index e2db1e65034c..1c41692bcaad 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected @@ -20,6 +20,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 24b3ee459843..43b8116d85fa 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1020,9 +1020,10 @@ ssa.cpp: # 235| r235_9(glval) = VariableAddress[x] : # 235| m235_10(int) = InitializeParameter[x] : &:r235_9 # 235| v235_11(void) = NoOp : -# 235| v235_12(void) = ReturnVoid : -# 235| v235_13(void) = AliasedUse : m235_3 -# 235| v235_14(void) = ExitFunction : +# 235| v235_12(void) = ReturnIndirection[#this] : &:r235_7, m235_8 +# 235| v235_13(void) = ReturnVoid : +# 235| v235_14(void) = AliasedUse : m235_3 +# 235| v235_15(void) = ExitFunction : # 236| void Constructible::g() # 236| Block 0 @@ -1035,9 +1036,10 @@ ssa.cpp: # 236| r236_7(glval) = Load : &:r236_5, m236_6 # 236| m236_8(Constructible) = InitializeIndirection[#this] : &:r236_7 # 236| v236_9(void) = NoOp : -# 236| v236_10(void) = ReturnVoid : -# 236| v236_11(void) = AliasedUse : m236_3 -# 236| v236_12(void) = ExitFunction : +# 236| v236_10(void) = ReturnIndirection[#this] : &:r236_7, m236_8 +# 236| v236_11(void) = ReturnVoid : +# 236| v236_12(void) = AliasedUse : m236_3 +# 236| v236_13(void) = ExitFunction : # 239| void ExplicitConstructorCalls() # 239| Block 0 @@ -1307,9 +1309,10 @@ ssa.cpp: # 286| r286_9(glval) = VariableAddress[x] : # 286| m286_10(int) = InitializeParameter[x] : &:r286_9 # 286| v286_11(void) = NoOp : -# 286| v286_12(void) = ReturnVoid : -# 286| v286_13(void) = AliasedUse : m286_3 -# 286| v286_14(void) = ExitFunction : +# 286| v286_12(void) = ReturnIndirection[#this] : &:r286_7, m286_8 +# 286| v286_13(void) = ReturnVoid : +# 286| v286_14(void) = AliasedUse : m286_3 +# 286| v286_15(void) = ExitFunction : # 287| void A::A(A*) # 287| Block 0 @@ -1326,10 +1329,11 @@ ssa.cpp: # 287| r287_11(A *) = Load : &:r287_9, m287_10 # 287| m287_12(unknown) = InitializeIndirection[p#0] : &:r287_11 # 287| v287_13(void) = NoOp : -# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_11, m287_12 -# 287| v287_15(void) = ReturnVoid : -# 287| v287_16(void) = AliasedUse : m287_3 -# 287| v287_17(void) = ExitFunction : +# 287| v287_14(void) = ReturnIndirection[#this] : &:r287_7, m287_8 +# 287| v287_15(void) = ReturnIndirection[p#0] : &:r287_11, m287_12 +# 287| v287_16(void) = ReturnVoid : +# 287| v287_17(void) = AliasedUse : m287_3 +# 287| v287_18(void) = ExitFunction : # 288| void A::A() # 288| Block 0 @@ -1342,9 +1346,10 @@ ssa.cpp: # 288| r288_7(glval) = Load : &:r288_5, m288_6 # 288| m288_8(A) = InitializeIndirection[#this] : &:r288_7 # 288| v288_9(void) = NoOp : -# 288| v288_10(void) = ReturnVoid : -# 288| v288_11(void) = AliasedUse : m288_3 -# 288| v288_12(void) = ExitFunction : +# 288| v288_10(void) = ReturnIndirection[#this] : &:r288_7, m288_8 +# 288| v288_11(void) = ReturnVoid : +# 288| v288_12(void) = AliasedUse : m288_3 +# 288| v288_13(void) = ExitFunction : # 291| Point* NewAliasing(int) # 291| Block 0 @@ -1499,6 +1504,7 @@ ssa.cpp: # 311| m311_6(int) = Store : &:r311_5, r311_2 # 311| m311_7(unknown) = Chi : total:m310_8, partial:m311_6 # 312| v312_1(void) = NoOp : -# 310| v310_11(void) = ReturnVoid : -# 310| v310_12(void) = AliasedUse : m310_3 -# 310| v310_13(void) = ExitFunction : +# 310| v310_11(void) = ReturnIndirection[#this] : &:r310_7, m311_7 +# 310| v310_12(void) = ReturnVoid : +# 310| v310_13(void) = AliasedUse : m310_3 +# 310| v310_14(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index c7762fc5fdd5..ef07fde174d5 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1013,9 +1013,10 @@ ssa.cpp: # 235| r235_9(glval) = VariableAddress[x] : # 235| m235_10(int) = InitializeParameter[x] : &:r235_9 # 235| v235_11(void) = NoOp : -# 235| v235_12(void) = ReturnVoid : -# 235| v235_13(void) = AliasedUse : m235_3 -# 235| v235_14(void) = ExitFunction : +# 235| v235_12(void) = ReturnIndirection[#this] : &:r235_7, m235_8 +# 235| v235_13(void) = ReturnVoid : +# 235| v235_14(void) = AliasedUse : m235_3 +# 235| v235_15(void) = ExitFunction : # 236| void Constructible::g() # 236| Block 0 @@ -1028,9 +1029,10 @@ ssa.cpp: # 236| r236_7(glval) = Load : &:r236_5, m236_6 # 236| m236_8(Constructible) = InitializeIndirection[#this] : &:r236_7 # 236| v236_9(void) = NoOp : -# 236| v236_10(void) = ReturnVoid : -# 236| v236_11(void) = AliasedUse : m236_3 -# 236| v236_12(void) = ExitFunction : +# 236| v236_10(void) = ReturnIndirection[#this] : &:r236_7, m236_8 +# 236| v236_11(void) = ReturnVoid : +# 236| v236_12(void) = AliasedUse : m236_3 +# 236| v236_13(void) = ExitFunction : # 239| void ExplicitConstructorCalls() # 239| Block 0 @@ -1295,9 +1297,10 @@ ssa.cpp: # 286| r286_9(glval) = VariableAddress[x] : # 286| m286_10(int) = InitializeParameter[x] : &:r286_9 # 286| v286_11(void) = NoOp : -# 286| v286_12(void) = ReturnVoid : -# 286| v286_13(void) = AliasedUse : m286_3 -# 286| v286_14(void) = ExitFunction : +# 286| v286_12(void) = ReturnIndirection[#this] : &:r286_7, m286_8 +# 286| v286_13(void) = ReturnVoid : +# 286| v286_14(void) = AliasedUse : m286_3 +# 286| v286_15(void) = ExitFunction : # 287| void A::A(A*) # 287| Block 0 @@ -1314,10 +1317,11 @@ ssa.cpp: # 287| r287_11(A *) = Load : &:r287_9, m287_10 # 287| m287_12(unknown) = InitializeIndirection[p#0] : &:r287_11 # 287| v287_13(void) = NoOp : -# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_11, m287_12 -# 287| v287_15(void) = ReturnVoid : -# 287| v287_16(void) = AliasedUse : m287_3 -# 287| v287_17(void) = ExitFunction : +# 287| v287_14(void) = ReturnIndirection[#this] : &:r287_7, m287_8 +# 287| v287_15(void) = ReturnIndirection[p#0] : &:r287_11, m287_12 +# 287| v287_16(void) = ReturnVoid : +# 287| v287_17(void) = AliasedUse : m287_3 +# 287| v287_18(void) = ExitFunction : # 288| void A::A() # 288| Block 0 @@ -1330,9 +1334,10 @@ ssa.cpp: # 288| r288_7(glval) = Load : &:r288_5, m288_6 # 288| m288_8(A) = InitializeIndirection[#this] : &:r288_7 # 288| v288_9(void) = NoOp : -# 288| v288_10(void) = ReturnVoid : -# 288| v288_11(void) = AliasedUse : m288_3 -# 288| v288_12(void) = ExitFunction : +# 288| v288_10(void) = ReturnIndirection[#this] : &:r288_7, m288_8 +# 288| v288_11(void) = ReturnVoid : +# 288| v288_12(void) = AliasedUse : m288_3 +# 288| v288_13(void) = ExitFunction : # 291| Point* NewAliasing(int) # 291| Block 0 @@ -1486,6 +1491,7 @@ ssa.cpp: # 311| m311_6(int) = Store : &:r311_5, r311_2 # 311| m311_7(unknown) = Chi : total:m310_8, partial:m311_6 # 312| v312_1(void) = NoOp : -# 310| v310_11(void) = ReturnVoid : -# 310| v310_12(void) = AliasedUse : m310_3 -# 310| v310_13(void) = ExitFunction : +# 310| v310_11(void) = ReturnIndirection[#this] : &:r310_7, m311_7 +# 310| v310_12(void) = ReturnVoid : +# 310| v310_13(void) = AliasedUse : m310_3 +# 310| v310_14(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp index b8788c5c6aae..34886b1f3434 100644 --- a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp +++ b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp @@ -291,7 +291,7 @@ struct A { Point *NewAliasing(int x) { Point* p = new Point; Point* q = new Point; - int j = new A(new A(x))->i; + int j = (new A(new A(x)))->i; A* a = new A; return p; } @@ -310,4 +310,4 @@ class ThisAliasTest { void setX(int arg) { this->x = arg; } -}; \ No newline at end of file +}; diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected index e2db1e65034c..1c41692bcaad 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected @@ -20,6 +20,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected index e2db1e65034c..1c41692bcaad 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected @@ -20,6 +20,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 3180c9211a5e..1d155eaf30d6 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -947,9 +947,10 @@ ssa.cpp: # 235| r235_8(glval) = VariableAddress[x] : # 235| m235_9(int) = InitializeParameter[x] : &:r235_8 # 235| v235_10(void) = NoOp : -# 235| v235_11(void) = ReturnVoid : -# 235| v235_12(void) = AliasedUse : ~m? -# 235| v235_13(void) = ExitFunction : +# 235| v235_11(void) = ReturnIndirection[#this] : &:r235_6, ~m? +# 235| v235_12(void) = ReturnVoid : +# 235| v235_13(void) = AliasedUse : ~m? +# 235| v235_14(void) = ExitFunction : # 236| void Constructible::g() # 236| Block 0 @@ -961,9 +962,10 @@ ssa.cpp: # 236| r236_6(glval) = Load : &:r236_4, m236_5 # 236| mu236_7(Constructible) = InitializeIndirection[#this] : &:r236_6 # 236| v236_8(void) = NoOp : -# 236| v236_9(void) = ReturnVoid : -# 236| v236_10(void) = AliasedUse : ~m? -# 236| v236_11(void) = ExitFunction : +# 236| v236_9(void) = ReturnIndirection[#this] : &:r236_6, ~m? +# 236| v236_10(void) = ReturnVoid : +# 236| v236_11(void) = AliasedUse : ~m? +# 236| v236_12(void) = ExitFunction : # 239| void ExplicitConstructorCalls() # 239| Block 0 @@ -1198,9 +1200,10 @@ ssa.cpp: # 286| r286_8(glval) = VariableAddress[x] : # 286| m286_9(int) = InitializeParameter[x] : &:r286_8 # 286| v286_10(void) = NoOp : -# 286| v286_11(void) = ReturnVoid : -# 286| v286_12(void) = AliasedUse : ~m? -# 286| v286_13(void) = ExitFunction : +# 286| v286_11(void) = ReturnIndirection[#this] : &:r286_6, ~m? +# 286| v286_12(void) = ReturnVoid : +# 286| v286_13(void) = AliasedUse : ~m? +# 286| v286_14(void) = ExitFunction : # 287| void A::A(A*) # 287| Block 0 @@ -1216,10 +1219,11 @@ ssa.cpp: # 287| r287_10(A *) = Load : &:r287_8, m287_9 # 287| mu287_11(unknown) = InitializeIndirection[p#0] : &:r287_10 # 287| v287_12(void) = NoOp : -# 287| v287_13(void) = ReturnIndirection[p#0] : &:r287_10, ~m? -# 287| v287_14(void) = ReturnVoid : -# 287| v287_15(void) = AliasedUse : ~m? -# 287| v287_16(void) = ExitFunction : +# 287| v287_13(void) = ReturnIndirection[#this] : &:r287_6, ~m? +# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_10, ~m? +# 287| v287_15(void) = ReturnVoid : +# 287| v287_16(void) = AliasedUse : ~m? +# 287| v287_17(void) = ExitFunction : # 288| void A::A() # 288| Block 0 @@ -1231,9 +1235,10 @@ ssa.cpp: # 288| r288_6(glval) = Load : &:r288_4, m288_5 # 288| mu288_7(A) = InitializeIndirection[#this] : &:r288_6 # 288| v288_8(void) = NoOp : -# 288| v288_9(void) = ReturnVoid : -# 288| v288_10(void) = AliasedUse : ~m? -# 288| v288_11(void) = ExitFunction : +# 288| v288_9(void) = ReturnIndirection[#this] : &:r288_6, ~m? +# 288| v288_10(void) = ReturnVoid : +# 288| v288_11(void) = AliasedUse : ~m? +# 288| v288_12(void) = ExitFunction : # 291| Point* NewAliasing(int) # 291| Block 0 @@ -1367,6 +1372,7 @@ ssa.cpp: # 311| r311_5(glval) = FieldAddress[x] : r311_4 # 311| mu311_6(int) = Store : &:r311_5, r311_2 # 312| v312_1(void) = NoOp : -# 310| v310_10(void) = ReturnVoid : -# 310| v310_11(void) = AliasedUse : ~m? -# 310| v310_12(void) = ExitFunction : +# 310| v310_10(void) = ReturnIndirection[#this] : &:r310_6, ~m? +# 310| v310_11(void) = ReturnVoid : +# 310| v310_12(void) = AliasedUse : ~m? +# 310| v310_13(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 3180c9211a5e..1d155eaf30d6 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -947,9 +947,10 @@ ssa.cpp: # 235| r235_8(glval) = VariableAddress[x] : # 235| m235_9(int) = InitializeParameter[x] : &:r235_8 # 235| v235_10(void) = NoOp : -# 235| v235_11(void) = ReturnVoid : -# 235| v235_12(void) = AliasedUse : ~m? -# 235| v235_13(void) = ExitFunction : +# 235| v235_11(void) = ReturnIndirection[#this] : &:r235_6, ~m? +# 235| v235_12(void) = ReturnVoid : +# 235| v235_13(void) = AliasedUse : ~m? +# 235| v235_14(void) = ExitFunction : # 236| void Constructible::g() # 236| Block 0 @@ -961,9 +962,10 @@ ssa.cpp: # 236| r236_6(glval) = Load : &:r236_4, m236_5 # 236| mu236_7(Constructible) = InitializeIndirection[#this] : &:r236_6 # 236| v236_8(void) = NoOp : -# 236| v236_9(void) = ReturnVoid : -# 236| v236_10(void) = AliasedUse : ~m? -# 236| v236_11(void) = ExitFunction : +# 236| v236_9(void) = ReturnIndirection[#this] : &:r236_6, ~m? +# 236| v236_10(void) = ReturnVoid : +# 236| v236_11(void) = AliasedUse : ~m? +# 236| v236_12(void) = ExitFunction : # 239| void ExplicitConstructorCalls() # 239| Block 0 @@ -1198,9 +1200,10 @@ ssa.cpp: # 286| r286_8(glval) = VariableAddress[x] : # 286| m286_9(int) = InitializeParameter[x] : &:r286_8 # 286| v286_10(void) = NoOp : -# 286| v286_11(void) = ReturnVoid : -# 286| v286_12(void) = AliasedUse : ~m? -# 286| v286_13(void) = ExitFunction : +# 286| v286_11(void) = ReturnIndirection[#this] : &:r286_6, ~m? +# 286| v286_12(void) = ReturnVoid : +# 286| v286_13(void) = AliasedUse : ~m? +# 286| v286_14(void) = ExitFunction : # 287| void A::A(A*) # 287| Block 0 @@ -1216,10 +1219,11 @@ ssa.cpp: # 287| r287_10(A *) = Load : &:r287_8, m287_9 # 287| mu287_11(unknown) = InitializeIndirection[p#0] : &:r287_10 # 287| v287_12(void) = NoOp : -# 287| v287_13(void) = ReturnIndirection[p#0] : &:r287_10, ~m? -# 287| v287_14(void) = ReturnVoid : -# 287| v287_15(void) = AliasedUse : ~m? -# 287| v287_16(void) = ExitFunction : +# 287| v287_13(void) = ReturnIndirection[#this] : &:r287_6, ~m? +# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_10, ~m? +# 287| v287_15(void) = ReturnVoid : +# 287| v287_16(void) = AliasedUse : ~m? +# 287| v287_17(void) = ExitFunction : # 288| void A::A() # 288| Block 0 @@ -1231,9 +1235,10 @@ ssa.cpp: # 288| r288_6(glval) = Load : &:r288_4, m288_5 # 288| mu288_7(A) = InitializeIndirection[#this] : &:r288_6 # 288| v288_8(void) = NoOp : -# 288| v288_9(void) = ReturnVoid : -# 288| v288_10(void) = AliasedUse : ~m? -# 288| v288_11(void) = ExitFunction : +# 288| v288_9(void) = ReturnIndirection[#this] : &:r288_6, ~m? +# 288| v288_10(void) = ReturnVoid : +# 288| v288_11(void) = AliasedUse : ~m? +# 288| v288_12(void) = ExitFunction : # 291| Point* NewAliasing(int) # 291| Block 0 @@ -1367,6 +1372,7 @@ ssa.cpp: # 311| r311_5(glval) = FieldAddress[x] : r311_4 # 311| mu311_6(int) = Store : &:r311_5, r311_2 # 312| v312_1(void) = NoOp : -# 310| v310_10(void) = ReturnVoid : -# 310| v310_11(void) = AliasedUse : ~m? -# 310| v310_12(void) = ExitFunction : +# 310| v310_10(void) = ReturnIndirection[#this] : &:r310_6, ~m? +# 310| v310_11(void) = ReturnVoid : +# 310| v310_12(void) = AliasedUse : ~m? +# 310| v310_13(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/lambdas/captures/elements.expected b/cpp/ql/test/library-tests/lambdas/captures/elements.expected index feeadc7a6040..8ba9cad40096 100644 --- a/cpp/ql/test/library-tests/lambdas/captures/elements.expected +++ b/cpp/ql/test/library-tests/lambdas/captures/elements.expected @@ -13,9 +13,9 @@ | captures.cpp:3:5:3:5 | (constructor) | | captures.cpp:3:5:3:5 | (constructor) | | captures.cpp:3:5:3:5 | (constructor) | -| captures.cpp:3:5:3:5 | declaration of (null) | -| captures.cpp:3:5:3:5 | declaration of (null) | -| captures.cpp:3:5:3:5 | definition of (null) | +| captures.cpp:3:5:3:5 | declaration of (constructor) | +| captures.cpp:3:5:3:5 | declaration of (constructor) | +| captures.cpp:3:5:3:5 | definition of (constructor) | | captures.cpp:3:5:3:5 | definition of operator= | | captures.cpp:3:5:3:5 | operator= | | captures.cpp:3:5:5:5 | [...](...){...} | @@ -50,9 +50,9 @@ | captures.cpp:9:5:9:5 | (constructor) | | captures.cpp:9:5:9:5 | (constructor) | | captures.cpp:9:5:9:5 | (constructor) | -| captures.cpp:9:5:9:5 | declaration of (null) | -| captures.cpp:9:5:9:5 | declaration of (null) | -| captures.cpp:9:5:9:5 | definition of (null) | +| captures.cpp:9:5:9:5 | declaration of (constructor) | +| captures.cpp:9:5:9:5 | declaration of (constructor) | +| captures.cpp:9:5:9:5 | definition of (constructor) | | captures.cpp:9:5:9:5 | definition of operator= | | captures.cpp:9:5:9:5 | operator= | | captures.cpp:9:5:11:5 | [...](...){...} | @@ -87,9 +87,9 @@ | captures.cpp:15:5:15:5 | (constructor) | | captures.cpp:15:5:15:5 | (constructor) | | captures.cpp:15:5:15:5 | (constructor) | -| captures.cpp:15:5:15:5 | declaration of (null) | -| captures.cpp:15:5:15:5 | declaration of (null) | -| captures.cpp:15:5:15:5 | definition of (null) | +| captures.cpp:15:5:15:5 | declaration of (constructor) | +| captures.cpp:15:5:15:5 | declaration of (constructor) | +| captures.cpp:15:5:15:5 | definition of (constructor) | | captures.cpp:15:5:15:5 | definition of operator= | | captures.cpp:15:5:15:5 | operator= | | captures.cpp:15:5:17:5 | [...](...){...} | @@ -129,9 +129,9 @@ | captures.cpp:22:19:22:19 | Unknown literal | | captures.cpp:22:19:22:19 | constructor init of field x | | captures.cpp:22:19:22:19 | constructor init of field y | -| captures.cpp:22:19:22:19 | declaration of (null) | -| captures.cpp:22:19:22:19 | definition of (null) | -| captures.cpp:22:19:22:19 | definition of (null) | +| captures.cpp:22:19:22:19 | declaration of (constructor) | +| captures.cpp:22:19:22:19 | definition of (constructor) | +| captures.cpp:22:19:22:19 | definition of (constructor) | | captures.cpp:22:19:22:19 | definition of operator= | | captures.cpp:22:19:22:19 | operator= | | captures.cpp:22:19:22:19 | return ... | @@ -187,9 +187,9 @@ | end_pos.cpp:9:15:9:15 | (constructor) | | end_pos.cpp:9:15:9:15 | Unknown literal | | end_pos.cpp:9:15:9:15 | constructor init of field ii | -| end_pos.cpp:9:15:9:15 | declaration of (null) | -| end_pos.cpp:9:15:9:15 | definition of (null) | -| end_pos.cpp:9:15:9:15 | definition of (null) | +| end_pos.cpp:9:15:9:15 | declaration of (constructor) | +| end_pos.cpp:9:15:9:15 | definition of (constructor) | +| end_pos.cpp:9:15:9:15 | definition of (constructor) | | end_pos.cpp:9:15:9:15 | definition of operator= | | end_pos.cpp:9:15:9:15 | operator= | | end_pos.cpp:9:15:9:15 | return ... | @@ -231,8 +231,10 @@ | file://:0:0:0:0 | const lambda [] type at line 9, col. 5 * | | file://:0:0:0:0 | const lambda [] type at line 9, col. 15 | | file://:0:0:0:0 | const lambda [] type at line 9, col. 15 & | +| file://:0:0:0:0 | const lambda [] type at line 9, col. 15 * | | file://:0:0:0:0 | const lambda [] type at line 15, col. 5 | | file://:0:0:0:0 | const lambda [] type at line 15, col. 5 & | +| file://:0:0:0:0 | const lambda [] type at line 15, col. 5 * | | file://:0:0:0:0 | const lambda [] type at line 22, col. 19 | | file://:0:0:0:0 | const lambda [] type at line 22, col. 19 & | | file://:0:0:0:0 | const lambda [] type at line 22, col. 19 * | @@ -277,8 +279,10 @@ | file://:0:0:0:0 | lambda [] type at line 9, col. 5 * | | file://:0:0:0:0 | lambda [] type at line 9, col. 15 & | | file://:0:0:0:0 | lambda [] type at line 9, col. 15 && | +| file://:0:0:0:0 | lambda [] type at line 9, col. 15 * | | file://:0:0:0:0 | lambda [] type at line 15, col. 5 & | | file://:0:0:0:0 | lambda [] type at line 15, col. 5 && | +| file://:0:0:0:0 | lambda [] type at line 15, col. 5 * | | file://:0:0:0:0 | lambda [] type at line 22, col. 19 & | | file://:0:0:0:0 | lambda [] type at line 22, col. 19 && | | file://:0:0:0:0 | lambda [] type at line 22, col. 19 * | diff --git a/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected b/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected index b60d112c9177..7ace5811466b 100644 --- a/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected +++ b/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected @@ -6,6 +6,8 @@ | file://:0:0:0:0 | p#0 | false | | test.cpp:0:0:0:0 | test.cpp | false | | test.cpp:2:1:2:61 | #define FOO class S{int i; void f(void) { int j; return; } }; | false | +| test.cpp:4:1:4:1 | S | false | +| test.cpp:4:1:4:1 | declaration of S | false | | test.cpp:4:1:4:1 | declaration of operator= | false | | test.cpp:4:1:4:1 | declaration of operator= | false | | test.cpp:4:1:4:1 | operator= | false | diff --git a/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.ql b/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.ql index 4ba665e98cc0..1c5b1c4b2de8 100644 --- a/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.ql +++ b/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.ql @@ -1,5 +1,5 @@ import cpp -from Block b, MacroAccess m +from BlockStmt b, MacroAccess m where affectedbymacroexpansion(unresolveElement(b), unresolveElement(m)) select b, m diff --git a/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.ql b/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.ql index afed90b56a60..cfaae6f4e8c6 100644 --- a/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.ql +++ b/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.ql @@ -1,5 +1,5 @@ import cpp -from Block b, MacroAccess m +from BlockStmt b, MacroAccess m where inmacroexpansion(unresolveElement(b), unresolveElement(m)) select b, m diff --git a/cpp/ql/test/library-tests/members/getters/members.expected b/cpp/ql/test/library-tests/members/getters/members.expected index a14ce851457c..fac28f04077a 100644 --- a/cpp/ql/test/library-tests/members/getters/members.expected +++ b/cpp/ql/test/library-tests/members/getters/members.expected @@ -8,6 +8,7 @@ | test.cpp:2:7:2:7 | C | test.cpp:8:10:8:23 | f_template_C_D | C::f_template_C_D(T) | getAMember(), getAMember(1), getAMemberFunction(), getCanonicalMember(1), getDeclaringType() | | test.cpp:2:7:2:7 | C | test.cpp:10:10:10:12 | f_C | C::f_C() | getAMember(), getAMember(2), getAMemberFunction(), getCanonicalMember(2), getDeclaringType() | | test.cpp:2:7:2:7 | C | test.cpp:11:10:11:14 | f_C_D | C::f_C_D() | getAMember(), getAMember(3), getAMemberFunction(), getCanonicalMember(3), getDeclaringType() | +| test.cpp:14:7:14:7 | D | test.cpp:14:7:14:7 | D | D::D() | getAMember(), getAMember(6), getAMemberFunction(), getCanonicalMember(6), getDeclaringType() | | test.cpp:14:7:14:7 | D | test.cpp:14:7:14:7 | operator= | D::operator=(D &&) | getAMember(), getAMember(5), getAMemberFunction(), getCanonicalMember(5), getDeclaringType() | | test.cpp:14:7:14:7 | D | test.cpp:14:7:14:7 | operator= | D::operator=(const D &) | getAMember(), getAMember(4), getAMemberFunction(), getCanonicalMember(4), getDeclaringType() | | test.cpp:14:7:14:7 | D | test.cpp:17:10:17:10 | f_template_C_D | D::f_template_C_D(double) | getAMember(), getAMember(0), getAMemberFunction(), getDeclaringType() | diff --git a/cpp/ql/test/library-tests/members/this/test.cpp b/cpp/ql/test/library-tests/members/this/test.cpp new file mode 100644 index 000000000000..ae155e748450 --- /dev/null +++ b/cpp/ql/test/library-tests/members/this/test.cpp @@ -0,0 +1,129 @@ + +int global; + +class C { + int x; + +public: + + void f1() { + // Implicit dereference of `this.` + x++; + } + + void f2() { + // Explicit dereference of `this.` + this->x++; + } + + int f3() const { + // We expect the type of `this` to be const-qualified. + return x; + } + + int f4() volatile { + // We expect the type of `this` to be volatile-qualified. + return x; + } + + int f5() const volatile { + // We expect the type of `this` to be qualified as both const and volatile. + return x; + } + + void f6() { + // No use of `this`, but we still expect to be able to get its type. + global++; + } + + float f7() const & { + // We expect the type of `this` to be const-qualified. + return x; + } + + float f8() && { + // We expect the type of `this` to be unqualified. + return x; + } +}; + +// We want to test that D* is in the database even when there's no use of it, +// not even through an implicit dereference of `this`. +class D { + void f() { + global++; + } +}; + +template +class InstantiatedTemplateClass { + int x; + +public: + + void f1() { + // Implicit dereference of `this.` + x++; + } + + void f2() { + // Explicit dereference of `this.` + this->x++; + } + + int f3() const { + // We expect the type of `this` to be const-qualified. + return x; + } + + int f4() volatile { + // We expect the type of `this` to be volatile-qualified. + return x; + } + + int f5() const volatile { + // We expect the type of `this` to be qualified as both const and volatile. + return x; + } + + void f6() { + // No use of `this`, but we still expect to be able to get its type. + global++; + } + + float f7() const & { + // We expect the type of `this` to be const-qualified. + return x; + } + + float f8() && { + // We expect the type of `this` to be unqualified. + return x; + } +}; + +void instantiate() { + InstantiatedTemplateClass x; + x.f1(); + x.f2(); + x.f3(); + x.f4(); + x.f5(); + x.f6(); + x.f7(); + + float val = InstantiatedTemplateClass().f8(); +} + +// Since there are no instantiations of this class, we don't expect +// MemberFunction::getTypeOfThis() to hold. +template +class UninstantiatedTemplateClass { + int x; + +public: + + void f1() { + x++; + } +}; diff --git a/cpp/ql/test/library-tests/members/this/this.expected b/cpp/ql/test/library-tests/members/this/this.expected new file mode 100644 index 000000000000..d7f166e4898e --- /dev/null +++ b/cpp/ql/test/library-tests/members/this/this.expected @@ -0,0 +1,48 @@ +thisExprType +| test.cpp:11:5:11:5 | this | file://:0:0:0:0 | C * | +| test.cpp:16:5:16:8 | this | file://:0:0:0:0 | C * | +| test.cpp:21:12:21:12 | this | file://:0:0:0:0 | const C * | +| test.cpp:26:12:26:12 | this | file://:0:0:0:0 | volatile C * | +| test.cpp:31:12:31:12 | this | file://:0:0:0:0 | const volatile C * | +| test.cpp:41:12:41:12 | this | file://:0:0:0:0 | const C * | +| test.cpp:46:12:46:12 | this | file://:0:0:0:0 | C * | +| test.cpp:66:5:66:5 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:66:5:66:5 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:71:5:71:8 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:71:5:71:8 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:76:12:76:12 | this | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:76:12:76:12 | this | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:81:12:81:12 | this | file://:0:0:0:0 | volatile InstantiatedTemplateClass * | +| test.cpp:81:12:81:12 | this | file://:0:0:0:0 | volatile InstantiatedTemplateClass * | +| test.cpp:86:12:86:12 | this | file://:0:0:0:0 | const volatile InstantiatedTemplateClass * | +| test.cpp:86:12:86:12 | this | file://:0:0:0:0 | const volatile InstantiatedTemplateClass * | +| test.cpp:96:12:96:12 | this | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:96:12:96:12 | this | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:101:12:101:12 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:101:12:101:12 | this | file://:0:0:0:0 | InstantiatedTemplateClass * | +#select +| test.cpp:9:8:9:9 | f1 | file://:0:0:0:0 | C * | +| test.cpp:14:8:14:9 | f2 | file://:0:0:0:0 | C * | +| test.cpp:19:7:19:8 | f3 | file://:0:0:0:0 | const C * | +| test.cpp:24:7:24:8 | f4 | file://:0:0:0:0 | volatile C * | +| test.cpp:29:7:29:8 | f5 | file://:0:0:0:0 | const volatile C * | +| test.cpp:34:8:34:9 | f6 | file://:0:0:0:0 | C * | +| test.cpp:39:9:39:10 | f7 | file://:0:0:0:0 | const C * | +| test.cpp:44:9:44:10 | f8 | file://:0:0:0:0 | C * | +| test.cpp:53:8:53:8 | f | file://:0:0:0:0 | D * | +| test.cpp:64:8:64:8 | f1 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:64:8:64:9 | f1 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:69:8:69:8 | f2 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:69:8:69:9 | f2 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:74:7:74:7 | f3 | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:74:7:74:8 | f3 | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:79:7:79:7 | f4 | file://:0:0:0:0 | volatile InstantiatedTemplateClass * | +| test.cpp:79:7:79:8 | f4 | file://:0:0:0:0 | volatile InstantiatedTemplateClass * | +| test.cpp:84:7:84:7 | f5 | file://:0:0:0:0 | const volatile InstantiatedTemplateClass * | +| test.cpp:84:7:84:8 | f5 | file://:0:0:0:0 | const volatile InstantiatedTemplateClass * | +| test.cpp:89:8:89:8 | f6 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:89:8:89:9 | f6 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:94:9:94:9 | f7 | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:94:9:94:10 | f7 | file://:0:0:0:0 | const InstantiatedTemplateClass * | +| test.cpp:99:9:99:9 | f8 | file://:0:0:0:0 | InstantiatedTemplateClass * | +| test.cpp:99:9:99:10 | f8 | file://:0:0:0:0 | InstantiatedTemplateClass * | diff --git a/cpp/ql/test/library-tests/members/this/this.ql b/cpp/ql/test/library-tests/members/this/this.ql new file mode 100644 index 000000000000..b4ab73254d29 --- /dev/null +++ b/cpp/ql/test/library-tests/members/this/this.ql @@ -0,0 +1,6 @@ +import cpp + +query predicate thisExprType(ThisExpr e, Type t) { t = e.getType() } + +from MemberFunction f +select f, f.getTypeOfThis() diff --git a/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected index f5ef2d77c986..5488cf803e6b 100644 --- a/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected +++ b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected @@ -2,8 +2,8 @@ | copy_from_prototype.cpp:3:7:3:7 | a | a::a(const a &) -> void | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:3:7:3:7 | operator= | a::operator=(a &&) -> a & | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:3:7:3:7 | operator= | a::operator=(const a &) -> a & | copy_from_prototype.cpp:3:7:3:7 | a | | -| copy_from_prototype.cpp:4:26:4:26 | a | a<>::a<(unnamed)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a<> | 123 | -| copy_from_prototype.cpp:4:26:4:26 | a | a::a<(unnamed)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a | | +| copy_from_prototype.cpp:4:26:4:26 | a | a<>::a<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a<> | 123 | +| copy_from_prototype.cpp:4:26:4:26 | a | a::a<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:7:7:7:7 | b | b::b() -> void | copy_from_prototype.cpp:7:7:7:7 | b | | | copy_from_prototype.cpp:7:7:7:7 | b | b::b(b &&) -> void | copy_from_prototype.cpp:7:7:7:7 | b | | | copy_from_prototype.cpp:7:7:7:7 | b | b::b(const b &) -> void | copy_from_prototype.cpp:7:7:7:7 | b | | @@ -13,8 +13,8 @@ | copy_from_prototype.cpp:13:7:13:7 | c | c::c(const c &) -> void | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:13:7:13:7 | operator= | c::operator=(c &&) -> c & | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:13:7:13:7 | operator= | c::operator=(const c &) -> c & | copy_from_prototype.cpp:13:7:13:7 | c | | -| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | X | -| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | | +| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | X | +| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:17:7:17:7 | d | d::d() -> void | copy_from_prototype.cpp:17:7:17:7 | d | | | copy_from_prototype.cpp:17:7:17:7 | d | d::d(const d &) -> void | copy_from_prototype.cpp:17:7:17:7 | d | | | copy_from_prototype.cpp:17:7:17:7 | d | d::d(d &&) -> void | copy_from_prototype.cpp:17:7:17:7 | d | | @@ -24,7 +24,7 @@ | copy_from_prototype.cpp:22:8:22:8 | e | e::e(e &&) -> void | copy_from_prototype.cpp:22:8:22:8 | e | | | copy_from_prototype.cpp:22:8:22:8 | operator= | e::operator=(const e &) -> e & | copy_from_prototype.cpp:22:8:22:8 | e | | | copy_from_prototype.cpp:22:8:22:8 | operator= | e::operator=(e &&) -> e & | copy_from_prototype.cpp:22:8:22:8 | e | | -| copy_from_prototype.cpp:23:26:23:26 | e | e::e<(unnamed)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | -| copy_from_prototype.cpp:26:35:26:43 | e | e::e<(unnamed)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | +| copy_from_prototype.cpp:23:26:23:26 | e | e::e<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | +| copy_from_prototype.cpp:26:35:26:43 | e | e::e<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | | file://:0:0:0:0 | operator= | __va_list_tag::operator=(__va_list_tag &&) -> __va_list_tag & | file://:0:0:0:0 | __va_list_tag | | | file://:0:0:0:0 | operator= | __va_list_tag::operator=(const __va_list_tag &) -> __va_list_tag & | file://:0:0:0:0 | __va_list_tag | | diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected index 2cc7f9a5386b..472ba0a1b7c7 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected +++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected @@ -1,483 +1,660 @@ -| inline_assembly.c:10:3:10:3 | y | 0.0 | -| inline_assembly.c:12:29:12:29 | x | 0.0 | -| inline_assembly.c:12:32:12:32 | y | 1.0 | -| inline_assembly.c:16:25:16:25 | x | 0.0 | -| inline_assembly.c:16:35:16:35 | y | 1.0 | -| inline_assembly.c:21:29:21:29 | x | 0.0 | -| inline_assembly.c:21:32:21:32 | y | 0.0 | -| minmax.c:18:37:18:37 | x | 1.0 | -| minmax.c:18:40:18:40 | y | 2.0 | -| minmax.c:18:43:18:43 | z | 3.0 | -| minmax.c:20:2:20:2 | z | -2.147483648E9 | -| minmax.c:22:8:22:8 | x | 1.0 | -| minmax.c:22:14:22:14 | y | 2.0 | -| minmax.c:22:18:22:18 | t | -2.147483648E9 | -| minmax.c:22:22:22:22 | x | 1.0 | -| minmax.c:23:3:23:3 | t | 0.0 | -| minmax.c:26:37:26:37 | x | 1.0 | -| minmax.c:26:40:26:40 | y | 2.0 | -| minmax.c:26:43:26:43 | z | 0.0 | -| test.c:8:5:8:9 | count | -2.147483648E9 | -| test.c:8:13:8:17 | count | -2.147483648E9 | -| test.c:10:10:10:14 | count | -2.147483648E9 | -| test.c:16:5:16:9 | count | -2.147483648E9 | -| test.c:16:14:16:18 | count | 0.0 | -| test.c:18:10:18:14 | count | 0.0 | -| test.c:24:5:24:9 | count | 0.0 | -| test.c:25:5:25:9 | count | -2.147483648E9 | -| test.c:25:13:25:17 | count | 1.0 | -| test.c:27:10:27:14 | count | 0.0 | -| test.c:33:8:33:8 | i | -2.147483648E9 | -| test.c:33:15:33:15 | i | 0.0 | -| test.c:33:22:33:22 | i | -2.147483648E9 | -| test.c:33:26:33:26 | i | 0.0 | -| test.c:34:5:34:9 | total | -2.147483648E9 | -| test.c:34:14:34:14 | i | 0.0 | -| test.c:36:10:36:14 | total | -2.147483648E9 | -| test.c:36:18:36:18 | i | 2.0 | -| test.c:42:8:42:8 | i | -2.147483648E9 | -| test.c:42:15:42:15 | i | 0.0 | -| test.c:42:22:42:22 | i | 0.0 | -| test.c:43:5:43:9 | total | -2.147483648E9 | -| test.c:43:14:43:14 | i | 0.0 | -| test.c:45:10:45:14 | total | -2.147483648E9 | -| test.c:45:18:45:18 | i | 2.0 | -| test.c:51:8:51:8 | i | -2.147483648E9 | -| test.c:51:15:51:15 | i | 0.0 | -| test.c:51:24:51:24 | i | -2.147483648E9 | -| test.c:51:28:51:28 | i | 0.0 | -| test.c:52:5:52:9 | total | -2.147483648E9 | -| test.c:52:14:52:14 | i | 0.0 | -| test.c:54:10:54:14 | total | -2.147483648E9 | -| test.c:54:18:54:18 | i | 2.0 | -| test.c:58:7:58:7 | i | -2.147483648E9 | -| test.c:59:9:59:9 | i | -2.147483648E9 | -| test.c:60:14:60:14 | i | -2.147483648E9 | -| test.c:67:15:67:15 | y | -2.147483648E9 | -| test.c:67:20:67:20 | y | -999.0 | -| test.c:68:9:68:9 | x | -2.147483648E9 | -| test.c:68:13:68:13 | y | -999.0 | -| test.c:69:14:69:14 | x | -2.147483648E9 | -| test.c:72:10:72:10 | y | -2.147483648E9 | -| test.c:76:7:76:7 | y | -2.147483648E9 | -| test.c:77:9:77:9 | x | -2.147483648E9 | -| test.c:81:9:81:9 | x | -2.147483648E9 | -| test.c:85:10:85:10 | x | 4.0 | -| test.c:89:7:89:7 | y | -2.147483648E9 | -| test.c:90:9:90:9 | x | -2.147483648E9 | -| test.c:90:13:90:13 | y | 8.0 | -| test.c:93:12:93:12 | x | 8.0 | -| test.c:100:3:100:3 | c | -128.0 | -| test.c:101:7:101:7 | c | -128.0 | -| test.c:104:7:104:7 | c | -128.0 | -| test.c:105:5:105:5 | c | -128.0 | -| test.c:106:9:106:9 | c | -128.0 | -| test.c:109:9:109:9 | c | -128.0 | -| test.c:119:10:119:10 | n | 0.0 | -| test.c:124:11:124:15 | Start | 0.0 | -| test.c:127:6:127:10 | Start | 0.0 | -| test.c:127:15:127:20 | Length | 0.0 | -| test.c:135:22:135:22 | c | -128.0 | -| test.c:137:20:137:20 | x | 0.0 | -| test.c:138:11:138:11 | i | -2.147483648E9 | -| test.c:139:19:139:19 | c | -128.0 | -| test.c:139:23:139:23 | i | -2.147483648E9 | -| test.c:139:27:139:28 | uc | 0.0 | -| test.c:139:32:139:32 | x | 0.0 | -| test.c:139:36:139:36 | y | 0.0 | -| test.c:139:40:139:40 | z | -2.147483648E9 | -| test.c:144:23:144:23 | x | -2.147483648E9 | -| test.c:145:32:145:32 | x | -2.147483648E9 | -| test.c:146:33:146:33 | x | -2.147483648E9 | -| test.c:147:31:147:31 | x | -2.147483648E9 | -| test.c:148:13:148:13 | x | -2.147483648E9 | -| test.c:149:23:149:23 | x | -2.147483648E9 | -| test.c:150:10:150:11 | x0 | -128.0 | -| test.c:150:15:150:16 | x1 | 0.0 | -| test.c:150:20:150:21 | x2 | 0.0 | -| test.c:150:25:150:26 | x3 | -2.147483648E9 | -| test.c:150:30:150:31 | c0 | -128.0 | -| test.c:150:35:150:36 | s0 | 0.0 | -| test.c:154:11:154:11 | x | -9.223372036854776E18 | -| test.c:154:20:154:20 | x | 1.0 | -| test.c:154:30:154:30 | x | 1.0 | -| test.c:154:35:154:35 | x | 1.0 | -| test.c:161:12:161:12 | a | -2.147483648E9 | -| test.c:161:17:161:17 | a | 3.0 | -| test.c:162:14:162:14 | a | 3.0 | -| test.c:163:14:163:14 | a | 3.0 | -| test.c:164:5:164:9 | total | 0.0 | -| test.c:164:14:164:14 | b | 3.0 | -| test.c:164:16:164:16 | c | -11.0 | -| test.c:166:12:166:12 | a | -2.147483648E9 | -| test.c:166:17:166:17 | a | 0.0 | -| test.c:167:14:167:14 | a | 0.0 | -| test.c:168:14:168:14 | a | 0.0 | -| test.c:169:5:169:9 | total | -8.0 | -| test.c:169:14:169:14 | b | 0.0 | -| test.c:169:16:169:16 | c | -11.0 | -| test.c:171:13:171:13 | a | -2.147483648E9 | -| test.c:171:18:171:18 | a | -7.0 | -| test.c:172:14:172:14 | a | -7.0 | -| test.c:173:14:173:14 | a | -7.0 | -| test.c:174:5:174:9 | total | -19.0 | -| test.c:174:14:174:14 | b | -7.0 | -| test.c:174:16:174:16 | c | -11.0 | -| test.c:176:13:176:13 | a | -2.147483648E9 | -| test.c:176:18:176:18 | a | -7.0 | -| test.c:177:14:177:14 | a | -7.0 | -| test.c:178:14:178:14 | a | -7.0 | -| test.c:179:5:179:9 | total | -37.0 | -| test.c:179:14:179:14 | b | -7.0 | -| test.c:179:16:179:16 | c | -1.0 | -| test.c:181:13:181:13 | a | -2.147483648E9 | -| test.c:181:18:181:18 | a | -7.0 | -| test.c:182:14:182:14 | a | -7.0 | -| test.c:183:14:183:14 | a | -7.0 | -| test.c:184:5:184:9 | total | -45.0 | -| test.c:184:14:184:14 | b | -7.0 | -| test.c:184:16:184:16 | c | -0.0 | -| test.c:186:13:186:13 | a | -2.147483648E9 | -| test.c:186:18:186:18 | a | -7.0 | -| test.c:187:14:187:14 | a | -7.0 | -| test.c:188:14:188:14 | a | -7.0 | -| test.c:189:5:189:9 | total | -52.0 | -| test.c:189:14:189:14 | b | -7.0 | -| test.c:189:16:189:16 | c | 2.0 | -| test.c:192:10:192:14 | total | -57.0 | -| test.c:200:12:200:12 | a | -2.147483648E9 | -| test.c:200:17:200:17 | a | 3.0 | -| test.c:200:33:200:33 | b | -2.147483648E9 | -| test.c:200:38:200:38 | b | 5.0 | -| test.c:201:13:201:13 | a | 3.0 | -| test.c:201:15:201:15 | b | 5.0 | -| test.c:202:5:202:9 | total | 0.0 | -| test.c:202:14:202:14 | r | -2.147483648E9 | -| test.c:204:12:204:12 | a | -2.147483648E9 | -| test.c:204:17:204:17 | a | 3.0 | -| test.c:204:33:204:33 | b | -2.147483648E9 | -| test.c:204:38:204:38 | b | 0.0 | -| test.c:205:13:205:13 | a | 3.0 | -| test.c:205:15:205:15 | b | 0.0 | -| test.c:206:5:206:9 | total | -2.147483648E9 | -| test.c:206:14:206:14 | r | -2.147483648E9 | -| test.c:208:12:208:12 | a | -2.147483648E9 | -| test.c:208:17:208:17 | a | 3.0 | -| test.c:208:35:208:35 | b | -2.147483648E9 | -| test.c:208:40:208:40 | b | -13.0 | -| test.c:209:13:209:13 | a | 3.0 | -| test.c:209:15:209:15 | b | -13.0 | -| test.c:210:5:210:9 | total | -2.147483648E9 | -| test.c:210:14:210:14 | r | -2.147483648E9 | -| test.c:212:12:212:12 | a | -2.147483648E9 | -| test.c:212:17:212:17 | a | 3.0 | -| test.c:212:35:212:35 | b | -2.147483648E9 | -| test.c:212:40:212:40 | b | -13.0 | -| test.c:213:13:213:13 | a | 3.0 | -| test.c:213:15:213:15 | b | -13.0 | -| test.c:214:5:214:9 | total | -2.147483648E9 | -| test.c:214:14:214:14 | r | -2.147483648E9 | -| test.c:216:12:216:12 | a | -2.147483648E9 | -| test.c:216:17:216:17 | a | 3.0 | -| test.c:216:35:216:35 | b | -2.147483648E9 | -| test.c:216:40:216:40 | b | -13.0 | -| test.c:217:13:217:13 | a | 3.0 | -| test.c:217:15:217:15 | b | -13.0 | -| test.c:218:5:218:9 | total | -2.147483648E9 | -| test.c:218:14:218:14 | r | -2.147483648E9 | -| test.c:221:10:221:14 | total | -2.147483648E9 | -| test.c:228:12:228:12 | a | -2.147483648E9 | -| test.c:228:17:228:17 | a | 0.0 | -| test.c:228:33:228:33 | b | -2.147483648E9 | -| test.c:228:38:228:38 | b | 5.0 | -| test.c:229:13:229:13 | a | 0.0 | -| test.c:229:15:229:15 | b | 5.0 | -| test.c:230:5:230:9 | total | 0.0 | -| test.c:230:14:230:14 | r | -2.147483648E9 | -| test.c:232:12:232:12 | a | -2.147483648E9 | -| test.c:232:17:232:17 | a | 0.0 | -| test.c:232:33:232:33 | b | -2.147483648E9 | -| test.c:232:38:232:38 | b | 0.0 | -| test.c:233:13:233:13 | a | 0.0 | -| test.c:233:15:233:15 | b | 0.0 | -| test.c:234:5:234:9 | total | -2.147483648E9 | -| test.c:234:14:234:14 | r | -2.147483648E9 | -| test.c:236:12:236:12 | a | -2.147483648E9 | -| test.c:236:17:236:17 | a | 0.0 | -| test.c:236:35:236:35 | b | -2.147483648E9 | -| test.c:236:40:236:40 | b | -13.0 | -| test.c:237:13:237:13 | a | 0.0 | -| test.c:237:15:237:15 | b | -13.0 | -| test.c:238:5:238:9 | total | -2.147483648E9 | -| test.c:238:14:238:14 | r | -2.147483648E9 | -| test.c:240:12:240:12 | a | -2.147483648E9 | -| test.c:240:17:240:17 | a | 0.0 | -| test.c:240:35:240:35 | b | -2.147483648E9 | -| test.c:240:40:240:40 | b | -13.0 | -| test.c:241:13:241:13 | a | 0.0 | -| test.c:241:15:241:15 | b | -13.0 | -| test.c:242:5:242:9 | total | -2.147483648E9 | -| test.c:242:14:242:14 | r | -2.147483648E9 | -| test.c:244:12:244:12 | a | -2.147483648E9 | -| test.c:244:17:244:17 | a | 0.0 | -| test.c:244:35:244:35 | b | -2.147483648E9 | -| test.c:244:40:244:40 | b | -13.0 | -| test.c:245:13:245:13 | a | 0.0 | -| test.c:245:15:245:15 | b | -13.0 | -| test.c:246:5:246:9 | total | -2.147483648E9 | -| test.c:246:14:246:14 | r | -2.147483648E9 | -| test.c:249:10:249:14 | total | -2.147483648E9 | -| test.c:256:14:256:14 | a | -2.147483648E9 | -| test.c:256:19:256:19 | a | -17.0 | -| test.c:256:35:256:35 | b | -2.147483648E9 | -| test.c:256:40:256:40 | b | 5.0 | -| test.c:257:13:257:13 | a | -17.0 | -| test.c:257:15:257:15 | b | 5.0 | -| test.c:258:5:258:9 | total | 0.0 | -| test.c:258:14:258:14 | r | -2.147483648E9 | -| test.c:260:14:260:14 | a | -2.147483648E9 | -| test.c:260:19:260:19 | a | -17.0 | -| test.c:260:35:260:35 | b | -2.147483648E9 | -| test.c:260:40:260:40 | b | 0.0 | -| test.c:261:13:261:13 | a | -17.0 | -| test.c:261:15:261:15 | b | 0.0 | -| test.c:262:5:262:9 | total | -2.147483648E9 | -| test.c:262:14:262:14 | r | -2.147483648E9 | -| test.c:264:14:264:14 | a | -2.147483648E9 | -| test.c:264:19:264:19 | a | -17.0 | -| test.c:264:37:264:37 | b | -2.147483648E9 | -| test.c:264:42:264:42 | b | -13.0 | -| test.c:265:13:265:13 | a | -17.0 | -| test.c:265:15:265:15 | b | -13.0 | -| test.c:266:5:266:9 | total | -2.147483648E9 | -| test.c:266:14:266:14 | r | -2.147483648E9 | -| test.c:268:14:268:14 | a | -2.147483648E9 | -| test.c:268:19:268:19 | a | -17.0 | -| test.c:268:37:268:37 | b | -2.147483648E9 | -| test.c:268:42:268:42 | b | -13.0 | -| test.c:269:13:269:13 | a | -17.0 | -| test.c:269:15:269:15 | b | -13.0 | -| test.c:270:5:270:9 | total | -2.147483648E9 | -| test.c:270:14:270:14 | r | -2.147483648E9 | -| test.c:272:14:272:14 | a | -2.147483648E9 | -| test.c:272:19:272:19 | a | -17.0 | -| test.c:272:37:272:37 | b | -2.147483648E9 | -| test.c:272:42:272:42 | b | -13.0 | -| test.c:273:13:273:13 | a | -17.0 | -| test.c:273:15:273:15 | b | -13.0 | -| test.c:274:5:274:9 | total | -2.147483648E9 | -| test.c:274:14:274:14 | r | -2.147483648E9 | -| test.c:277:10:277:14 | total | -2.147483648E9 | -| test.c:284:14:284:14 | a | -2.147483648E9 | -| test.c:284:19:284:19 | a | -17.0 | -| test.c:284:34:284:34 | b | -2.147483648E9 | -| test.c:284:39:284:39 | b | 5.0 | -| test.c:285:13:285:13 | a | -17.0 | -| test.c:285:15:285:15 | b | 5.0 | -| test.c:286:5:286:9 | total | 0.0 | -| test.c:286:14:286:14 | r | -2.147483648E9 | -| test.c:288:14:288:14 | a | -2.147483648E9 | -| test.c:288:19:288:19 | a | -17.0 | -| test.c:288:34:288:34 | b | -2.147483648E9 | -| test.c:288:39:288:39 | b | 0.0 | -| test.c:289:13:289:13 | a | -17.0 | -| test.c:289:15:289:15 | b | 0.0 | -| test.c:290:5:290:9 | total | -2.147483648E9 | -| test.c:290:14:290:14 | r | -2.147483648E9 | -| test.c:292:14:292:14 | a | -2.147483648E9 | -| test.c:292:19:292:19 | a | -17.0 | -| test.c:292:36:292:36 | b | -2.147483648E9 | -| test.c:292:41:292:41 | b | -13.0 | -| test.c:293:13:293:13 | a | -17.0 | -| test.c:293:15:293:15 | b | -13.0 | -| test.c:294:5:294:9 | total | -2.147483648E9 | -| test.c:294:14:294:14 | r | -2.147483648E9 | -| test.c:296:14:296:14 | a | -2.147483648E9 | -| test.c:296:19:296:19 | a | -17.0 | -| test.c:296:36:296:36 | b | -2.147483648E9 | -| test.c:296:41:296:41 | b | -13.0 | -| test.c:297:13:297:13 | a | -17.0 | -| test.c:297:15:297:15 | b | -13.0 | -| test.c:298:5:298:9 | total | -2.147483648E9 | -| test.c:298:14:298:14 | r | -2.147483648E9 | -| test.c:300:14:300:14 | a | -2.147483648E9 | -| test.c:300:19:300:19 | a | -17.0 | -| test.c:300:36:300:36 | b | -2.147483648E9 | -| test.c:300:41:300:41 | b | -13.0 | -| test.c:301:13:301:13 | a | -17.0 | -| test.c:301:15:301:15 | b | -13.0 | -| test.c:302:5:302:9 | total | -2.147483648E9 | -| test.c:302:14:302:14 | r | -2.147483648E9 | -| test.c:305:10:305:14 | total | -2.147483648E9 | -| test.c:312:14:312:14 | a | -2.147483648E9 | -| test.c:312:19:312:19 | a | -17.0 | -| test.c:312:35:312:35 | b | -2.147483648E9 | -| test.c:312:40:312:40 | b | 5.0 | -| test.c:313:13:313:13 | a | -17.0 | -| test.c:313:15:313:15 | b | 5.0 | -| test.c:314:5:314:9 | total | 0.0 | -| test.c:314:14:314:14 | r | -2.147483648E9 | -| test.c:316:14:316:14 | a | -2.147483648E9 | -| test.c:316:19:316:19 | a | -17.0 | -| test.c:316:35:316:35 | b | -2.147483648E9 | -| test.c:316:40:316:40 | b | 0.0 | -| test.c:317:13:317:13 | a | -17.0 | -| test.c:317:15:317:15 | b | 0.0 | -| test.c:318:5:318:9 | total | -2.147483648E9 | -| test.c:318:14:318:14 | r | -2.147483648E9 | -| test.c:320:14:320:14 | a | -2.147483648E9 | -| test.c:320:19:320:19 | a | -17.0 | -| test.c:320:37:320:37 | b | -2.147483648E9 | -| test.c:320:42:320:42 | b | -13.0 | -| test.c:321:13:321:13 | a | -17.0 | -| test.c:321:15:321:15 | b | -13.0 | -| test.c:322:5:322:9 | total | -2.147483648E9 | -| test.c:322:14:322:14 | r | -2.147483648E9 | -| test.c:324:14:324:14 | a | -2.147483648E9 | -| test.c:324:19:324:19 | a | -17.0 | -| test.c:324:37:324:37 | b | -2.147483648E9 | -| test.c:324:42:324:42 | b | -13.0 | -| test.c:325:13:325:13 | a | -17.0 | -| test.c:325:15:325:15 | b | -13.0 | -| test.c:326:5:326:9 | total | -2.147483648E9 | -| test.c:326:14:326:14 | r | -2.147483648E9 | -| test.c:328:14:328:14 | a | -2.147483648E9 | -| test.c:328:19:328:19 | a | -17.0 | -| test.c:328:37:328:37 | b | -2.147483648E9 | -| test.c:328:42:328:42 | b | -13.0 | -| test.c:329:13:329:13 | a | -17.0 | -| test.c:329:15:329:15 | b | -13.0 | -| test.c:330:5:330:9 | total | -2.147483648E9 | -| test.c:330:14:330:14 | r | -2.147483648E9 | -| test.c:333:10:333:14 | total | -2.147483648E9 | -| test.c:338:7:338:7 | x | -2.147483648E9 | -| test.c:342:10:342:10 | i | 0.0 | -| test.c:343:5:343:5 | i | 0.0 | -| test.c:345:3:345:3 | d | -2.147483648E9 | -| test.c:345:7:345:7 | i | 3.0 | -| test.c:346:7:346:7 | x | 0.0 | -| test.c:347:9:347:9 | d | 3.0 | -| test.c:347:14:347:14 | x | 0.0 | -| test.c:357:3:357:4 | y1 | 0.0 | -| test.c:357:8:357:8 | x | 0.0 | -| test.c:357:18:357:18 | x | 0.0 | -| test.c:358:3:358:4 | y2 | 0.0 | -| test.c:358:8:358:8 | x | 0.0 | -| test.c:358:24:358:24 | x | 0.0 | -| test.c:359:3:359:4 | y3 | 0.0 | -| test.c:360:3:360:4 | y4 | 0.0 | -| test.c:361:3:361:4 | y5 | 0.0 | -| test.c:362:3:362:4 | y6 | 0.0 | -| test.c:363:3:363:4 | y7 | 0.0 | -| test.c:364:3:364:4 | y8 | 0.0 | -| test.c:365:7:365:7 | x | 0.0 | -| test.c:366:5:366:6 | y3 | 0.0 | -| test.c:366:10:366:10 | x | 0.0 | -| test.c:367:5:367:6 | y4 | 0.0 | -| test.c:367:10:367:10 | x | 0.0 | -| test.c:368:5:368:6 | y5 | 0.0 | -| test.c:368:11:368:11 | x | 0.0 | -| test.c:369:5:369:6 | y6 | 0.0 | -| test.c:369:27:369:27 | x | 0.0 | -| test.c:370:5:370:6 | y7 | 0.0 | -| test.c:370:27:370:27 | x | 0.0 | -| test.c:371:5:371:6 | y8 | 0.0 | -| test.c:371:28:371:28 | x | 0.0 | -| test.c:373:10:373:11 | y1 | 0.0 | -| test.c:373:15:373:16 | y2 | 0.0 | -| test.c:373:20:373:21 | y3 | 0.0 | -| test.c:373:25:373:26 | y4 | 0.0 | -| test.c:373:30:373:31 | y5 | 0.0 | -| test.c:373:35:373:36 | y6 | 0.0 | -| test.c:373:40:373:41 | y7 | 0.0 | -| test.c:373:45:373:46 | y8 | 0.0 | -| test.c:379:3:379:4 | y1 | 0.0 | -| test.c:379:8:379:8 | x | 0.0 | -| test.c:379:18:379:18 | x | 101.0 | -| test.c:380:3:380:4 | y2 | 0.0 | -| test.c:380:8:380:8 | x | 0.0 | -| test.c:380:25:380:25 | x | 101.0 | -| test.c:381:3:381:4 | y3 | 0.0 | -| test.c:382:3:382:4 | y4 | 0.0 | -| test.c:383:3:383:4 | y5 | 0.0 | -| test.c:384:7:384:7 | x | 0.0 | -| test.c:385:5:385:6 | y3 | 0.0 | -| test.c:385:11:385:11 | x | 300.0 | -| test.c:386:5:386:6 | y4 | 0.0 | -| test.c:386:11:386:11 | x | 300.0 | -| test.c:387:5:387:6 | y5 | 0.0 | -| test.c:387:27:387:27 | x | 300.0 | -| test.c:389:10:389:11 | y1 | 101.0 | -| test.c:389:15:389:16 | y2 | 101.0 | -| test.c:389:20:389:21 | y3 | 0.0 | -| test.c:389:25:389:26 | y4 | 100.0 | -| test.c:389:30:389:31 | y5 | 0.0 | -| test.c:394:20:394:20 | x | 0.0 | -| test.c:394:30:394:30 | x | 0.0 | -| test.c:397:3:397:4 | y1 | 0.0 | -| test.c:397:11:397:11 | y | 0.0 | -| test.c:397:14:397:14 | y | 1.0 | -| test.c:398:3:398:4 | y2 | 0.0 | -| test.c:398:9:398:9 | y | 1.0 | -| test.c:398:14:398:14 | y | 2.0 | -| test.c:398:22:398:22 | y | 5.0 | -| test.c:399:10:399:11 | y1 | 1.0 | -| test.c:399:15:399:16 | y2 | 5.0 | -| test.c:407:3:407:3 | i | -2.147483648E9 | -| test.c:408:7:408:7 | i | 10.0 | -| test.c:410:3:410:3 | i | -2.147483648E9 | -| test.c:411:3:411:3 | i | 10.0 | -| test.c:412:7:412:7 | i | -2.147483648E9 | -| test.c:414:3:414:3 | i | -2.147483648E9 | -| test.c:415:3:415:3 | i | 40.0 | -| test.c:416:7:416:7 | i | -2.147483648E9 | -| test.c:418:3:418:3 | i | -2.147483648E9 | -| test.c:418:7:418:7 | j | -2.147483648E9 | -| test.c:419:7:419:7 | i | 40.0 | -| test.c:421:3:421:3 | i | -2.147483648E9 | -| test.c:421:8:421:8 | j | 40.0 | -| test.c:422:7:422:7 | i | 50.0 | -| test.c:424:3:424:3 | i | -2.147483648E9 | -| test.c:424:13:424:13 | j | -2.147483648E9 | -| test.c:425:7:425:7 | i | -2.147483648E9 | -| test.cpp:10:7:10:7 | b | -2.147483648E9 | -| test.cpp:11:5:11:5 | x | -2.147483648E9 | -| test.cpp:13:10:13:10 | x | -2.147483648E9 | -| test.cpp:18:30:18:30 | x | -2.147483648E9 | -| test.cpp:19:10:19:11 | x0 | -128.0 | -| test.cpp:27:7:27:7 | y | -2.147483648E9 | -| test.cpp:28:5:28:5 | x | -2.147483648E9 | -| test.cpp:30:7:30:7 | y | -2.147483648E9 | -| test.cpp:31:5:31:5 | x | -2.147483648E9 | -| test.cpp:33:7:33:7 | y | -2.147483648E9 | -| test.cpp:34:5:34:5 | x | -2.147483648E9 | -| test.cpp:36:7:36:7 | y | -2.147483648E9 | -| test.cpp:37:5:37:5 | x | -2.147483648E9 | -| test.cpp:39:7:39:7 | y | -2.147483648E9 | -| test.cpp:40:5:40:5 | x | -2.147483648E9 | -| test.cpp:42:7:42:7 | y | -2.147483648E9 | -| test.cpp:43:5:43:5 | x | -2.147483648E9 | -| test.cpp:45:7:45:7 | y | -2.147483648E9 | -| test.cpp:46:5:46:5 | x | -2.147483648E9 | -| test.cpp:51:7:51:7 | x | -2.147483648E9 | -| test.cpp:52:21:52:21 | x | 0.0 | -| test.cpp:53:5:53:5 | t | 0.0 | -| test.cpp:53:15:53:16 | xb | 0.0 | -| test.cpp:56:7:56:7 | x | -2.147483648E9 | -| test.cpp:57:21:57:21 | x | 1.0 | -| test.cpp:58:5:58:5 | t | 0.0 | -| test.cpp:58:15:58:16 | xb | 1.0 | -| test.cpp:61:7:61:7 | x | -2.147483648E9 | -| test.cpp:62:21:62:21 | x | -2.147483648E9 | -| test.cpp:63:5:63:5 | t | 0.0 | -| test.cpp:63:15:63:16 | xb | 1.0 | -| test.cpp:66:19:66:19 | x | -2.147483648E9 | -| test.cpp:67:3:67:3 | t | 0.0 | -| test.cpp:67:13:67:14 | xb | 0.0 | -| test.cpp:69:10:69:10 | b | 0.0 | -| test.cpp:69:21:69:21 | t | 0.0 | -| test.cpp:74:30:74:30 | c | 0.0 | -| test.cpp:74:34:74:34 | c | 0.0 | -| test.cpp:75:22:75:30 | c_times_2 | 0.0 | -| test.cpp:77:5:77:13 | c_times_2 | 0.0 | -| test.cpp:79:3:79:11 | c_times_2 | 0.0 | +| inline_assembly.c:10:3:10:3 | y | 0 | +| inline_assembly.c:12:29:12:29 | x | 0 | +| inline_assembly.c:12:32:12:32 | y | 1 | +| inline_assembly.c:16:25:16:25 | x | 0 | +| inline_assembly.c:16:35:16:35 | y | 1 | +| inline_assembly.c:21:29:21:29 | x | 0 | +| inline_assembly.c:21:32:21:32 | y | 0 | +| minmax.c:18:37:18:37 | x | 1 | +| minmax.c:18:40:18:40 | y | 2 | +| minmax.c:18:43:18:43 | z | 3 | +| minmax.c:20:2:20:2 | z | -2147483648 | +| minmax.c:22:8:22:8 | x | 1 | +| minmax.c:22:14:22:14 | y | 2 | +| minmax.c:22:18:22:18 | t | -2147483648 | +| minmax.c:22:22:22:22 | x | 1 | +| minmax.c:23:3:23:3 | t | 0 | +| minmax.c:26:37:26:37 | x | 1 | +| minmax.c:26:40:26:40 | y | 2 | +| minmax.c:26:43:26:43 | z | 0 | +| test.c:8:5:8:9 | count | -2147483648 | +| test.c:8:13:8:17 | count | -2147483648 | +| test.c:10:10:10:14 | count | -2147483648 | +| test.c:16:5:16:9 | count | -2147483648 | +| test.c:16:14:16:18 | count | 0 | +| test.c:18:10:18:14 | count | 0 | +| test.c:24:5:24:9 | count | 0 | +| test.c:25:5:25:9 | count | -2147483648 | +| test.c:25:13:25:17 | count | 1 | +| test.c:27:10:27:14 | count | 0 | +| test.c:33:8:33:8 | i | -2147483648 | +| test.c:33:15:33:15 | i | 0 | +| test.c:33:22:33:22 | i | -2147483648 | +| test.c:33:26:33:26 | i | 0 | +| test.c:34:5:34:9 | total | -2147483648 | +| test.c:34:14:34:14 | i | 0 | +| test.c:36:10:36:14 | total | -2147483648 | +| test.c:36:18:36:18 | i | 2 | +| test.c:42:8:42:8 | i | -2147483648 | +| test.c:42:15:42:15 | i | 0 | +| test.c:42:22:42:22 | i | 0 | +| test.c:43:5:43:9 | total | -2147483648 | +| test.c:43:14:43:14 | i | 0 | +| test.c:45:10:45:14 | total | -2147483648 | +| test.c:45:18:45:18 | i | 2 | +| test.c:51:8:51:8 | i | -2147483648 | +| test.c:51:15:51:15 | i | 0 | +| test.c:51:24:51:24 | i | -2147483648 | +| test.c:51:28:51:28 | i | 0 | +| test.c:52:5:52:9 | total | -2147483648 | +| test.c:52:14:52:14 | i | 0 | +| test.c:54:10:54:14 | total | -2147483648 | +| test.c:54:18:54:18 | i | 2 | +| test.c:58:7:58:7 | i | -2147483648 | +| test.c:59:9:59:9 | i | -2147483648 | +| test.c:60:14:60:14 | i | -2147483648 | +| test.c:67:15:67:15 | y | -2147483648 | +| test.c:67:20:67:20 | y | -999 | +| test.c:68:9:68:9 | x | -2147483648 | +| test.c:68:13:68:13 | y | -999 | +| test.c:69:14:69:14 | x | -2147483648 | +| test.c:72:10:72:10 | y | -2147483648 | +| test.c:76:7:76:7 | y | -2147483648 | +| test.c:77:9:77:9 | x | -2147483648 | +| test.c:81:9:81:9 | x | -2147483648 | +| test.c:85:10:85:10 | x | 4 | +| test.c:89:7:89:7 | y | -2147483648 | +| test.c:90:9:90:9 | x | -2147483648 | +| test.c:90:13:90:13 | y | 8 | +| test.c:93:12:93:12 | x | 8 | +| test.c:100:3:100:3 | c | -128 | +| test.c:101:7:101:7 | c | -128 | +| test.c:104:7:104:7 | c | -128 | +| test.c:105:5:105:5 | c | -128 | +| test.c:106:9:106:9 | c | -128 | +| test.c:109:9:109:9 | c | -128 | +| test.c:119:10:119:10 | n | 0 | +| test.c:124:11:124:15 | Start | 0 | +| test.c:127:6:127:10 | Start | 0 | +| test.c:127:15:127:20 | Length | 0 | +| test.c:135:22:135:22 | c | -128 | +| test.c:137:20:137:20 | x | 0 | +| test.c:138:11:138:11 | i | -2147483648 | +| test.c:139:19:139:19 | c | -128 | +| test.c:139:23:139:23 | i | -2147483648 | +| test.c:139:27:139:28 | uc | 0 | +| test.c:139:32:139:32 | x | 0 | +| test.c:139:36:139:36 | y | 0 | +| test.c:139:40:139:40 | z | -2147483648 | +| test.c:144:23:144:23 | x | -2147483648 | +| test.c:145:32:145:32 | x | -2147483648 | +| test.c:146:33:146:33 | x | -2147483648 | +| test.c:147:31:147:31 | x | -2147483648 | +| test.c:148:13:148:13 | x | -2147483648 | +| test.c:149:23:149:23 | x | -2147483648 | +| test.c:150:10:150:11 | x0 | -128 | +| test.c:150:15:150:16 | x1 | 0 | +| test.c:150:20:150:21 | x2 | 0 | +| test.c:150:25:150:26 | x3 | -2147483648 | +| test.c:150:30:150:31 | c0 | -128 | +| test.c:150:35:150:36 | s0 | 0 | +| test.c:154:11:154:11 | x | -9223372036854775808 | +| test.c:154:20:154:20 | x | 1 | +| test.c:154:30:154:30 | x | 1 | +| test.c:154:35:154:35 | x | 1 | +| test.c:161:12:161:12 | a | -2147483648 | +| test.c:161:17:161:17 | a | 3 | +| test.c:162:14:162:14 | a | 3 | +| test.c:163:14:163:14 | a | 3 | +| test.c:164:5:164:9 | total | 0 | +| test.c:164:14:164:14 | b | 3 | +| test.c:164:16:164:16 | c | -11 | +| test.c:166:12:166:12 | a | -2147483648 | +| test.c:166:17:166:17 | a | 0 | +| test.c:167:14:167:14 | a | 0 | +| test.c:168:14:168:14 | a | 0 | +| test.c:169:5:169:9 | total | -8 | +| test.c:169:14:169:14 | b | 0 | +| test.c:169:16:169:16 | c | -11 | +| test.c:171:13:171:13 | a | -2147483648 | +| test.c:171:18:171:18 | a | -7 | +| test.c:172:14:172:14 | a | -7 | +| test.c:173:14:173:14 | a | -7 | +| test.c:174:5:174:9 | total | -19 | +| test.c:174:14:174:14 | b | -7 | +| test.c:174:16:174:16 | c | -11 | +| test.c:176:13:176:13 | a | -2147483648 | +| test.c:176:18:176:18 | a | -7 | +| test.c:177:14:177:14 | a | -7 | +| test.c:178:14:178:14 | a | -7 | +| test.c:179:5:179:9 | total | -37 | +| test.c:179:14:179:14 | b | -7 | +| test.c:179:16:179:16 | c | -1 | +| test.c:181:13:181:13 | a | -2147483648 | +| test.c:181:18:181:18 | a | -7 | +| test.c:182:14:182:14 | a | -7 | +| test.c:183:14:183:14 | a | -7 | +| test.c:184:5:184:9 | total | -45 | +| test.c:184:14:184:14 | b | -7 | +| test.c:184:16:184:16 | c | 0 | +| test.c:186:13:186:13 | a | -2147483648 | +| test.c:186:18:186:18 | a | -7 | +| test.c:187:14:187:14 | a | -7 | +| test.c:188:14:188:14 | a | -7 | +| test.c:189:5:189:9 | total | -52 | +| test.c:189:14:189:14 | b | -7 | +| test.c:189:16:189:16 | c | 2 | +| test.c:192:10:192:14 | total | -57 | +| test.c:200:12:200:12 | a | -2147483648 | +| test.c:200:17:200:17 | a | 3 | +| test.c:200:33:200:33 | b | -2147483648 | +| test.c:200:38:200:38 | b | 5 | +| test.c:201:13:201:13 | a | 3 | +| test.c:201:15:201:15 | b | 5 | +| test.c:202:5:202:9 | total | 0 | +| test.c:202:14:202:14 | r | -2147483648 | +| test.c:204:12:204:12 | a | -2147483648 | +| test.c:204:17:204:17 | a | 3 | +| test.c:204:33:204:33 | b | -2147483648 | +| test.c:204:38:204:38 | b | 0 | +| test.c:205:13:205:13 | a | 3 | +| test.c:205:15:205:15 | b | 0 | +| test.c:206:5:206:9 | total | -2147483648 | +| test.c:206:14:206:14 | r | -2147483648 | +| test.c:208:12:208:12 | a | -2147483648 | +| test.c:208:17:208:17 | a | 3 | +| test.c:208:35:208:35 | b | -2147483648 | +| test.c:208:40:208:40 | b | -13 | +| test.c:209:13:209:13 | a | 3 | +| test.c:209:15:209:15 | b | -13 | +| test.c:210:5:210:9 | total | -2147483648 | +| test.c:210:14:210:14 | r | -2147483648 | +| test.c:212:12:212:12 | a | -2147483648 | +| test.c:212:17:212:17 | a | 3 | +| test.c:212:35:212:35 | b | -2147483648 | +| test.c:212:40:212:40 | b | -13 | +| test.c:213:13:213:13 | a | 3 | +| test.c:213:15:213:15 | b | -13 | +| test.c:214:5:214:9 | total | -2147483648 | +| test.c:214:14:214:14 | r | -2147483648 | +| test.c:216:12:216:12 | a | -2147483648 | +| test.c:216:17:216:17 | a | 3 | +| test.c:216:35:216:35 | b | -2147483648 | +| test.c:216:40:216:40 | b | -13 | +| test.c:217:13:217:13 | a | 3 | +| test.c:217:15:217:15 | b | -13 | +| test.c:218:5:218:9 | total | -2147483648 | +| test.c:218:14:218:14 | r | -2147483648 | +| test.c:221:10:221:14 | total | -2147483648 | +| test.c:228:12:228:12 | a | -2147483648 | +| test.c:228:17:228:17 | a | 0 | +| test.c:228:33:228:33 | b | -2147483648 | +| test.c:228:38:228:38 | b | 5 | +| test.c:229:13:229:13 | a | 0 | +| test.c:229:15:229:15 | b | 5 | +| test.c:230:5:230:9 | total | 0 | +| test.c:230:14:230:14 | r | -2147483648 | +| test.c:232:12:232:12 | a | -2147483648 | +| test.c:232:17:232:17 | a | 0 | +| test.c:232:33:232:33 | b | -2147483648 | +| test.c:232:38:232:38 | b | 0 | +| test.c:233:13:233:13 | a | 0 | +| test.c:233:15:233:15 | b | 0 | +| test.c:234:5:234:9 | total | -2147483648 | +| test.c:234:14:234:14 | r | -2147483648 | +| test.c:236:12:236:12 | a | -2147483648 | +| test.c:236:17:236:17 | a | 0 | +| test.c:236:35:236:35 | b | -2147483648 | +| test.c:236:40:236:40 | b | -13 | +| test.c:237:13:237:13 | a | 0 | +| test.c:237:15:237:15 | b | -13 | +| test.c:238:5:238:9 | total | -2147483648 | +| test.c:238:14:238:14 | r | -2147483648 | +| test.c:240:12:240:12 | a | -2147483648 | +| test.c:240:17:240:17 | a | 0 | +| test.c:240:35:240:35 | b | -2147483648 | +| test.c:240:40:240:40 | b | -13 | +| test.c:241:13:241:13 | a | 0 | +| test.c:241:15:241:15 | b | -13 | +| test.c:242:5:242:9 | total | -2147483648 | +| test.c:242:14:242:14 | r | -2147483648 | +| test.c:244:12:244:12 | a | -2147483648 | +| test.c:244:17:244:17 | a | 0 | +| test.c:244:35:244:35 | b | -2147483648 | +| test.c:244:40:244:40 | b | -13 | +| test.c:245:13:245:13 | a | 0 | +| test.c:245:15:245:15 | b | -13 | +| test.c:246:5:246:9 | total | -2147483648 | +| test.c:246:14:246:14 | r | -2147483648 | +| test.c:249:10:249:14 | total | -2147483648 | +| test.c:256:14:256:14 | a | -2147483648 | +| test.c:256:19:256:19 | a | -17 | +| test.c:256:35:256:35 | b | -2147483648 | +| test.c:256:40:256:40 | b | 5 | +| test.c:257:13:257:13 | a | -17 | +| test.c:257:15:257:15 | b | 5 | +| test.c:258:5:258:9 | total | 0 | +| test.c:258:14:258:14 | r | -2147483648 | +| test.c:260:14:260:14 | a | -2147483648 | +| test.c:260:19:260:19 | a | -17 | +| test.c:260:35:260:35 | b | -2147483648 | +| test.c:260:40:260:40 | b | 0 | +| test.c:261:13:261:13 | a | -17 | +| test.c:261:15:261:15 | b | 0 | +| test.c:262:5:262:9 | total | -2147483648 | +| test.c:262:14:262:14 | r | -2147483648 | +| test.c:264:14:264:14 | a | -2147483648 | +| test.c:264:19:264:19 | a | -17 | +| test.c:264:37:264:37 | b | -2147483648 | +| test.c:264:42:264:42 | b | -13 | +| test.c:265:13:265:13 | a | -17 | +| test.c:265:15:265:15 | b | -13 | +| test.c:266:5:266:9 | total | -2147483648 | +| test.c:266:14:266:14 | r | -2147483648 | +| test.c:268:14:268:14 | a | -2147483648 | +| test.c:268:19:268:19 | a | -17 | +| test.c:268:37:268:37 | b | -2147483648 | +| test.c:268:42:268:42 | b | -13 | +| test.c:269:13:269:13 | a | -17 | +| test.c:269:15:269:15 | b | -13 | +| test.c:270:5:270:9 | total | -2147483648 | +| test.c:270:14:270:14 | r | -2147483648 | +| test.c:272:14:272:14 | a | -2147483648 | +| test.c:272:19:272:19 | a | -17 | +| test.c:272:37:272:37 | b | -2147483648 | +| test.c:272:42:272:42 | b | -13 | +| test.c:273:13:273:13 | a | -17 | +| test.c:273:15:273:15 | b | -13 | +| test.c:274:5:274:9 | total | -2147483648 | +| test.c:274:14:274:14 | r | -2147483648 | +| test.c:277:10:277:14 | total | -2147483648 | +| test.c:284:14:284:14 | a | -2147483648 | +| test.c:284:19:284:19 | a | -17 | +| test.c:284:34:284:34 | b | -2147483648 | +| test.c:284:39:284:39 | b | 5 | +| test.c:285:13:285:13 | a | -17 | +| test.c:285:15:285:15 | b | 5 | +| test.c:286:5:286:9 | total | 0 | +| test.c:286:14:286:14 | r | -2147483648 | +| test.c:288:14:288:14 | a | -2147483648 | +| test.c:288:19:288:19 | a | -17 | +| test.c:288:34:288:34 | b | -2147483648 | +| test.c:288:39:288:39 | b | 0 | +| test.c:289:13:289:13 | a | -17 | +| test.c:289:15:289:15 | b | 0 | +| test.c:290:5:290:9 | total | -2147483648 | +| test.c:290:14:290:14 | r | -2147483648 | +| test.c:292:14:292:14 | a | -2147483648 | +| test.c:292:19:292:19 | a | -17 | +| test.c:292:36:292:36 | b | -2147483648 | +| test.c:292:41:292:41 | b | -13 | +| test.c:293:13:293:13 | a | -17 | +| test.c:293:15:293:15 | b | -13 | +| test.c:294:5:294:9 | total | -2147483648 | +| test.c:294:14:294:14 | r | -2147483648 | +| test.c:296:14:296:14 | a | -2147483648 | +| test.c:296:19:296:19 | a | -17 | +| test.c:296:36:296:36 | b | -2147483648 | +| test.c:296:41:296:41 | b | -13 | +| test.c:297:13:297:13 | a | -17 | +| test.c:297:15:297:15 | b | -13 | +| test.c:298:5:298:9 | total | -2147483648 | +| test.c:298:14:298:14 | r | -2147483648 | +| test.c:300:14:300:14 | a | -2147483648 | +| test.c:300:19:300:19 | a | -17 | +| test.c:300:36:300:36 | b | -2147483648 | +| test.c:300:41:300:41 | b | -13 | +| test.c:301:13:301:13 | a | -17 | +| test.c:301:15:301:15 | b | -13 | +| test.c:302:5:302:9 | total | -2147483648 | +| test.c:302:14:302:14 | r | -2147483648 | +| test.c:305:10:305:14 | total | -2147483648 | +| test.c:312:14:312:14 | a | -2147483648 | +| test.c:312:19:312:19 | a | -17 | +| test.c:312:35:312:35 | b | -2147483648 | +| test.c:312:40:312:40 | b | 5 | +| test.c:313:13:313:13 | a | -17 | +| test.c:313:15:313:15 | b | 5 | +| test.c:314:5:314:9 | total | 0 | +| test.c:314:14:314:14 | r | -2147483648 | +| test.c:316:14:316:14 | a | -2147483648 | +| test.c:316:19:316:19 | a | -17 | +| test.c:316:35:316:35 | b | -2147483648 | +| test.c:316:40:316:40 | b | 0 | +| test.c:317:13:317:13 | a | -17 | +| test.c:317:15:317:15 | b | 0 | +| test.c:318:5:318:9 | total | -2147483648 | +| test.c:318:14:318:14 | r | -2147483648 | +| test.c:320:14:320:14 | a | -2147483648 | +| test.c:320:19:320:19 | a | -17 | +| test.c:320:37:320:37 | b | -2147483648 | +| test.c:320:42:320:42 | b | -13 | +| test.c:321:13:321:13 | a | -17 | +| test.c:321:15:321:15 | b | -13 | +| test.c:322:5:322:9 | total | -2147483648 | +| test.c:322:14:322:14 | r | -2147483648 | +| test.c:324:14:324:14 | a | -2147483648 | +| test.c:324:19:324:19 | a | -17 | +| test.c:324:37:324:37 | b | -2147483648 | +| test.c:324:42:324:42 | b | -13 | +| test.c:325:13:325:13 | a | -17 | +| test.c:325:15:325:15 | b | -13 | +| test.c:326:5:326:9 | total | -2147483648 | +| test.c:326:14:326:14 | r | -2147483648 | +| test.c:328:14:328:14 | a | -2147483648 | +| test.c:328:19:328:19 | a | -17 | +| test.c:328:37:328:37 | b | -2147483648 | +| test.c:328:42:328:42 | b | -13 | +| test.c:329:13:329:13 | a | -17 | +| test.c:329:15:329:15 | b | -13 | +| test.c:330:5:330:9 | total | -2147483648 | +| test.c:330:14:330:14 | r | -2147483648 | +| test.c:333:10:333:14 | total | -2147483648 | +| test.c:338:7:338:7 | x | -2147483648 | +| test.c:342:10:342:10 | i | 0 | +| test.c:343:5:343:5 | i | 0 | +| test.c:345:3:345:3 | d | -2147483648 | +| test.c:345:7:345:7 | i | 3 | +| test.c:346:7:346:7 | x | 0 | +| test.c:347:9:347:9 | d | 3 | +| test.c:347:14:347:14 | x | 0 | +| test.c:357:3:357:4 | y1 | 0 | +| test.c:357:8:357:8 | x | 0 | +| test.c:357:18:357:18 | x | 0 | +| test.c:358:3:358:4 | y2 | 0 | +| test.c:358:8:358:8 | x | 0 | +| test.c:358:24:358:24 | x | 0 | +| test.c:359:3:359:4 | y3 | 0 | +| test.c:360:3:360:4 | y4 | 0 | +| test.c:361:3:361:4 | y5 | 0 | +| test.c:362:3:362:4 | y6 | 0 | +| test.c:363:3:363:4 | y7 | 0 | +| test.c:364:3:364:4 | y8 | 0 | +| test.c:365:7:365:7 | x | 0 | +| test.c:366:5:366:6 | y3 | 0 | +| test.c:366:10:366:10 | x | 0 | +| test.c:367:5:367:6 | y4 | 0 | +| test.c:367:10:367:10 | x | 0 | +| test.c:368:5:368:6 | y5 | 0 | +| test.c:368:11:368:11 | x | 0 | +| test.c:369:5:369:6 | y6 | 0 | +| test.c:369:27:369:27 | x | 0 | +| test.c:370:5:370:6 | y7 | 0 | +| test.c:370:27:370:27 | x | 0 | +| test.c:371:5:371:6 | y8 | 0 | +| test.c:371:28:371:28 | x | 0 | +| test.c:373:10:373:11 | y1 | 0 | +| test.c:373:15:373:16 | y2 | 0 | +| test.c:373:20:373:21 | y3 | 0 | +| test.c:373:25:373:26 | y4 | 0 | +| test.c:373:30:373:31 | y5 | 0 | +| test.c:373:35:373:36 | y6 | 0 | +| test.c:373:40:373:41 | y7 | 0 | +| test.c:373:45:373:46 | y8 | 0 | +| test.c:379:3:379:4 | y1 | 0 | +| test.c:379:8:379:8 | x | 0 | +| test.c:379:18:379:18 | x | 101 | +| test.c:380:3:380:4 | y2 | 0 | +| test.c:380:8:380:8 | x | 0 | +| test.c:380:25:380:25 | x | 101 | +| test.c:381:3:381:4 | y3 | 0 | +| test.c:382:3:382:4 | y4 | 0 | +| test.c:383:3:383:4 | y5 | 0 | +| test.c:384:7:384:7 | x | 0 | +| test.c:385:5:385:6 | y3 | 0 | +| test.c:385:11:385:11 | x | 300 | +| test.c:386:5:386:6 | y4 | 0 | +| test.c:386:11:386:11 | x | 300 | +| test.c:387:5:387:6 | y5 | 0 | +| test.c:387:27:387:27 | x | 300 | +| test.c:389:10:389:11 | y1 | 101 | +| test.c:389:15:389:16 | y2 | 101 | +| test.c:389:20:389:21 | y3 | 0 | +| test.c:389:25:389:26 | y4 | 100 | +| test.c:389:30:389:31 | y5 | 0 | +| test.c:394:20:394:20 | x | 0 | +| test.c:394:30:394:30 | x | 0 | +| test.c:397:3:397:4 | y1 | 0 | +| test.c:397:11:397:11 | y | 0 | +| test.c:397:14:397:14 | y | 1 | +| test.c:398:3:398:4 | y2 | 0 | +| test.c:398:9:398:9 | y | 1 | +| test.c:398:14:398:14 | y | 2 | +| test.c:398:22:398:22 | y | 5 | +| test.c:399:10:399:11 | y1 | 1 | +| test.c:399:15:399:16 | y2 | 5 | +| test.c:407:3:407:3 | i | -2147483648 | +| test.c:408:7:408:7 | i | 10 | +| test.c:410:3:410:3 | i | -2147483648 | +| test.c:411:3:411:3 | i | 10 | +| test.c:412:7:412:7 | i | 20 | +| test.c:414:3:414:3 | i | -2147483648 | +| test.c:415:3:415:3 | i | 40 | +| test.c:416:7:416:7 | i | 30 | +| test.c:418:3:418:3 | i | -2147483648 | +| test.c:418:7:418:7 | j | -2147483648 | +| test.c:419:7:419:7 | i | 40 | +| test.c:421:3:421:3 | i | -2147483648 | +| test.c:421:8:421:8 | j | 40 | +| test.c:422:7:422:7 | i | 50 | +| test.c:424:3:424:3 | i | -2147483648 | +| test.c:424:13:424:13 | j | 50 | +| test.c:425:7:425:7 | i | 60 | +| test.c:432:12:432:12 | a | 0 | +| test.c:432:17:432:17 | a | 3 | +| test.c:432:33:432:33 | b | 0 | +| test.c:432:38:432:38 | b | 5 | +| test.c:433:13:433:13 | a | 3 | +| test.c:433:15:433:15 | b | 5 | +| test.c:434:5:434:9 | total | 0 | +| test.c:434:14:434:14 | r | 15 | +| test.c:436:12:436:12 | a | 0 | +| test.c:436:17:436:17 | a | 3 | +| test.c:436:33:436:33 | b | 0 | +| test.c:436:38:436:38 | b | 0 | +| test.c:437:13:437:13 | a | 3 | +| test.c:437:15:437:15 | b | 0 | +| test.c:438:5:438:9 | total | 0 | +| test.c:438:14:438:14 | r | 0 | +| test.c:440:12:440:12 | a | 0 | +| test.c:440:17:440:17 | a | 3 | +| test.c:440:34:440:34 | b | 0 | +| test.c:440:39:440:39 | b | 13 | +| test.c:441:13:441:13 | a | 3 | +| test.c:441:15:441:15 | b | 13 | +| test.c:442:5:442:9 | total | 0 | +| test.c:442:14:442:14 | r | 39 | +| test.c:445:10:445:14 | total | 0 | +| test.c:451:12:451:12 | b | 0 | +| test.c:451:17:451:17 | b | 5 | +| test.c:452:16:452:16 | b | 5 | +| test.c:453:5:453:9 | total | 0 | +| test.c:453:14:453:14 | r | 55 | +| test.c:455:12:455:12 | b | 0 | +| test.c:455:17:455:17 | b | 0 | +| test.c:456:16:456:16 | b | 0 | +| test.c:457:5:457:9 | total | 0 | +| test.c:457:14:457:14 | r | 0 | +| test.c:459:13:459:13 | b | 0 | +| test.c:459:18:459:18 | b | 13 | +| test.c:460:16:460:16 | b | 13 | +| test.c:461:5:461:9 | total | 0 | +| test.c:461:14:461:14 | r | 143 | +| test.c:464:10:464:14 | total | 0 | +| test.c:469:3:469:3 | x | 0 | +| test.c:469:7:469:7 | y | 0 | +| test.c:470:3:470:4 | xy | 0 | +| test.c:470:8:470:8 | x | 1000000003 | +| test.c:470:12:470:12 | y | 1000000003 | +| test.c:471:10:471:11 | xy | 1000000006000000000 | +| test.c:476:3:476:3 | x | 0 | +| test.c:477:3:477:3 | y | 0 | +| test.c:478:3:478:4 | xy | 0 | +| test.c:478:8:478:8 | x | 274177 | +| test.c:478:12:478:12 | y | 67280421310721 | +| test.c:479:10:479:11 | xy | 18446744073709551616 | +| test.c:483:7:483:8 | ui | 0 | +| test.c:484:43:484:44 | ui | 10 | +| test.c:484:48:484:49 | ui | 10 | +| test.c:485:12:485:17 | result | 100 | +| test.c:487:7:487:8 | ul | 0 | +| test.c:488:28:488:29 | ul | 10 | +| test.c:488:33:488:34 | ul | 10 | +| test.c:489:12:489:17 | result | 0 | +| test.c:495:7:495:8 | ui | 0 | +| test.c:495:19:495:20 | ui | 0 | +| test.c:496:5:496:6 | ui | 2 | +| test.c:496:11:496:12 | ui | 2 | +| test.c:497:12:497:13 | ui | 4 | +| test.c:501:3:501:9 | uiconst | 10 | +| test.c:504:3:504:9 | ulconst | 10 | +| test.c:505:10:505:16 | uiconst | 40 | +| test.c:505:20:505:26 | ulconst | 40 | +| test.c:509:7:509:7 | i | -2147483648 | +| test.c:509:18:509:18 | i | -1 | +| test.c:510:5:510:5 | i | -2147483648 | +| test.c:510:13:510:13 | i | -1 | +| test.c:511:9:511:9 | i | -5 | +| test.c:513:5:513:5 | i | -2147483648 | +| test.c:513:9:513:9 | i | -5 | +| test.c:514:9:514:9 | i | -30 | +| test.c:516:5:516:5 | i | -30 | +| test.c:517:9:517:9 | i | -210 | +| test.c:519:5:519:5 | i | -210 | +| test.c:520:9:520:9 | i | -1155 | +| test.c:522:7:522:7 | i | -2147483648 | +| test.c:523:5:523:5 | i | -2147483648 | +| test.c:523:9:523:9 | i | -1 | +| test.c:524:9:524:9 | i | 1 | +| test.c:526:3:526:3 | i | -2147483648 | +| test.c:526:7:526:7 | i | -2147483648 | +| test.c:527:10:527:10 | i | -2147483648 | +| test.c:530:3:530:3 | i | -2147483648 | +| test.c:530:10:530:11 | sc | 1 | +| test.c:532:7:532:7 | i | -128 | +| test.c:539:7:539:7 | n | 0 | +| test.c:541:7:541:7 | n | 0 | +| test.c:542:9:542:9 | n | 1 | +| test.c:545:7:545:7 | n | 0 | +| test.c:546:9:546:9 | n | 1 | +| test.c:548:9:548:9 | n | 0 | +| test.c:551:8:551:8 | n | 0 | +| test.c:552:9:552:9 | n | 0 | +| test.c:554:9:554:9 | n | 1 | +| test.c:557:10:557:10 | n | 0 | +| test.c:558:5:558:5 | n | 1 | +| test.c:561:7:561:7 | n | 0 | +| test.c:565:7:565:7 | n | -32768 | +| test.c:568:7:568:7 | n | 0 | +| test.c:569:9:569:9 | n | 0 | +| test.c:571:9:571:9 | n | 1 | +| test.c:574:7:574:7 | n | 0 | +| test.c:575:9:575:9 | n | 1 | +| test.c:577:9:577:9 | n | 0 | +| test.c:580:10:580:10 | n | 0 | +| test.c:581:5:581:5 | n | 1 | +| test.c:584:7:584:7 | n | 0 | +| test.c:588:7:588:7 | n | -32768 | +| test.c:589:9:589:9 | n | -32768 | +| test.c:590:11:590:11 | n | 0 | +| test.c:594:7:594:7 | n | -32768 | +| test.c:595:13:595:13 | n | 5 | +| test.c:598:9:598:9 | n | 6 | +| test.c:601:7:601:7 | n | -32768 | +| test.c:601:22:601:22 | n | -32767 | +| test.c:602:9:602:9 | n | -32766 | +| test.c:605:7:605:7 | n | -32768 | +| test.c:606:5:606:5 | n | 0 | +| test.c:606:10:606:10 | n | 1 | +| test.c:606:14:606:14 | n | 0 | +| test.c:607:6:607:6 | n | 0 | +| test.c:607:10:607:10 | n | 0 | +| test.c:607:14:607:14 | n | 1 | +| test.c:618:7:618:8 | ss | -32768 | +| test.c:619:9:619:10 | ss | 0 | +| test.c:622:7:622:8 | ss | -32768 | +| test.c:623:9:623:10 | ss | -32768 | +| test.c:626:14:626:15 | us | 0 | +| test.c:627:9:627:10 | us | 0 | +| test.c:630:14:630:15 | us | 0 | +| test.c:631:9:631:10 | us | 0 | +| test.c:634:7:634:8 | ss | -32768 | +| test.c:635:9:635:10 | ss | -32768 | +| test.c:638:7:638:8 | ss | -32768 | +| test.c:639:9:639:10 | ss | -1 | +| test.c:645:8:645:8 | s | -2147483648 | +| test.c:645:15:645:15 | s | 0 | +| test.c:645:23:645:23 | s | 0 | +| test.c:646:18:646:18 | s | 0 | +| test.c:646:22:646:22 | s | 0 | +| test.c:647:9:647:14 | result | 0 | +| test.c:653:7:653:7 | i | 0 | +| test.c:654:9:654:9 | i | -2147483648 | +| test.c:658:7:658:7 | u | 0 | +| test.c:659:9:659:9 | u | 0 | +| test.cpp:10:7:10:7 | b | -2147483648 | +| test.cpp:11:5:11:5 | x | -2147483648 | +| test.cpp:13:10:13:10 | x | -2147483648 | +| test.cpp:18:30:18:30 | x | -2147483648 | +| test.cpp:19:10:19:11 | x0 | -128 | +| test.cpp:27:7:27:7 | y | -2147483648 | +| test.cpp:28:5:28:5 | x | -2147483648 | +| test.cpp:30:7:30:7 | y | -2147483648 | +| test.cpp:31:5:31:5 | x | -2147483648 | +| test.cpp:33:7:33:7 | y | -2147483648 | +| test.cpp:34:5:34:5 | x | -2147483648 | +| test.cpp:36:7:36:7 | y | -2147483648 | +| test.cpp:37:5:37:5 | x | -2147483648 | +| test.cpp:39:7:39:7 | y | -2147483648 | +| test.cpp:40:5:40:5 | x | -2147483648 | +| test.cpp:42:7:42:7 | y | -2147483648 | +| test.cpp:43:5:43:5 | x | -2147483648 | +| test.cpp:45:7:45:7 | y | -2147483648 | +| test.cpp:46:5:46:5 | x | -2147483648 | +| test.cpp:51:7:51:7 | x | -2147483648 | +| test.cpp:52:21:52:21 | x | 0 | +| test.cpp:53:5:53:5 | t | 0 | +| test.cpp:53:15:53:16 | xb | 0 | +| test.cpp:56:7:56:7 | x | -2147483648 | +| test.cpp:57:21:57:21 | x | 1 | +| test.cpp:58:5:58:5 | t | 0 | +| test.cpp:58:15:58:16 | xb | 1 | +| test.cpp:61:7:61:7 | x | -2147483648 | +| test.cpp:62:21:62:21 | x | -2147483648 | +| test.cpp:63:5:63:5 | t | 0 | +| test.cpp:63:15:63:16 | xb | 1 | +| test.cpp:66:19:66:19 | x | -2147483648 | +| test.cpp:67:3:67:3 | t | 0 | +| test.cpp:67:13:67:14 | xb | 0 | +| test.cpp:69:10:69:10 | b | 0 | +| test.cpp:69:21:69:21 | t | 0 | +| test.cpp:74:30:74:30 | c | 0 | +| test.cpp:74:34:74:34 | c | 0 | +| test.cpp:75:22:75:30 | c_times_2 | 0 | +| test.cpp:77:5:77:13 | c_times_2 | 0 | +| test.cpp:79:3:79:11 | c_times_2 | 0 | +| test.cpp:83:16:83:22 | aliased | -2147483648 | +| test.cpp:85:7:85:7 | i | -2147483648 | +| test.cpp:86:12:86:12 | i | 2 | +| test.cpp:88:7:88:8 | ci | -2147483648 | +| test.cpp:89:12:89:13 | ci | 2 | +| test.cpp:91:7:91:13 | aliased | -2147483648 | +| test.cpp:92:12:92:18 | aliased | -2147483648 | +| test.cpp:94:7:94:11 | alias | -2147483648 | +| test.cpp:95:12:95:16 | alias | -2147483648 | +| test.cpp:97:10:97:10 | i | -2147483648 | +| test.cpp:97:22:97:22 | i | -2147483648 | +| test.cpp:98:5:98:5 | i | -2147483648 | +| test.cpp:105:7:105:7 | n | -32768 | +| test.cpp:108:7:108:7 | n | 0 | +| test.cpp:109:5:109:5 | n | 1 | +| test.cpp:111:5:111:5 | n | 0 | +| test.cpp:114:8:114:8 | n | 0 | +| test.cpp:115:5:115:5 | n | 0 | +| test.cpp:117:5:117:5 | n | 1 | +| test.cpp:120:3:120:3 | n | 0 | +| test.cpp:120:8:120:8 | n | 1 | +| test.cpp:120:12:120:12 | n | 0 | +| test.cpp:121:4:121:4 | n | 0 | +| test.cpp:121:8:121:8 | n | 0 | +| test.cpp:121:12:121:12 | n | 1 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.ql b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.ql index 67da63679723..70c2d8590e9e 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.ql +++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.ql @@ -1,4 +1,4 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis from VariableAccess expr -select expr, lowerBound(expr) +select expr, lowerBound(expr).toString() diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryLower.expected b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryLower.expected index 7f0469925834..fedd3853ce2f 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryLower.expected +++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryLower.expected @@ -13,3 +13,7 @@ | test.c:386:10:386:21 | ... ? ... : ... | 100.0 | 100.0 | 5.0 | | test.c:387:10:387:38 | ... ? ... : ... | 0.0 | 100.0 | 5.0 | | test.c:394:20:394:36 | ... ? ... : ... | 0.0 | 0.0 | 100.0 | +| test.c:606:5:606:14 | ... ? ... : ... | 0.0 | 1.0 | 0.0 | +| test.c:607:5:607:14 | ... ? ... : ... | 0.0 | 0.0 | 1.0 | +| test.cpp:120:3:120:12 | ... ? ... : ... | 0.0 | 1.0 | 0.0 | +| test.cpp:121:3:121:12 | ... ? ... : ... | 0.0 | 0.0 | 1.0 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryUpper.expected b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryUpper.expected index e2c21e309ae5..0b8fe8c11648 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryUpper.expected +++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryUpper.expected @@ -13,3 +13,7 @@ | test.c:386:10:386:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 5.0 | | test.c:387:10:387:38 | ... ? ... : ... | 255.0 | 4.294967295E9 | 5.0 | | test.c:394:20:394:36 | ... ? ... : ... | 100.0 | 99.0 | 100.0 | +| test.c:606:5:606:14 | ... ? ... : ... | 32767.0 | 32767.0 | 0.0 | +| test.c:607:5:607:14 | ... ? ... : ... | 32767.0 | 0.0 | 32767.0 | +| test.cpp:120:3:120:12 | ... ? ... : ... | 32767.0 | 32767.0 | 0.0 | +| test.cpp:121:3:121:12 | ... ? ... : ... | 32767.0 | 0.0 | 32767.0 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.c b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.c index 692520051f89..28c9f94d959f 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.c +++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.c @@ -422,5 +422,240 @@ void test17() { out(i); // 50 i = 20 + (j -= 10); - out(i); // 60 [BUG: the analysis thinks it's 2^-31 .. 2^31-1] + out(i); // 60 +} + +// Tests for unsigned multiplication. +int test_unsigned_mult01(unsigned int a, unsigned b) { + int total = 0; + + if (3 <= a && a <= 11 && 5 <= b && b <= 23) { + int r = a*b; // 15 .. 253 + total += r; + } + if (3 <= a && a <= 11 && 0 <= b && b <= 23) { + int r = a*b; // 0 .. 253 + total += r; + } + if (3 <= a && a <= 11 && 13 <= b && b <= 23) { + int r = a*b; // 39 .. 253 + total += r; + } + + return total; +} + +int test_unsigned_mult02(unsigned b) { + int total = 0; + + if (5 <= b && b <= 23) { + int r = 11*b; // 55 .. 253 + total += r; + } + if (0 <= b && b <= 23) { + int r = 11*b; // 0 .. 253 + total += r; + } + if (13 <= b && b <= 23) { + int r = 11*b; // 143 .. 253 + total += r; + } + + return total; +} + +unsigned long mult_rounding() { + unsigned long x, y, xy; + x = y = 1000000003UL; // 1e9 + 3 + xy = x * y; + return xy; // BUG: upper bound should be >= 1000000006000000009UL +} + +unsigned long mult_overflow() { + unsigned long x, y, xy; + x = 274177UL; + y = 67280421310721UL; + xy = x * y; + return xy; // BUG: upper bound should be >= 18446744073709551617UL +} + +unsigned long mult_lower_bound(unsigned int ui, unsigned long ul) { + if (ui >= 10) { + unsigned long result = (unsigned long)ui * ui; + return result; // BUG: upper bound should be >= 18446744065119617025 + } + if (ul >= 10) { + unsigned long result = ul * ul; + return result; // lower bound is correctly 0 (overflow is possible) + } + return 0; +} + +unsigned long mul_assign(unsigned int ui) { + if (ui <= 10 && ui >= 2) { + ui *= ui + 0; + return ui; // 4 .. 100 + } + + unsigned int uiconst = 10; + uiconst *= 4; + + unsigned long ulconst = 10; + ulconst *= 4; + return uiconst + ulconst; // 40 .. 40 for both +} + +int mul_by_constant(int i, int j) { + if (i >= -1 && i <= 2) { + i = 5 * i; + out(i); // -5 .. 10 + + i = i * -3; + out(i); // -30 .. 15 + + i *= 7; + out(i); // -210 .. 105 + + i *= -11; + out(i); // -1155 .. 2310 + } + if (i == -1) { + i = i * (int)0xffFFffFF; // fully converted literal is -1 + out(i); // 1 .. 1 + } + i = i * -1; + out( i); // -2^31 .. 2^31-1 + + signed char sc = 1; + i = (*&sc *= 2); + out(sc); // demonstrate that we couldn't analyze the LHS of the `*=` above... + out(i); // -128 .. 127 // ... but we can still bound its result by its type. + + return 0; +} + + +int notequal_type_endpoint(unsigned n) { + out(n); // 0 .. + + if (n > 0) { + out(n); // 1 .. + } + + if (n != 0) { + out(n); // 1 .. + } else { + out(n); // 0 .. 0 + } + + if (!n) { + out(n); // 0 .. 0 + } else { + out(n); // 1 .. + } + + while (n != 0) { + n--; // 1 .. + } + + out(n); // 0 .. 0 +} + +void notequal_refinement(short n) { + if (n < 0) + return; + + if (n == 0) { + out(n); // 0 .. 0 + } else { + out(n); // 1 .. + } + + if (n) { + out(n); // 1 .. + } else { + out(n); // 0 .. 0 + } + + while (n != 0) { + n--; // 1 .. + } + + out(n); // 0 .. 0 +} + +void notequal_variations(short n, float f) { + if (n != 0) { + if (n >= 0) { + out(n); // 1 .. [BUG: we can't handle `!=` coming first] + } + } + + if (n >= 5) { + if (2 * n - 10 == 0) { // Same as `n == 10/2` (modulo overflow) + return; + } + out(n); // 6 .. + } + + if (n != -32768 && n != -32767) { + out(n); // -32766 .. + } + + if (n >= 0) { + n ? n : n; // ? 1.. : 0..0 + !n ? n : n; // ? 0..0 : 1.. + } +} + +void two_bounds_from_one_test(short ss, unsigned short us) { + // These tests demonstrate how the range analysis is often able to deduce + // both an upper bound and a lower bound even when there is only one + // inequality in the source. For example `signedInt < 4U` establishes that + // `signedInt >= 0` since if `signedInt` were negative then it would be + // greater than 4 in the unsigned comparison. + + if (ss < sizeof(int)) { // Lower bound added in `linearBoundFromGuard` + out(ss); // 0 .. 3 + } + + if (ss < 0x8001) { // Lower bound removed in `getDefLowerBounds` + out(ss); // -32768 .. 32767 + } + + if ((short)us >= 0) { + out(us); // 0 .. 32767 + } + + if ((short)us >= -1) { + out(us); // 0 .. 65535 + } + + if (ss >= sizeof(int)) { // test is true for negative numbers + out(ss); // -32768 .. 32767 + } + + if (ss + 1 < sizeof(int)) { + out(ss); // -1 .. 2 + } +} + +void widen_recursive_expr() { + int s; + for (s = 0; s < 10; s++) { + int result = s + s; // 0 .. 9 [BUG: upper bound is 15 due to widening] + out(result); // 0 .. 18 [BUG: upper bound is 127 due to double widening] + } +} + +void guard_bound_out_of_range(void) { + int i = 0; + if (i < 0) { + out(i); // unreachable [BUG: is -max .. +max] + } + + unsigned int u = 0; + if (u < 0) { + out(u); // unreachable [BUG: is 0 .. +max] + } } diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp index e37f12c61cbf..515633f4f737 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp +++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp @@ -78,3 +78,45 @@ void use_after_cast(unsigned char c) } c_times_2; } + +int ref_to_number(int &i, const int &ci, int &aliased) { + int &alias = aliased; // no range analysis for either of the two aliased variables + + if (i >= 2) + return i; + + if (ci >= 2) + return ci; + + if (aliased >= 2) + return aliased; + + if (alias >= 2) + return alias; + + for (; i <= 12345; i++) { // test that widening works for references + i; + } + + return 0; +} + +void notequal_refinement(short n) { + if (n < 0) + return; + + if (n) { + n; // 1 .. + } else { + n; // 0 .. 0 + } + + if (!n) { + n; // 0 .. 0 + } else { + n; // 1 .. + } + + n ? n : n; // ? 1.. : 0..0 + !n ? n : n; // ? 0..0 : 1.. +} diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected index d80c88e6a59a..0ea6a6ca311c 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected +++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected @@ -1,483 +1,660 @@ -| inline_assembly.c:10:3:10:3 | y | 4.294967295E9 | -| inline_assembly.c:12:29:12:29 | x | 0.0 | -| inline_assembly.c:12:32:12:32 | y | 1.0 | -| inline_assembly.c:16:25:16:25 | x | 0.0 | -| inline_assembly.c:16:35:16:35 | y | 1.0 | -| inline_assembly.c:21:29:21:29 | x | 4.294967295E9 | -| inline_assembly.c:21:32:21:32 | y | 4.294967295E9 | -| minmax.c:18:37:18:37 | x | 1.0 | -| minmax.c:18:40:18:40 | y | 2.0 | -| minmax.c:18:43:18:43 | z | 3.0 | -| minmax.c:20:2:20:2 | z | 2.147483647E9 | -| minmax.c:22:8:22:8 | x | 1.0 | -| minmax.c:22:14:22:14 | y | 2.0 | -| minmax.c:22:18:22:18 | t | 2.147483647E9 | -| minmax.c:22:22:22:22 | x | 1.0 | -| minmax.c:23:3:23:3 | t | 1.0 | -| minmax.c:26:37:26:37 | x | 1.0 | -| minmax.c:26:40:26:40 | y | 2.0 | -| minmax.c:26:43:26:43 | z | 1.0 | -| test.c:8:5:8:9 | count | 2.147483647E9 | -| test.c:8:13:8:17 | count | 2.147483647E9 | -| test.c:10:10:10:14 | count | 2.147483647E9 | -| test.c:16:5:16:9 | count | 2.147483647E9 | -| test.c:16:14:16:18 | count | 15.0 | -| test.c:18:10:18:14 | count | 15.0 | -| test.c:24:5:24:9 | count | 15.0 | -| test.c:25:5:25:9 | count | 2.147483647E9 | -| test.c:25:13:25:17 | count | 127.0 | -| test.c:27:10:27:14 | count | 15.0 | -| test.c:33:8:33:8 | i | 2.147483647E9 | -| test.c:33:15:33:15 | i | 2.0 | -| test.c:33:22:33:22 | i | 2.147483647E9 | -| test.c:33:26:33:26 | i | 1.0 | -| test.c:34:5:34:9 | total | 2.147483647E9 | -| test.c:34:14:34:14 | i | 1.0 | -| test.c:36:10:36:14 | total | 2.147483647E9 | -| test.c:36:18:36:18 | i | 2.0 | -| test.c:42:8:42:8 | i | 2.147483647E9 | -| test.c:42:15:42:15 | i | 2.0 | -| test.c:42:22:42:22 | i | 1.0 | -| test.c:43:5:43:9 | total | 2.147483647E9 | -| test.c:43:14:43:14 | i | 1.0 | -| test.c:45:10:45:14 | total | 2.147483647E9 | -| test.c:45:18:45:18 | i | 2.0 | -| test.c:51:8:51:8 | i | 2.147483647E9 | -| test.c:51:15:51:15 | i | 2.0 | -| test.c:51:24:51:24 | i | 2.147483647E9 | -| test.c:51:28:51:28 | i | 1.0 | -| test.c:52:5:52:9 | total | 2.147483647E9 | -| test.c:52:14:52:14 | i | 1.0 | -| test.c:54:10:54:14 | total | 2.147483647E9 | -| test.c:54:18:54:18 | i | 2.0 | -| test.c:58:7:58:7 | i | 2.147483647E9 | -| test.c:59:9:59:9 | i | 3.0 | -| test.c:60:14:60:14 | i | 3.0 | -| test.c:67:15:67:15 | y | 2.147483647E9 | -| test.c:67:20:67:20 | y | 2.147483647E9 | -| test.c:68:9:68:9 | x | 2.147483647E9 | -| test.c:68:13:68:13 | y | 9.0 | -| test.c:69:14:69:14 | x | 6.0 | -| test.c:72:10:72:10 | y | 2.147483647E9 | -| test.c:76:7:76:7 | y | 2.147483647E9 | -| test.c:77:9:77:9 | x | 2.147483647E9 | -| test.c:81:9:81:9 | x | 2.147483647E9 | -| test.c:85:10:85:10 | x | 2.147483647E9 | -| test.c:89:7:89:7 | y | 2.147483647E9 | -| test.c:90:9:90:9 | x | 2.147483647E9 | -| test.c:90:13:90:13 | y | 2.147483647E9 | -| test.c:93:12:93:12 | x | 2.147483647E9 | -| test.c:100:3:100:3 | c | 127.0 | -| test.c:101:7:101:7 | c | 127.0 | -| test.c:104:7:104:7 | c | 127.0 | -| test.c:105:5:105:5 | c | 127.0 | -| test.c:106:9:106:9 | c | 127.0 | -| test.c:109:9:109:9 | c | 127.0 | -| test.c:119:10:119:10 | n | 1.8446744073709552E19 | -| test.c:124:11:124:15 | Start | 1.8446744073709552E19 | -| test.c:127:6:127:10 | Start | 1.8446744073709552E19 | -| test.c:127:15:127:20 | Length | 1.8446744073709552E19 | -| test.c:135:22:135:22 | c | 127.0 | -| test.c:137:20:137:20 | x | 0.0 | -| test.c:138:11:138:11 | i | 2.147483647E9 | -| test.c:139:19:139:19 | c | 127.0 | -| test.c:139:23:139:23 | i | 2.147483647E9 | -| test.c:139:27:139:28 | uc | 255.0 | -| test.c:139:32:139:32 | x | 0.0 | -| test.c:139:36:139:36 | y | 4.294967295E9 | -| test.c:139:40:139:40 | z | 2.147483647E9 | -| test.c:144:23:144:23 | x | 2.147483647E9 | -| test.c:145:32:145:32 | x | 2.147483647E9 | -| test.c:146:33:146:33 | x | 2.147483647E9 | -| test.c:147:31:147:31 | x | 2.147483647E9 | -| test.c:148:13:148:13 | x | 2.147483647E9 | -| test.c:149:23:149:23 | x | 2.147483647E9 | -| test.c:150:10:150:11 | x0 | 127.0 | -| test.c:150:15:150:16 | x1 | 255.0 | -| test.c:150:20:150:21 | x2 | 65535.0 | -| test.c:150:25:150:26 | x3 | 2.147483647E9 | -| test.c:150:30:150:31 | c0 | 127.0 | -| test.c:150:35:150:36 | s0 | 65535.0 | -| test.c:154:11:154:11 | x | 9.223372036854776E18 | -| test.c:154:20:154:20 | x | 9.223372036854776E18 | -| test.c:154:30:154:30 | x | 9.223372036854776E18 | -| test.c:154:35:154:35 | x | 2.147483647E9 | -| test.c:161:12:161:12 | a | 2.147483647E9 | -| test.c:161:17:161:17 | a | 2.147483647E9 | -| test.c:162:14:162:14 | a | 11.0 | -| test.c:163:14:163:14 | a | 11.0 | -| test.c:164:5:164:9 | total | 0.0 | -| test.c:164:14:164:14 | b | 11.0 | -| test.c:164:16:164:16 | c | -3.0 | -| test.c:166:12:166:12 | a | 2.147483647E9 | -| test.c:166:17:166:17 | a | 2.147483647E9 | -| test.c:167:14:167:14 | a | 11.0 | -| test.c:168:14:168:14 | a | 11.0 | -| test.c:169:5:169:9 | total | 8.0 | -| test.c:169:14:169:14 | b | 11.0 | -| test.c:169:16:169:16 | c | -0.0 | -| test.c:171:13:171:13 | a | 2.147483647E9 | -| test.c:171:18:171:18 | a | 2.147483647E9 | -| test.c:172:14:172:14 | a | 11.0 | -| test.c:173:14:173:14 | a | 11.0 | -| test.c:174:5:174:9 | total | 19.0 | -| test.c:174:14:174:14 | b | 11.0 | -| test.c:174:16:174:16 | c | 7.0 | -| test.c:176:13:176:13 | a | 2.147483647E9 | -| test.c:176:18:176:18 | a | 2.147483647E9 | -| test.c:177:14:177:14 | a | 1.0 | -| test.c:178:14:178:14 | a | 1.0 | -| test.c:179:5:179:9 | total | 37.0 | -| test.c:179:14:179:14 | b | 1.0 | -| test.c:179:16:179:16 | c | 7.0 | -| test.c:181:13:181:13 | a | 2.147483647E9 | -| test.c:181:18:181:18 | a | 2.147483647E9 | -| test.c:182:14:182:14 | a | 0.0 | -| test.c:183:14:183:14 | a | 0.0 | -| test.c:184:5:184:9 | total | 45.0 | -| test.c:184:14:184:14 | b | 0.0 | -| test.c:184:16:184:16 | c | 7.0 | -| test.c:186:13:186:13 | a | 2.147483647E9 | -| test.c:186:18:186:18 | a | 2.147483647E9 | -| test.c:187:14:187:14 | a | -2.0 | -| test.c:188:14:188:14 | a | -2.0 | -| test.c:189:5:189:9 | total | 52.0 | -| test.c:189:14:189:14 | b | -2.0 | -| test.c:189:16:189:16 | c | 7.0 | -| test.c:192:10:192:14 | total | 57.0 | -| test.c:200:12:200:12 | a | 2.147483647E9 | -| test.c:200:17:200:17 | a | 2.147483647E9 | -| test.c:200:33:200:33 | b | 2.147483647E9 | -| test.c:200:38:200:38 | b | 2.147483647E9 | -| test.c:201:13:201:13 | a | 11.0 | -| test.c:201:15:201:15 | b | 23.0 | -| test.c:202:5:202:9 | total | 0.0 | -| test.c:202:14:202:14 | r | 2.147483647E9 | -| test.c:204:12:204:12 | a | 2.147483647E9 | -| test.c:204:17:204:17 | a | 2.147483647E9 | -| test.c:204:33:204:33 | b | 2.147483647E9 | -| test.c:204:38:204:38 | b | 2.147483647E9 | -| test.c:205:13:205:13 | a | 11.0 | -| test.c:205:15:205:15 | b | 23.0 | -| test.c:206:5:206:9 | total | 2.147483647E9 | -| test.c:206:14:206:14 | r | 2.147483647E9 | -| test.c:208:12:208:12 | a | 2.147483647E9 | -| test.c:208:17:208:17 | a | 2.147483647E9 | -| test.c:208:35:208:35 | b | 2.147483647E9 | -| test.c:208:40:208:40 | b | 2.147483647E9 | -| test.c:209:13:209:13 | a | 11.0 | -| test.c:209:15:209:15 | b | 23.0 | -| test.c:210:5:210:9 | total | 2.147483647E9 | -| test.c:210:14:210:14 | r | 2.147483647E9 | -| test.c:212:12:212:12 | a | 2.147483647E9 | -| test.c:212:17:212:17 | a | 2.147483647E9 | -| test.c:212:35:212:35 | b | 2.147483647E9 | -| test.c:212:40:212:40 | b | 2.147483647E9 | -| test.c:213:13:213:13 | a | 11.0 | -| test.c:213:15:213:15 | b | 0.0 | -| test.c:214:5:214:9 | total | 2.147483647E9 | -| test.c:214:14:214:14 | r | 2.147483647E9 | -| test.c:216:12:216:12 | a | 2.147483647E9 | -| test.c:216:17:216:17 | a | 2.147483647E9 | -| test.c:216:35:216:35 | b | 2.147483647E9 | -| test.c:216:40:216:40 | b | 2.147483647E9 | -| test.c:217:13:217:13 | a | 11.0 | -| test.c:217:15:217:15 | b | -7.0 | -| test.c:218:5:218:9 | total | 2.147483647E9 | -| test.c:218:14:218:14 | r | 2.147483647E9 | -| test.c:221:10:221:14 | total | 2.147483647E9 | -| test.c:228:12:228:12 | a | 2.147483647E9 | -| test.c:228:17:228:17 | a | 2.147483647E9 | -| test.c:228:33:228:33 | b | 2.147483647E9 | -| test.c:228:38:228:38 | b | 2.147483647E9 | -| test.c:229:13:229:13 | a | 11.0 | -| test.c:229:15:229:15 | b | 23.0 | -| test.c:230:5:230:9 | total | 0.0 | -| test.c:230:14:230:14 | r | 2.147483647E9 | -| test.c:232:12:232:12 | a | 2.147483647E9 | -| test.c:232:17:232:17 | a | 2.147483647E9 | -| test.c:232:33:232:33 | b | 2.147483647E9 | -| test.c:232:38:232:38 | b | 2.147483647E9 | -| test.c:233:13:233:13 | a | 11.0 | -| test.c:233:15:233:15 | b | 23.0 | -| test.c:234:5:234:9 | total | 2.147483647E9 | -| test.c:234:14:234:14 | r | 2.147483647E9 | -| test.c:236:12:236:12 | a | 2.147483647E9 | -| test.c:236:17:236:17 | a | 2.147483647E9 | -| test.c:236:35:236:35 | b | 2.147483647E9 | -| test.c:236:40:236:40 | b | 2.147483647E9 | -| test.c:237:13:237:13 | a | 11.0 | -| test.c:237:15:237:15 | b | 23.0 | -| test.c:238:5:238:9 | total | 2.147483647E9 | -| test.c:238:14:238:14 | r | 2.147483647E9 | -| test.c:240:12:240:12 | a | 2.147483647E9 | -| test.c:240:17:240:17 | a | 2.147483647E9 | -| test.c:240:35:240:35 | b | 2.147483647E9 | -| test.c:240:40:240:40 | b | 2.147483647E9 | -| test.c:241:13:241:13 | a | 11.0 | -| test.c:241:15:241:15 | b | 0.0 | -| test.c:242:5:242:9 | total | 2.147483647E9 | -| test.c:242:14:242:14 | r | 2.147483647E9 | -| test.c:244:12:244:12 | a | 2.147483647E9 | -| test.c:244:17:244:17 | a | 2.147483647E9 | -| test.c:244:35:244:35 | b | 2.147483647E9 | -| test.c:244:40:244:40 | b | 2.147483647E9 | -| test.c:245:13:245:13 | a | 11.0 | -| test.c:245:15:245:15 | b | -7.0 | -| test.c:246:5:246:9 | total | 2.147483647E9 | -| test.c:246:14:246:14 | r | 2.147483647E9 | -| test.c:249:10:249:14 | total | 2.147483647E9 | -| test.c:256:14:256:14 | a | 2.147483647E9 | -| test.c:256:19:256:19 | a | 2.147483647E9 | -| test.c:256:35:256:35 | b | 2.147483647E9 | -| test.c:256:40:256:40 | b | 2.147483647E9 | -| test.c:257:13:257:13 | a | 11.0 | -| test.c:257:15:257:15 | b | 23.0 | -| test.c:258:5:258:9 | total | 0.0 | -| test.c:258:14:258:14 | r | 2.147483647E9 | -| test.c:260:14:260:14 | a | 2.147483647E9 | -| test.c:260:19:260:19 | a | 2.147483647E9 | -| test.c:260:35:260:35 | b | 2.147483647E9 | -| test.c:260:40:260:40 | b | 2.147483647E9 | -| test.c:261:13:261:13 | a | 11.0 | -| test.c:261:15:261:15 | b | 23.0 | -| test.c:262:5:262:9 | total | 2.147483647E9 | -| test.c:262:14:262:14 | r | 2.147483647E9 | -| test.c:264:14:264:14 | a | 2.147483647E9 | -| test.c:264:19:264:19 | a | 2.147483647E9 | -| test.c:264:37:264:37 | b | 2.147483647E9 | -| test.c:264:42:264:42 | b | 2.147483647E9 | -| test.c:265:13:265:13 | a | 11.0 | -| test.c:265:15:265:15 | b | 23.0 | -| test.c:266:5:266:9 | total | 2.147483647E9 | -| test.c:266:14:266:14 | r | 2.147483647E9 | -| test.c:268:14:268:14 | a | 2.147483647E9 | -| test.c:268:19:268:19 | a | 2.147483647E9 | -| test.c:268:37:268:37 | b | 2.147483647E9 | -| test.c:268:42:268:42 | b | 2.147483647E9 | -| test.c:269:13:269:13 | a | 11.0 | -| test.c:269:15:269:15 | b | 0.0 | -| test.c:270:5:270:9 | total | 2.147483647E9 | -| test.c:270:14:270:14 | r | 2.147483647E9 | -| test.c:272:14:272:14 | a | 2.147483647E9 | -| test.c:272:19:272:19 | a | 2.147483647E9 | -| test.c:272:37:272:37 | b | 2.147483647E9 | -| test.c:272:42:272:42 | b | 2.147483647E9 | -| test.c:273:13:273:13 | a | 11.0 | -| test.c:273:15:273:15 | b | -7.0 | -| test.c:274:5:274:9 | total | 2.147483647E9 | -| test.c:274:14:274:14 | r | 2.147483647E9 | -| test.c:277:10:277:14 | total | 2.147483647E9 | -| test.c:284:14:284:14 | a | 2.147483647E9 | -| test.c:284:19:284:19 | a | 2.147483647E9 | -| test.c:284:34:284:34 | b | 2.147483647E9 | -| test.c:284:39:284:39 | b | 2.147483647E9 | -| test.c:285:13:285:13 | a | 0.0 | -| test.c:285:15:285:15 | b | 23.0 | -| test.c:286:5:286:9 | total | 0.0 | -| test.c:286:14:286:14 | r | 2.147483647E9 | -| test.c:288:14:288:14 | a | 2.147483647E9 | -| test.c:288:19:288:19 | a | 2.147483647E9 | -| test.c:288:34:288:34 | b | 2.147483647E9 | -| test.c:288:39:288:39 | b | 2.147483647E9 | -| test.c:289:13:289:13 | a | 0.0 | -| test.c:289:15:289:15 | b | 23.0 | -| test.c:290:5:290:9 | total | 2.147483647E9 | -| test.c:290:14:290:14 | r | 2.147483647E9 | -| test.c:292:14:292:14 | a | 2.147483647E9 | -| test.c:292:19:292:19 | a | 2.147483647E9 | -| test.c:292:36:292:36 | b | 2.147483647E9 | -| test.c:292:41:292:41 | b | 2.147483647E9 | -| test.c:293:13:293:13 | a | 0.0 | -| test.c:293:15:293:15 | b | 23.0 | -| test.c:294:5:294:9 | total | 2.147483647E9 | -| test.c:294:14:294:14 | r | 2.147483647E9 | -| test.c:296:14:296:14 | a | 2.147483647E9 | -| test.c:296:19:296:19 | a | 2.147483647E9 | -| test.c:296:36:296:36 | b | 2.147483647E9 | -| test.c:296:41:296:41 | b | 2.147483647E9 | -| test.c:297:13:297:13 | a | 0.0 | -| test.c:297:15:297:15 | b | 0.0 | -| test.c:298:5:298:9 | total | 2.147483647E9 | -| test.c:298:14:298:14 | r | 2.147483647E9 | -| test.c:300:14:300:14 | a | 2.147483647E9 | -| test.c:300:19:300:19 | a | 2.147483647E9 | -| test.c:300:36:300:36 | b | 2.147483647E9 | -| test.c:300:41:300:41 | b | 2.147483647E9 | -| test.c:301:13:301:13 | a | 0.0 | -| test.c:301:15:301:15 | b | -7.0 | -| test.c:302:5:302:9 | total | 2.147483647E9 | -| test.c:302:14:302:14 | r | 2.147483647E9 | -| test.c:305:10:305:14 | total | 2.147483647E9 | -| test.c:312:14:312:14 | a | 2.147483647E9 | -| test.c:312:19:312:19 | a | 2.147483647E9 | -| test.c:312:35:312:35 | b | 2.147483647E9 | -| test.c:312:40:312:40 | b | 2.147483647E9 | -| test.c:313:13:313:13 | a | -2.0 | -| test.c:313:15:313:15 | b | 23.0 | -| test.c:314:5:314:9 | total | 0.0 | -| test.c:314:14:314:14 | r | 2.147483647E9 | -| test.c:316:14:316:14 | a | 2.147483647E9 | -| test.c:316:19:316:19 | a | 2.147483647E9 | -| test.c:316:35:316:35 | b | 2.147483647E9 | -| test.c:316:40:316:40 | b | 2.147483647E9 | -| test.c:317:13:317:13 | a | -2.0 | -| test.c:317:15:317:15 | b | 23.0 | -| test.c:318:5:318:9 | total | 2.147483647E9 | -| test.c:318:14:318:14 | r | 2.147483647E9 | -| test.c:320:14:320:14 | a | 2.147483647E9 | -| test.c:320:19:320:19 | a | 2.147483647E9 | -| test.c:320:37:320:37 | b | 2.147483647E9 | -| test.c:320:42:320:42 | b | 2.147483647E9 | -| test.c:321:13:321:13 | a | -2.0 | -| test.c:321:15:321:15 | b | 23.0 | -| test.c:322:5:322:9 | total | 2.147483647E9 | -| test.c:322:14:322:14 | r | 2.147483647E9 | -| test.c:324:14:324:14 | a | 2.147483647E9 | -| test.c:324:19:324:19 | a | 2.147483647E9 | -| test.c:324:37:324:37 | b | 2.147483647E9 | -| test.c:324:42:324:42 | b | 2.147483647E9 | -| test.c:325:13:325:13 | a | -2.0 | -| test.c:325:15:325:15 | b | 0.0 | -| test.c:326:5:326:9 | total | 2.147483647E9 | -| test.c:326:14:326:14 | r | 2.147483647E9 | -| test.c:328:14:328:14 | a | 2.147483647E9 | -| test.c:328:19:328:19 | a | 2.147483647E9 | -| test.c:328:37:328:37 | b | 2.147483647E9 | -| test.c:328:42:328:42 | b | 2.147483647E9 | -| test.c:329:13:329:13 | a | -2.0 | -| test.c:329:15:329:15 | b | -7.0 | -| test.c:330:5:330:9 | total | 2.147483647E9 | -| test.c:330:14:330:14 | r | 2.147483647E9 | -| test.c:333:10:333:14 | total | 2.147483647E9 | -| test.c:338:7:338:7 | x | 2.147483647E9 | -| test.c:342:10:342:10 | i | 7.0 | -| test.c:343:5:343:5 | i | 2.0 | -| test.c:345:3:345:3 | d | 2.147483647E9 | -| test.c:345:7:345:7 | i | 7.0 | -| test.c:346:7:346:7 | x | 2.147483647E9 | -| test.c:347:9:347:9 | d | 7.0 | -| test.c:347:14:347:14 | x | -1.0 | -| test.c:357:3:357:4 | y1 | 4.294967295E9 | -| test.c:357:8:357:8 | x | 4.294967295E9 | -| test.c:357:18:357:18 | x | 99.0 | -| test.c:358:3:358:4 | y2 | 4.294967295E9 | -| test.c:358:8:358:8 | x | 4.294967295E9 | -| test.c:358:24:358:24 | x | 99.0 | -| test.c:359:3:359:4 | y3 | 4.294967295E9 | -| test.c:360:3:360:4 | y4 | 4.294967295E9 | -| test.c:361:3:361:4 | y5 | 4.294967295E9 | -| test.c:362:3:362:4 | y6 | 4.294967295E9 | -| test.c:363:3:363:4 | y7 | 4.294967295E9 | -| test.c:364:3:364:4 | y8 | 4.294967295E9 | -| test.c:365:7:365:7 | x | 4.294967295E9 | -| test.c:366:5:366:6 | y3 | 4.294967295E9 | -| test.c:366:10:366:10 | x | 299.0 | -| test.c:367:5:367:6 | y4 | 4.294967295E9 | -| test.c:367:10:367:10 | x | 299.0 | -| test.c:368:5:368:6 | y5 | 4.294967295E9 | -| test.c:368:11:368:11 | x | 299.0 | -| test.c:369:5:369:6 | y6 | 4.294967295E9 | -| test.c:369:27:369:27 | x | 299.0 | -| test.c:370:5:370:6 | y7 | 4.294967295E9 | -| test.c:370:27:370:27 | x | 299.0 | -| test.c:371:5:371:6 | y8 | 4.294967295E9 | -| test.c:371:28:371:28 | x | 299.0 | -| test.c:373:10:373:11 | y1 | 99.0 | -| test.c:373:15:373:16 | y2 | 99.0 | -| test.c:373:20:373:21 | y3 | 299.0 | -| test.c:373:25:373:26 | y4 | 500.0 | -| test.c:373:30:373:31 | y5 | 300.0 | -| test.c:373:35:373:36 | y6 | 255.0 | -| test.c:373:40:373:41 | y7 | 500.0 | -| test.c:373:45:373:46 | y8 | 300.0 | -| test.c:379:3:379:4 | y1 | 4.294967295E9 | -| test.c:379:8:379:8 | x | 4.294967295E9 | -| test.c:379:18:379:18 | x | 4.294967295E9 | -| test.c:380:3:380:4 | y2 | 4.294967295E9 | -| test.c:380:8:380:8 | x | 4.294967295E9 | -| test.c:380:25:380:25 | x | 4.294967295E9 | -| test.c:381:3:381:4 | y3 | 4.294967295E9 | -| test.c:382:3:382:4 | y4 | 4.294967295E9 | -| test.c:383:3:383:4 | y5 | 4.294967295E9 | -| test.c:384:7:384:7 | x | 4.294967295E9 | -| test.c:385:5:385:6 | y3 | 4.294967295E9 | -| test.c:385:11:385:11 | x | 4.294967295E9 | -| test.c:386:5:386:6 | y4 | 4.294967295E9 | -| test.c:386:11:386:11 | x | 4.294967295E9 | -| test.c:387:5:387:6 | y5 | 4.294967295E9 | -| test.c:387:27:387:27 | x | 4.294967295E9 | -| test.c:389:10:389:11 | y1 | 4.294967295E9 | -| test.c:389:15:389:16 | y2 | 4.294967295E9 | -| test.c:389:20:389:21 | y3 | 4.294967295E9 | -| test.c:389:25:389:26 | y4 | 4.294967295E9 | -| test.c:389:30:389:31 | y5 | 1000.0 | -| test.c:394:20:394:20 | x | 4.294967295E9 | -| test.c:394:30:394:30 | x | 99.0 | -| test.c:397:3:397:4 | y1 | 4.294967295E9 | -| test.c:397:11:397:11 | y | 100.0 | -| test.c:397:14:397:14 | y | 101.0 | -| test.c:398:3:398:4 | y2 | 4.294967295E9 | -| test.c:398:9:398:9 | y | 101.0 | -| test.c:398:14:398:14 | y | 102.0 | -| test.c:398:22:398:22 | y | 105.0 | -| test.c:399:10:399:11 | y1 | 101.0 | -| test.c:399:15:399:16 | y2 | 105.0 | -| test.c:407:3:407:3 | i | 2.147483647E9 | -| test.c:408:7:408:7 | i | 10.0 | -| test.c:410:3:410:3 | i | 2.147483647E9 | -| test.c:411:3:411:3 | i | 10.0 | -| test.c:412:7:412:7 | i | 2.147483647E9 | -| test.c:414:3:414:3 | i | 2.147483647E9 | -| test.c:415:3:415:3 | i | 40.0 | -| test.c:416:7:416:7 | i | 2.147483647E9 | -| test.c:418:3:418:3 | i | 2.147483647E9 | -| test.c:418:7:418:7 | j | 2.147483647E9 | -| test.c:419:7:419:7 | i | 40.0 | -| test.c:421:3:421:3 | i | 2.147483647E9 | -| test.c:421:8:421:8 | j | 40.0 | -| test.c:422:7:422:7 | i | 50.0 | -| test.c:424:3:424:3 | i | 2.147483647E9 | -| test.c:424:13:424:13 | j | 2.147483647E9 | -| test.c:425:7:425:7 | i | 2.147483647E9 | -| test.cpp:10:7:10:7 | b | 2.147483647E9 | -| test.cpp:11:5:11:5 | x | 2.147483647E9 | -| test.cpp:13:10:13:10 | x | 2.147483647E9 | -| test.cpp:18:30:18:30 | x | 2.147483647E9 | -| test.cpp:19:10:19:11 | x0 | 127.0 | -| test.cpp:27:7:27:7 | y | 2.147483647E9 | -| test.cpp:28:5:28:5 | x | 2.147483647E9 | -| test.cpp:30:7:30:7 | y | 2.147483647E9 | -| test.cpp:31:5:31:5 | x | 2.147483647E9 | -| test.cpp:33:7:33:7 | y | 2.147483647E9 | -| test.cpp:34:5:34:5 | x | 2.147483647E9 | -| test.cpp:36:7:36:7 | y | 2.147483647E9 | -| test.cpp:37:5:37:5 | x | 2.147483647E9 | -| test.cpp:39:7:39:7 | y | 2.147483647E9 | -| test.cpp:40:5:40:5 | x | 2.147483647E9 | -| test.cpp:42:7:42:7 | y | 2.147483647E9 | -| test.cpp:43:5:43:5 | x | 2.147483647E9 | -| test.cpp:45:7:45:7 | y | 2.147483647E9 | -| test.cpp:46:5:46:5 | x | 2.147483647E9 | -| test.cpp:51:7:51:7 | x | 2.147483647E9 | -| test.cpp:52:21:52:21 | x | 0.0 | -| test.cpp:53:5:53:5 | t | 0.0 | -| test.cpp:53:15:53:16 | xb | 0.0 | -| test.cpp:56:7:56:7 | x | 2.147483647E9 | -| test.cpp:57:21:57:21 | x | 2.147483647E9 | -| test.cpp:58:5:58:5 | t | 0.0 | -| test.cpp:58:15:58:16 | xb | 1.0 | -| test.cpp:61:7:61:7 | x | 2.147483647E9 | -| test.cpp:62:21:62:21 | x | -1.0 | -| test.cpp:63:5:63:5 | t | 1.0 | -| test.cpp:63:15:63:16 | xb | 1.0 | -| test.cpp:66:19:66:19 | x | 2.147483647E9 | -| test.cpp:67:3:67:3 | t | 2.0 | -| test.cpp:67:13:67:14 | xb | 1.0 | -| test.cpp:69:10:69:10 | b | 1.0 | -| test.cpp:69:21:69:21 | t | 3.0 | -| test.cpp:74:30:74:30 | c | 255.0 | -| test.cpp:74:34:74:34 | c | 255.0 | -| test.cpp:75:22:75:30 | c_times_2 | 510.0 | -| test.cpp:77:5:77:13 | c_times_2 | 510.0 | -| test.cpp:79:3:79:11 | c_times_2 | 510.0 | +| inline_assembly.c:10:3:10:3 | y | 4294967295 | +| inline_assembly.c:12:29:12:29 | x | 0 | +| inline_assembly.c:12:32:12:32 | y | 1 | +| inline_assembly.c:16:25:16:25 | x | 0 | +| inline_assembly.c:16:35:16:35 | y | 1 | +| inline_assembly.c:21:29:21:29 | x | 4294967295 | +| inline_assembly.c:21:32:21:32 | y | 4294967295 | +| minmax.c:18:37:18:37 | x | 1 | +| minmax.c:18:40:18:40 | y | 2 | +| minmax.c:18:43:18:43 | z | 3 | +| minmax.c:20:2:20:2 | z | 2147483647 | +| minmax.c:22:8:22:8 | x | 1 | +| minmax.c:22:14:22:14 | y | 2 | +| minmax.c:22:18:22:18 | t | 2147483647 | +| minmax.c:22:22:22:22 | x | 1 | +| minmax.c:23:3:23:3 | t | 1 | +| minmax.c:26:37:26:37 | x | 1 | +| minmax.c:26:40:26:40 | y | 2 | +| minmax.c:26:43:26:43 | z | 1 | +| test.c:8:5:8:9 | count | 2147483647 | +| test.c:8:13:8:17 | count | 2147483647 | +| test.c:10:10:10:14 | count | 2147483647 | +| test.c:16:5:16:9 | count | 2147483647 | +| test.c:16:14:16:18 | count | 15 | +| test.c:18:10:18:14 | count | 15 | +| test.c:24:5:24:9 | count | 15 | +| test.c:25:5:25:9 | count | 2147483647 | +| test.c:25:13:25:17 | count | 127 | +| test.c:27:10:27:14 | count | 15 | +| test.c:33:8:33:8 | i | 2147483647 | +| test.c:33:15:33:15 | i | 2 | +| test.c:33:22:33:22 | i | 2147483647 | +| test.c:33:26:33:26 | i | 1 | +| test.c:34:5:34:9 | total | 2147483647 | +| test.c:34:14:34:14 | i | 1 | +| test.c:36:10:36:14 | total | 2147483647 | +| test.c:36:18:36:18 | i | 2 | +| test.c:42:8:42:8 | i | 2147483647 | +| test.c:42:15:42:15 | i | 2 | +| test.c:42:22:42:22 | i | 1 | +| test.c:43:5:43:9 | total | 2147483647 | +| test.c:43:14:43:14 | i | 1 | +| test.c:45:10:45:14 | total | 2147483647 | +| test.c:45:18:45:18 | i | 2 | +| test.c:51:8:51:8 | i | 2147483647 | +| test.c:51:15:51:15 | i | 2 | +| test.c:51:24:51:24 | i | 2147483647 | +| test.c:51:28:51:28 | i | 1 | +| test.c:52:5:52:9 | total | 2147483647 | +| test.c:52:14:52:14 | i | 1 | +| test.c:54:10:54:14 | total | 2147483647 | +| test.c:54:18:54:18 | i | 2 | +| test.c:58:7:58:7 | i | 2147483647 | +| test.c:59:9:59:9 | i | 3 | +| test.c:60:14:60:14 | i | 3 | +| test.c:67:15:67:15 | y | 2147483647 | +| test.c:67:20:67:20 | y | 2147483647 | +| test.c:68:9:68:9 | x | 2147483647 | +| test.c:68:13:68:13 | y | 9 | +| test.c:69:14:69:14 | x | 6 | +| test.c:72:10:72:10 | y | 2147483647 | +| test.c:76:7:76:7 | y | 2147483647 | +| test.c:77:9:77:9 | x | 2147483647 | +| test.c:81:9:81:9 | x | 2147483647 | +| test.c:85:10:85:10 | x | 2147483647 | +| test.c:89:7:89:7 | y | 2147483647 | +| test.c:90:9:90:9 | x | 2147483647 | +| test.c:90:13:90:13 | y | 2147483647 | +| test.c:93:12:93:12 | x | 2147483647 | +| test.c:100:3:100:3 | c | 127 | +| test.c:101:7:101:7 | c | 127 | +| test.c:104:7:104:7 | c | 127 | +| test.c:105:5:105:5 | c | 127 | +| test.c:106:9:106:9 | c | 127 | +| test.c:109:9:109:9 | c | 127 | +| test.c:119:10:119:10 | n | 18446744073709551616 | +| test.c:124:11:124:15 | Start | 18446744073709551616 | +| test.c:127:6:127:10 | Start | 18446744073709551616 | +| test.c:127:15:127:20 | Length | 18446744073709551616 | +| test.c:135:22:135:22 | c | 127 | +| test.c:137:20:137:20 | x | 0 | +| test.c:138:11:138:11 | i | 2147483647 | +| test.c:139:19:139:19 | c | 127 | +| test.c:139:23:139:23 | i | 2147483647 | +| test.c:139:27:139:28 | uc | 255 | +| test.c:139:32:139:32 | x | 0 | +| test.c:139:36:139:36 | y | 4294967295 | +| test.c:139:40:139:40 | z | 2147483647 | +| test.c:144:23:144:23 | x | 2147483647 | +| test.c:145:32:145:32 | x | 2147483647 | +| test.c:146:33:146:33 | x | 2147483647 | +| test.c:147:31:147:31 | x | 2147483647 | +| test.c:148:13:148:13 | x | 2147483647 | +| test.c:149:23:149:23 | x | 2147483647 | +| test.c:150:10:150:11 | x0 | 127 | +| test.c:150:15:150:16 | x1 | 255 | +| test.c:150:20:150:21 | x2 | 65535 | +| test.c:150:25:150:26 | x3 | 2147483647 | +| test.c:150:30:150:31 | c0 | 127 | +| test.c:150:35:150:36 | s0 | 65535 | +| test.c:154:11:154:11 | x | 9223372036854775808 | +| test.c:154:20:154:20 | x | 9223372036854775808 | +| test.c:154:30:154:30 | x | 9223372036854775808 | +| test.c:154:35:154:35 | x | 2147483647 | +| test.c:161:12:161:12 | a | 2147483647 | +| test.c:161:17:161:17 | a | 2147483647 | +| test.c:162:14:162:14 | a | 11 | +| test.c:163:14:163:14 | a | 11 | +| test.c:164:5:164:9 | total | 0 | +| test.c:164:14:164:14 | b | 11 | +| test.c:164:16:164:16 | c | -3 | +| test.c:166:12:166:12 | a | 2147483647 | +| test.c:166:17:166:17 | a | 2147483647 | +| test.c:167:14:167:14 | a | 11 | +| test.c:168:14:168:14 | a | 11 | +| test.c:169:5:169:9 | total | 8 | +| test.c:169:14:169:14 | b | 11 | +| test.c:169:16:169:16 | c | 0 | +| test.c:171:13:171:13 | a | 2147483647 | +| test.c:171:18:171:18 | a | 2147483647 | +| test.c:172:14:172:14 | a | 11 | +| test.c:173:14:173:14 | a | 11 | +| test.c:174:5:174:9 | total | 19 | +| test.c:174:14:174:14 | b | 11 | +| test.c:174:16:174:16 | c | 7 | +| test.c:176:13:176:13 | a | 2147483647 | +| test.c:176:18:176:18 | a | 2147483647 | +| test.c:177:14:177:14 | a | 1 | +| test.c:178:14:178:14 | a | 1 | +| test.c:179:5:179:9 | total | 37 | +| test.c:179:14:179:14 | b | 1 | +| test.c:179:16:179:16 | c | 7 | +| test.c:181:13:181:13 | a | 2147483647 | +| test.c:181:18:181:18 | a | 2147483647 | +| test.c:182:14:182:14 | a | 0 | +| test.c:183:14:183:14 | a | 0 | +| test.c:184:5:184:9 | total | 45 | +| test.c:184:14:184:14 | b | 0 | +| test.c:184:16:184:16 | c | 7 | +| test.c:186:13:186:13 | a | 2147483647 | +| test.c:186:18:186:18 | a | 2147483647 | +| test.c:187:14:187:14 | a | -2 | +| test.c:188:14:188:14 | a | -2 | +| test.c:189:5:189:9 | total | 52 | +| test.c:189:14:189:14 | b | -2 | +| test.c:189:16:189:16 | c | 7 | +| test.c:192:10:192:14 | total | 57 | +| test.c:200:12:200:12 | a | 2147483647 | +| test.c:200:17:200:17 | a | 2147483647 | +| test.c:200:33:200:33 | b | 2147483647 | +| test.c:200:38:200:38 | b | 2147483647 | +| test.c:201:13:201:13 | a | 11 | +| test.c:201:15:201:15 | b | 23 | +| test.c:202:5:202:9 | total | 0 | +| test.c:202:14:202:14 | r | 2147483647 | +| test.c:204:12:204:12 | a | 2147483647 | +| test.c:204:17:204:17 | a | 2147483647 | +| test.c:204:33:204:33 | b | 2147483647 | +| test.c:204:38:204:38 | b | 2147483647 | +| test.c:205:13:205:13 | a | 11 | +| test.c:205:15:205:15 | b | 23 | +| test.c:206:5:206:9 | total | 2147483647 | +| test.c:206:14:206:14 | r | 2147483647 | +| test.c:208:12:208:12 | a | 2147483647 | +| test.c:208:17:208:17 | a | 2147483647 | +| test.c:208:35:208:35 | b | 2147483647 | +| test.c:208:40:208:40 | b | 2147483647 | +| test.c:209:13:209:13 | a | 11 | +| test.c:209:15:209:15 | b | 23 | +| test.c:210:5:210:9 | total | 2147483647 | +| test.c:210:14:210:14 | r | 2147483647 | +| test.c:212:12:212:12 | a | 2147483647 | +| test.c:212:17:212:17 | a | 2147483647 | +| test.c:212:35:212:35 | b | 2147483647 | +| test.c:212:40:212:40 | b | 2147483647 | +| test.c:213:13:213:13 | a | 11 | +| test.c:213:15:213:15 | b | 0 | +| test.c:214:5:214:9 | total | 2147483647 | +| test.c:214:14:214:14 | r | 2147483647 | +| test.c:216:12:216:12 | a | 2147483647 | +| test.c:216:17:216:17 | a | 2147483647 | +| test.c:216:35:216:35 | b | 2147483647 | +| test.c:216:40:216:40 | b | 2147483647 | +| test.c:217:13:217:13 | a | 11 | +| test.c:217:15:217:15 | b | -7 | +| test.c:218:5:218:9 | total | 2147483647 | +| test.c:218:14:218:14 | r | 2147483647 | +| test.c:221:10:221:14 | total | 2147483647 | +| test.c:228:12:228:12 | a | 2147483647 | +| test.c:228:17:228:17 | a | 2147483647 | +| test.c:228:33:228:33 | b | 2147483647 | +| test.c:228:38:228:38 | b | 2147483647 | +| test.c:229:13:229:13 | a | 11 | +| test.c:229:15:229:15 | b | 23 | +| test.c:230:5:230:9 | total | 0 | +| test.c:230:14:230:14 | r | 2147483647 | +| test.c:232:12:232:12 | a | 2147483647 | +| test.c:232:17:232:17 | a | 2147483647 | +| test.c:232:33:232:33 | b | 2147483647 | +| test.c:232:38:232:38 | b | 2147483647 | +| test.c:233:13:233:13 | a | 11 | +| test.c:233:15:233:15 | b | 23 | +| test.c:234:5:234:9 | total | 2147483647 | +| test.c:234:14:234:14 | r | 2147483647 | +| test.c:236:12:236:12 | a | 2147483647 | +| test.c:236:17:236:17 | a | 2147483647 | +| test.c:236:35:236:35 | b | 2147483647 | +| test.c:236:40:236:40 | b | 2147483647 | +| test.c:237:13:237:13 | a | 11 | +| test.c:237:15:237:15 | b | 23 | +| test.c:238:5:238:9 | total | 2147483647 | +| test.c:238:14:238:14 | r | 2147483647 | +| test.c:240:12:240:12 | a | 2147483647 | +| test.c:240:17:240:17 | a | 2147483647 | +| test.c:240:35:240:35 | b | 2147483647 | +| test.c:240:40:240:40 | b | 2147483647 | +| test.c:241:13:241:13 | a | 11 | +| test.c:241:15:241:15 | b | 0 | +| test.c:242:5:242:9 | total | 2147483647 | +| test.c:242:14:242:14 | r | 2147483647 | +| test.c:244:12:244:12 | a | 2147483647 | +| test.c:244:17:244:17 | a | 2147483647 | +| test.c:244:35:244:35 | b | 2147483647 | +| test.c:244:40:244:40 | b | 2147483647 | +| test.c:245:13:245:13 | a | 11 | +| test.c:245:15:245:15 | b | -7 | +| test.c:246:5:246:9 | total | 2147483647 | +| test.c:246:14:246:14 | r | 2147483647 | +| test.c:249:10:249:14 | total | 2147483647 | +| test.c:256:14:256:14 | a | 2147483647 | +| test.c:256:19:256:19 | a | 2147483647 | +| test.c:256:35:256:35 | b | 2147483647 | +| test.c:256:40:256:40 | b | 2147483647 | +| test.c:257:13:257:13 | a | 11 | +| test.c:257:15:257:15 | b | 23 | +| test.c:258:5:258:9 | total | 0 | +| test.c:258:14:258:14 | r | 2147483647 | +| test.c:260:14:260:14 | a | 2147483647 | +| test.c:260:19:260:19 | a | 2147483647 | +| test.c:260:35:260:35 | b | 2147483647 | +| test.c:260:40:260:40 | b | 2147483647 | +| test.c:261:13:261:13 | a | 11 | +| test.c:261:15:261:15 | b | 23 | +| test.c:262:5:262:9 | total | 2147483647 | +| test.c:262:14:262:14 | r | 2147483647 | +| test.c:264:14:264:14 | a | 2147483647 | +| test.c:264:19:264:19 | a | 2147483647 | +| test.c:264:37:264:37 | b | 2147483647 | +| test.c:264:42:264:42 | b | 2147483647 | +| test.c:265:13:265:13 | a | 11 | +| test.c:265:15:265:15 | b | 23 | +| test.c:266:5:266:9 | total | 2147483647 | +| test.c:266:14:266:14 | r | 2147483647 | +| test.c:268:14:268:14 | a | 2147483647 | +| test.c:268:19:268:19 | a | 2147483647 | +| test.c:268:37:268:37 | b | 2147483647 | +| test.c:268:42:268:42 | b | 2147483647 | +| test.c:269:13:269:13 | a | 11 | +| test.c:269:15:269:15 | b | 0 | +| test.c:270:5:270:9 | total | 2147483647 | +| test.c:270:14:270:14 | r | 2147483647 | +| test.c:272:14:272:14 | a | 2147483647 | +| test.c:272:19:272:19 | a | 2147483647 | +| test.c:272:37:272:37 | b | 2147483647 | +| test.c:272:42:272:42 | b | 2147483647 | +| test.c:273:13:273:13 | a | 11 | +| test.c:273:15:273:15 | b | -7 | +| test.c:274:5:274:9 | total | 2147483647 | +| test.c:274:14:274:14 | r | 2147483647 | +| test.c:277:10:277:14 | total | 2147483647 | +| test.c:284:14:284:14 | a | 2147483647 | +| test.c:284:19:284:19 | a | 2147483647 | +| test.c:284:34:284:34 | b | 2147483647 | +| test.c:284:39:284:39 | b | 2147483647 | +| test.c:285:13:285:13 | a | 0 | +| test.c:285:15:285:15 | b | 23 | +| test.c:286:5:286:9 | total | 0 | +| test.c:286:14:286:14 | r | 2147483647 | +| test.c:288:14:288:14 | a | 2147483647 | +| test.c:288:19:288:19 | a | 2147483647 | +| test.c:288:34:288:34 | b | 2147483647 | +| test.c:288:39:288:39 | b | 2147483647 | +| test.c:289:13:289:13 | a | 0 | +| test.c:289:15:289:15 | b | 23 | +| test.c:290:5:290:9 | total | 2147483647 | +| test.c:290:14:290:14 | r | 2147483647 | +| test.c:292:14:292:14 | a | 2147483647 | +| test.c:292:19:292:19 | a | 2147483647 | +| test.c:292:36:292:36 | b | 2147483647 | +| test.c:292:41:292:41 | b | 2147483647 | +| test.c:293:13:293:13 | a | 0 | +| test.c:293:15:293:15 | b | 23 | +| test.c:294:5:294:9 | total | 2147483647 | +| test.c:294:14:294:14 | r | 2147483647 | +| test.c:296:14:296:14 | a | 2147483647 | +| test.c:296:19:296:19 | a | 2147483647 | +| test.c:296:36:296:36 | b | 2147483647 | +| test.c:296:41:296:41 | b | 2147483647 | +| test.c:297:13:297:13 | a | 0 | +| test.c:297:15:297:15 | b | 0 | +| test.c:298:5:298:9 | total | 2147483647 | +| test.c:298:14:298:14 | r | 2147483647 | +| test.c:300:14:300:14 | a | 2147483647 | +| test.c:300:19:300:19 | a | 2147483647 | +| test.c:300:36:300:36 | b | 2147483647 | +| test.c:300:41:300:41 | b | 2147483647 | +| test.c:301:13:301:13 | a | 0 | +| test.c:301:15:301:15 | b | -7 | +| test.c:302:5:302:9 | total | 2147483647 | +| test.c:302:14:302:14 | r | 2147483647 | +| test.c:305:10:305:14 | total | 2147483647 | +| test.c:312:14:312:14 | a | 2147483647 | +| test.c:312:19:312:19 | a | 2147483647 | +| test.c:312:35:312:35 | b | 2147483647 | +| test.c:312:40:312:40 | b | 2147483647 | +| test.c:313:13:313:13 | a | -2 | +| test.c:313:15:313:15 | b | 23 | +| test.c:314:5:314:9 | total | 0 | +| test.c:314:14:314:14 | r | 2147483647 | +| test.c:316:14:316:14 | a | 2147483647 | +| test.c:316:19:316:19 | a | 2147483647 | +| test.c:316:35:316:35 | b | 2147483647 | +| test.c:316:40:316:40 | b | 2147483647 | +| test.c:317:13:317:13 | a | -2 | +| test.c:317:15:317:15 | b | 23 | +| test.c:318:5:318:9 | total | 2147483647 | +| test.c:318:14:318:14 | r | 2147483647 | +| test.c:320:14:320:14 | a | 2147483647 | +| test.c:320:19:320:19 | a | 2147483647 | +| test.c:320:37:320:37 | b | 2147483647 | +| test.c:320:42:320:42 | b | 2147483647 | +| test.c:321:13:321:13 | a | -2 | +| test.c:321:15:321:15 | b | 23 | +| test.c:322:5:322:9 | total | 2147483647 | +| test.c:322:14:322:14 | r | 2147483647 | +| test.c:324:14:324:14 | a | 2147483647 | +| test.c:324:19:324:19 | a | 2147483647 | +| test.c:324:37:324:37 | b | 2147483647 | +| test.c:324:42:324:42 | b | 2147483647 | +| test.c:325:13:325:13 | a | -2 | +| test.c:325:15:325:15 | b | 0 | +| test.c:326:5:326:9 | total | 2147483647 | +| test.c:326:14:326:14 | r | 2147483647 | +| test.c:328:14:328:14 | a | 2147483647 | +| test.c:328:19:328:19 | a | 2147483647 | +| test.c:328:37:328:37 | b | 2147483647 | +| test.c:328:42:328:42 | b | 2147483647 | +| test.c:329:13:329:13 | a | -2 | +| test.c:329:15:329:15 | b | -7 | +| test.c:330:5:330:9 | total | 2147483647 | +| test.c:330:14:330:14 | r | 2147483647 | +| test.c:333:10:333:14 | total | 2147483647 | +| test.c:338:7:338:7 | x | 2147483647 | +| test.c:342:10:342:10 | i | 7 | +| test.c:343:5:343:5 | i | 2 | +| test.c:345:3:345:3 | d | 2147483647 | +| test.c:345:7:345:7 | i | 7 | +| test.c:346:7:346:7 | x | 2147483647 | +| test.c:347:9:347:9 | d | 7 | +| test.c:347:14:347:14 | x | -1 | +| test.c:357:3:357:4 | y1 | 4294967295 | +| test.c:357:8:357:8 | x | 4294967295 | +| test.c:357:18:357:18 | x | 99 | +| test.c:358:3:358:4 | y2 | 4294967295 | +| test.c:358:8:358:8 | x | 4294967295 | +| test.c:358:24:358:24 | x | 99 | +| test.c:359:3:359:4 | y3 | 4294967295 | +| test.c:360:3:360:4 | y4 | 4294967295 | +| test.c:361:3:361:4 | y5 | 4294967295 | +| test.c:362:3:362:4 | y6 | 4294967295 | +| test.c:363:3:363:4 | y7 | 4294967295 | +| test.c:364:3:364:4 | y8 | 4294967295 | +| test.c:365:7:365:7 | x | 4294967295 | +| test.c:366:5:366:6 | y3 | 4294967295 | +| test.c:366:10:366:10 | x | 299 | +| test.c:367:5:367:6 | y4 | 4294967295 | +| test.c:367:10:367:10 | x | 299 | +| test.c:368:5:368:6 | y5 | 4294967295 | +| test.c:368:11:368:11 | x | 299 | +| test.c:369:5:369:6 | y6 | 4294967295 | +| test.c:369:27:369:27 | x | 299 | +| test.c:370:5:370:6 | y7 | 4294967295 | +| test.c:370:27:370:27 | x | 299 | +| test.c:371:5:371:6 | y8 | 4294967295 | +| test.c:371:28:371:28 | x | 299 | +| test.c:373:10:373:11 | y1 | 99 | +| test.c:373:15:373:16 | y2 | 99 | +| test.c:373:20:373:21 | y3 | 299 | +| test.c:373:25:373:26 | y4 | 500 | +| test.c:373:30:373:31 | y5 | 300 | +| test.c:373:35:373:36 | y6 | 255 | +| test.c:373:40:373:41 | y7 | 500 | +| test.c:373:45:373:46 | y8 | 300 | +| test.c:379:3:379:4 | y1 | 4294967295 | +| test.c:379:8:379:8 | x | 4294967295 | +| test.c:379:18:379:18 | x | 4294967295 | +| test.c:380:3:380:4 | y2 | 4294967295 | +| test.c:380:8:380:8 | x | 4294967295 | +| test.c:380:25:380:25 | x | 4294967295 | +| test.c:381:3:381:4 | y3 | 4294967295 | +| test.c:382:3:382:4 | y4 | 4294967295 | +| test.c:383:3:383:4 | y5 | 4294967295 | +| test.c:384:7:384:7 | x | 4294967295 | +| test.c:385:5:385:6 | y3 | 4294967295 | +| test.c:385:11:385:11 | x | 4294967295 | +| test.c:386:5:386:6 | y4 | 4294967295 | +| test.c:386:11:386:11 | x | 4294967295 | +| test.c:387:5:387:6 | y5 | 4294967295 | +| test.c:387:27:387:27 | x | 4294967295 | +| test.c:389:10:389:11 | y1 | 4294967295 | +| test.c:389:15:389:16 | y2 | 4294967295 | +| test.c:389:20:389:21 | y3 | 4294967295 | +| test.c:389:25:389:26 | y4 | 4294967295 | +| test.c:389:30:389:31 | y5 | 1000 | +| test.c:394:20:394:20 | x | 4294967295 | +| test.c:394:30:394:30 | x | 99 | +| test.c:397:3:397:4 | y1 | 4294967295 | +| test.c:397:11:397:11 | y | 100 | +| test.c:397:14:397:14 | y | 101 | +| test.c:398:3:398:4 | y2 | 4294967295 | +| test.c:398:9:398:9 | y | 101 | +| test.c:398:14:398:14 | y | 102 | +| test.c:398:22:398:22 | y | 105 | +| test.c:399:10:399:11 | y1 | 101 | +| test.c:399:15:399:16 | y2 | 105 | +| test.c:407:3:407:3 | i | 2147483647 | +| test.c:408:7:408:7 | i | 10 | +| test.c:410:3:410:3 | i | 2147483647 | +| test.c:411:3:411:3 | i | 10 | +| test.c:412:7:412:7 | i | 20 | +| test.c:414:3:414:3 | i | 2147483647 | +| test.c:415:3:415:3 | i | 40 | +| test.c:416:7:416:7 | i | 30 | +| test.c:418:3:418:3 | i | 2147483647 | +| test.c:418:7:418:7 | j | 2147483647 | +| test.c:419:7:419:7 | i | 40 | +| test.c:421:3:421:3 | i | 2147483647 | +| test.c:421:8:421:8 | j | 40 | +| test.c:422:7:422:7 | i | 50 | +| test.c:424:3:424:3 | i | 2147483647 | +| test.c:424:13:424:13 | j | 50 | +| test.c:425:7:425:7 | i | 60 | +| test.c:432:12:432:12 | a | 4294967295 | +| test.c:432:17:432:17 | a | 4294967295 | +| test.c:432:33:432:33 | b | 4294967295 | +| test.c:432:38:432:38 | b | 4294967295 | +| test.c:433:13:433:13 | a | 11 | +| test.c:433:15:433:15 | b | 23 | +| test.c:434:5:434:9 | total | 0 | +| test.c:434:14:434:14 | r | 253 | +| test.c:436:12:436:12 | a | 4294967295 | +| test.c:436:17:436:17 | a | 4294967295 | +| test.c:436:33:436:33 | b | 4294967295 | +| test.c:436:38:436:38 | b | 4294967295 | +| test.c:437:13:437:13 | a | 11 | +| test.c:437:15:437:15 | b | 23 | +| test.c:438:5:438:9 | total | 253 | +| test.c:438:14:438:14 | r | 253 | +| test.c:440:12:440:12 | a | 4294967295 | +| test.c:440:17:440:17 | a | 4294967295 | +| test.c:440:34:440:34 | b | 4294967295 | +| test.c:440:39:440:39 | b | 4294967295 | +| test.c:441:13:441:13 | a | 11 | +| test.c:441:15:441:15 | b | 23 | +| test.c:442:5:442:9 | total | 506 | +| test.c:442:14:442:14 | r | 253 | +| test.c:445:10:445:14 | total | 759 | +| test.c:451:12:451:12 | b | 4294967295 | +| test.c:451:17:451:17 | b | 4294967295 | +| test.c:452:16:452:16 | b | 23 | +| test.c:453:5:453:9 | total | 0 | +| test.c:453:14:453:14 | r | 253 | +| test.c:455:12:455:12 | b | 4294967295 | +| test.c:455:17:455:17 | b | 4294967295 | +| test.c:456:16:456:16 | b | 23 | +| test.c:457:5:457:9 | total | 253 | +| test.c:457:14:457:14 | r | 253 | +| test.c:459:13:459:13 | b | 4294967295 | +| test.c:459:18:459:18 | b | 4294967295 | +| test.c:460:16:460:16 | b | 23 | +| test.c:461:5:461:9 | total | 506 | +| test.c:461:14:461:14 | r | 253 | +| test.c:464:10:464:14 | total | 759 | +| test.c:469:3:469:3 | x | 18446744073709551616 | +| test.c:469:7:469:7 | y | 18446744073709551616 | +| test.c:470:3:470:4 | xy | 18446744073709551616 | +| test.c:470:8:470:8 | x | 1000000003 | +| test.c:470:12:470:12 | y | 1000000003 | +| test.c:471:10:471:11 | xy | 1000000006000000000 | +| test.c:476:3:476:3 | x | 18446744073709551616 | +| test.c:477:3:477:3 | y | 18446744073709551616 | +| test.c:478:3:478:4 | xy | 18446744073709551616 | +| test.c:478:8:478:8 | x | 274177 | +| test.c:478:12:478:12 | y | 67280421310721 | +| test.c:479:10:479:11 | xy | 18446744073709551616 | +| test.c:483:7:483:8 | ui | 4294967295 | +| test.c:484:43:484:44 | ui | 4294967295 | +| test.c:484:48:484:49 | ui | 4294967295 | +| test.c:485:12:485:17 | result | 18446744065119617024 | +| test.c:487:7:487:8 | ul | 18446744073709551616 | +| test.c:488:28:488:29 | ul | 18446744073709551616 | +| test.c:488:33:488:34 | ul | 18446744073709551616 | +| test.c:489:12:489:17 | result | 18446744073709551616 | +| test.c:495:7:495:8 | ui | 4294967295 | +| test.c:495:19:495:20 | ui | 10 | +| test.c:496:5:496:6 | ui | 10 | +| test.c:496:11:496:12 | ui | 10 | +| test.c:497:12:497:13 | ui | 100 | +| test.c:501:3:501:9 | uiconst | 10 | +| test.c:504:3:504:9 | ulconst | 10 | +| test.c:505:10:505:16 | uiconst | 40 | +| test.c:505:20:505:26 | ulconst | 40 | +| test.c:509:7:509:7 | i | 2147483647 | +| test.c:509:18:509:18 | i | 2147483647 | +| test.c:510:5:510:5 | i | 2147483647 | +| test.c:510:13:510:13 | i | 2 | +| test.c:511:9:511:9 | i | 10 | +| test.c:513:5:513:5 | i | 2147483647 | +| test.c:513:9:513:9 | i | 10 | +| test.c:514:9:514:9 | i | 15 | +| test.c:516:5:516:5 | i | 15 | +| test.c:517:9:517:9 | i | 105 | +| test.c:519:5:519:5 | i | 105 | +| test.c:520:9:520:9 | i | 2310 | +| test.c:522:7:522:7 | i | 2147483647 | +| test.c:523:5:523:5 | i | 2147483647 | +| test.c:523:9:523:9 | i | -1 | +| test.c:524:9:524:9 | i | 1 | +| test.c:526:3:526:3 | i | 2147483647 | +| test.c:526:7:526:7 | i | 2147483647 | +| test.c:527:10:527:10 | i | 2147483647 | +| test.c:530:3:530:3 | i | 2147483647 | +| test.c:530:10:530:11 | sc | 1 | +| test.c:532:7:532:7 | i | 127 | +| test.c:539:7:539:7 | n | 4294967295 | +| test.c:541:7:541:7 | n | 4294967295 | +| test.c:542:9:542:9 | n | 4294967295 | +| test.c:545:7:545:7 | n | 4294967295 | +| test.c:546:9:546:9 | n | 4294967295 | +| test.c:548:9:548:9 | n | 0 | +| test.c:551:8:551:8 | n | 4294967295 | +| test.c:552:9:552:9 | n | 0 | +| test.c:554:9:554:9 | n | 4294967295 | +| test.c:557:10:557:10 | n | 4294967295 | +| test.c:558:5:558:5 | n | 4294967295 | +| test.c:561:7:561:7 | n | 0 | +| test.c:565:7:565:7 | n | 32767 | +| test.c:568:7:568:7 | n | 32767 | +| test.c:569:9:569:9 | n | 0 | +| test.c:571:9:571:9 | n | 32767 | +| test.c:574:7:574:7 | n | 32767 | +| test.c:575:9:575:9 | n | 32767 | +| test.c:577:9:577:9 | n | 0 | +| test.c:580:10:580:10 | n | 32767 | +| test.c:581:5:581:5 | n | 32767 | +| test.c:584:7:584:7 | n | 0 | +| test.c:588:7:588:7 | n | 32767 | +| test.c:589:9:589:9 | n | 32767 | +| test.c:590:11:590:11 | n | 32767 | +| test.c:594:7:594:7 | n | 32767 | +| test.c:595:13:595:13 | n | 32767 | +| test.c:598:9:598:9 | n | 32767 | +| test.c:601:7:601:7 | n | 32767 | +| test.c:601:22:601:22 | n | 32767 | +| test.c:602:9:602:9 | n | 32767 | +| test.c:605:7:605:7 | n | 32767 | +| test.c:606:5:606:5 | n | 32767 | +| test.c:606:10:606:10 | n | 32767 | +| test.c:606:14:606:14 | n | 0 | +| test.c:607:6:607:6 | n | 32767 | +| test.c:607:10:607:10 | n | 0 | +| test.c:607:14:607:14 | n | 32767 | +| test.c:618:7:618:8 | ss | 32767 | +| test.c:619:9:619:10 | ss | 3 | +| test.c:622:7:622:8 | ss | 32767 | +| test.c:623:9:623:10 | ss | 32767 | +| test.c:626:14:626:15 | us | 65535 | +| test.c:627:9:627:10 | us | 32767 | +| test.c:630:14:630:15 | us | 65535 | +| test.c:631:9:631:10 | us | 65535 | +| test.c:634:7:634:8 | ss | 32767 | +| test.c:635:9:635:10 | ss | 32767 | +| test.c:638:7:638:8 | ss | 32767 | +| test.c:639:9:639:10 | ss | 2 | +| test.c:645:8:645:8 | s | 2147483647 | +| test.c:645:15:645:15 | s | 127 | +| test.c:645:23:645:23 | s | 15 | +| test.c:646:18:646:18 | s | 15 | +| test.c:646:22:646:22 | s | 15 | +| test.c:647:9:647:14 | result | 127 | +| test.c:653:7:653:7 | i | 0 | +| test.c:654:9:654:9 | i | 2147483647 | +| test.c:658:7:658:7 | u | 0 | +| test.c:659:9:659:9 | u | 4294967295 | +| test.cpp:10:7:10:7 | b | 2147483647 | +| test.cpp:11:5:11:5 | x | 2147483647 | +| test.cpp:13:10:13:10 | x | 2147483647 | +| test.cpp:18:30:18:30 | x | 2147483647 | +| test.cpp:19:10:19:11 | x0 | 127 | +| test.cpp:27:7:27:7 | y | 2147483647 | +| test.cpp:28:5:28:5 | x | 2147483647 | +| test.cpp:30:7:30:7 | y | 2147483647 | +| test.cpp:31:5:31:5 | x | 2147483647 | +| test.cpp:33:7:33:7 | y | 2147483647 | +| test.cpp:34:5:34:5 | x | 2147483647 | +| test.cpp:36:7:36:7 | y | 2147483647 | +| test.cpp:37:5:37:5 | x | 2147483647 | +| test.cpp:39:7:39:7 | y | 2147483647 | +| test.cpp:40:5:40:5 | x | 2147483647 | +| test.cpp:42:7:42:7 | y | 2147483647 | +| test.cpp:43:5:43:5 | x | 2147483647 | +| test.cpp:45:7:45:7 | y | 2147483647 | +| test.cpp:46:5:46:5 | x | 2147483647 | +| test.cpp:51:7:51:7 | x | 2147483647 | +| test.cpp:52:21:52:21 | x | 0 | +| test.cpp:53:5:53:5 | t | 0 | +| test.cpp:53:15:53:16 | xb | 0 | +| test.cpp:56:7:56:7 | x | 2147483647 | +| test.cpp:57:21:57:21 | x | 2147483647 | +| test.cpp:58:5:58:5 | t | 0 | +| test.cpp:58:15:58:16 | xb | 1 | +| test.cpp:61:7:61:7 | x | 2147483647 | +| test.cpp:62:21:62:21 | x | -1 | +| test.cpp:63:5:63:5 | t | 1 | +| test.cpp:63:15:63:16 | xb | 1 | +| test.cpp:66:19:66:19 | x | 2147483647 | +| test.cpp:67:3:67:3 | t | 2 | +| test.cpp:67:13:67:14 | xb | 1 | +| test.cpp:69:10:69:10 | b | 1 | +| test.cpp:69:21:69:21 | t | 3 | +| test.cpp:74:30:74:30 | c | 255 | +| test.cpp:74:34:74:34 | c | 255 | +| test.cpp:75:22:75:30 | c_times_2 | 510 | +| test.cpp:77:5:77:13 | c_times_2 | 510 | +| test.cpp:79:3:79:11 | c_times_2 | 510 | +| test.cpp:83:16:83:22 | aliased | 2147483647 | +| test.cpp:85:7:85:7 | i | 2147483647 | +| test.cpp:86:12:86:12 | i | 2147483647 | +| test.cpp:88:7:88:8 | ci | 2147483647 | +| test.cpp:89:12:89:13 | ci | 2147483647 | +| test.cpp:91:7:91:13 | aliased | 2147483647 | +| test.cpp:92:12:92:18 | aliased | 2147483647 | +| test.cpp:94:7:94:11 | alias | 2147483647 | +| test.cpp:95:12:95:16 | alias | 2147483647 | +| test.cpp:97:10:97:10 | i | 65535 | +| test.cpp:97:22:97:22 | i | 32767 | +| test.cpp:98:5:98:5 | i | 32767 | +| test.cpp:105:7:105:7 | n | 32767 | +| test.cpp:108:7:108:7 | n | 32767 | +| test.cpp:109:5:109:5 | n | 32767 | +| test.cpp:111:5:111:5 | n | 0 | +| test.cpp:114:8:114:8 | n | 32767 | +| test.cpp:115:5:115:5 | n | 0 | +| test.cpp:117:5:117:5 | n | 32767 | +| test.cpp:120:3:120:3 | n | 32767 | +| test.cpp:120:8:120:8 | n | 32767 | +| test.cpp:120:12:120:12 | n | 0 | +| test.cpp:121:4:121:4 | n | 32767 | +| test.cpp:121:8:121:8 | n | 0 | +| test.cpp:121:12:121:12 | n | 32767 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.ql b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.ql index 341e5784aa36..26d188ce89cb 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.ql +++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.ql @@ -1,4 +1,4 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis from VariableAccess expr -select expr, upperBound(expr) +select expr, upperBound(expr).toString() diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql deleted file mode 100644 index ef158f0de28f..000000000000 --- a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql +++ /dev/null @@ -1,19 +0,0 @@ -import semmle.code.cpp.rangeanalysis.RangeAnalysis -import semmle.code.cpp.ir.IR -import semmle.code.cpp.controlflow.IRGuards -import semmle.code.cpp.ir.ValueNumbering - -query predicate instructionBounds( - Instruction i, Bound b, int delta, boolean upper, Reason reason, Location reasonLoc -) { - ( - i.getAUse() instanceof ArgumentOperand - or - exists(ReturnValueInstruction retInstr | retInstr.getReturnValueOperand() = i.getAUse()) - ) and - boundedInstruction(i, b, delta, upper, reason) and - not valueNumber(b.getInstruction()) = valueNumber(i) and - if reason instanceof CondReason - then reasonLoc = reason.(CondReason).getCond().getLocation() - else reasonLoc instanceof UnknownDefaultLocation -} diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql b/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql deleted file mode 100644 index fbfeb583283e..000000000000 --- a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql +++ /dev/null @@ -1,19 +0,0 @@ -import semmle.code.cpp.rangeanalysis.SignAnalysis -import semmle.code.cpp.ir.IR - -string getASignString(Instruction i) { - positiveInstruction(i) and - result = "positive" - or - negativeInstruction(i) and - result = "negative" - or - strictlyPositiveInstruction(i) and - result = "strictlyPositive" - or - strictlyNegativeInstruction(i) and - result = "strictlyNegative" -} - -from Instruction i -select i, strictconcat(string s | s = getASignString(i) | s, " ") diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes1.expected b/cpp/ql/test/library-tests/scopes/scopes/Scopes1.expected index 01915a9253ed..f89e8fbeb8bc 100644 --- a/cpp/ql/test/library-tests/scopes/scopes/Scopes1.expected +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes1.expected @@ -21,8 +21,10 @@ | scopes.cpp:2:7:2:7 | A | Class | scopes.cpp:2:7:2:7 | operator= | | | scopes.cpp:7:7:7:7 | B | Class | scopes.cpp:7:7:7:7 | operator= | | | scopes.cpp:7:7:7:7 | B | Class | scopes.cpp:7:7:7:7 | operator= | | +| scopes.cpp:11:7:11:7 | C | Class | scopes.cpp:11:7:11:7 | C | | | scopes.cpp:11:7:11:7 | C | Class | scopes.cpp:11:7:11:7 | operator= | | | scopes.cpp:11:7:11:7 | C | Class | scopes.cpp:11:7:11:7 | operator= | | +| scopes.cpp:15:7:15:7 | D | Class | scopes.cpp:15:7:15:7 | D | | | scopes.cpp:15:7:15:7 | D | Class | scopes.cpp:15:7:15:7 | operator= | | | scopes.cpp:15:7:15:7 | D | Class | scopes.cpp:15:7:15:7 | operator= | | | scopes.cpp:15:7:15:7 | D | Class | scopes.cpp:21:7:21:10 | E | NestedClass | @@ -32,6 +34,7 @@ | scopes.cpp:21:7:21:10 | E | NestedClass | scopes.cpp:31:7:31:13 | F | NestedClass | | scopes.cpp:25:8:25:8 | G | NestedClass | scopes.cpp:25:8:25:8 | operator= | | | scopes.cpp:25:8:25:8 | G | NestedClass | scopes.cpp:25:8:25:8 | operator= | | +| scopes.cpp:31:7:31:13 | F | NestedClass | scopes.cpp:31:13:31:13 | F | | | scopes.cpp:31:7:31:13 | F | NestedClass | scopes.cpp:31:13:31:13 | operator= | | | scopes.cpp:31:7:31:13 | F | NestedClass | scopes.cpp:31:13:31:13 | operator= | | | scopes.cpp:31:7:31:13 | F | NestedClass | scopes.cpp:32:8:32:27 | doubleNestedFunction | | @@ -39,6 +42,7 @@ | scopes.cpp:46:8:46:8 | S | Class | scopes.cpp:46:8:46:8 | operator= | | | scopes.cpp:46:8:46:8 | S | Class | scopes.cpp:47:7:47:8 | af | | | scopes.cpp:46:8:46:8 | S | Class | scopes.cpp:50:7:50:8 | ag | | +| scopes.cpp:58:7:58:10 | Name | Class | scopes.cpp:58:7:58:7 | Name | | | scopes.cpp:58:7:58:10 | Name | Class | scopes.cpp:58:7:58:7 | operator= | | | scopes.cpp:58:7:58:10 | Name | Class | scopes.cpp:58:7:58:7 | operator= | | | scopes.cpp:58:7:58:10 | Name | Class | scopes.cpp:59:14:59:14 | s | | @@ -59,9 +63,11 @@ | scopes.cpp:74:11:74:13 | One | Namespace | scopes.cpp:95:8:95:8 | I | Class | | scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:76:18:76:18 | T | | | scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:79:5:79:5 | t | | +| scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:77:8:77:8 | H | | | scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:77:8:77:8 | operator= | | | scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:77:8:77:8 | operator= | | | scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:79:5:79:5 | t | | +| scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:77:8:77:8 | H | | | scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:77:8:77:8 | operator= | | | scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:77:8:77:8 | operator= | | | scopes.cpp:77:8:77:8 | H | Class | scopes.cpp:79:5:79:5 | t | | diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes3.expected b/cpp/ql/test/library-tests/scopes/scopes/Scopes3.expected index af73b99dff8d..94f46c73f754 100644 --- a/cpp/ql/test/library-tests/scopes/scopes/Scopes3.expected +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes3.expected @@ -4,32 +4,38 @@ | scopes.cpp:2:7:2:7 | operator= | | scopes.cpp:2:7:2:7 | A | 2 | | scopes.cpp:7:7:7:7 | operator= | | scopes.cpp:7:7:7:7 | B | 2 | | scopes.cpp:7:7:7:7 | operator= | | scopes.cpp:7:7:7:7 | B | 2 | -| scopes.cpp:11:7:11:7 | operator= | | scopes.cpp:11:7:11:7 | C | 2 | -| scopes.cpp:11:7:11:7 | operator= | | scopes.cpp:11:7:11:7 | C | 2 | -| scopes.cpp:15:7:15:7 | operator= | | scopes.cpp:15:7:15:7 | D | 3 | -| scopes.cpp:15:7:15:7 | operator= | | scopes.cpp:15:7:15:7 | D | 3 | +| scopes.cpp:11:7:11:7 | C | Constructor | scopes.cpp:11:7:11:7 | C | 3 | +| scopes.cpp:11:7:11:7 | operator= | | scopes.cpp:11:7:11:7 | C | 3 | +| scopes.cpp:11:7:11:7 | operator= | | scopes.cpp:11:7:11:7 | C | 3 | +| scopes.cpp:15:7:15:7 | D | Constructor | scopes.cpp:15:7:15:7 | D | 4 | +| scopes.cpp:15:7:15:7 | operator= | | scopes.cpp:15:7:15:7 | D | 4 | +| scopes.cpp:15:7:15:7 | operator= | | scopes.cpp:15:7:15:7 | D | 4 | | scopes.cpp:21:10:21:10 | operator= | | scopes.cpp:21:7:21:10 | E | 4 | | scopes.cpp:21:10:21:10 | operator= | | scopes.cpp:21:7:21:10 | E | 4 | | scopes.cpp:25:8:25:8 | operator= | | scopes.cpp:25:8:25:8 | G | 2 | | scopes.cpp:25:8:25:8 | operator= | | scopes.cpp:25:8:25:8 | G | 2 | -| scopes.cpp:31:13:31:13 | operator= | | scopes.cpp:31:7:31:13 | F | 3 | -| scopes.cpp:31:13:31:13 | operator= | | scopes.cpp:31:7:31:13 | F | 3 | -| scopes.cpp:32:8:32:27 | doubleNestedFunction | | scopes.cpp:31:7:31:13 | F | 3 | +| scopes.cpp:31:13:31:13 | F | Constructor | scopes.cpp:31:7:31:13 | F | 4 | +| scopes.cpp:31:13:31:13 | operator= | | scopes.cpp:31:7:31:13 | F | 4 | +| scopes.cpp:31:13:31:13 | operator= | | scopes.cpp:31:7:31:13 | F | 4 | +| scopes.cpp:32:8:32:27 | doubleNestedFunction | | scopes.cpp:31:7:31:13 | F | 4 | | scopes.cpp:46:8:46:8 | operator= | | scopes.cpp:46:8:46:8 | S | 4 | | scopes.cpp:46:8:46:8 | operator= | | scopes.cpp:46:8:46:8 | S | 4 | | scopes.cpp:47:7:47:8 | af | | scopes.cpp:46:8:46:8 | S | 4 | | scopes.cpp:50:7:50:8 | ag | | scopes.cpp:46:8:46:8 | S | 4 | -| scopes.cpp:58:7:58:7 | operator= | | scopes.cpp:58:7:58:10 | Name | 3 | -| scopes.cpp:58:7:58:7 | operator= | | scopes.cpp:58:7:58:10 | Name | 3 | +| scopes.cpp:58:7:58:7 | Name | Constructor | scopes.cpp:58:7:58:10 | Name | 4 | +| scopes.cpp:58:7:58:7 | operator= | | scopes.cpp:58:7:58:10 | Name | 4 | +| scopes.cpp:58:7:58:7 | operator= | | scopes.cpp:58:7:58:10 | Name | 4 | | scopes.cpp:62:7:62:7 | Table | Constructor | scopes.cpp:62:7:62:11 | Table | 8 | | scopes.cpp:62:7:62:7 | operator= | | scopes.cpp:62:7:62:11 | Table | 8 | | scopes.cpp:66:3:66:7 | Table | Constructor | scopes.cpp:62:7:62:11 | Table | 8 | | scopes.cpp:67:3:67:8 | ~Table | | scopes.cpp:62:7:62:11 | Table | 8 | | scopes.cpp:68:9:68:14 | lookup | | scopes.cpp:62:7:62:11 | Table | 8 | | scopes.cpp:69:8:69:13 | insert | | scopes.cpp:62:7:62:11 | Table | 8 | -| scopes.cpp:77:8:77:8 | operator= | | scopes.cpp:77:8:77:8 | H | 3 | -| scopes.cpp:77:8:77:8 | operator= | | scopes.cpp:77:8:77:8 | H | 3 | -| scopes.cpp:77:8:77:8 | operator= | | scopes.cpp:77:8:77:8 | H | 3 | -| scopes.cpp:77:8:77:8 | operator= | | scopes.cpp:77:8:77:8 | H | 3 | +| scopes.cpp:77:8:77:8 | H | Constructor | scopes.cpp:77:8:77:8 | H | 4 | +| scopes.cpp:77:8:77:8 | H | Constructor | scopes.cpp:77:8:77:8 | H | 4 | +| scopes.cpp:77:8:77:8 | operator= | | scopes.cpp:77:8:77:8 | H | 4 | +| scopes.cpp:77:8:77:8 | operator= | | scopes.cpp:77:8:77:8 | H | 4 | +| scopes.cpp:77:8:77:8 | operator= | | scopes.cpp:77:8:77:8 | H | 4 | +| scopes.cpp:77:8:77:8 | operator= | | scopes.cpp:77:8:77:8 | H | 4 | | scopes.cpp:95:8:95:8 | operator= | | scopes.cpp:95:8:95:8 | I | 3 | | scopes.cpp:95:8:95:8 | operator= | | scopes.cpp:95:8:95:8 | I | 3 | diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes4.expected b/cpp/ql/test/library-tests/scopes/scopes/Scopes4.expected index 5a4c8bcf4af7..fda8ff9a7d8d 100644 --- a/cpp/ql/test/library-tests/scopes/scopes/Scopes4.expected +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes4.expected @@ -6,14 +6,17 @@ | scopes.cpp:2:7:2:7 | operator= | | 1 | 1 | | scopes.cpp:7:7:7:7 | operator= | | 1 | 1 | | scopes.cpp:7:7:7:7 | operator= | | 1 | 1 | +| scopes.cpp:11:7:11:7 | C | | 1 | 1 | | scopes.cpp:11:7:11:7 | operator= | | 1 | 1 | | scopes.cpp:11:7:11:7 | operator= | | 1 | 1 | +| scopes.cpp:15:7:15:7 | D | | 1 | 1 | | scopes.cpp:15:7:15:7 | operator= | | 1 | 1 | | scopes.cpp:15:7:15:7 | operator= | | 1 | 1 | | scopes.cpp:21:10:21:10 | operator= | | 1 | 1 | | scopes.cpp:21:10:21:10 | operator= | | 1 | 1 | | scopes.cpp:25:8:25:8 | operator= | | 1 | 1 | | scopes.cpp:25:8:25:8 | operator= | | 1 | 1 | +| scopes.cpp:31:13:31:13 | F | | 1 | 1 | | scopes.cpp:31:13:31:13 | operator= | | 1 | 1 | | scopes.cpp:31:13:31:13 | operator= | | 1 | 1 | | scopes.cpp:32:8:32:27 | doubleNestedFunction | | 1 | 1 | @@ -23,6 +26,7 @@ | scopes.cpp:47:7:47:8 | af | | 1 | 1 | | scopes.cpp:50:7:50:8 | ag | | 1 | 1 | | scopes.cpp:53:6:53:6 | g | isTopLevel() | 1 | 1 | +| scopes.cpp:58:7:58:7 | Name | | 1 | 1 | | scopes.cpp:58:7:58:7 | operator= | | 1 | 1 | | scopes.cpp:58:7:58:7 | operator= | | 1 | 1 | | scopes.cpp:62:7:62:7 | Table | | 1 | 1 | @@ -32,6 +36,8 @@ | scopes.cpp:68:9:68:14 | lookup | | 1 | 1 | | scopes.cpp:69:8:69:13 | insert | | 1 | 1 | | scopes.cpp:72:16:72:21 | strlen | isTopLevel() | 1 | 1 | +| scopes.cpp:77:8:77:8 | H | | 1 | 0 | +| scopes.cpp:77:8:77:8 | H | | 1 | 0 | | scopes.cpp:77:8:77:8 | operator= | | 1 | 0 | | scopes.cpp:77:8:77:8 | operator= | | 1 | 0 | | scopes.cpp:77:8:77:8 | operator= | | 1 | 0 | diff --git a/cpp/ql/test/library-tests/security/encryption/test.cpp b/cpp/ql/test/library-tests/security/encryption/test.cpp new file mode 100644 index 000000000000..0e4f8292eb5c --- /dev/null +++ b/cpp/ql/test/library-tests/security/encryption/test.cpp @@ -0,0 +1,12 @@ + +void des_function(); // insecure +void function_using_des(); // insecure +void EncryptWithDES(); // insecure + +void aes_function(); // secure +void function_using_aes(); // secure +void EncryptionWithAES(); // secure + +void abc_function(); +void function_using_abc(); +void EncryptionWithABC(); diff --git a/cpp/ql/test/library-tests/security/encryption/test.expected b/cpp/ql/test/library-tests/security/encryption/test.expected new file mode 100644 index 000000000000..a497544d029f --- /dev/null +++ b/cpp/ql/test/library-tests/security/encryption/test.expected @@ -0,0 +1,9 @@ +| test.cpp:2:6:2:17 | des_function | getInsecureAlgorithmRegex | +| test.cpp:3:6:3:23 | function_using_des | getInsecureAlgorithmRegex | +| test.cpp:4:6:4:19 | EncryptWithDES | getInsecureAlgorithmRegex | +| test.cpp:6:6:6:17 | aes_function | getSecureAlgorithmRegex | +| test.cpp:7:6:7:23 | function_using_aes | getSecureAlgorithmRegex | +| test.cpp:8:6:8:22 | EncryptionWithAES | getSecureAlgorithmRegex | +| test.cpp:10:6:10:17 | abc_function | | +| test.cpp:11:6:11:23 | function_using_abc | | +| test.cpp:12:6:12:22 | EncryptionWithABC | | diff --git a/cpp/ql/test/library-tests/security/encryption/test.ql b/cpp/ql/test/library-tests/security/encryption/test.ql new file mode 100644 index 000000000000..6539611aa3a2 --- /dev/null +++ b/cpp/ql/test/library-tests/security/encryption/test.ql @@ -0,0 +1,14 @@ +import default +import semmle.code.cpp.security.Encryption + +string describe(Function f) { + f.getName().regexpMatch(getSecureAlgorithmRegex()) and + result = "getSecureAlgorithmRegex" + or + f.getName().regexpMatch(getInsecureAlgorithmRegex()) and + result = "getInsecureAlgorithmRegex" +} + +from Function f +where exists(f.getLocation().getFile()) +select f, concat(describe(f), ", ") diff --git a/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected index 264bb16db99b..d2b316e22119 100644 --- a/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected +++ b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected @@ -25,12 +25,16 @@ | cpp.cpp:30:5:30:7 | Bar | int -> int -> int -> int -> void | true | | cpp.cpp:31:5:31:7 | Bar | int -> int -> int -> int -> int -> void | false | | cpp.cpp:32:5:32:7 | Bar | int -> int -> int -> int -> int -> int -> void | false | +| cpp.cpp:35:7:35:7 | PurelyDestructible | void | false | | cpp.cpp:35:7:35:7 | operator= | const PurelyDestructible & -> PurelyDestructible & | false | | cpp.cpp:37:5:37:23 | ~PurelyDestructible | void | true | +| cpp.cpp:40:7:40:7 | ImpurelyDestructible | void | false | | cpp.cpp:40:7:40:7 | operator= | const ImpurelyDestructible & -> ImpurelyDestructible & | false | | cpp.cpp:42:5:42:25 | ~ImpurelyDestructible | void | false | +| cpp.cpp:45:7:45:7 | SuperPurelyDestructible | void | false | | cpp.cpp:45:7:45:7 | operator= | const SuperPurelyDestructible & -> SuperPurelyDestructible & | false | | cpp.cpp:47:5:47:28 | ~SuperPurelyDestructible | void | true | +| cpp.cpp:50:7:50:7 | SuperImpurelyDestructible | void | false | | cpp.cpp:50:7:50:7 | operator= | const SuperImpurelyDestructible & -> SuperImpurelyDestructible & | false | | cpp.cpp:52:5:52:30 | ~SuperImpurelyDestructible | void | false | | cpp.cpp:55:7:55:7 | operator= | const someClass & -> someClass & | false | diff --git a/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected b/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected index bb28af216711..22282641b373 100644 --- a/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected +++ b/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected @@ -24,6 +24,7 @@ | copy.cpp:38:8:38:16 | operator= | | defaulted | | copy.cpp:41:9:41:9 | operator= | | | | copy.cpp:46:5:46:7 | Sub | deleted | defaulted | +| copy.cpp:49:9:49:9 | HasPointer | | | | copy.cpp:49:9:49:9 | operator= | | | | copy.cpp:49:9:49:9 | operator= | | | | copy.cpp:54:9:54:9 | HasArray | deleted | | @@ -35,6 +36,7 @@ | copy.cpp:59:9:59:9 | operator= | | | | copy.cpp:59:9:59:9 | operator= | | | | copy.cpp:67:9:67:9 | Wrapper | | | +| copy.cpp:67:9:67:9 | Wrapper | | | | copy.cpp:67:9:67:9 | Wrapper | deleted | | | copy.cpp:67:9:67:9 | Wrapper | deleted | | | copy.cpp:67:9:67:9 | operator= | | | @@ -46,12 +48,14 @@ | copy.cpp:72:9:72:9 | NotCopyable | deleted | | | copy.cpp:72:9:72:9 | NotCopyable | deleted | | | copy.cpp:72:9:72:9 | operator= | deleted | | +| copy.cpp:76:9:76:9 | CopyableComposition | | | | copy.cpp:76:9:76:9 | operator= | | | | copy.cpp:76:9:76:9 | operator= | | | | copy.cpp:80:9:80:9 | NotCopyableComposition | | | | copy.cpp:80:9:80:9 | NotCopyableComposition | deleted | | | copy.cpp:80:9:80:9 | NotCopyableComposition | deleted | | | copy.cpp:80:9:80:9 | operator= | deleted | | +| copy.cpp:84:9:84:9 | CopyableInheritance | | | | copy.cpp:84:9:84:9 | operator= | | | | copy.cpp:84:9:84:9 | operator= | | | | copy.cpp:86:9:86:9 | NotCopyableInheritance | | | @@ -64,16 +68,16 @@ | copy.cpp:91:11:91:11 | operator= | | | | copy.cpp:95:9:95:9 | operator= | | | | copy.cpp:95:9:95:9 | operator= | | | +| copy.cpp:100:9:100:9 | Derived | | | | copy.cpp:100:9:100:9 | operator= | | | | copy.cpp:100:9:100:9 | operator= | | | | copy.cpp:106:9:106:9 | MoveCtor | deleted | | | copy.cpp:106:9:106:9 | operator= | deleted | | | copy.cpp:108:5:108:12 | MoveCtor | | | +| copy.cpp:111:9:111:9 | MoveAssign | | | | copy.cpp:111:9:111:9 | MoveAssign | deleted | | | copy.cpp:111:9:111:9 | operator= | deleted | | | copy.cpp:113:17:113:25 | operator= | | | -| copy.cpp:120:9:120:9 | OnlyCtor | | | -| copy.cpp:120:9:120:9 | OnlyCtor | | | | copy.cpp:120:9:120:9 | OnlyCtor | deleted | | | copy.cpp:120:9:120:9 | operator= | deleted | | | copy.cpp:126:11:126:19 | operator= | | | diff --git a/cpp/ql/test/library-tests/specifiers2/cpp20.cpp b/cpp/ql/test/library-tests/specifiers2/cpp20.cpp new file mode 100644 index 000000000000..d260eaecf813 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers2/cpp20.cpp @@ -0,0 +1,10 @@ +// semmle-extractor-options: --edg --clang --edg --c++20 + +namespace cpp20 { + +class TestConstexpr { + constexpr int member_constexpr() { return 0; } // not const in C++ >= 14 + constexpr int member_const_constexpr() const { return 0; } +}; + +} // namespace cpp20 diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2.expected b/cpp/ql/test/library-tests/specifiers2/specifiers2.expected index 26c72b4e3196..2ccfc7b84a02 100644 --- a/cpp/ql/test/library-tests/specifiers2/specifiers2.expected +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2.expected @@ -1,5 +1,22 @@ | Class | specifiers2pp.cpp:8:7:8:13 | MyClass | MyClass | abstract | | Class | specifiers2pp.cpp:24:7:24:14 | MyClass2 | MyClass2 | abstract | +| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | extern | +| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | extern | +| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | inline | +| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | inline | +| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | is_constexpr | +| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | is_constexpr | +| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | public | +| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | public | +| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | declared_constexpr | +| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | inline | +| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | is_constexpr | +| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | private | +| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | const | +| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | declared_constexpr | +| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | inline | +| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | is_constexpr | +| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | private | | Function | specifiers2.c:11:6:11:6 | f | f | extern | | Function | specifiers2.c:12:13:12:13 | f | f | extern | | Function | specifiers2.c:13:13:13:13 | f | f | extern | @@ -62,6 +79,8 @@ | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | extern | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | inline | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | is_constexpr | +| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | is_constexpr | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:33:5:33:18 | fun | fun | public | @@ -69,12 +88,32 @@ | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | extern | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | inline | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | is_constexpr | +| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | is_constexpr | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | public | | Function | specifiers2pp.cpp:40:12:40:18 | someFun | someFun | extern | | Function | specifiers2pp.cpp:41:16:41:23 | someFun2 | someFun2 | extern | | Function | specifiers2pp.cpp:43:9:43:16 | someFun3 | someFun3 | extern | | Function | specifiers2pp.cpp:44:16:44:23 | someFun4 | someFun4 | static | +| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | extern | +| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | extern | +| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | is_constexpr | +| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | is_constexpr | +| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | public | +| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | public | +| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | const | +| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | declared_constexpr | +| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | inline | +| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | is_constexpr | +| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | private | +| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | const | +| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | declared_constexpr | +| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | inline | +| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | is_constexpr | +| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | private | | FunctionDeclarationEntry | specifiers2.c:11:6:11:6 | declaration of f | f | c_linkage | | FunctionDeclarationEntry | specifiers2.c:11:6:11:6 | declaration of f | f | void_param_list | | FunctionDeclarationEntry | specifiers2.c:12:13:12:13 | declaration of f | f | c_linkage | diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2pp.cpp b/cpp/ql/test/library-tests/specifiers2/specifiers2pp.cpp index 7bceb782ecaf..37d5e894c889 100644 --- a/cpp/ql/test/library-tests/specifiers2/specifiers2pp.cpp +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2pp.cpp @@ -58,3 +58,8 @@ template using Const = const T; using Const_int = Const; typedef volatile Const_int volatile_Const_int; + +class TestConstexpr { + constexpr int member_constexpr() { return 0; } // const in C++11 + constexpr int member_const_constexpr() const { return 0; } +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected index e33f5f851cd9..14648db75c59 100644 --- a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected @@ -18,37 +18,46 @@ | a2.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 2 | i | | a2.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 3 | operator= | | a2.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 4 | operator= | -| b1.cpp:6:7:6:21 | AppleCompatible | 3 members | 2 locations | 0 | apple_x | -| b1.cpp:6:7:6:21 | AppleCompatible | 3 members | 2 locations | 1 | operator= | -| b1.cpp:6:7:6:21 | AppleCompatible | 3 members | 2 locations | 2 | operator= | -| b1.cpp:11:7:11:22 | BananaCompatible | 3 members | 2 locations | 0 | banana_x | -| b1.cpp:11:7:11:22 | BananaCompatible | 3 members | 2 locations | 1 | operator= | -| b1.cpp:11:7:11:22 | BananaCompatible | 3 members | 2 locations | 2 | operator= | -| b1.cpp:16:7:16:12 | Cherry | 3 members | 1 locations | 0 | cherry_x | -| b1.cpp:16:7:16:12 | Cherry | 3 members | 1 locations | 1 | operator= | -| b1.cpp:16:7:16:12 | Cherry | 3 members | 1 locations | 2 | operator= | -| b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 0 | damson_x | -| b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 1 | bar | -| b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 1 | foo | -| b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 2 | operator= | -| b1.cpp:23:7:23:12 | Damson | 5 members | 2 locations | 3 | operator= | -| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 0 | apple_x | -| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 1 | operator= | -| b1.cpp:29:9:29:23 | AppleCompatible | 3 members | 1 locations | 2 | operator= | -| b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 0 | apple_x | -| b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 1 | operator= | -| b2.cpp:2:7:2:21 | AppleCompatible | 3 members | 2 locations | 2 | operator= | -| b2.cpp:9:7:9:22 | BananaCompatible | 3 members | 2 locations | 0 | banana_x | -| b2.cpp:9:7:9:22 | BananaCompatible | 3 members | 2 locations | 1 | operator= | -| b2.cpp:9:7:9:22 | BananaCompatible | 3 members | 2 locations | 2 | operator= | -| b2.cpp:14:7:14:12 | Cherry | 3 members | 1 locations | 0 | cherry_x | -| b2.cpp:14:7:14:12 | Cherry | 3 members | 1 locations | 1 | operator= | -| b2.cpp:14:7:14:12 | Cherry | 3 members | 1 locations | 2 | operator= | -| b2.cpp:21:7:21:12 | Damson | 5 members | 2 locations | 0 | damson_x | -| b2.cpp:21:7:21:12 | Damson | 5 members | 2 locations | 1 | bar | -| b2.cpp:21:7:21:12 | Damson | 5 members | 2 locations | 1 | foo | -| b2.cpp:21:7:21:12 | Damson | 5 members | 2 locations | 2 | operator= | -| b2.cpp:21:7:21:12 | Damson | 5 members | 2 locations | 3 | operator= | +| b1.cpp:6:7:6:21 | AppleCompatible | 4 members | 2 locations | 0 | apple_x | +| b1.cpp:6:7:6:21 | AppleCompatible | 4 members | 2 locations | 1 | operator= | +| b1.cpp:6:7:6:21 | AppleCompatible | 4 members | 2 locations | 2 | operator= | +| b1.cpp:6:7:6:21 | AppleCompatible | 4 members | 2 locations | 3 | AppleCompatible | +| b1.cpp:11:7:11:22 | BananaCompatible | 4 members | 2 locations | 0 | banana_x | +| b1.cpp:11:7:11:22 | BananaCompatible | 4 members | 2 locations | 1 | operator= | +| b1.cpp:11:7:11:22 | BananaCompatible | 4 members | 2 locations | 2 | operator= | +| b1.cpp:11:7:11:22 | BananaCompatible | 4 members | 2 locations | 3 | BananaCompatible | +| b1.cpp:16:7:16:12 | Cherry | 4 members | 1 locations | 0 | cherry_x | +| b1.cpp:16:7:16:12 | Cherry | 4 members | 1 locations | 1 | operator= | +| b1.cpp:16:7:16:12 | Cherry | 4 members | 1 locations | 2 | operator= | +| b1.cpp:16:7:16:12 | Cherry | 4 members | 1 locations | 3 | Cherry | +| b1.cpp:23:7:23:12 | Damson | 6 members | 2 locations | 0 | damson_x | +| b1.cpp:23:7:23:12 | Damson | 6 members | 2 locations | 1 | bar | +| b1.cpp:23:7:23:12 | Damson | 6 members | 2 locations | 1 | foo | +| b1.cpp:23:7:23:12 | Damson | 6 members | 2 locations | 2 | operator= | +| b1.cpp:23:7:23:12 | Damson | 6 members | 2 locations | 3 | operator= | +| b1.cpp:23:7:23:12 | Damson | 6 members | 2 locations | 4 | Damson | +| b1.cpp:29:9:29:23 | AppleCompatible | 4 members | 1 locations | 0 | apple_x | +| b1.cpp:29:9:29:23 | AppleCompatible | 4 members | 1 locations | 1 | operator= | +| b1.cpp:29:9:29:23 | AppleCompatible | 4 members | 1 locations | 2 | operator= | +| b1.cpp:29:9:29:23 | AppleCompatible | 4 members | 1 locations | 3 | AppleCompatible | +| b2.cpp:2:7:2:21 | AppleCompatible | 4 members | 2 locations | 0 | apple_x | +| b2.cpp:2:7:2:21 | AppleCompatible | 4 members | 2 locations | 1 | operator= | +| b2.cpp:2:7:2:21 | AppleCompatible | 4 members | 2 locations | 2 | operator= | +| b2.cpp:2:7:2:21 | AppleCompatible | 4 members | 2 locations | 3 | AppleCompatible | +| b2.cpp:9:7:9:22 | BananaCompatible | 4 members | 2 locations | 0 | banana_x | +| b2.cpp:9:7:9:22 | BananaCompatible | 4 members | 2 locations | 1 | operator= | +| b2.cpp:9:7:9:22 | BananaCompatible | 4 members | 2 locations | 2 | operator= | +| b2.cpp:9:7:9:22 | BananaCompatible | 4 members | 2 locations | 3 | BananaCompatible | +| b2.cpp:14:7:14:12 | Cherry | 4 members | 1 locations | 0 | cherry_x | +| b2.cpp:14:7:14:12 | Cherry | 4 members | 1 locations | 1 | operator= | +| b2.cpp:14:7:14:12 | Cherry | 4 members | 1 locations | 2 | operator= | +| b2.cpp:14:7:14:12 | Cherry | 4 members | 1 locations | 3 | Cherry | +| b2.cpp:21:7:21:12 | Damson | 6 members | 2 locations | 0 | damson_x | +| b2.cpp:21:7:21:12 | Damson | 6 members | 2 locations | 1 | bar | +| b2.cpp:21:7:21:12 | Damson | 6 members | 2 locations | 1 | foo | +| b2.cpp:21:7:21:12 | Damson | 6 members | 2 locations | 2 | operator= | +| b2.cpp:21:7:21:12 | Damson | 6 members | 2 locations | 3 | operator= | +| b2.cpp:21:7:21:12 | Damson | 6 members | 2 locations | 4 | Damson | | file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 0 | gp_offset | | file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 1 | fp_offset | | file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 2 | overflow_arg_area | diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected b/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected index 801306a572ab..29af417fe988 100644 --- a/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected @@ -2,5 +2,5 @@ | x.cpp:3:6:3:10 | bar_x | a.h:4:8:4:10 | Bar | 3 | | x.cpp:19:6:19:10 | foo_x | y.cpp:4:8:4:10 | Foo | 3 | | x.cpp:23:5:23:17 | templateField | x.cpp:6:10:6:12 | Foo | 3 | -| x.cpp:23:5:23:17 | templateField | x.cpp:12:9:12:11 | Foo | 3 | +| x.cpp:23:5:23:17 | templateField | x.cpp:12:9:12:11 | Foo | 4 | | x.cpp:26:18:26:29 | template_foo | x.cpp:22:7:22:14 | Template | 0 | diff --git a/cpp/ql/test/library-tests/switch/blocks.ql b/cpp/ql/test/library-tests/switch/blocks.ql index 794c597d9a90..0c97f65ec3d8 100644 --- a/cpp/ql/test/library-tests/switch/blocks.ql +++ b/cpp/ql/test/library-tests/switch/blocks.ql @@ -1,5 +1,5 @@ import cpp -from Function f, Block b +from Function f, BlockStmt b where b = f.getEntryPoint() select f, b, b.getAStmt() diff --git a/cpp/ql/test/library-tests/syntax-zoo/Compare.qll b/cpp/ql/test/library-tests/syntax-zoo/Compare.qll deleted file mode 100644 index 536dd9f7108a..000000000000 --- a/cpp/ql/test/library-tests/syntax-zoo/Compare.qll +++ /dev/null @@ -1,148 +0,0 @@ -import cpp -import semmle.code.cpp.controlflow.internal.CFG - -class DestructorCallEnhanced extends DestructorCall { - override string toString() { - if exists(this.getQualifier().(VariableAccess).getTarget().getName()) - then - result = - "call to " + this.getQualifier().(VariableAccess).getTarget().getName() + "." + - this.getTarget().getName() - else result = super.toString() - } -} - -predicate differentEdge(ControlFlowNode n1, ControlFlowNode n2, string msg) { - successors(n1, n2) and - not qlCFGSuccessor(n1, n2) and - msg = "Standard edge, only from extractor" - or - not successors(n1, n2) and - qlCFGSuccessor(n1, n2) and - msg = "Standard edge, only from QL" - or - truecond_base(n1, n2) and - not qlCFGTrueSuccessor(n1, n2) and - msg = "True edge, only from extractor" - or - not truecond_base(n1, n2) and - qlCFGTrueSuccessor(n1, n2) and - msg = "True edge, only from QL" - or - falsecond_base(n1, n2) and - not qlCFGFalseSuccessor(n1, n2) and - msg = "False edge, only from extractor" - or - not falsecond_base(n1, n2) and - qlCFGFalseSuccessor(n1, n2) and - msg = "False edge, only from QL" -} - -predicate differentScope(Element e) { - exists(ControlFlowNode n1 | - getScopeElement(n1) = e and - differentEdge(n1, _, _) - ) -} - -private predicate isInFunction(ControlFlowNode x, Function f) { - f = x.getControlFlowScope() - or - exists(ControlFlowNode y | - successors(unresolveElement(x), unresolveElement(y)) - or - successors(unresolveElement(y), unresolveElement(x)) - | - isInFunction(y, f) - ) -} - -Element getScopeElement(ControlFlowNode x) { - isInFunction(x, result) - or - not isInFunction(x, _) and - result = x.getFile() -} - -string getScopeName(ControlFlowNode x) { - exists(Function scope | scope = getScopeElement(x) | - differentScope(scope) and - result = - scope.getFile().getBaseName().splitAt(".", 0) + "__" + - scope.getQualifiedName().replaceAll("::", "_") - ) - or - exists(File scope | scope = getScopeElement(x) | - differentScope(scope) and - result = scope.getBaseName() - ) -} - -module QLCFG { - private predicate isNode(boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label) { - isEdge = false and x = y and label = x.toString() - } - - private predicate isSuccessor(boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label) { - exists(string truelabel, string falselabel | - isEdge = true and - qlCFGSuccessor(x, y) and - (if qlCFGTrueSuccessor(x, y) then truelabel = "T" else truelabel = "") and - (if qlCFGFalseSuccessor(x, y) then falselabel = "F" else falselabel = "") and - label = truelabel + falselabel - ) - } - - predicate qltestGraph( - Element scopeElement, string scopeString, boolean isEdge, ControlFlowNode x, ControlFlowNode y, - string label - ) { - scopeElement = getScopeElement(x) and - scopeString = getScopeName(x) + "_ql" and - ( - isNode(isEdge, x, y, label) - or - isSuccessor(isEdge, x, y, label) - ) - } -} - -module ExtractorCFG { - predicate isNode(boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label) { - isEdge = false and x = y and label = x.toString() - } - - predicate isSuccessor(boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label) { - exists(string truelabel, string falselabel | - isEdge = true and - successors(x, y) and - (if truecond_base(x, y) then truelabel = "T" else truelabel = "") and - (if falsecond_base(x, y) then falselabel = "F" else falselabel = "") and - label = truelabel + falselabel - ) - } - - predicate qltestGraph( - Element scopeElement, string scopeString, boolean isEdge, ControlFlowNode x, ControlFlowNode y, - string label - ) { - scopeElement = getScopeElement(x) and - scopeString = getScopeName(x) + "_extractor" and - ( - isNode(isEdge, x, y, label) - or - isSuccessor(isEdge, x, y, label) - ) - } -} - -module AllCFG { - predicate qltestGraph( - Element scopeElement, string scopeString, boolean isEdge, ControlFlowNode x, ControlFlowNode y, - string label - ) { - QLCFG::qltestGraph(scopeElement, scopeString, isEdge, x, y, label) - or - ExtractorCFG::qltestGraph(scopeElement, scopeString, isEdge, x, y, label) - } -} diff --git a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected index 4d951d3f1d0c..389b3a9496d3 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected @@ -1,32 +1,24 @@ missingOperand -| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| cpp11.cpp:77:19:77:21 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:76:8:76:8 | IR: apply | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | -| cpp11.cpp:82:11:82:14 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:81:8:81:8 | IR: apply2 | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | -| cpp11.cpp:82:45:82:48 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | IR: operator() | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | -| cpp11.cpp:82:51:82:51 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | IR: operator() | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | -| cpp11.cpp:88:25:88:30 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | IR: main | void lambda::main() | -| cpp11.cpp:88:33:88:38 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | IR: main | void lambda::main() | -| destructors.cpp:51:36:51:38 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | destructors.cpp:49:7:49:7 | IR: f | int cond_destruct::f(int) | -| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| misc.c:125:5:125:11 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:97:6:97:10 | IR: misc3 | void misc3() | +| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| cpp11.cpp:77:19:77:21 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:76:8:76:8 | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | +| cpp11.cpp:82:11:82:14 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:81:8:81:8 | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | +| cpp11.cpp:82:45:82:48 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | +| cpp11.cpp:82:51:82:51 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | +| cpp11.cpp:88:25:88:30 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | void lambda::main() | void lambda::main() | +| cpp11.cpp:88:33:88:38 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | void lambda::main() | void lambda::main() | +| destructors.cpp:51:36:51:38 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | destructors.cpp:49:7:49:7 | int cond_destruct::f(int) | int cond_destruct::f(int) | +| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| misc.c:125:5:125:11 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:97:6:97:10 | void misc3() | void misc3() | unexpectedOperand duplicateOperand missingPhiOperand @@ -34,475 +26,79 @@ missingOperandType duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor -| VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y | -| condition_decls.cpp:16:19:16:20 | Chi: call to BoxedInt | -| condition_decls.cpp:26:23:26:24 | Chi: call to BoxedInt | -| condition_decls.cpp:41:22:41:23 | Chi: call to BoxedInt | -| condition_decls.cpp:48:52:48:53 | Chi: call to BoxedInt | -| misc.c:171:10:171:13 | Uninitialized: definition of str2 | -| misc.c:219:47:219:48 | InitializeIndirection: sp | -| ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x | -| ms_try_mix.cpp:11:12:11:15 | Chi: call to C | -| ms_try_mix.cpp:28:12:28:15 | Chi: call to C | -| ms_try_mix.cpp:48:10:48:13 | Chi: call to C | -| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | -| vla.c:5:9:5:14 | Uninitialized: definition of matrix | -| vla.c:11:6:11:16 | Chi: vla_typedef | +| VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y | Instruction 'InitializeIndirection: y' has no successors in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor(int, int*) | void CallDestructor(int, int*) | +| condition_decls.cpp:16:19:16:20 | Chi: call to BoxedInt | Instruction 'Chi: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:15:6:15:17 | void if_decl_bind(int) | void if_decl_bind(int) | +| condition_decls.cpp:26:23:26:24 | Chi: call to BoxedInt | Instruction 'Chi: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:25:6:25:21 | void switch_decl_bind(int) | void switch_decl_bind(int) | +| condition_decls.cpp:41:22:41:23 | Chi: call to BoxedInt | Instruction 'Chi: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:40:6:40:20 | void while_decl_bind(int) | void while_decl_bind(int) | +| condition_decls.cpp:48:52:48:53 | Chi: call to BoxedInt | Instruction 'Chi: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:47:6:47:18 | void for_decl_bind(int) | void for_decl_bind(int) | +| misc.c:171:10:171:13 | Uninitialized: definition of str2 | Instruction 'Uninitialized: definition of str2' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:219:47:219:48 | InitializeIndirection: sp | Instruction 'InitializeIndirection: sp' has no successors in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x | Instruction 'Uninitialized: definition of x' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_mix.cpp:11:12:11:15 | Chi: call to C | Instruction 'Chi: call to C' has no successors in function '$@'. | ms_try_mix.cpp:10:6:10:18 | void ms_except_mix(int) | void ms_except_mix(int) | +| ms_try_mix.cpp:28:12:28:15 | Chi: call to C | Instruction 'Chi: call to C' has no successors in function '$@'. | ms_try_mix.cpp:27:6:27:19 | void ms_finally_mix(int) | void ms_finally_mix(int) | +| ms_try_mix.cpp:48:10:48:13 | Chi: call to C | Instruction 'Chi: call to C' has no successors in function '$@'. | ms_try_mix.cpp:47:6:47:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() | +| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | stmt_expr.cpp:21:6:21:6 | void stmtexpr::g(int) | void stmtexpr::g(int) | +| vla.c:5:9:5:14 | Uninitialized: definition of matrix | Instruction 'Uninitialized: definition of matrix' has no successors in function '$@'. | vla.c:3:5:3:8 | int main(int, char**) | int main(int, char**) | +| vla.c:11:6:11:16 | Chi: vla_typedef | Instruction 'Chi: vla_typedef' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | ambiguousSuccessors -| allocators.cpp:14:5:14:8 | Chi: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| allocators.cpp:14:5:14:8 | Chi: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| allocators.cpp:14:5:14:8 | Chi: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| allocators.cpp:14:5:14:8 | Chi: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| conditional_destructors.cpp:29:6:29:7 | Chi: f1 | Goto | 2 | conditional_destructors.cpp:30:9:30:13 | FunctionAddress: call to C1 | -| conditional_destructors.cpp:29:6:29:7 | Chi: f1 | Goto | 2 | forstmt.cpp:2:14:2:14 | VariableAddress: definition of i | -| conditional_destructors.cpp:38:6:38:7 | Chi: f2 | Goto | 2 | conditional_destructors.cpp:39:9:39:13 | FunctionAddress: call to C2 | -| conditional_destructors.cpp:38:6:38:7 | Chi: f2 | Goto | 2 | forstmt.cpp:9:14:9:14 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| dostmt.c:8:6:8:18 | Chi: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| dostmt.c:8:6:8:18 | Chi: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| dostmt.c:8:6:8:18 | Chi: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| dostmt.c:8:6:8:18 | Chi: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| dostmt.c:16:6:16:18 | Chi: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| dostmt.c:16:6:16:18 | Chi: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| dostmt.c:16:6:16:18 | Chi: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| dostmt.c:16:6:16:18 | Chi: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| dostmt.c:25:6:25:18 | Chi: always_true_3 | Goto | 2 | dostmt.c:27:5:27:7 | NoOp: label ...: | -| dostmt.c:25:6:25:18 | Chi: always_true_3 | Goto | 2 | whilestmt.c:33:9:33:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| forstmt.cpp:1:6:1:7 | Chi: f1 | Goto | 2 | conditional_destructors.cpp:30:9:30:13 | FunctionAddress: call to C1 | -| forstmt.cpp:1:6:1:7 | Chi: f1 | Goto | 2 | forstmt.cpp:2:14:2:14 | VariableAddress: definition of i | -| forstmt.cpp:8:6:8:7 | Chi: f2 | Goto | 2 | conditional_destructors.cpp:39:9:39:13 | FunctionAddress: call to C2 | -| forstmt.cpp:8:6:8:7 | Chi: f2 | Goto | 2 | forstmt.cpp:9:14:9:14 | VariableAddress: definition of i | -| ifelsestmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| ifelsestmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| ifelsestmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | Chi: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | Chi: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | Chi: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| ifelsestmt.c:19:6:19:18 | Chi: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| ifelsestmt.c:19:6:19:18 | Chi: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| ifelsestmt.c:19:6:19:18 | Chi: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| ifelsestmt.c:19:6:19:18 | Chi: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | Chi: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| ifelsestmt.c:29:6:29:18 | Chi: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | Chi: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | Chi: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | dostmt.c:33:7:33:7 | VariableAddress: definition of i | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | ifelsestmt.c:38:6:38:6 | VariableAddress: x | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | ifstmt.c:28:6:28:6 | VariableAddress: x | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | whilestmt.c:40:7:40:7 | VariableAddress: definition of i | -| ifstmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| ifstmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| ifstmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| ifstmt.c:8:6:8:19 | Chi: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| ifstmt.c:8:6:8:19 | Chi: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| ifstmt.c:8:6:8:19 | Chi: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| ifstmt.c:14:6:14:18 | Chi: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| ifstmt.c:14:6:14:18 | Chi: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| ifstmt.c:14:6:14:18 | Chi: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| ifstmt.c:14:6:14:18 | Chi: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| ifstmt.c:21:6:21:18 | Chi: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| ifstmt.c:21:6:21:18 | Chi: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| ifstmt.c:21:6:21:18 | Chi: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| ifstmt.c:21:6:21:18 | Chi: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | dostmt.c:33:7:33:7 | VariableAddress: definition of i | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | ifelsestmt.c:38:6:38:6 | VariableAddress: x | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | ifstmt.c:28:6:28:6 | VariableAddress: x | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | whilestmt.c:40:7:40:7 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| no_dynamic_init.cpp:9:5:9:8 | Chi: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| no_dynamic_init.cpp:9:5:9:8 | Chi: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| no_dynamic_init.cpp:9:5:9:8 | Chi: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| no_dynamic_init.cpp:9:5:9:8 | Chi: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nonmembercallexpr.c:1:6:1:6 | Chi: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... | -| nonmembercallexpr.c:1:6:1:6 | Chi: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| parameterinitializer.cpp:18:5:18:8 | Chi: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| parameterinitializer.cpp:18:5:18:8 | Chi: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| parameterinitializer.cpp:18:5:18:8 | Chi: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| parameterinitializer.cpp:18:5:18:8 | Chi: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| revsubscriptexpr.c:1:6:1:6 | Chi: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... | -| revsubscriptexpr.c:1:6:1:6 | Chi: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| stream_it.cpp:16:5:16:8 | Chi: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| stream_it.cpp:16:5:16:8 | Chi: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| stream_it.cpp:16:5:16:8 | Chi: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| stream_it.cpp:16:5:16:8 | Chi: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| whilestmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| whilestmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| whilestmt.c:1:6:1:19 | Chi: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| whilestmt.c:8:6:8:19 | Chi: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| whilestmt.c:8:6:8:19 | Chi: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| whilestmt.c:8:6:8:19 | Chi: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| whilestmt.c:15:6:15:18 | Chi: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| whilestmt.c:15:6:15:18 | Chi: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| whilestmt.c:15:6:15:18 | Chi: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| whilestmt.c:15:6:15:18 | Chi: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| whilestmt.c:23:6:23:18 | Chi: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| whilestmt.c:23:6:23:18 | Chi: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| whilestmt.c:23:6:23:18 | Chi: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| whilestmt.c:23:6:23:18 | Chi: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| whilestmt.c:32:6:32:18 | Chi: always_true_3 | Goto | 2 | dostmt.c:27:5:27:7 | NoOp: label ...: | -| whilestmt.c:32:6:32:18 | Chi: always_true_3 | Goto | 2 | whilestmt.c:33:9:33:9 | Constant: 1 | +| allocators.cpp:14:5:14:8 | Chi: main | Instruction 'Chi: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| array_delete.cpp:5:6:5:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| assignexpr.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| break_labels.c:2:11:2:11 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| conditional_destructors.cpp:29:6:29:7 | Chi: f1 | Instruction 'Chi: f1' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:38:6:38:7 | Chi: f2 | Instruction 'Chi: f2' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| constmemberaccess.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| constructorinitializer.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| defconstructornewexpr.cpp:3:6:3:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| defdestructordeleteexpr.cpp:3:6:3:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| deleteexpr.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| dostmt.c:8:6:8:18 | Chi: always_true_1 | Instruction 'Chi: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| dostmt.c:16:6:16:18 | Chi: always_true_2 | Instruction 'Chi: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| dostmt.c:25:6:25:18 | Chi: always_true_3 | Instruction 'Chi: always_true_3' has 2 successors of kind 'Goto' in function '$@'. | dostmt.c:25:6:25:18 | void always_true_3() | void always_true_3() | +| duff.c:2:12:2:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| fieldaccess.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| forstmt.cpp:1:6:1:7 | Chi: f1 | Instruction 'Chi: f1' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| forstmt.cpp:8:6:8:7 | Chi: f2 | Instruction 'Chi: f2' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| ifelsestmt.c:1:6:1:19 | Chi: always_false_1 | Instruction 'Chi: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| ifelsestmt.c:11:6:11:19 | Chi: always_false_2 | Instruction 'Chi: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| ifelsestmt.c:19:6:19:18 | Chi: always_true_1 | Instruction 'Chi: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| ifelsestmt.c:29:6:29:18 | Chi: always_true_2 | Instruction 'Chi: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Instruction 'InitializeParameter: y' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:32:6:32:11 | void normal(int, int) | void normal(int, int) | +| ifstmt.c:1:6:1:19 | Chi: always_false_1 | Instruction 'Chi: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| ifstmt.c:8:6:8:19 | Chi: always_false_2 | Instruction 'Chi: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| ifstmt.c:14:6:14:18 | Chi: always_true_1 | Instruction 'Chi: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| ifstmt.c:21:6:21:18 | Chi: always_true_2 | Instruction 'Chi: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| ifstmt.c:27:24:27:24 | InitializeParameter: y | Instruction 'InitializeParameter: y' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:32:6:32:11 | void normal(int, int) | void normal(int, int) | +| membercallexpr.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| membercallexpr_args.cpp:7:6:7:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| newexpr.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| no_dynamic_init.cpp:9:5:9:8 | Chi: main | Instruction 'Chi: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nonmembercallexpr.c:1:6:1:6 | Chi: g | Instruction 'Chi: g' has 2 successors of kind 'Goto' in function '$@'. | nonmembercallexpr.c:1:6:1:6 | void g(); void g())(); void(* g(); void(* g())() | void g(); void g())(); void(* g(); void(* g())() | +| parameterinitializer.cpp:18:5:18:8 | Chi: main | Instruction 'Chi: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| pmcallexpr.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| revsubscriptexpr.c:1:6:1:6 | Chi: g | Instruction 'Chi: g' has 2 successors of kind 'Goto' in function '$@'. | nonmembercallexpr.c:1:6:1:6 | void g(); void g())(); void(* g(); void(* g())() | void g(); void g())(); void(* g(); void(* g())() | +| staticmembercallexpr.cpp:6:6:6:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| staticmembercallexpr_args.cpp:7:6:7:6 | Chi: f | Instruction 'Chi: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| stream_it.cpp:16:5:16:8 | Chi: main | Instruction 'Chi: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| switchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| whilestmt.c:1:6:1:19 | Chi: always_false_1 | Instruction 'Chi: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| whilestmt.c:8:6:8:19 | Chi: always_false_2 | Instruction 'Chi: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| whilestmt.c:15:6:15:18 | Chi: always_true_1 | Instruction 'Chi: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| whilestmt.c:23:6:23:18 | Chi: always_true_2 | Instruction 'Chi: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| whilestmt.c:32:6:32:18 | Chi: always_true_3 | Instruction 'Chi: always_true_3' has 2 successors of kind 'Goto' in function '$@'. | dostmt.c:25:6:25:18 | void always_true_3() | void always_true_3() | unexplainedLoop unnecessaryPhiInstruction memoryOperandDefinitionIsUnmodeled @@ -516,6 +112,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected index 6b0256c95f58..19478a17a16e 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected @@ -8,26 +8,7 @@ uniqueEnclosingCallable | misc.c:210:24:210:24 | 0 | Node should have one enclosing callable but has 0. | | misc.c:210:24:210:28 | ... + ... | Node should have one enclosing callable but has 0. | | misc.c:210:28:210:28 | 1 | Node should have one enclosing callable but has 0. | -uniqueTypeBound -| bad_asts.cpp:19:10:19:10 | constructor init of field x [post-this] | Node should have one type bound but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field x [pre-this] | Node should have one type bound but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field y [post-this] | Node should have one type bound but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field y [pre-this] | Node should have one type bound but has 0. | -| cpp17.cpp:15:5:15:45 | call to unknown function | Node should have one type bound but has 0. | -| ir.cpp:784:15:784:15 | constructor init of field middlevb2_s [post-this] | Node should have one type bound but has 0. | -| ir.cpp:784:15:784:15 | constructor init of field middlevb2_s [pre-this] | Node should have one type bound but has 0. | -| static_init_templates.cpp:240:7:240:7 | constructor init of field mcc [post-this] | Node should have one type bound but has 0. | -| static_init_templates.cpp:240:7:240:7 | constructor init of field mcc [pre-this] | Node should have one type bound but has 0. | -uniqueTypeRepr -| bad_asts.cpp:19:10:19:10 | constructor init of field x [post-this] | Node should have one type representation but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field x [pre-this] | Node should have one type representation but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field y [post-this] | Node should have one type representation but has 0. | -| bad_asts.cpp:19:10:19:10 | constructor init of field y [pre-this] | Node should have one type representation but has 0. | -| cpp17.cpp:15:5:15:45 | call to unknown function | Node should have one type representation but has 0. | -| ir.cpp:784:15:784:15 | constructor init of field middlevb2_s [post-this] | Node should have one type representation but has 0. | -| ir.cpp:784:15:784:15 | constructor init of field middlevb2_s [pre-this] | Node should have one type representation but has 0. | -| static_init_templates.cpp:240:7:240:7 | constructor init of field mcc [post-this] | Node should have one type representation but has 0. | -| static_init_templates.cpp:240:7:240:7 | constructor init of field mcc [pre-this] | Node should have one type representation but has 0. | +uniqueType uniqueNodeLocation | break_labels.c:2:11:2:11 | i | Node should have one location but has 4. | | break_labels.c:2:11:2:11 | x | Node should have one location but has 4. | @@ -79,7 +60,6 @@ postHasUniquePre uniquePostUpdate postIsInSameCallable reverseRead -storeIsPostUpdate argHasPostUpdate | builtin.cpp:15:31:15:35 | * ... | ArgumentNode is missing PostUpdateNode. | | conditional_destructors.cpp:30:9:30:13 | call to C1 | ArgumentNode is missing PostUpdateNode. | @@ -90,3 +70,56 @@ argHasPostUpdate | destructors.cpp:52:14:52:16 | ref | ArgumentNode is missing PostUpdateNode. | | ir.cpp:623:5:623:5 | r | ArgumentNode is missing PostUpdateNode. | | ir.cpp:625:5:625:5 | s | ArgumentNode is missing PostUpdateNode. | +postWithInFlow +| VacuousDestructorCall.cpp:10:22:10:22 | i [inner post update] | PostUpdateNode should not be the target of local flow. | +| allocators.cpp:4:11:4:13 | m_x [post update] | PostUpdateNode should not be the target of local flow. | +| allocators.cpp:4:17:4:19 | m_y [post update] | PostUpdateNode should not be the target of local flow. | +| assignexpr.cpp:9:4:9:4 | i [post update] | PostUpdateNode should not be the target of local flow. | +| builtin.c:34:23:34:31 | staticint [inner post update] | PostUpdateNode should not be the target of local flow. | +| builtin.c:39:37:39:45 | carry_out [inner post update] | PostUpdateNode should not be the target of local flow. | +| builtin.c:43:41:43:49 | staticint [inner post update] | PostUpdateNode should not be the target of local flow. | +| builtin.c:51:30:51:38 | staticint [inner post update] | PostUpdateNode should not be the target of local flow. | +| builtin.c:54:29:54:38 | atomic_int [inner post update] | PostUpdateNode should not be the target of local flow. | +| condition_decls.cpp:3:5:3:9 | m_ptr [post update] | PostUpdateNode should not be the target of local flow. | +| condition_decls.cpp:17:11:17:15 | m_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| condition_decls.cpp:20:11:20:15 | m_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| condition_decls.cpp:28:11:28:15 | m_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| condition_decls.cpp:31:11:31:15 | m_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| condition_decls.cpp:34:9:34:13 | m_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| conditional_destructors.cpp:6:13:6:15 | val [post update] | PostUpdateNode should not be the target of local flow. | +| conditional_destructors.cpp:18:13:18:15 | val [post update] | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:7:7:7:8 | el [post update] | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:77:19:77:21 | call to Val | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:82:11:82:14 | call to Val | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:82:45:82:48 | call to Val | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:82:51:82:51 | call to Val | PostUpdateNode should not be the target of local flow. | +| ir.cpp:177:5:177:5 | p [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:177:5:177:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:178:5:178:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:178:7:178:7 | p [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:183:5:183:5 | a [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:183:5:183:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:184:5:184:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:184:7:184:7 | a [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:342:5:342:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:342:6:342:6 | p [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:428:8:428:8 | x [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:429:8:429:8 | y [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:643:15:643:17 | m_a [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:644:11:644:14 | this [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:644:17:644:19 | m_a [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:645:9:645:11 | m_a [post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:654:11:654:14 | this [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:745:8:745:8 | base_s [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:754:8:754:8 | middle_s [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:763:8:763:8 | derived_s [inner post update] | PostUpdateNode should not be the target of local flow. | +| ir.cpp:809:7:809:13 | call to Base | PostUpdateNode should not be the target of local flow. | +| ir.cpp:810:7:810:26 | call to Base | PostUpdateNode should not be the target of local flow. | +| ir.cpp:823:7:823:13 | call to Base | PostUpdateNode should not be the target of local flow. | +| ir.cpp:824:7:824:26 | call to Base | PostUpdateNode should not be the target of local flow. | +| misc.c:130:7:130:7 | i [post update] | PostUpdateNode should not be the target of local flow. | +| misc.c:131:9:131:9 | i [post update] | PostUpdateNode should not be the target of local flow. | +| misc.c:220:3:220:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| misc.c:220:4:220:5 | sp [inner post update] | PostUpdateNode should not be the target of local flow. | +| static_init_templates.cpp:3:2:3:4 | ref [post update] | PostUpdateNode should not be the target of local flow. | +| static_init_templates.cpp:21:2:21:4 | val [post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index dd2598dc9f86..fc13d6d8a42e 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -1,51 +1,140 @@ uniqueEnclosingCallable -uniqueTypeBound -uniqueTypeRepr +uniqueType uniqueNodeLocation | aggregateinitializer.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| aggregateinitializer.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | aggregateinitializer.c:1:6:1:6 | Unreached | Node should have one location but has 20. | +| allocators.cpp:14:5:14:8 | Address | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | AliasedDefinition | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | AliasedUse | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | Chi | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | ChiPartial | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | ChiTotal | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | EnterFunction | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | ExitFunction | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | InitializeNonLocal | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Load | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | Phi | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | ReturnValue | Node should have one location but has 4. | +| allocators.cpp:14:5:14:8 | SideEffect | Node should have one location but has 4. | | allocators.cpp:14:5:14:8 | VariableAddress | Node should have one location but has 4. | | array_delete.cpp:5:6:5:6 | AliasedDefinition | Node should have one location but has 14. | | array_delete.cpp:5:6:5:6 | AliasedUse | Node should have one location but has 14. | | array_delete.cpp:5:6:5:6 | Chi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | ChiPartial | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | ChiTotal | Node should have one location but has 14. | | array_delete.cpp:5:6:5:6 | EnterFunction | Node should have one location but has 14. | | array_delete.cpp:5:6:5:6 | ExitFunction | Node should have one location but has 14. | | array_delete.cpp:5:6:5:6 | InitializeNonLocal | Node should have one location but has 14. | | array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | Phi | Node should have one location but has 14. | | array_delete.cpp:5:6:5:6 | ReturnVoid | Node should have one location but has 14. | +| array_delete.cpp:5:6:5:6 | SideEffect | Node should have one location but has 14. | | assignexpr.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | assignexpr.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | assignexpr.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | assignexpr.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | assignexpr.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | assignexpr.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | assignexpr.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| assignexpr.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | | break_labels.c:2:5:2:5 | AliasedDefinition | Node should have one location but has 20. | | break_labels.c:2:5:2:5 | AliasedUse | Node should have one location but has 20. | | break_labels.c:2:5:2:5 | Chi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | ChiPartial | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | ChiTotal | Node should have one location but has 20. | | break_labels.c:2:5:2:5 | EnterFunction | Node should have one location but has 20. | | break_labels.c:2:5:2:5 | ExitFunction | Node should have one location but has 20. | | break_labels.c:2:5:2:5 | InitializeNonLocal | Node should have one location but has 20. | | break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | Phi | Node should have one location but has 20. | | break_labels.c:2:5:2:5 | ReturnVoid | Node should have one location but has 20. | +| break_labels.c:2:5:2:5 | SideEffect | Node should have one location but has 20. | | break_labels.c:2:5:2:5 | Unreached | Node should have one location but has 20. | +| break_labels.c:2:11:2:11 | Address | Node should have one location but has 4. | | break_labels.c:2:11:2:11 | VariableAddress | Node should have one location but has 4. | | break_labels.c:2:11:2:11 | i | Node should have one location but has 4. | | break_labels.c:2:11:2:11 | i | Node should have one location but has 4. | @@ -54,29 +143,56 @@ uniqueNodeLocation | conditional_destructors.cpp:29:6:29:7 | AliasedDefinition | Node should have one location but has 2. | | conditional_destructors.cpp:29:6:29:7 | AliasedUse | Node should have one location but has 2. | | conditional_destructors.cpp:29:6:29:7 | Chi | Node should have one location but has 2. | +| conditional_destructors.cpp:29:6:29:7 | ChiPartial | Node should have one location but has 2. | +| conditional_destructors.cpp:29:6:29:7 | ChiTotal | Node should have one location but has 2. | | conditional_destructors.cpp:29:6:29:7 | EnterFunction | Node should have one location but has 2. | | conditional_destructors.cpp:29:6:29:7 | ExitFunction | Node should have one location but has 2. | | conditional_destructors.cpp:29:6:29:7 | InitializeNonLocal | Node should have one location but has 2. | | conditional_destructors.cpp:29:6:29:7 | Phi | Node should have one location but has 2. | +| conditional_destructors.cpp:29:6:29:7 | Phi | Node should have one location but has 2. | +| conditional_destructors.cpp:29:6:29:7 | Phi | Node should have one location but has 2. | | conditional_destructors.cpp:29:6:29:7 | ReturnVoid | Node should have one location but has 2. | +| conditional_destructors.cpp:29:6:29:7 | SideEffect | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | AliasedDefinition | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | AliasedUse | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | Chi | Node should have one location but has 2. | +| conditional_destructors.cpp:38:6:38:7 | ChiPartial | Node should have one location but has 2. | +| conditional_destructors.cpp:38:6:38:7 | ChiTotal | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | EnterFunction | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | ExitFunction | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | InitializeNonLocal | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | Phi | Node should have one location but has 2. | +| conditional_destructors.cpp:38:6:38:7 | Phi | Node should have one location but has 2. | +| conditional_destructors.cpp:38:6:38:7 | Phi | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | ReturnVoid | Node should have one location but has 2. | +| conditional_destructors.cpp:38:6:38:7 | SideEffect | Node should have one location but has 2. | | conditional_destructors.cpp:38:6:38:7 | Unreached | Node should have one location but has 2. | | constmemberaccess.cpp:3:7:3:7 | x | Node should have one location but has 2. | | constmemberaccess.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | constmemberaccess.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | constmemberaccess.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | constmemberaccess.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | constmemberaccess.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | constmemberaccess.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| constmemberaccess.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | | constructorinitializer.cpp:3:9:3:9 | i | Node should have one location but has 2. | | constructorinitializer.cpp:3:9:3:9 | x | Node should have one location but has 2. | | constructorinitializer.cpp:3:16:3:16 | j | Node should have one location but has 2. | @@ -84,72 +200,173 @@ uniqueNodeLocation | constructorinitializer.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | constructorinitializer.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | constructorinitializer.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | constructorinitializer.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | constructorinitializer.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | constructorinitializer.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| constructorinitializer.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | | defconstructornewexpr.cpp:3:6:3:6 | AliasedDefinition | Node should have one location but has 14. | | defconstructornewexpr.cpp:3:6:3:6 | AliasedUse | Node should have one location but has 14. | | defconstructornewexpr.cpp:3:6:3:6 | Chi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | ChiPartial | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | ChiTotal | Node should have one location but has 14. | | defconstructornewexpr.cpp:3:6:3:6 | EnterFunction | Node should have one location but has 14. | | defconstructornewexpr.cpp:3:6:3:6 | ExitFunction | Node should have one location but has 14. | | defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal | Node should have one location but has 14. | | defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | | defconstructornewexpr.cpp:3:6:3:6 | ReturnVoid | Node should have one location but has 14. | +| defconstructornewexpr.cpp:3:6:3:6 | SideEffect | Node should have one location but has 14. | | defdestructordeleteexpr.cpp:3:6:3:6 | AliasedDefinition | Node should have one location but has 14. | | defdestructordeleteexpr.cpp:3:6:3:6 | AliasedUse | Node should have one location but has 14. | | defdestructordeleteexpr.cpp:3:6:3:6 | Chi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | ChiPartial | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | ChiTotal | Node should have one location but has 14. | | defdestructordeleteexpr.cpp:3:6:3:6 | EnterFunction | Node should have one location but has 14. | | defdestructordeleteexpr.cpp:3:6:3:6 | ExitFunction | Node should have one location but has 14. | | defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal | Node should have one location but has 14. | | defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | Phi | Node should have one location but has 14. | | defdestructordeleteexpr.cpp:3:6:3:6 | ReturnVoid | Node should have one location but has 14. | +| defdestructordeleteexpr.cpp:3:6:3:6 | SideEffect | Node should have one location but has 14. | | deleteexpr.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | deleteexpr.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | deleteexpr.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | deleteexpr.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | deleteexpr.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | deleteexpr.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | deleteexpr.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| deleteexpr.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | | dostmt.c:8:6:8:18 | AliasedDefinition | Node should have one location but has 4. | | dostmt.c:8:6:8:18 | AliasedUse | Node should have one location but has 4. | | dostmt.c:8:6:8:18 | Chi | Node should have one location but has 4. | +| dostmt.c:8:6:8:18 | ChiPartial | Node should have one location but has 4. | +| dostmt.c:8:6:8:18 | ChiTotal | Node should have one location but has 4. | | dostmt.c:8:6:8:18 | EnterFunction | Node should have one location but has 4. | | dostmt.c:8:6:8:18 | ExitFunction | Node should have one location but has 4. | | dostmt.c:8:6:8:18 | InitializeNonLocal | Node should have one location but has 4. | | dostmt.c:8:6:8:18 | ReturnVoid | Node should have one location but has 4. | +| dostmt.c:8:6:8:18 | SideEffect | Node should have one location but has 4. | | dostmt.c:8:6:8:18 | Unreached | Node should have one location but has 4. | | dostmt.c:16:6:16:18 | AliasedDefinition | Node should have one location but has 4. | | dostmt.c:16:6:16:18 | AliasedUse | Node should have one location but has 4. | | dostmt.c:16:6:16:18 | Chi | Node should have one location but has 4. | +| dostmt.c:16:6:16:18 | ChiPartial | Node should have one location but has 4. | +| dostmt.c:16:6:16:18 | ChiTotal | Node should have one location but has 4. | | dostmt.c:16:6:16:18 | EnterFunction | Node should have one location but has 4. | | dostmt.c:16:6:16:18 | ExitFunction | Node should have one location but has 4. | | dostmt.c:16:6:16:18 | InitializeNonLocal | Node should have one location but has 4. | | dostmt.c:16:6:16:18 | ReturnVoid | Node should have one location but has 4. | +| dostmt.c:16:6:16:18 | SideEffect | Node should have one location but has 4. | | dostmt.c:16:6:16:18 | Unreached | Node should have one location but has 4. | | dostmt.c:25:6:25:18 | AliasedDefinition | Node should have one location but has 2. | | dostmt.c:25:6:25:18 | Chi | Node should have one location but has 2. | +| dostmt.c:25:6:25:18 | ChiPartial | Node should have one location but has 2. | +| dostmt.c:25:6:25:18 | ChiTotal | Node should have one location but has 2. | | dostmt.c:25:6:25:18 | EnterFunction | Node should have one location but has 2. | | dostmt.c:25:6:25:18 | InitializeNonLocal | Node should have one location but has 2. | | dostmt.c:25:6:25:18 | Unreached | Node should have one location but has 2. | | dostmt.c:32:6:32:11 | AliasedDefinition | Node should have one location but has 4. | | dostmt.c:32:6:32:11 | AliasedUse | Node should have one location but has 4. | | dostmt.c:32:6:32:11 | Chi | Node should have one location but has 4. | +| dostmt.c:32:6:32:11 | ChiPartial | Node should have one location but has 4. | +| dostmt.c:32:6:32:11 | ChiTotal | Node should have one location but has 4. | | dostmt.c:32:6:32:11 | EnterFunction | Node should have one location but has 4. | | dostmt.c:32:6:32:11 | ExitFunction | Node should have one location but has 4. | | dostmt.c:32:6:32:11 | InitializeNonLocal | Node should have one location but has 4. | | dostmt.c:32:6:32:11 | ReturnVoid | Node should have one location but has 4. | +| dostmt.c:32:6:32:11 | SideEffect | Node should have one location but has 4. | | duff.c:2:6:2:6 | AliasedDefinition | Node should have one location but has 20. | | duff.c:2:6:2:6 | AliasedUse | Node should have one location but has 20. | | duff.c:2:6:2:6 | Chi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | ChiPartial | Node should have one location but has 20. | +| duff.c:2:6:2:6 | ChiTotal | Node should have one location but has 20. | | duff.c:2:6:2:6 | EnterFunction | Node should have one location but has 20. | | duff.c:2:6:2:6 | ExitFunction | Node should have one location but has 20. | | duff.c:2:6:2:6 | InitializeNonLocal | Node should have one location but has 20. | | duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | +| duff.c:2:6:2:6 | Phi | Node should have one location but has 20. | | duff.c:2:6:2:6 | ReturnVoid | Node should have one location but has 20. | +| duff.c:2:6:2:6 | SideEffect | Node should have one location but has 20. | | duff.c:2:6:2:6 | Unreached | Node should have one location but has 20. | +| duff.c:2:12:2:12 | Address | Node should have one location but has 4. | | duff.c:2:12:2:12 | VariableAddress | Node should have one location but has 4. | | duff.c:2:12:2:12 | i | Node should have one location but has 4. | | duff.c:2:12:2:12 | i | Node should have one location but has 4. | @@ -158,51 +375,158 @@ uniqueNodeLocation | dummyblock.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | dummyblock.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | dummyblock.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | dummyblock.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | dummyblock.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | dummyblock.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | | dummyblock.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| dummyblock.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | dummyblock.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | Phi | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| emptyblock.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | emptyblock.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | enum.c:5:5:5:5 | AliasedDefinition | Node should have one location but has 20. | | enum.c:5:5:5:5 | AliasedUse | Node should have one location but has 20. | | enum.c:5:5:5:5 | Chi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | ChiPartial | Node should have one location but has 20. | +| enum.c:5:5:5:5 | ChiTotal | Node should have one location but has 20. | | enum.c:5:5:5:5 | EnterFunction | Node should have one location but has 20. | | enum.c:5:5:5:5 | ExitFunction | Node should have one location but has 20. | | enum.c:5:5:5:5 | InitializeNonLocal | Node should have one location but has 20. | | enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | +| enum.c:5:5:5:5 | Phi | Node should have one location but has 20. | | enum.c:5:5:5:5 | ReturnVoid | Node should have one location but has 20. | +| enum.c:5:5:5:5 | SideEffect | Node should have one location but has 20. | | enum.c:5:5:5:5 | Unreached | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| exprstmt.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | exprstmt.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | fieldaccess.cpp:3:7:3:7 | x | Node should have one location but has 2. | | fieldaccess.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | fieldaccess.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | fieldaccess.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | fieldaccess.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | fieldaccess.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | fieldaccess.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | fieldaccess.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| fieldaccess.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | | file://:0:0:0:0 | *p#2 | Node should have one location but has 0. | +| file://:0:0:0:0 | Address | Node should have one location but has 0. | +| file://:0:0:0:0 | Address | Node should have one location but has 0. | +| file://:0:0:0:0 | Address | Node should have one location but has 0. | +| file://:0:0:0:0 | Address | Node should have one location but has 0. | +| file://:0:0:0:0 | Load | Node should have one location but has 0. | | file://:0:0:0:0 | Load | Node should have one location but has 0. | | file://:0:0:0:0 | ReturnIndirection | Node should have one location but has 0. | +| file://:0:0:0:0 | SideEffect | Node should have one location but has 0. | | file://:0:0:0:0 | VariableAddress | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | @@ -215,8 +539,6 @@ uniqueNodeLocation | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | -| file://:0:0:0:0 | p#0 | Node should have one location but has 0. | -| file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#1 | Node should have one location but has 0. | | file://:0:0:0:0 | p#1 | Node should have one location but has 0. | | file://:0:0:0:0 | p#1 | Node should have one location but has 0. | @@ -233,165 +555,327 @@ uniqueNodeLocation | forstmt.cpp:1:6:1:7 | AliasedDefinition | Node should have one location but has 2. | | forstmt.cpp:1:6:1:7 | AliasedUse | Node should have one location but has 2. | | forstmt.cpp:1:6:1:7 | Chi | Node should have one location but has 2. | +| forstmt.cpp:1:6:1:7 | ChiPartial | Node should have one location but has 2. | +| forstmt.cpp:1:6:1:7 | ChiTotal | Node should have one location but has 2. | | forstmt.cpp:1:6:1:7 | EnterFunction | Node should have one location but has 2. | | forstmt.cpp:1:6:1:7 | ExitFunction | Node should have one location but has 2. | | forstmt.cpp:1:6:1:7 | InitializeNonLocal | Node should have one location but has 2. | | forstmt.cpp:1:6:1:7 | Phi | Node should have one location but has 2. | +| forstmt.cpp:1:6:1:7 | Phi | Node should have one location but has 2. | +| forstmt.cpp:1:6:1:7 | Phi | Node should have one location but has 2. | | forstmt.cpp:1:6:1:7 | ReturnVoid | Node should have one location but has 2. | +| forstmt.cpp:1:6:1:7 | SideEffect | Node should have one location but has 2. | | forstmt.cpp:8:6:8:7 | AliasedDefinition | Node should have one location but has 2. | | forstmt.cpp:8:6:8:7 | AliasedUse | Node should have one location but has 2. | | forstmt.cpp:8:6:8:7 | Chi | Node should have one location but has 2. | +| forstmt.cpp:8:6:8:7 | ChiPartial | Node should have one location but has 2. | +| forstmt.cpp:8:6:8:7 | ChiTotal | Node should have one location but has 2. | | forstmt.cpp:8:6:8:7 | EnterFunction | Node should have one location but has 2. | | forstmt.cpp:8:6:8:7 | ExitFunction | Node should have one location but has 2. | | forstmt.cpp:8:6:8:7 | InitializeNonLocal | Node should have one location but has 2. | | forstmt.cpp:8:6:8:7 | Phi | Node should have one location but has 2. | -| forstmt.cpp:8:6:8:7 | ReturnVoid | Node should have one location but has 2. | +| forstmt.cpp:8:6:8:7 | Phi | Node should have one location but has 2. | +| forstmt.cpp:8:6:8:7 | Phi | Node should have one location but has 2. | +| forstmt.cpp:8:6:8:7 | ReturnVoid | Node should have one location but has 2. | +| forstmt.cpp:8:6:8:7 | SideEffect | Node should have one location but has 2. | | forstmt.cpp:8:6:8:7 | Unreached | Node should have one location but has 2. | | ifelsestmt.c:1:6:1:19 | AliasedDefinition | Node should have one location but has 3. | | ifelsestmt.c:1:6:1:19 | AliasedUse | Node should have one location but has 3. | | ifelsestmt.c:1:6:1:19 | Chi | Node should have one location but has 3. | +| ifelsestmt.c:1:6:1:19 | ChiPartial | Node should have one location but has 3. | +| ifelsestmt.c:1:6:1:19 | ChiTotal | Node should have one location but has 3. | | ifelsestmt.c:1:6:1:19 | EnterFunction | Node should have one location but has 3. | | ifelsestmt.c:1:6:1:19 | ExitFunction | Node should have one location but has 3. | | ifelsestmt.c:1:6:1:19 | InitializeNonLocal | Node should have one location but has 3. | | ifelsestmt.c:1:6:1:19 | ReturnVoid | Node should have one location but has 3. | +| ifelsestmt.c:1:6:1:19 | SideEffect | Node should have one location but has 3. | | ifelsestmt.c:1:6:1:19 | Unreached | Node should have one location but has 3. | | ifelsestmt.c:11:6:11:19 | AliasedDefinition | Node should have one location but has 3. | | ifelsestmt.c:11:6:11:19 | AliasedUse | Node should have one location but has 3. | | ifelsestmt.c:11:6:11:19 | Chi | Node should have one location but has 3. | +| ifelsestmt.c:11:6:11:19 | ChiPartial | Node should have one location but has 3. | +| ifelsestmt.c:11:6:11:19 | ChiTotal | Node should have one location but has 3. | | ifelsestmt.c:11:6:11:19 | EnterFunction | Node should have one location but has 3. | | ifelsestmt.c:11:6:11:19 | ExitFunction | Node should have one location but has 3. | | ifelsestmt.c:11:6:11:19 | InitializeNonLocal | Node should have one location but has 3. | | ifelsestmt.c:11:6:11:19 | ReturnVoid | Node should have one location but has 3. | +| ifelsestmt.c:11:6:11:19 | SideEffect | Node should have one location but has 3. | | ifelsestmt.c:11:6:11:19 | Unreached | Node should have one location but has 3. | | ifelsestmt.c:19:6:19:18 | AliasedDefinition | Node should have one location but has 4. | | ifelsestmt.c:19:6:19:18 | AliasedUse | Node should have one location but has 4. | | ifelsestmt.c:19:6:19:18 | Chi | Node should have one location but has 4. | +| ifelsestmt.c:19:6:19:18 | ChiPartial | Node should have one location but has 4. | +| ifelsestmt.c:19:6:19:18 | ChiTotal | Node should have one location but has 4. | | ifelsestmt.c:19:6:19:18 | EnterFunction | Node should have one location but has 4. | | ifelsestmt.c:19:6:19:18 | ExitFunction | Node should have one location but has 4. | | ifelsestmt.c:19:6:19:18 | InitializeNonLocal | Node should have one location but has 4. | | ifelsestmt.c:19:6:19:18 | ReturnVoid | Node should have one location but has 4. | +| ifelsestmt.c:19:6:19:18 | SideEffect | Node should have one location but has 4. | | ifelsestmt.c:19:6:19:18 | Unreached | Node should have one location but has 4. | | ifelsestmt.c:29:6:29:18 | AliasedDefinition | Node should have one location but has 4. | | ifelsestmt.c:29:6:29:18 | AliasedUse | Node should have one location but has 4. | | ifelsestmt.c:29:6:29:18 | Chi | Node should have one location but has 4. | +| ifelsestmt.c:29:6:29:18 | ChiPartial | Node should have one location but has 4. | +| ifelsestmt.c:29:6:29:18 | ChiTotal | Node should have one location but has 4. | | ifelsestmt.c:29:6:29:18 | EnterFunction | Node should have one location but has 4. | | ifelsestmt.c:29:6:29:18 | ExitFunction | Node should have one location but has 4. | | ifelsestmt.c:29:6:29:18 | InitializeNonLocal | Node should have one location but has 4. | | ifelsestmt.c:29:6:29:18 | ReturnVoid | Node should have one location but has 4. | +| ifelsestmt.c:29:6:29:18 | SideEffect | Node should have one location but has 4. | | ifelsestmt.c:29:6:29:18 | Unreached | Node should have one location but has 4. | | ifelsestmt.c:37:6:37:11 | AliasedDefinition | Node should have one location but has 4. | | ifelsestmt.c:37:6:37:11 | AliasedUse | Node should have one location but has 4. | | ifelsestmt.c:37:6:37:11 | Chi | Node should have one location but has 4. | +| ifelsestmt.c:37:6:37:11 | ChiPartial | Node should have one location but has 4. | +| ifelsestmt.c:37:6:37:11 | ChiTotal | Node should have one location but has 4. | | ifelsestmt.c:37:6:37:11 | EnterFunction | Node should have one location but has 4. | | ifelsestmt.c:37:6:37:11 | ExitFunction | Node should have one location but has 4. | | ifelsestmt.c:37:6:37:11 | InitializeNonLocal | Node should have one location but has 4. | | ifelsestmt.c:37:6:37:11 | ReturnVoid | Node should have one location but has 4. | +| ifelsestmt.c:37:6:37:11 | SideEffect | Node should have one location but has 4. | +| ifelsestmt.c:37:17:37:17 | Address | Node should have one location but has 2. | | ifelsestmt.c:37:17:37:17 | VariableAddress | Node should have one location but has 2. | | ifelsestmt.c:37:17:37:17 | x | Node should have one location but has 2. | | ifelsestmt.c:37:17:37:17 | x | Node should have one location but has 2. | +| ifelsestmt.c:37:24:37:24 | Address | Node should have one location but has 2. | | ifelsestmt.c:37:24:37:24 | VariableAddress | Node should have one location but has 2. | | ifelsestmt.c:37:24:37:24 | y | Node should have one location but has 2. | | ifelsestmt.c:37:24:37:24 | y | Node should have one location but has 2. | | ifstmt.c:1:6:1:19 | AliasedDefinition | Node should have one location but has 3. | | ifstmt.c:1:6:1:19 | AliasedUse | Node should have one location but has 3. | | ifstmt.c:1:6:1:19 | Chi | Node should have one location but has 3. | +| ifstmt.c:1:6:1:19 | ChiPartial | Node should have one location but has 3. | +| ifstmt.c:1:6:1:19 | ChiTotal | Node should have one location but has 3. | | ifstmt.c:1:6:1:19 | EnterFunction | Node should have one location but has 3. | | ifstmt.c:1:6:1:19 | ExitFunction | Node should have one location but has 3. | | ifstmt.c:1:6:1:19 | InitializeNonLocal | Node should have one location but has 3. | | ifstmt.c:1:6:1:19 | ReturnVoid | Node should have one location but has 3. | +| ifstmt.c:1:6:1:19 | SideEffect | Node should have one location but has 3. | | ifstmt.c:1:6:1:19 | Unreached | Node should have one location but has 3. | | ifstmt.c:8:6:8:19 | AliasedDefinition | Node should have one location but has 3. | | ifstmt.c:8:6:8:19 | AliasedUse | Node should have one location but has 3. | | ifstmt.c:8:6:8:19 | Chi | Node should have one location but has 3. | +| ifstmt.c:8:6:8:19 | ChiPartial | Node should have one location but has 3. | +| ifstmt.c:8:6:8:19 | ChiTotal | Node should have one location but has 3. | | ifstmt.c:8:6:8:19 | EnterFunction | Node should have one location but has 3. | | ifstmt.c:8:6:8:19 | ExitFunction | Node should have one location but has 3. | | ifstmt.c:8:6:8:19 | InitializeNonLocal | Node should have one location but has 3. | | ifstmt.c:8:6:8:19 | ReturnVoid | Node should have one location but has 3. | +| ifstmt.c:8:6:8:19 | SideEffect | Node should have one location but has 3. | | ifstmt.c:8:6:8:19 | Unreached | Node should have one location but has 3. | | ifstmt.c:14:6:14:18 | AliasedDefinition | Node should have one location but has 4. | | ifstmt.c:14:6:14:18 | AliasedUse | Node should have one location but has 4. | | ifstmt.c:14:6:14:18 | Chi | Node should have one location but has 4. | +| ifstmt.c:14:6:14:18 | ChiPartial | Node should have one location but has 4. | +| ifstmt.c:14:6:14:18 | ChiTotal | Node should have one location but has 4. | | ifstmt.c:14:6:14:18 | EnterFunction | Node should have one location but has 4. | | ifstmt.c:14:6:14:18 | ExitFunction | Node should have one location but has 4. | | ifstmt.c:14:6:14:18 | InitializeNonLocal | Node should have one location but has 4. | | ifstmt.c:14:6:14:18 | ReturnVoid | Node should have one location but has 4. | +| ifstmt.c:14:6:14:18 | SideEffect | Node should have one location but has 4. | | ifstmt.c:14:6:14:18 | Unreached | Node should have one location but has 4. | | ifstmt.c:21:6:21:18 | AliasedDefinition | Node should have one location but has 4. | | ifstmt.c:21:6:21:18 | AliasedUse | Node should have one location but has 4. | | ifstmt.c:21:6:21:18 | Chi | Node should have one location but has 4. | +| ifstmt.c:21:6:21:18 | ChiPartial | Node should have one location but has 4. | +| ifstmt.c:21:6:21:18 | ChiTotal | Node should have one location but has 4. | | ifstmt.c:21:6:21:18 | EnterFunction | Node should have one location but has 4. | | ifstmt.c:21:6:21:18 | ExitFunction | Node should have one location but has 4. | | ifstmt.c:21:6:21:18 | InitializeNonLocal | Node should have one location but has 4. | | ifstmt.c:21:6:21:18 | ReturnVoid | Node should have one location but has 4. | +| ifstmt.c:21:6:21:18 | SideEffect | Node should have one location but has 4. | | ifstmt.c:21:6:21:18 | Unreached | Node should have one location but has 4. | | ifstmt.c:27:6:27:11 | AliasedDefinition | Node should have one location but has 4. | | ifstmt.c:27:6:27:11 | AliasedUse | Node should have one location but has 4. | | ifstmt.c:27:6:27:11 | Chi | Node should have one location but has 4. | +| ifstmt.c:27:6:27:11 | ChiPartial | Node should have one location but has 4. | +| ifstmt.c:27:6:27:11 | ChiTotal | Node should have one location but has 4. | | ifstmt.c:27:6:27:11 | EnterFunction | Node should have one location but has 4. | | ifstmt.c:27:6:27:11 | ExitFunction | Node should have one location but has 4. | | ifstmt.c:27:6:27:11 | InitializeNonLocal | Node should have one location but has 4. | | ifstmt.c:27:6:27:11 | ReturnVoid | Node should have one location but has 4. | +| ifstmt.c:27:6:27:11 | SideEffect | Node should have one location but has 4. | +| ifstmt.c:27:17:27:17 | Address | Node should have one location but has 2. | | ifstmt.c:27:17:27:17 | VariableAddress | Node should have one location but has 2. | | ifstmt.c:27:17:27:17 | x | Node should have one location but has 2. | | ifstmt.c:27:17:27:17 | x | Node should have one location but has 2. | +| ifstmt.c:27:24:27:24 | Address | Node should have one location but has 2. | | ifstmt.c:27:24:27:24 | VariableAddress | Node should have one location but has 2. | | ifstmt.c:27:24:27:24 | y | Node should have one location but has 2. | | ifstmt.c:27:24:27:24 | y | Node should have one location but has 2. | | initializer.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | initializer.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | initializer.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | initializer.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | initializer.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | initializer.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | Phi | Node should have one location but has 20. | | initializer.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| initializer.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | initializer.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| landexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | landexpr.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| lorexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | lorexpr.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| ltrbinopexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | ltrbinopexpr.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | membercallexpr.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | membercallexpr.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | membercallexpr.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | membercallexpr.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | membercallexpr.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | membercallexpr.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | membercallexpr.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| membercallexpr.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | | membercallexpr_args.cpp:3:6:3:6 | d | Node should have one location but has 2. | | membercallexpr_args.cpp:4:14:4:14 | x | Node should have one location but has 2. | | membercallexpr_args.cpp:4:21:4:21 | y | Node should have one location but has 2. | | membercallexpr_args.cpp:7:6:7:6 | AliasedDefinition | Node should have one location but has 14. | | membercallexpr_args.cpp:7:6:7:6 | AliasedUse | Node should have one location but has 14. | | membercallexpr_args.cpp:7:6:7:6 | Chi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | ChiPartial | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | ChiTotal | Node should have one location but has 14. | | membercallexpr_args.cpp:7:6:7:6 | EnterFunction | Node should have one location but has 14. | | membercallexpr_args.cpp:7:6:7:6 | ExitFunction | Node should have one location but has 14. | | membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal | Node should have one location but has 14. | | membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | | membercallexpr_args.cpp:7:6:7:6 | ReturnVoid | Node should have one location but has 14. | +| membercallexpr_args.cpp:7:6:7:6 | SideEffect | Node should have one location but has 14. | | newexpr.cpp:3:9:3:9 | i | Node should have one location but has 2. | | newexpr.cpp:3:9:3:9 | x | Node should have one location but has 2. | | newexpr.cpp:3:16:3:16 | j | Node should have one location but has 2. | @@ -399,30 +883,82 @@ uniqueNodeLocation | newexpr.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | newexpr.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | newexpr.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | newexpr.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | newexpr.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | newexpr.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | newexpr.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| newexpr.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | +| no_dynamic_init.cpp:9:5:9:8 | Address | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | AliasedDefinition | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | AliasedUse | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | Chi | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | ChiPartial | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | ChiTotal | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | EnterFunction | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | ExitFunction | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Load | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | Phi | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | ReturnValue | Node should have one location but has 4. | +| no_dynamic_init.cpp:9:5:9:8 | SideEffect | Node should have one location but has 4. | | no_dynamic_init.cpp:9:5:9:8 | VariableAddress | Node should have one location but has 4. | | nodefaultswitchstmt.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | nodefaultswitchstmt.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | nodefaultswitchstmt.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | nodefaultswitchstmt.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | nodefaultswitchstmt.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | nodefaultswitchstmt.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | | nodefaultswitchstmt.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | nodefaultswitchstmt.c:1:6:1:6 | Unreached | Node should have one location but has 20. | +| nodefaultswitchstmt.c:1:12:1:12 | Address | Node should have one location but has 4. | | nodefaultswitchstmt.c:1:12:1:12 | VariableAddress | Node should have one location but has 4. | | nodefaultswitchstmt.c:1:12:1:12 | i | Node should have one location but has 4. | | nodefaultswitchstmt.c:1:12:1:12 | i | Node should have one location but has 4. | @@ -431,118 +967,328 @@ uniqueNodeLocation | nonmembercallexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 2. | | nonmembercallexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 2. | | nonmembercallexpr.c:1:6:1:6 | Chi | Node should have one location but has 2. | +| nonmembercallexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 2. | +| nonmembercallexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 2. | | nonmembercallexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 2. | | nonmembercallexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 2. | | nonmembercallexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 2. | | nonmembercallexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 2. | +| nonmembercallexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 2. | | nonmembercallexpr.c:3:6:3:6 | AliasedDefinition | Node should have one location but has 20. | | nonmembercallexpr.c:3:6:3:6 | AliasedUse | Node should have one location but has 20. | | nonmembercallexpr.c:3:6:3:6 | Chi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | ChiPartial | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | ChiTotal | Node should have one location but has 20. | | nonmembercallexpr.c:3:6:3:6 | EnterFunction | Node should have one location but has 20. | | nonmembercallexpr.c:3:6:3:6 | ExitFunction | Node should have one location but has 20. | | nonmembercallexpr.c:3:6:3:6 | InitializeNonLocal | Node should have one location but has 20. | | nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | | nonmembercallexpr.c:3:6:3:6 | ReturnVoid | Node should have one location but has 20. | +| nonmembercallexpr.c:3:6:3:6 | SideEffect | Node should have one location but has 20. | | nonmembercallexpr.c:3:6:3:6 | Unreached | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | AliasedDefinition | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | AliasedUse | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | Chi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | ChiPartial | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | ChiTotal | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | EnterFunction | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | ExitFunction | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | InitializeNonLocal | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | Phi | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | ReturnVoid | Node should have one location but has 20. | +| nonmemberfp2callexpr.c:3:6:3:6 | SideEffect | Node should have one location but has 20. | | nonmemberfp2callexpr.c:3:6:3:6 | Unreached | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| nonmemberfpcallexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | nonmemberfpcallexpr.c:1:6:1:6 | Unreached | Node should have one location but has 20. | +| parameterinitializer.cpp:18:5:18:8 | Address | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | AliasedDefinition | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | AliasedUse | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | Chi | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | ChiPartial | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | ChiTotal | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | EnterFunction | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | ExitFunction | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Load | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | Phi | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | ReturnValue | Node should have one location but has 4. | +| parameterinitializer.cpp:18:5:18:8 | SideEffect | Node should have one location but has 4. | | parameterinitializer.cpp:18:5:18:8 | VariableAddress | Node should have one location but has 4. | | pmcallexpr.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | pmcallexpr.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | pmcallexpr.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | pmcallexpr.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | pmcallexpr.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | pmcallexpr.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| pmcallexpr.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | | questionexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | questionexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | questionexpr.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | questionexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | questionexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | questionexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | | questionexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| questionexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | questionexpr.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | revsubscriptexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 2. | | revsubscriptexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 2. | | revsubscriptexpr.c:1:6:1:6 | Chi | Node should have one location but has 2. | +| revsubscriptexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 2. | +| revsubscriptexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 2. | | revsubscriptexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 2. | | revsubscriptexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 2. | | revsubscriptexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 2. | | revsubscriptexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 2. | +| revsubscriptexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 2. | | staticmembercallexpr.cpp:6:6:6:6 | AliasedDefinition | Node should have one location but has 14. | | staticmembercallexpr.cpp:6:6:6:6 | AliasedUse | Node should have one location but has 14. | | staticmembercallexpr.cpp:6:6:6:6 | Chi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | ChiPartial | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | ChiTotal | Node should have one location but has 14. | | staticmembercallexpr.cpp:6:6:6:6 | EnterFunction | Node should have one location but has 14. | | staticmembercallexpr.cpp:6:6:6:6 | ExitFunction | Node should have one location but has 14. | | staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal | Node should have one location but has 14. | | staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | Phi | Node should have one location but has 14. | | staticmembercallexpr.cpp:6:6:6:6 | ReturnVoid | Node should have one location but has 14. | +| staticmembercallexpr.cpp:6:6:6:6 | SideEffect | Node should have one location but has 14. | | staticmembercallexpr_args.cpp:3:6:3:6 | d | Node should have one location but has 2. | | staticmembercallexpr_args.cpp:4:21:4:21 | x | Node should have one location but has 2. | | staticmembercallexpr_args.cpp:4:28:4:28 | y | Node should have one location but has 2. | | staticmembercallexpr_args.cpp:7:6:7:6 | AliasedDefinition | Node should have one location but has 14. | | staticmembercallexpr_args.cpp:7:6:7:6 | AliasedUse | Node should have one location but has 14. | | staticmembercallexpr_args.cpp:7:6:7:6 | Chi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | ChiPartial | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | ChiTotal | Node should have one location but has 14. | | staticmembercallexpr_args.cpp:7:6:7:6 | EnterFunction | Node should have one location but has 14. | | staticmembercallexpr_args.cpp:7:6:7:6 | ExitFunction | Node should have one location but has 14. | | staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal | Node should have one location but has 14. | | staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | Phi | Node should have one location but has 14. | | staticmembercallexpr_args.cpp:7:6:7:6 | ReturnVoid | Node should have one location but has 14. | +| staticmembercallexpr_args.cpp:7:6:7:6 | SideEffect | Node should have one location but has 14. | +| stream_it.cpp:16:5:16:8 | Address | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | AliasedDefinition | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | AliasedUse | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | Chi | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | ChiPartial | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | ChiTotal | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | EnterFunction | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | ExitFunction | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | InitializeNonLocal | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Load | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | Phi | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | ReturnValue | Node should have one location but has 4. | +| stream_it.cpp:16:5:16:8 | SideEffect | Node should have one location but has 4. | | stream_it.cpp:16:5:16:8 | VariableAddress | Node should have one location but has 4. | | subscriptexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | subscriptexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | subscriptexpr.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | subscriptexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | subscriptexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | subscriptexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | | subscriptexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| subscriptexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | subscriptexpr.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| switchstmt.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | switchstmt.c:1:6:1:6 | Unreached | Node should have one location but has 20. | +| switchstmt.c:1:12:1:12 | Address | Node should have one location but has 4. | | switchstmt.c:1:12:1:12 | VariableAddress | Node should have one location but has 4. | | switchstmt.c:1:12:1:12 | i | Node should have one location but has 4. | | switchstmt.c:1:12:1:12 | i | Node should have one location but has 4. | @@ -551,67 +1297,126 @@ uniqueNodeLocation | tinyforstmt.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | tinyforstmt.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | tinyforstmt.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | tinyforstmt.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | tinyforstmt.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | tinyforstmt.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | Phi | Node should have one location but has 20. | | tinyforstmt.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| tinyforstmt.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | tinyforstmt.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | AliasedUse | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | Chi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | ChiPartial | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | ChiTotal | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | EnterFunction | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | ExitFunction | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | InitializeNonLocal | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | Phi | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | ReturnVoid | Node should have one location but has 20. | +| unaryopexpr.c:1:6:1:6 | SideEffect | Node should have one location but has 20. | | unaryopexpr.c:1:6:1:6 | Unreached | Node should have one location but has 20. | | whilestmt.c:1:6:1:19 | AliasedDefinition | Node should have one location but has 3. | | whilestmt.c:1:6:1:19 | AliasedUse | Node should have one location but has 3. | | whilestmt.c:1:6:1:19 | Chi | Node should have one location but has 3. | +| whilestmt.c:1:6:1:19 | ChiPartial | Node should have one location but has 3. | +| whilestmt.c:1:6:1:19 | ChiTotal | Node should have one location but has 3. | | whilestmt.c:1:6:1:19 | EnterFunction | Node should have one location but has 3. | | whilestmt.c:1:6:1:19 | ExitFunction | Node should have one location but has 3. | | whilestmt.c:1:6:1:19 | InitializeNonLocal | Node should have one location but has 3. | | whilestmt.c:1:6:1:19 | ReturnVoid | Node should have one location but has 3. | +| whilestmt.c:1:6:1:19 | SideEffect | Node should have one location but has 3. | | whilestmt.c:1:6:1:19 | Unreached | Node should have one location but has 3. | | whilestmt.c:8:6:8:19 | AliasedDefinition | Node should have one location but has 3. | | whilestmt.c:8:6:8:19 | AliasedUse | Node should have one location but has 3. | | whilestmt.c:8:6:8:19 | Chi | Node should have one location but has 3. | +| whilestmt.c:8:6:8:19 | ChiPartial | Node should have one location but has 3. | +| whilestmt.c:8:6:8:19 | ChiTotal | Node should have one location but has 3. | | whilestmt.c:8:6:8:19 | EnterFunction | Node should have one location but has 3. | | whilestmt.c:8:6:8:19 | ExitFunction | Node should have one location but has 3. | | whilestmt.c:8:6:8:19 | InitializeNonLocal | Node should have one location but has 3. | | whilestmt.c:8:6:8:19 | ReturnVoid | Node should have one location but has 3. | +| whilestmt.c:8:6:8:19 | SideEffect | Node should have one location but has 3. | | whilestmt.c:8:6:8:19 | Unreached | Node should have one location but has 3. | | whilestmt.c:15:6:15:18 | AliasedDefinition | Node should have one location but has 4. | | whilestmt.c:15:6:15:18 | AliasedUse | Node should have one location but has 4. | | whilestmt.c:15:6:15:18 | Chi | Node should have one location but has 4. | +| whilestmt.c:15:6:15:18 | ChiPartial | Node should have one location but has 4. | +| whilestmt.c:15:6:15:18 | ChiTotal | Node should have one location but has 4. | | whilestmt.c:15:6:15:18 | EnterFunction | Node should have one location but has 4. | | whilestmt.c:15:6:15:18 | ExitFunction | Node should have one location but has 4. | | whilestmt.c:15:6:15:18 | InitializeNonLocal | Node should have one location but has 4. | | whilestmt.c:15:6:15:18 | ReturnVoid | Node should have one location but has 4. | +| whilestmt.c:15:6:15:18 | SideEffect | Node should have one location but has 4. | | whilestmt.c:15:6:15:18 | Unreached | Node should have one location but has 4. | | whilestmt.c:23:6:23:18 | AliasedDefinition | Node should have one location but has 4. | | whilestmt.c:23:6:23:18 | AliasedUse | Node should have one location but has 4. | | whilestmt.c:23:6:23:18 | Chi | Node should have one location but has 4. | +| whilestmt.c:23:6:23:18 | ChiPartial | Node should have one location but has 4. | +| whilestmt.c:23:6:23:18 | ChiTotal | Node should have one location but has 4. | | whilestmt.c:23:6:23:18 | EnterFunction | Node should have one location but has 4. | | whilestmt.c:23:6:23:18 | ExitFunction | Node should have one location but has 4. | | whilestmt.c:23:6:23:18 | InitializeNonLocal | Node should have one location but has 4. | | whilestmt.c:23:6:23:18 | ReturnVoid | Node should have one location but has 4. | +| whilestmt.c:23:6:23:18 | SideEffect | Node should have one location but has 4. | | whilestmt.c:23:6:23:18 | Unreached | Node should have one location but has 4. | | whilestmt.c:32:6:32:18 | AliasedDefinition | Node should have one location but has 2. | | whilestmt.c:32:6:32:18 | Chi | Node should have one location but has 2. | +| whilestmt.c:32:6:32:18 | ChiPartial | Node should have one location but has 2. | +| whilestmt.c:32:6:32:18 | ChiTotal | Node should have one location but has 2. | | whilestmt.c:32:6:32:18 | EnterFunction | Node should have one location but has 2. | | whilestmt.c:32:6:32:18 | InitializeNonLocal | Node should have one location but has 2. | | whilestmt.c:32:6:32:18 | Unreached | Node should have one location but has 2. | | whilestmt.c:39:6:39:11 | AliasedDefinition | Node should have one location but has 4. | | whilestmt.c:39:6:39:11 | AliasedUse | Node should have one location but has 4. | | whilestmt.c:39:6:39:11 | Chi | Node should have one location but has 4. | +| whilestmt.c:39:6:39:11 | ChiPartial | Node should have one location but has 4. | +| whilestmt.c:39:6:39:11 | ChiTotal | Node should have one location but has 4. | | whilestmt.c:39:6:39:11 | EnterFunction | Node should have one location but has 4. | | whilestmt.c:39:6:39:11 | ExitFunction | Node should have one location but has 4. | | whilestmt.c:39:6:39:11 | InitializeNonLocal | Node should have one location but has 4. | | whilestmt.c:39:6:39:11 | ReturnVoid | Node should have one location but has 4. | +| whilestmt.c:39:6:39:11 | SideEffect | Node should have one location but has 4. | missingLocation -| Nodes without location: 30 | +| Nodes without location: 34 | uniqueNodeToString | break_labels.c:2:11:2:11 | i | Node should have one toString but has 2. | | break_labels.c:2:11:2:11 | i | Node should have one toString but has 2. | @@ -666,5 +1471,93 @@ postHasUniquePre uniquePostUpdate postIsInSameCallable reverseRead -storeIsPostUpdate argHasPostUpdate +postWithInFlow +| aggregateinitializer.c:3:14:3:18 | Chi | PostUpdateNode should not be the target of local flow. | +| aggregateinitializer.c:3:21:3:25 | Chi | PostUpdateNode should not be the target of local flow. | +| allocators.cpp:3:27:3:27 | Chi | PostUpdateNode should not be the target of local flow. | +| allocators.cpp:3:35:3:35 | Chi | PostUpdateNode should not be the target of local flow. | +| allocators.cpp:4:11:4:23 | Chi | PostUpdateNode should not be the target of local flow. | +| allocators.cpp:4:17:4:23 | Chi | PostUpdateNode should not be the target of local flow. | +| assignexpr.cpp:9:2:9:12 | Store | PostUpdateNode should not be the target of local flow. | +| bad_asts.cpp:15:10:15:12 | Store | PostUpdateNode should not be the target of local flow. | +| builtin.c:14:26:14:26 | Chi | PostUpdateNode should not be the target of local flow. | +| builtin.c:14:29:14:29 | Chi | PostUpdateNode should not be the target of local flow. | +| builtin.c:14:32:14:32 | Chi | PostUpdateNode should not be the target of local flow. | +| builtin.c:14:35:14:35 | Chi | PostUpdateNode should not be the target of local flow. | +| condition_decls.cpp:3:5:3:22 | Chi | PostUpdateNode should not be the target of local flow. | +| condition_decls.cpp:3:21:3:21 | Chi | PostUpdateNode should not be the target of local flow. | +| conditional_destructors.cpp:6:13:6:19 | Chi | PostUpdateNode should not be the target of local flow. | +| conditional_destructors.cpp:18:13:18:19 | Chi | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:65:19:65:45 | Store | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:82:17:82:55 | Chi | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:82:45:82:48 | Chi | PostUpdateNode should not be the target of local flow. | +| defdestructordeleteexpr.cpp:4:9:4:15 | Chi | PostUpdateNode should not be the target of local flow. | +| deleteexpr.cpp:7:9:7:15 | Chi | PostUpdateNode should not be the target of local flow. | +| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | +| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | +| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:177:5:177:12 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:178:5:178:12 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:183:5:183:12 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:184:5:184:12 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:342:5:342:10 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:428:5:428:12 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:429:5:429:15 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:504:19:504:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:504:22:504:22 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:505:16:505:21 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:505:19:505:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:506:16:506:18 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:506:16:506:18 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:513:14:513:16 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:513:14:513:16 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:514:14:514:26 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:514:19:514:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:514:22:514:22 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:515:19:515:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:515:22:515:22 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:515:29:515:29 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:515:32:515:32 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:516:17:516:21 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:516:19:516:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:516:24:516:28 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:516:26:516:26 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:521:19:521:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:521:22:521:22 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:521:25:521:25 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:522:16:522:21 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:522:19:522:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:531:14:531:14 | Store | PostUpdateNode should not be the target of local flow. | +| ir.cpp:577:16:577:21 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:577:19:577:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:578:19:578:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:578:22:578:22 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:579:16:579:21 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:579:19:579:19 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:643:9:643:21 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:644:9:644:23 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:645:9:645:15 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:659:9:659:14 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:660:13:660:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:661:9:661:13 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:943:3:943:11 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:947:3:947:25 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:962:17:962:47 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:962:17:962:47 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:962:17:962:47 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:962:26:962:30 | Chi | PostUpdateNode should not be the target of local flow. | +| ir.cpp:962:41:962:45 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:130:5:130:11 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:131:5:131:13 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:154:32:154:32 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:154:35:154:35 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:154:40:154:40 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:154:43:154:43 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:157:14:157:18 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:158:14:158:18 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:160:31:160:33 | Chi | PostUpdateNode should not be the target of local flow. | +| misc.c:160:31:160:33 | Chi | PostUpdateNode should not be the target of local flow. | +| range_analysis.c:102:5:102:15 | Chi | PostUpdateNode should not be the target of local flow. | +| static_init_templates.cpp:3:2:3:8 | Chi | PostUpdateNode should not be the target of local flow. | +| static_init_templates.cpp:21:2:21:12 | Chi | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected index 7039bed7dd7d..61da658e201c 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected @@ -1,40 +1,32 @@ missingOperand -| condition_decls.cpp:16:6:16:20 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) | -| condition_decls.cpp:26:10:26:24 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:41:9:41:23 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | condition_decls.cpp:40:6:40:20 | IR: while_decl_bind | void while_decl_bind(int) | -| condition_decls.cpp:48:39:48:53 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | condition_decls.cpp:47:6:47:18 | IR: for_decl_bind | void for_decl_bind(int) | -| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| cpp11.cpp:77:19:77:21 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:76:8:76:8 | IR: apply | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | -| cpp11.cpp:82:11:82:14 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:81:8:81:8 | IR: apply2 | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | -| cpp11.cpp:82:45:82:48 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | IR: operator() | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | -| cpp11.cpp:82:51:82:51 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | IR: operator() | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | -| cpp11.cpp:88:25:88:30 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | IR: main | void lambda::main() | -| cpp11.cpp:88:33:88:38 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | IR: main | void lambda::main() | -| destructors.cpp:51:36:51:38 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | destructors.cpp:49:7:49:7 | IR: f | int cond_destruct::f(int) | -| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| misc.c:125:5:125:11 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:97:6:97:10 | IR: misc3 | void misc3() | -| misc.c:220:3:223:3 | Store: ... = ... | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | -| misc.c:220:9:223:3 | FieldAddress: {...} | Instruction 'FieldAddress' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | -| misc.c:220:9:223:3 | FieldAddress: {...} | Instruction 'FieldAddress' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | -| try_catch.cpp:23:5:23:18 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | try_catch.cpp:19:6:19:23 | IR: throw_from_nonstmt | void throw_from_nonstmt(int) | +| condition_decls.cpp:16:6:16:20 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | condition_decls.cpp:15:6:15:17 | void if_decl_bind(int) | void if_decl_bind(int) | +| condition_decls.cpp:26:10:26:24 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | condition_decls.cpp:25:6:25:21 | void switch_decl_bind(int) | void switch_decl_bind(int) | +| condition_decls.cpp:41:9:41:23 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | condition_decls.cpp:40:6:40:20 | void while_decl_bind(int) | void while_decl_bind(int) | +| condition_decls.cpp:48:39:48:53 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | condition_decls.cpp:47:6:47:18 | void for_decl_bind(int) | void for_decl_bind(int) | +| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| cpp11.cpp:77:19:77:21 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:76:8:76:8 | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | +| cpp11.cpp:82:11:82:14 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:81:8:81:8 | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | +| cpp11.cpp:82:45:82:48 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | +| cpp11.cpp:82:51:82:51 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | +| cpp11.cpp:88:25:88:30 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | void lambda::main() | void lambda::main() | +| cpp11.cpp:88:33:88:38 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | void lambda::main() | void lambda::main() | +| destructors.cpp:51:36:51:38 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | destructors.cpp:49:7:49:7 | int cond_destruct::f(int) | int cond_destruct::f(int) | +| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| misc.c:125:5:125:11 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:97:6:97:10 | void misc3() | void misc3() | +| misc.c:220:3:223:3 | Store: ... = ... | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| misc.c:220:9:223:3 | FieldAddress: {...} | Instruction 'FieldAddress' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| misc.c:220:9:223:3 | FieldAddress: {...} | Instruction 'FieldAddress' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| try_catch.cpp:23:5:23:18 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | try_catch.cpp:19:6:19:23 | void throw_from_nonstmt(int) | void throw_from_nonstmt(int) | unexpectedOperand duplicateOperand missingPhiOperand @@ -42,519 +34,121 @@ missingOperandType duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor -| CPP-309.cpp:7:5:7:20 | InitializeDynamicAllocation: new[] | -| VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y | -| VacuousDestructorCall.cpp:3:3:3:3 | VariableAddress: x | -| VacuousDestructorCall.cpp:4:3:4:3 | Load: y | -| condition_decls.cpp:16:19:16:20 | IndirectMayWriteSideEffect: call to BoxedInt | -| condition_decls.cpp:26:19:26:20 | IndirectMayWriteSideEffect: bi | -| condition_decls.cpp:26:23:26:24 | IndirectMayWriteSideEffect: call to BoxedInt | -| condition_decls.cpp:41:22:41:23 | IndirectMayWriteSideEffect: call to BoxedInt | -| condition_decls.cpp:48:52:48:53 | IndirectMayWriteSideEffect: call to BoxedInt | -| cpp17.cpp:15:5:15:45 | InitializeDynamicAllocation: new | -| enum.c:6:9:6:9 | Constant: (int)... | -| file://:0:0:0:0 | CompareNE: (bool)... | -| file://:0:0:0:0 | CompareNE: (bool)... | -| file://:0:0:0:0 | CompareNE: (bool)... | -| misc.c:171:10:171:13 | Uninitialized: definition of str2 | -| misc.c:171:15:171:31 | Add: ... + ... | -| misc.c:173:14:173:26 | Mul: ... * ... | -| misc.c:173:37:173:39 | Store: array to pointer conversion | -| misc.c:174:17:174:22 | CallSideEffect: call to getInt | -| misc.c:174:30:174:35 | CallSideEffect: call to getInt | -| misc.c:174:55:174:60 | Store: (char ****)... | -| misc.c:219:47:219:48 | InitializeIndirection: sp | -| misc.c:221:10:221:10 | Store: 1 | -| misc.c:222:10:222:10 | Store: 2 | -| ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x | -| ms_try_except.cpp:7:13:7:17 | Store: ... = ... | -| ms_try_except.cpp:9:19:9:19 | Load: j | -| ms_try_except.cpp:10:13:10:17 | Store: ... = ... | -| ms_try_except.cpp:14:13:14:17 | Store: ... = ... | -| ms_try_except.cpp:17:13:17:17 | Store: ... = ... | -| ms_try_except.cpp:19:17:19:21 | Sub: ... - ... | -| ms_try_except.cpp:20:9:20:13 | Store: ... = ... | -| ms_try_mix.cpp:11:12:11:15 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:16:13:16:19 | ThrowValue: throw ... | -| ms_try_mix.cpp:18:16:18:19 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:20:15:20:39 | Constant: 1 | -| ms_try_mix.cpp:21:16:21:19 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:28:12:28:15 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:33:13:33:19 | ThrowValue: throw ... | -| ms_try_mix.cpp:35:16:35:19 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:38:16:38:19 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:48:10:48:13 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:51:5:51:11 | ThrowValue: throw ... | -| ms_try_mix.cpp:53:13:54:3 | NoOp: { ... } | -| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | -| stmt_expr.cpp:29:11:32:11 | CopyValue: (statement expression) | -| stmt_in_type.cpp:5:53:5:53 | Constant: 1 | -| vla.c:5:9:5:14 | Uninitialized: definition of matrix | -| vla.c:5:16:5:19 | Load: argc | -| vla.c:5:27:5:33 | BufferReadSideEffect: (const char *)... | -| vla.c:11:6:11:16 | InitializeNonLocal: vla_typedef | -| vla.c:12:33:12:44 | Add: ... + ... | -| vla.c:12:50:12:62 | Mul: ... * ... | -| vla.c:13:12:13:14 | Uninitialized: definition of var | -| vla.c:14:36:14:47 | Add: ... + ... | -| vla.c:14:53:14:65 | Mul: ... * ... | -| vla.c:14:74:14:79 | CallSideEffect: call to getInt | -| vla.c:14:92:14:94 | Store: (char *)... | +| VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y | Instruction 'InitializeIndirection: y' has no successors in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor(int, int*) | void CallDestructor(int, int*) | +| VacuousDestructorCall.cpp:3:3:3:3 | VariableAddress: x | Instruction 'VariableAddress: x' has no successors in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor(int, int*) | void CallDestructor(int, int*) | +| VacuousDestructorCall.cpp:4:3:4:3 | Load: y | Instruction 'Load: y' has no successors in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor(int, int*) | void CallDestructor(int, int*) | +| condition_decls.cpp:16:19:16:20 | IndirectMayWriteSideEffect: call to BoxedInt | Instruction 'IndirectMayWriteSideEffect: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:15:6:15:17 | void if_decl_bind(int) | void if_decl_bind(int) | +| condition_decls.cpp:26:19:26:20 | IndirectMayWriteSideEffect: bi | Instruction 'IndirectMayWriteSideEffect: bi' has no successors in function '$@'. | condition_decls.cpp:25:6:25:21 | void switch_decl_bind(int) | void switch_decl_bind(int) | +| condition_decls.cpp:26:23:26:24 | IndirectMayWriteSideEffect: call to BoxedInt | Instruction 'IndirectMayWriteSideEffect: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:25:6:25:21 | void switch_decl_bind(int) | void switch_decl_bind(int) | +| condition_decls.cpp:41:22:41:23 | IndirectMayWriteSideEffect: call to BoxedInt | Instruction 'IndirectMayWriteSideEffect: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:40:6:40:20 | void while_decl_bind(int) | void while_decl_bind(int) | +| condition_decls.cpp:48:52:48:53 | IndirectMayWriteSideEffect: call to BoxedInt | Instruction 'IndirectMayWriteSideEffect: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:47:6:47:18 | void for_decl_bind(int) | void for_decl_bind(int) | +| enum.c:6:9:6:9 | Constant: (int)... | Instruction 'Constant: (int)...' has no successors in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| file://:0:0:0:0 | CompareNE: (bool)... | Instruction 'CompareNE: (bool)...' has no successors in function '$@'. | condition_decls.cpp:15:6:15:17 | void if_decl_bind(int) | void if_decl_bind(int) | +| file://:0:0:0:0 | CompareNE: (bool)... | Instruction 'CompareNE: (bool)...' has no successors in function '$@'. | condition_decls.cpp:40:6:40:20 | void while_decl_bind(int) | void while_decl_bind(int) | +| file://:0:0:0:0 | CompareNE: (bool)... | Instruction 'CompareNE: (bool)...' has no successors in function '$@'. | condition_decls.cpp:47:6:47:18 | void for_decl_bind(int) | void for_decl_bind(int) | +| misc.c:171:10:171:13 | Uninitialized: definition of str2 | Instruction 'Uninitialized: definition of str2' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:171:15:171:31 | Add: ... + ... | Instruction 'Add: ... + ...' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:173:14:173:26 | Mul: ... * ... | Instruction 'Mul: ... * ...' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:173:37:173:39 | Store: array to pointer conversion | Instruction 'Store: array to pointer conversion' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:174:17:174:22 | CallSideEffect: call to getInt | Instruction 'CallSideEffect: call to getInt' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:174:30:174:35 | CallSideEffect: call to getInt | Instruction 'CallSideEffect: call to getInt' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:174:55:174:60 | Store: (char ****)... | Instruction 'Store: (char ****)...' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:219:47:219:48 | InitializeIndirection: sp | Instruction 'InitializeIndirection: sp' has no successors in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| misc.c:221:10:221:10 | Store: 1 | Instruction 'Store: 1' has no successors in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| misc.c:222:10:222:10 | Store: 2 | Instruction 'Store: 2' has no successors in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x | Instruction 'Uninitialized: definition of x' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:7:13:7:17 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:9:19:9:19 | Load: j | Instruction 'Load: j' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:10:13:10:17 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:14:13:14:17 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:17:13:17:17 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:19:17:19:21 | Sub: ... - ... | Instruction 'Sub: ... - ...' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_except.cpp:20:9:20:13 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_mix.cpp:11:12:11:15 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:10:6:10:18 | void ms_except_mix(int) | void ms_except_mix(int) | +| ms_try_mix.cpp:16:13:16:19 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:10:6:10:18 | void ms_except_mix(int) | void ms_except_mix(int) | +| ms_try_mix.cpp:18:16:18:19 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:10:6:10:18 | void ms_except_mix(int) | void ms_except_mix(int) | +| ms_try_mix.cpp:20:15:20:39 | Constant: 1 | Instruction 'Constant: 1' has no successors in function '$@'. | ms_try_mix.cpp:10:6:10:18 | void ms_except_mix(int) | void ms_except_mix(int) | +| ms_try_mix.cpp:21:16:21:19 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:10:6:10:18 | void ms_except_mix(int) | void ms_except_mix(int) | +| ms_try_mix.cpp:28:12:28:15 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:27:6:27:19 | void ms_finally_mix(int) | void ms_finally_mix(int) | +| ms_try_mix.cpp:33:13:33:19 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:27:6:27:19 | void ms_finally_mix(int) | void ms_finally_mix(int) | +| ms_try_mix.cpp:35:16:35:19 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:27:6:27:19 | void ms_finally_mix(int) | void ms_finally_mix(int) | +| ms_try_mix.cpp:38:16:38:19 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:27:6:27:19 | void ms_finally_mix(int) | void ms_finally_mix(int) | +| ms_try_mix.cpp:48:10:48:13 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:47:6:47:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() | +| ms_try_mix.cpp:51:5:51:11 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:47:6:47:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() | +| ms_try_mix.cpp:53:13:54:3 | NoOp: { ... } | Instruction 'NoOp: { ... }' has no successors in function '$@'. | ms_try_mix.cpp:47:6:47:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() | +| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | stmt_expr.cpp:21:6:21:6 | void stmtexpr::g(int) | void stmtexpr::g(int) | +| stmt_expr.cpp:29:11:32:11 | CopyValue: (statement expression) | Instruction 'CopyValue: (statement expression)' has no successors in function '$@'. | stmt_expr.cpp:21:6:21:6 | void stmtexpr::g(int) | void stmtexpr::g(int) | +| stmt_in_type.cpp:5:53:5:53 | Constant: 1 | Instruction 'Constant: 1' has no successors in function '$@'. | stmt_in_type.cpp:2:6:2:12 | void cpp_fun() | void cpp_fun() | +| vla.c:5:9:5:14 | Uninitialized: definition of matrix | Instruction 'Uninitialized: definition of matrix' has no successors in function '$@'. | vla.c:3:5:3:8 | int main(int, char**) | int main(int, char**) | +| vla.c:5:16:5:19 | Load: argc | Instruction 'Load: argc' has no successors in function '$@'. | vla.c:3:5:3:8 | int main(int, char**) | int main(int, char**) | +| vla.c:5:27:5:33 | BufferReadSideEffect: (const char *)... | Instruction 'BufferReadSideEffect: (const char *)...' has no successors in function '$@'. | vla.c:3:5:3:8 | int main(int, char**) | int main(int, char**) | +| vla.c:11:6:11:16 | InitializeNonLocal: vla_typedef | Instruction 'InitializeNonLocal: vla_typedef' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | +| vla.c:12:33:12:44 | Add: ... + ... | Instruction 'Add: ... + ...' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | +| vla.c:12:50:12:62 | Mul: ... * ... | Instruction 'Mul: ... * ...' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | +| vla.c:13:12:13:14 | Uninitialized: definition of var | Instruction 'Uninitialized: definition of var' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | +| vla.c:14:36:14:47 | Add: ... + ... | Instruction 'Add: ... + ...' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | +| vla.c:14:53:14:65 | Mul: ... * ... | Instruction 'Mul: ... * ...' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | +| vla.c:14:74:14:79 | CallSideEffect: call to getInt | Instruction 'CallSideEffect: call to getInt' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | +| vla.c:14:92:14:94 | Store: (char *)... | Instruction 'Store: (char *)...' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | ambiguousSuccessors -| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| conditional_destructors.cpp:29:6:29:7 | InitializeNonLocal: f1 | Goto | 2 | conditional_destructors.cpp:30:9:30:13 | FunctionAddress: call to C1 | -| conditional_destructors.cpp:29:6:29:7 | InitializeNonLocal: f1 | Goto | 2 | forstmt.cpp:2:14:2:14 | VariableAddress: definition of i | -| conditional_destructors.cpp:38:6:38:7 | InitializeNonLocal: f2 | Goto | 2 | conditional_destructors.cpp:39:9:39:13 | FunctionAddress: call to C2 | -| conditional_destructors.cpp:38:6:38:7 | InitializeNonLocal: f2 | Goto | 2 | forstmt.cpp:9:14:9:14 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| dostmt.c:25:6:25:18 | InitializeNonLocal: always_true_3 | Goto | 2 | dostmt.c:27:5:27:7 | NoOp: label ...: | -| dostmt.c:25:6:25:18 | InitializeNonLocal: always_true_3 | Goto | 2 | whilestmt.c:33:9:33:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| forstmt.cpp:1:6:1:7 | InitializeNonLocal: f1 | Goto | 2 | conditional_destructors.cpp:30:9:30:13 | FunctionAddress: call to C1 | -| forstmt.cpp:1:6:1:7 | InitializeNonLocal: f1 | Goto | 2 | forstmt.cpp:2:14:2:14 | VariableAddress: definition of i | -| forstmt.cpp:8:6:8:7 | InitializeNonLocal: f2 | Goto | 2 | conditional_destructors.cpp:39:9:39:13 | FunctionAddress: call to C2 | -| forstmt.cpp:8:6:8:7 | InitializeNonLocal: f2 | Goto | 2 | forstmt.cpp:9:14:9:14 | VariableAddress: definition of i | -| ifelsestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| ifelsestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| ifelsestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | InitializeNonLocal: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | dostmt.c:33:7:33:7 | VariableAddress: definition of i | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | ifelsestmt.c:38:6:38:6 | VariableAddress: x | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | ifstmt.c:28:6:28:6 | VariableAddress: x | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | whilestmt.c:40:7:40:7 | VariableAddress: definition of i | -| ifstmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| ifstmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| ifstmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| ifstmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| ifstmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| ifstmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | dostmt.c:33:7:33:7 | VariableAddress: definition of i | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | ifelsestmt.c:38:6:38:6 | VariableAddress: x | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | ifstmt.c:28:6:28:6 | VariableAddress: x | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | whilestmt.c:40:7:40:7 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nonmembercallexpr.c:1:6:1:6 | InitializeNonLocal: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... | -| nonmembercallexpr.c:1:6:1:6 | InitializeNonLocal: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| revsubscriptexpr.c:1:6:1:6 | InitializeNonLocal: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... | -| revsubscriptexpr.c:1:6:1:6 | InitializeNonLocal: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| whilestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| whilestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| whilestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| whilestmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| whilestmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| whilestmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| whilestmt.c:32:6:32:18 | InitializeNonLocal: always_true_3 | Goto | 2 | dostmt.c:27:5:27:7 | NoOp: label ...: | -| whilestmt.c:32:6:32:18 | InitializeNonLocal: always_true_3 | Goto | 2 | whilestmt.c:33:9:33:9 | Constant: 1 | +| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Instruction 'InitializeNonLocal: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| break_labels.c:2:11:2:11 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| conditional_destructors.cpp:29:6:29:7 | InitializeNonLocal: f1 | Instruction 'InitializeNonLocal: f1' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:38:6:38:7 | InitializeNonLocal: f2 | Instruction 'InitializeNonLocal: f2' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Instruction 'InitializeNonLocal: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Instruction 'InitializeNonLocal: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| dostmt.c:25:6:25:18 | InitializeNonLocal: always_true_3 | Instruction 'InitializeNonLocal: always_true_3' has 2 successors of kind 'Goto' in function '$@'. | dostmt.c:25:6:25:18 | void always_true_3() | void always_true_3() | +| duff.c:2:12:2:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| forstmt.cpp:1:6:1:7 | InitializeNonLocal: f1 | Instruction 'InitializeNonLocal: f1' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| forstmt.cpp:8:6:8:7 | InitializeNonLocal: f2 | Instruction 'InitializeNonLocal: f2' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| ifelsestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Instruction 'InitializeNonLocal: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| ifelsestmt.c:11:6:11:19 | InitializeNonLocal: always_false_2 | Instruction 'InitializeNonLocal: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Instruction 'InitializeNonLocal: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Instruction 'InitializeNonLocal: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Instruction 'InitializeParameter: y' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:32:6:32:11 | void normal(int, int) | void normal(int, int) | +| ifstmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Instruction 'InitializeNonLocal: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| ifstmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Instruction 'InitializeNonLocal: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Instruction 'InitializeNonLocal: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Instruction 'InitializeNonLocal: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| ifstmt.c:27:24:27:24 | InitializeParameter: y | Instruction 'InitializeParameter: y' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:32:6:32:11 | void normal(int, int) | void normal(int, int) | +| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Instruction 'InitializeNonLocal: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nonmembercallexpr.c:1:6:1:6 | InitializeNonLocal: g | Instruction 'InitializeNonLocal: g' has 2 successors of kind 'Goto' in function '$@'. | nonmembercallexpr.c:1:6:1:6 | void g(); void g())(); void(* g(); void(* g())() | void g(); void g())(); void(* g(); void(* g())() | +| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Instruction 'InitializeNonLocal: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| revsubscriptexpr.c:1:6:1:6 | InitializeNonLocal: g | Instruction 'InitializeNonLocal: g' has 2 successors of kind 'Goto' in function '$@'. | nonmembercallexpr.c:1:6:1:6 | void g(); void g())(); void(* g(); void(* g())() | void g(); void g())(); void(* g(); void(* g())() | +| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Instruction 'InitializeNonLocal: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| switchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| whilestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Instruction 'InitializeNonLocal: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| whilestmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Instruction 'InitializeNonLocal: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Instruction 'InitializeNonLocal: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Instruction 'InitializeNonLocal: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| whilestmt.c:32:6:32:18 | InitializeNonLocal: always_true_3 | Instruction 'InitializeNonLocal: always_true_3' has 2 successors of kind 'Goto' in function '$@'. | dostmt.c:25:6:25:18 | void always_true_3() | void always_true_3() | unexplainedLoop unnecessaryPhiInstruction memoryOperandDefinitionIsUnmodeled @@ -564,14 +158,16 @@ containsLoopOfForwardEdges lostReachability backEdgeCountMismatch useNotDominatedByDefinition -| VacuousDestructorCall.cpp:2:29:2:29 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | IR: CallDestructor | void CallDestructor(int, int*) | -| misc.c:219:47:219:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | -| try_catch.cpp:21:13:21:24 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | try_catch.cpp:19:6:19:23 | IR: throw_from_nonstmt | void throw_from_nonstmt(int) | -| vla.c:3:27:3:30 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) | +| VacuousDestructorCall.cpp:2:29:2:29 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor(int, int*) | void CallDestructor(int, int*) | +| misc.c:219:47:219:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| static_init_templates.cpp:15:1:15:18 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | static_init_templates.cpp:15:1:15:18 | void MyClass::MyClass() | void MyClass::MyClass() | +| try_catch.cpp:21:13:21:24 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | try_catch.cpp:19:6:19:23 | void throw_from_nonstmt(int) | void throw_from_nonstmt(int) | +| vla.c:3:27:3:30 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | int main(int, char**) | int main(int, char**) | switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected index 4964664c579e..4307483dfee4 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected @@ -1,32 +1,24 @@ missingOperand -| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:1:6:1:7 | IR: f1 | void f1() | -| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | IR: f2 | void f2() | -| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | forstmt.cpp:8:6:8:7 | IR: f2 | void f2() | -| cpp11.cpp:77:19:77:21 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:76:8:76:8 | IR: apply | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | -| cpp11.cpp:82:11:82:14 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:81:8:81:8 | IR: apply2 | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | -| cpp11.cpp:82:45:82:48 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | IR: operator() | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | -| cpp11.cpp:82:51:82:51 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | IR: operator() | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | -| cpp11.cpp:88:25:88:30 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | IR: main | void lambda::main() | -| cpp11.cpp:88:33:88:38 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | IR: main | void lambda::main() | -| destructors.cpp:51:36:51:38 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | destructors.cpp:49:7:49:7 | IR: f | int cond_destruct::f(int) | -| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() | -| misc.c:125:5:125:11 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:97:6:97:10 | IR: misc3 | void misc3() | +| conditional_destructors.cpp:30:9:30:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:30:18:30:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:33:9:33:13 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:33:18:33:22 | IndirectMayWriteSideEffect: call to C1 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:39:9:39:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:39:18:39:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:42:9:42:13 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| conditional_destructors.cpp:42:18:42:22 | IndirectMayWriteSideEffect: call to C2 | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| cpp11.cpp:77:19:77:21 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:76:8:76:8 | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | void lambda::apply<(void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)>(lambda::Val, (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)) | +| cpp11.cpp:82:11:82:14 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:81:8:81:8 | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val) | +| cpp11.cpp:82:45:82:48 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | +| cpp11.cpp:82:51:82:51 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:82:20:82:20 | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | void (void lambda::apply2(int(*)(lambda::Val, lambda::Val), lambda::Val, lambda::Val))::(lambda [] type at line 82, col. 17)::operator()(lambda::Val) const | +| cpp11.cpp:88:25:88:30 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | void lambda::main() | void lambda::main() | +| cpp11.cpp:88:33:88:38 | IndirectMayWriteSideEffect: call to Val | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | cpp11.cpp:87:8:87:11 | void lambda::main() | void lambda::main() | +| destructors.cpp:51:36:51:38 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | destructors.cpp:49:7:49:7 | int cond_destruct::f(int) | int cond_destruct::f(int) | +| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() | +| misc.c:125:5:125:11 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:97:6:97:10 | void misc3() | void misc3() | unexpectedOperand duplicateOperand missingPhiOperand @@ -34,475 +26,79 @@ missingOperandType duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor -| VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y | -| condition_decls.cpp:16:19:16:20 | IndirectMayWriteSideEffect: call to BoxedInt | -| condition_decls.cpp:26:23:26:24 | IndirectMayWriteSideEffect: call to BoxedInt | -| condition_decls.cpp:41:22:41:23 | IndirectMayWriteSideEffect: call to BoxedInt | -| condition_decls.cpp:48:52:48:53 | IndirectMayWriteSideEffect: call to BoxedInt | -| misc.c:171:10:171:13 | Uninitialized: definition of str2 | -| misc.c:219:47:219:48 | InitializeIndirection: sp | -| ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x | -| ms_try_mix.cpp:11:12:11:15 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:28:12:28:15 | IndirectMayWriteSideEffect: call to C | -| ms_try_mix.cpp:48:10:48:13 | IndirectMayWriteSideEffect: call to C | -| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | -| vla.c:5:9:5:14 | Uninitialized: definition of matrix | -| vla.c:11:6:11:16 | InitializeNonLocal: vla_typedef | +| VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y | Instruction 'InitializeIndirection: y' has no successors in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor(int, int*) | void CallDestructor(int, int*) | +| condition_decls.cpp:16:19:16:20 | IndirectMayWriteSideEffect: call to BoxedInt | Instruction 'IndirectMayWriteSideEffect: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:15:6:15:17 | void if_decl_bind(int) | void if_decl_bind(int) | +| condition_decls.cpp:26:23:26:24 | IndirectMayWriteSideEffect: call to BoxedInt | Instruction 'IndirectMayWriteSideEffect: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:25:6:25:21 | void switch_decl_bind(int) | void switch_decl_bind(int) | +| condition_decls.cpp:41:22:41:23 | IndirectMayWriteSideEffect: call to BoxedInt | Instruction 'IndirectMayWriteSideEffect: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:40:6:40:20 | void while_decl_bind(int) | void while_decl_bind(int) | +| condition_decls.cpp:48:52:48:53 | IndirectMayWriteSideEffect: call to BoxedInt | Instruction 'IndirectMayWriteSideEffect: call to BoxedInt' has no successors in function '$@'. | condition_decls.cpp:47:6:47:18 | void for_decl_bind(int) | void for_decl_bind(int) | +| misc.c:171:10:171:13 | Uninitialized: definition of str2 | Instruction 'Uninitialized: definition of str2' has no successors in function '$@'. | misc.c:168:6:168:8 | void vla() | void vla() | +| misc.c:219:47:219:48 | InitializeIndirection: sp | Instruction 'InitializeIndirection: sp' has no successors in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) | +| ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x | Instruction 'Uninitialized: definition of x' has no successors in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) | +| ms_try_mix.cpp:11:12:11:15 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:10:6:10:18 | void ms_except_mix(int) | void ms_except_mix(int) | +| ms_try_mix.cpp:28:12:28:15 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:27:6:27:19 | void ms_finally_mix(int) | void ms_finally_mix(int) | +| ms_try_mix.cpp:48:10:48:13 | IndirectMayWriteSideEffect: call to C | Instruction 'IndirectMayWriteSideEffect: call to C' has no successors in function '$@'. | ms_try_mix.cpp:47:6:47:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() | +| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | stmt_expr.cpp:21:6:21:6 | void stmtexpr::g(int) | void stmtexpr::g(int) | +| vla.c:5:9:5:14 | Uninitialized: definition of matrix | Instruction 'Uninitialized: definition of matrix' has no successors in function '$@'. | vla.c:3:5:3:8 | int main(int, char**) | int main(int, char**) | +| vla.c:11:6:11:16 | InitializeNonLocal: vla_typedef | Instruction 'InitializeNonLocal: vla_typedef' has no successors in function '$@'. | vla.c:11:6:11:16 | void vla_typedef() | void vla_typedef() | ambiguousSuccessors -| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| break_labels.c:2:11:2:11 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| break_labels.c:2:11:2:11 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| conditional_destructors.cpp:29:6:29:7 | InitializeNonLocal: f1 | Goto | 2 | conditional_destructors.cpp:30:9:30:13 | FunctionAddress: call to C1 | -| conditional_destructors.cpp:29:6:29:7 | InitializeNonLocal: f1 | Goto | 2 | forstmt.cpp:2:14:2:14 | VariableAddress: definition of i | -| conditional_destructors.cpp:38:6:38:7 | InitializeNonLocal: f2 | Goto | 2 | conditional_destructors.cpp:39:9:39:13 | FunctionAddress: call to C2 | -| conditional_destructors.cpp:38:6:38:7 | InitializeNonLocal: f2 | Goto | 2 | forstmt.cpp:9:14:9:14 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| dostmt.c:25:6:25:18 | InitializeNonLocal: always_true_3 | Goto | 2 | dostmt.c:27:5:27:7 | NoOp: label ...: | -| dostmt.c:25:6:25:18 | InitializeNonLocal: always_true_3 | Goto | 2 | whilestmt.c:33:9:33:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| duff.c:2:12:2:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| duff.c:2:12:2:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| forstmt.cpp:1:6:1:7 | InitializeNonLocal: f1 | Goto | 2 | conditional_destructors.cpp:30:9:30:13 | FunctionAddress: call to C1 | -| forstmt.cpp:1:6:1:7 | InitializeNonLocal: f1 | Goto | 2 | forstmt.cpp:2:14:2:14 | VariableAddress: definition of i | -| forstmt.cpp:8:6:8:7 | InitializeNonLocal: f2 | Goto | 2 | conditional_destructors.cpp:39:9:39:13 | FunctionAddress: call to C2 | -| forstmt.cpp:8:6:8:7 | InitializeNonLocal: f2 | Goto | 2 | forstmt.cpp:9:14:9:14 | VariableAddress: definition of i | -| ifelsestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| ifelsestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| ifelsestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| ifelsestmt.c:11:6:11:19 | InitializeNonLocal: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | dostmt.c:33:7:33:7 | VariableAddress: definition of i | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | ifelsestmt.c:38:6:38:6 | VariableAddress: x | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | ifstmt.c:28:6:28:6 | VariableAddress: x | -| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Goto | 4 | whilestmt.c:40:7:40:7 | VariableAddress: definition of i | -| ifstmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| ifstmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| ifstmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| ifstmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| ifstmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| ifstmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | dostmt.c:33:7:33:7 | VariableAddress: definition of i | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | ifelsestmt.c:38:6:38:6 | VariableAddress: x | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | ifstmt.c:28:6:28:6 | VariableAddress: x | -| ifstmt.c:27:24:27:24 | InitializeParameter: y | Goto | 4 | whilestmt.c:40:7:40:7 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| nonmembercallexpr.c:1:6:1:6 | InitializeNonLocal: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... | -| nonmembercallexpr.c:1:6:1:6 | InitializeNonLocal: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| revsubscriptexpr.c:1:6:1:6 | InitializeNonLocal: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... | -| revsubscriptexpr.c:1:6:1:6 | InitializeNonLocal: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | assignexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | constmemberaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | constructorinitializer.cpp:7:6:7:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | defconstructornewexpr.cpp:4:2:4:6 | FunctionAddress: new | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | defdestructordeleteexpr.cpp:4:5:4:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | deleteexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | fieldaccess.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | membercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | newexpr.cpp:7:6:7:6 | VariableAddress: definition of a | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | pmcallexpr.cpp:7:5:7:5 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c | -| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i | -| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo | -| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... | -| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f | -| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | break_labels.c:3:9:3:14 | VariableAddress: definition of result | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | duff.c:3:9:3:9 | VariableAddress: definition of n | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | dummyblock.c:2:9:2:9 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | emptyblock.c:2:5:3:5 | NoOp: { ... } | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | exprstmt.c:2:5:2:5 | Constant: 1 | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | initializer.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | landexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | lorexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | ltrbinopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nodefaultswitchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmembercallexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfp2callexpr.c:4:2:4:2 | FunctionAddress: call to g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | nonmemberfpcallexpr.c:2:8:2:8 | VariableAddress: definition of g | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | questionexpr.c:2:6:2:6 | VariableAddress: definition of a | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | subscriptexpr.c:2:9:2:9 | VariableAddress: definition of x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: i | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | switchstmt.c:2:14:2:14 | VariableAddress: x | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | tinyforstmt.c:3:9:3:9 | NoOp: ; | -| switchstmt.c:1:12:1:12 | InitializeParameter: x | Goto | 19 | unaryopexpr.c:2:9:2:9 | VariableAddress: definition of i | -| whilestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifelsestmt.c:2:6:2:6 | Constant: 0 | -| whilestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | ifstmt.c:2:6:2:6 | Constant: 0 | -| whilestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Goto | 3 | whilestmt.c:2:9:2:9 | Constant: 0 | -| whilestmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifelsestmt.c:12:6:12:6 | Constant: 0 | -| whilestmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | ifstmt.c:9:6:9:6 | Constant: 0 | -| whilestmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Goto | 3 | whilestmt.c:9:7:9:10 | VariableAddress: definition of done | -| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Goto | 4 | dostmt.c:10:5:10:7 | NoOp: label ...: | -| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifelsestmt.c:20:6:20:6 | Constant: 1 | -| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Goto | 4 | ifstmt.c:15:6:15:6 | Constant: 1 | -| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Goto | 4 | whilestmt.c:16:9:16:9 | Constant: 1 | -| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Goto | 4 | dostmt.c:18:5:18:7 | NoOp: label ...: | -| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifelsestmt.c:30:6:30:6 | Constant: 1 | -| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Goto | 4 | ifstmt.c:22:6:22:6 | Constant: 1 | -| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Goto | 4 | whilestmt.c:24:9:24:9 | Constant: 1 | -| whilestmt.c:32:6:32:18 | InitializeNonLocal: always_true_3 | Goto | 2 | dostmt.c:27:5:27:7 | NoOp: label ...: | -| whilestmt.c:32:6:32:18 | InitializeNonLocal: always_true_3 | Goto | 2 | whilestmt.c:33:9:33:9 | Constant: 1 | +| allocators.cpp:14:5:14:8 | InitializeNonLocal: main | Instruction 'InitializeNonLocal: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| array_delete.cpp:5:6:5:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| assignexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| break_labels.c:2:11:2:11 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| break_labels.c:2:11:2:11 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| conditional_destructors.cpp:29:6:29:7 | InitializeNonLocal: f1 | Instruction 'InitializeNonLocal: f1' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| conditional_destructors.cpp:38:6:38:7 | InitializeNonLocal: f2 | Instruction 'InitializeNonLocal: f2' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| constmemberaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| constructorinitializer.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| defconstructornewexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| defdestructordeleteexpr.cpp:3:6:3:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| deleteexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| dostmt.c:8:6:8:18 | InitializeNonLocal: always_true_1 | Instruction 'InitializeNonLocal: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| dostmt.c:16:6:16:18 | InitializeNonLocal: always_true_2 | Instruction 'InitializeNonLocal: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| dostmt.c:25:6:25:18 | InitializeNonLocal: always_true_3 | Instruction 'InitializeNonLocal: always_true_3' has 2 successors of kind 'Goto' in function '$@'. | dostmt.c:25:6:25:18 | void always_true_3() | void always_true_3() | +| duff.c:2:12:2:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| duff.c:2:12:2:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| fieldaccess.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| forstmt.cpp:1:6:1:7 | InitializeNonLocal: f1 | Instruction 'InitializeNonLocal: f1' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:29:6:29:7 | void f1() | void f1() | +| forstmt.cpp:8:6:8:7 | InitializeNonLocal: f2 | Instruction 'InitializeNonLocal: f2' has 2 successors of kind 'Goto' in function '$@'. | conditional_destructors.cpp:38:6:38:7 | void f2() | void f2() | +| ifelsestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Instruction 'InitializeNonLocal: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| ifelsestmt.c:11:6:11:19 | InitializeNonLocal: always_false_2 | Instruction 'InitializeNonLocal: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| ifelsestmt.c:19:6:19:18 | InitializeNonLocal: always_true_1 | Instruction 'InitializeNonLocal: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| ifelsestmt.c:29:6:29:18 | InitializeNonLocal: always_true_2 | Instruction 'InitializeNonLocal: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| ifelsestmt.c:37:24:37:24 | InitializeParameter: y | Instruction 'InitializeParameter: y' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:32:6:32:11 | void normal(int, int) | void normal(int, int) | +| ifstmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Instruction 'InitializeNonLocal: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| ifstmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Instruction 'InitializeNonLocal: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| ifstmt.c:14:6:14:18 | InitializeNonLocal: always_true_1 | Instruction 'InitializeNonLocal: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| ifstmt.c:21:6:21:18 | InitializeNonLocal: always_true_2 | Instruction 'InitializeNonLocal: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| ifstmt.c:27:24:27:24 | InitializeParameter: y | Instruction 'InitializeParameter: y' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:32:6:32:11 | void normal(int, int) | void normal(int, int) | +| membercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| membercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| newexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| no_dynamic_init.cpp:9:5:9:8 | InitializeNonLocal: main | Instruction 'InitializeNonLocal: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| nonmembercallexpr.c:1:6:1:6 | InitializeNonLocal: g | Instruction 'InitializeNonLocal: g' has 2 successors of kind 'Goto' in function '$@'. | nonmembercallexpr.c:1:6:1:6 | void g(); void g())(); void(* g(); void(* g())() | void g(); void g())(); void(* g(); void(* g())() | +| parameterinitializer.cpp:18:5:18:8 | InitializeNonLocal: main | Instruction 'InitializeNonLocal: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| pmcallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| revsubscriptexpr.c:1:6:1:6 | InitializeNonLocal: g | Instruction 'InitializeNonLocal: g' has 2 successors of kind 'Goto' in function '$@'. | nonmembercallexpr.c:1:6:1:6 | void g(); void g())(); void(* g(); void(* g())() | void g(); void g())(); void(* g(); void(* g())() | +| staticmembercallexpr.cpp:6:6:6:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| staticmembercallexpr_args.cpp:7:6:7:6 | InitializeNonLocal: f | Instruction 'InitializeNonLocal: f' has 14 successors of kind 'Goto' in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | +| stream_it.cpp:16:5:16:8 | InitializeNonLocal: main | Instruction 'InitializeNonLocal: main' has 4 successors of kind 'Goto' in function '$@'. | allocators.cpp:14:5:14:8 | int main() | int main() | +| switchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: i | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: i' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| switchstmt.c:1:12:1:12 | InitializeParameter: x | Instruction 'InitializeParameter: x' has 19 successors of kind 'Goto' in function '$@'. | aggregateinitializer.c:1:6:1:6 | int f(int); void f(int) | int f(int); void f(int) | +| whilestmt.c:1:6:1:19 | InitializeNonLocal: always_false_1 | Instruction 'InitializeNonLocal: always_false_1' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:1:6:1:19 | void always_false_1() | void always_false_1() | +| whilestmt.c:8:6:8:19 | InitializeNonLocal: always_false_2 | Instruction 'InitializeNonLocal: always_false_2' has 3 successors of kind 'Goto' in function '$@'. | ifelsestmt.c:11:6:11:19 | void always_false_2() | void always_false_2() | +| whilestmt.c:15:6:15:18 | InitializeNonLocal: always_true_1 | Instruction 'InitializeNonLocal: always_true_1' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:8:6:8:18 | void always_true_1() | void always_true_1() | +| whilestmt.c:23:6:23:18 | InitializeNonLocal: always_true_2 | Instruction 'InitializeNonLocal: always_true_2' has 4 successors of kind 'Goto' in function '$@'. | dostmt.c:16:6:16:18 | void always_true_2() | void always_true_2() | +| whilestmt.c:32:6:32:18 | InitializeNonLocal: always_true_3 | Instruction 'InitializeNonLocal: always_true_3' has 2 successors of kind 'Goto' in function '$@'. | dostmt.c:25:6:25:18 | void always_true_3() | void always_true_3() | unexplainedLoop unnecessaryPhiInstruction memoryOperandDefinitionIsUnmodeled @@ -516,6 +112,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected b/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected index f38eef610e44..c1d6e7fe9fa8 100644 --- a/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected +++ b/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected @@ -1,7 +1,7 @@ | file://:0:0:0:0 | __va_list_tag | | | test.cpp:3:8:3:9 | s1<> | {...} | -| test.cpp:3:8:3:9 | s1<> | (null) | +| test.cpp:3:8:3:9 | s1<> | (unnamed template parameter constant) | | test.cpp:5:8:5:9 | s2 | T | | test.cpp:5:8:5:9 | s2 | T | -| test.cpp:7:8:7:9 | s3> | (unnamed) | +| test.cpp:7:8:7:9 | s3> | (unnamed template parameter) | | test.cpp:7:8:7:9 | s3> | T | diff --git a/cpp/ql/test/library-tests/templates/decls/decls.expected b/cpp/ql/test/library-tests/templates/decls/decls.expected index 43a691e53c9c..cfb16b638281 100644 --- a/cpp/ql/test/library-tests/templates/decls/decls.expected +++ b/cpp/ql/test/library-tests/templates/decls/decls.expected @@ -7,7 +7,7 @@ | decls.cpp:4:30:4:34 | p#0 | | decls.cpp:4:30:4:34 | p#0 | | decls.cpp:6:17:6:17 | f | -| decls.cpp:8:18:8:18 | (unnamed) | +| decls.cpp:8:18:8:18 | (unnamed template parameter) | | decls.cpp:8:25:8:25 | g | | file://:0:0:0:0 | __va_list_tag | | file://:0:0:0:0 | auto | diff --git a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected index 516e1067f782..5f066a7e4fde 100644 --- a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected +++ b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected @@ -54,6 +54,7 @@ | file://:0:0:0:0 | char8_t | | file://:0:0:0:0 | char16_t | | file://:0:0:0:0 | char32_t | +| file://:0:0:0:0 | composite * | | file://:0:0:0:0 | composite & | | file://:0:0:0:0 | composite && | | file://:0:0:0:0 | composite * | @@ -156,6 +157,7 @@ | file://:0:0:0:0 | restrict | | file://:0:0:0:0 | rule & | | file://:0:0:0:0 | rule && | +| file://:0:0:0:0 | rule * | | file://:0:0:0:0 | sealed | | file://:0:0:0:0 | selectany | | file://:0:0:0:0 | short | diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected index a97246dd8991..d064fd7c8218 100644 --- a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected @@ -47,8 +47,10 @@ | isfromtemplateinstantiation.cpp:40:2:40:2 | return ... | isfromtemplateinstantiation.cpp:38:26:38:26 | normal_class::a_template_method() | | isfromtemplateinstantiation.cpp:44:26:44:26 | declaration of operator= | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | isfromtemplateinstantiation.cpp:44:26:44:26 | declaration of operator= | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:44:26:44:26 | definition of template_class | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | isfromtemplateinstantiation.cpp:44:26:44:26 | template_class::operator=(const template_class &) | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | isfromtemplateinstantiation.cpp:44:26:44:26 | template_class::operator=(template_class &&) | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:44:26:44:26 | template_class::template_class() | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | isfromtemplateinstantiation.cpp:46:4:46:4 | definition of t | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | isfromtemplateinstantiation.cpp:46:4:46:4 | t | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | isfromtemplateinstantiation.cpp:49:7:49:7 | definition of b_method | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected index d1e3cb3de156..f4f3321a90bd 100644 --- a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected @@ -347,6 +347,7 @@ isFromUninstantiatedTemplate | isfromtemplateinstantiation.cpp:26:1:26:1 | return ... | I | | Stmt | | | isfromtemplateinstantiation.cpp:29:7:29:7 | declaration of operator= | | | DeclarationEntry | | | isfromtemplateinstantiation.cpp:29:7:29:7 | declaration of operator= | | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:29:7:29:7 | normal_class | | | Declaration | | | isfromtemplateinstantiation.cpp:29:7:29:7 | operator= | | | Declaration | | | isfromtemplateinstantiation.cpp:29:7:29:7 | operator= | | | Declaration | | | isfromtemplateinstantiation.cpp:29:7:29:18 | normal_class | | | Declaration | | @@ -361,6 +362,7 @@ isFromUninstantiatedTemplate | isfromtemplateinstantiation.cpp:44:26:44:26 | declaration of operator= | I | | DeclarationEntry | | | isfromtemplateinstantiation.cpp:44:26:44:26 | operator= | I | | Declaration | | | isfromtemplateinstantiation.cpp:44:26:44:26 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:44:26:44:26 | template_class | I | | Declaration | | | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | T | Declaration | | | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | I | | Declaration | | | isfromtemplateinstantiation.cpp:46:4:46:4 | definition of t | | T | Definition | | diff --git a/cpp/ql/test/library-tests/typedefs/Typedefs2.ql b/cpp/ql/test/library-tests/typedefs/Typedefs2.ql index 11c7e075909e..122b826b556b 100644 --- a/cpp/ql/test/library-tests/typedefs/Typedefs2.ql +++ b/cpp/ql/test/library-tests/typedefs/Typedefs2.ql @@ -1,6 +1,6 @@ import cpp -from Function f1, Block body, Declaration d +from Function f1, BlockStmt body, Declaration d where body = f1.getBlock() and d = body.getADeclaration() diff --git a/cpp/ql/test/library-tests/unions/Unions1.expected b/cpp/ql/test/library-tests/unions/Unions1.expected index 440e9f8f1b9e..386f03d82de1 100644 --- a/cpp/ql/test/library-tests/unions/Unions1.expected +++ b/cpp/ql/test/library-tests/unions/Unions1.expected @@ -1,6 +1,6 @@ | unions.cpp:4:8:4:12 | Entry | Struct | | operator= | | unions.cpp:13:7:13:11 | Value | Struct, Union | | operator= | -| unions.cpp:19:8:19:22 | EntryWithMethod | Struct | Entry | getAsInt, operator= | +| unions.cpp:19:8:19:22 | EntryWithMethod | Struct | Entry | EntryWithMethod, getAsInt, operator= | | unions.cpp:27:9:27:20 | MyLocalUnion | LocalUnion, Struct, Union | | operator= | | unions.cpp:33:7:33:13 | MyClass | | | operator= | | unions.cpp:36:9:36:21 | MyNestedUnion | NestedUnion, Struct, Union | | operator= | diff --git a/cpp/ql/test/library-tests/unnamed/elements.expected b/cpp/ql/test/library-tests/unnamed/elements.expected index 8df7d4578a22..52176aa66df7 100644 --- a/cpp/ql/test/library-tests/unnamed/elements.expected +++ b/cpp/ql/test/library-tests/unnamed/elements.expected @@ -1,6 +1,6 @@ | file://:0:0:0:0 | | Other | | file://:0:0:0:0 | (global namespace) | Other | -| file://:0:0:0:0 | | Other | +| file://:0:0:0:0 | (unnamed global/namespace variable) | Other | | file://:0:0:0:0 | _Complex __float128 | Other | | file://:0:0:0:0 | _Complex double | Other | | file://:0:0:0:0 | _Complex float | Other | @@ -111,8 +111,8 @@ | test.c:0:0:0:0 | test.c | Other | | test.c:2:6:2:6 | a | Other | | test.c:2:6:2:6 | definition of a | Other | -| test.c:2:10:2:18 | | Variable access | +| test.c:2:10:2:18 | (unnamed global/namespace variable) | Variable access | | test.c:2:10:2:18 | array to pointer conversion | Other | | test.c:2:10:2:18 | initializer for a | Other | -| test.c:2:17:2:18 | initializer for | Other | +| test.c:2:17:2:18 | initializer for (unnamed global/namespace variable) | Other | | test.c:2:17:2:18 | {...} | Other | diff --git a/cpp/ql/test/library-tests/using-aliases/using-alias.ql b/cpp/ql/test/library-tests/using-aliases/using-alias.ql index 79287777e4b1..65a628996be3 100644 --- a/cpp/ql/test/library-tests/using-aliases/using-alias.ql +++ b/cpp/ql/test/library-tests/using-aliases/using-alias.ql @@ -1,4 +1,4 @@ import cpp from TypedefType t -select t, t.getCanonicalQLClass(), t.getUnderlyingType() +select t, t.getAPrimaryQlClass(), t.getUnderlyingType() diff --git a/cpp/ql/test/library-tests/variables/global/vardecl.expected b/cpp/ql/test/library-tests/variables/global/vardecl.expected new file mode 100644 index 000000000000..34339189e485 --- /dev/null +++ b/cpp/ql/test/library-tests/variables/global/vardecl.expected @@ -0,0 +1,6 @@ +| a.c:4:5:4:6 | definition of is | array of {int} | 1 | +| a.h:2:12:2:13 | declaration of is | array of 4 {int} | 1 | +| file://:0:0:0:0 | definition of fp_offset | unsigned int | 1 | +| file://:0:0:0:0 | definition of gp_offset | unsigned int | 1 | +| file://:0:0:0:0 | definition of overflow_arg_area | pointer to {void} | 1 | +| file://:0:0:0:0 | definition of reg_save_area | pointer to {void} | 1 | diff --git a/cpp/ql/test/library-tests/variables/global/vardecl.ql b/cpp/ql/test/library-tests/variables/global/vardecl.ql new file mode 100644 index 000000000000..52f902e1064c --- /dev/null +++ b/cpp/ql/test/library-tests/variables/global/vardecl.ql @@ -0,0 +1,5 @@ +import cpp + +from VariableDeclarationEntry vd, Type t +where t = vd.getType() +select vd, t.explain(), count(Type u | u = vd.getType()) diff --git a/cpp/ql/test/library-tests/variables/global/variables.expected b/cpp/ql/test/library-tests/variables/global/variables.expected index 7907cdb755a4..01ef82a0dcb8 100644 --- a/cpp/ql/test/library-tests/variables/global/variables.expected +++ b/cpp/ql/test/library-tests/variables/global/variables.expected @@ -1,5 +1,4 @@ -| a.c:4:5:4:6 | is | array of 4 {int} | 2 | -| a.c:4:5:4:6 | is | array of {int} | 2 | +| a.c:4:5:4:6 | is | array of {int} | 1 | | file://:0:0:0:0 | fp_offset | unsigned int | 1 | | file://:0:0:0:0 | gp_offset | unsigned int | 1 | | file://:0:0:0:0 | overflow_arg_area | pointer to {void} | 1 | diff --git a/cpp/ql/test/library-tests/vla/blocks.ql b/cpp/ql/test/library-tests/vla/blocks.ql index 4de353e388cd..bfd541e0162a 100644 --- a/cpp/ql/test/library-tests/vla/blocks.ql +++ b/cpp/ql/test/library-tests/vla/blocks.ql @@ -1,4 +1,4 @@ import cpp -from Block b, int i +from BlockStmt b, int i select b, i, b.getStmt(i) diff --git a/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/DeclarationHidesParameter.expected b/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/DeclarationHidesParameter.expected index a42d9db0c7a3..0e21d90fbcc4 100644 --- a/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/DeclarationHidesParameter.expected +++ b/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/DeclarationHidesParameter.expected @@ -1,2 +1,7 @@ | hiding.cpp:4:17:4:18 | ii | Local variable 'ii' hides a $@. | hiding.cpp:2:12:2:13 | definition of ii | parameter of the same name | | hiding.cpp:15:15:15:16 | kk | Local variable 'kk' hides a $@. | hiding.cpp:12:25:12:26 | definition of kk | parameter of the same name | +| hiding.cpp:28:7:28:7 | a | Local variable 'a' hides a $@. | hiding.cpp:26:21:26:21 | definition of a | parameter of the same name | +| hiding.cpp:45:7:45:7 | a | Local variable 'a' hides a $@. | hiding.cpp:43:41:43:41 | definition of a | parameter of the same name | +| hiding.cpp:64:11:64:11 | i | Local variable 'i' hides a $@. | hiding.cpp:61:20:61:20 | definition of i | parameter of the same name | +| hiding.cpp:78:7:78:10 | arg1 | Local variable 'arg1' hides a $@. | hiding.cpp:74:28:74:31 | definition of arg1 | parameter of the same name | +| hiding.cpp:79:5:79:8 | arg2 | Local variable 'arg2' hides a $@. | hiding.cpp:74:36:74:39 | definition of arg2 | parameter of the same name | diff --git a/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/hiding.cpp b/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/hiding.cpp index 8503155db4fb..0b08a0ae612f 100644 --- a/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/hiding.cpp +++ b/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/hiding.cpp @@ -1,7 +1,7 @@ void f(int ii) { if (1) { - for(int ii = 1; ii < 10; ii++) { + for(int ii = 1; ii < 10; ii++) { // local variable hides parameter of the same name ; } } @@ -12,7 +12,7 @@ namespace foo { void f2(int ii, int kk) { try { for (ii = 0; ii < 3; ii++) { - int kk; + int kk; // local variable hides parameter of the same name } } catch (int ee) { @@ -21,4 +21,61 @@ namespace foo { } } +void myFunction(int a, int b, int c); +void myFunction(int a, int b, int _c) { + { + int a = a; // local variable hides parameter of the same name + int _b = b; + int c = _c; + + // ... + } +} + +template +class MyTemplateClass { +public: + void myMethod(int a, int b, int c); +}; + +template +void MyTemplateClass :: myMethod(int a, int b, int _c) { + { + int a = a; // local variable hides parameter of the same name + int _b = b; + int c = _c; + + // ... + } +} + +MyTemplateClass mtc_i; + +void test() { + mtc_i.myMethod(0, 0, 0); +} + +#define MYMACRO for (int i = 0; i < 10; i++) {} + +void testMacro(int i) { + MYMACRO; + + for (int i = 0; i < 10; i++) {}; // local variable hides parameter of the same name +} + +#include "hiding.h" + +void myClass::myCaller(void) { + this->myMethod(5, 6); +} + +template +void myClass::myMethod(int arg1, T arg2) { + { + int protoArg1; + T protoArg2; + int arg1; // local variable hides parameter of the same name + T arg2; // local variable hides parameter of the same name + } +} diff --git a/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/hiding.h b/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/hiding.h new file mode 100644 index 000000000000..ee5d1b8f3a8e --- /dev/null +++ b/cpp/ql/test/query-tests/Best Practices/Hiding/DeclarationHidesParameter/hiding.h @@ -0,0 +1,7 @@ + +class myClass { +public: + template + void myMethod(int protoArg1, T protoArg2); + void myCaller(void); +}; diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.c b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.c index 9c56fda859c3..9fc257e3eebf 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.c +++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.c @@ -365,7 +365,7 @@ int callCommand(void) return 0; } -int shifts(void) +void shifts(void) { unsigned int x = 3; @@ -374,7 +374,7 @@ int shifts(void) if (x >> 1 == 1) {} // always true [NOT DETECTED] } -int bitwise_ands() +void bitwise_ands() { unsigned int x = 0xFF; @@ -382,3 +382,42 @@ int bitwise_ands() if ((x & 2) >= 2) {} if ((x & 2) >= 3) {} // always false } + +void unsigned_mult(unsigned int x, unsigned int y) { + if(x < 13 && y < 35) { + if(x * y > 1024) {} // always false + if(x * y < 204) {} + if(x >= 3 && y >= 2) { + if(x * y < 5) {} // always false + } + } +} + +void mult_rounding() { + unsigned long x, y, xy; + x = y = 1000000003UL; // 1e9 + 3 + xy = 1000000006000000009UL; // x * y, precisely + // Even though the range analysis wrongly considers x*y to be xy - 9, there + // are no PointlessComparison false positives in these tests because alerts + // are suppressed when ulp() < 1, which roughly means that the number is + // larger than 2^53. + if (x * y < xy) {} // always false [NOT DETECTED] + if (x * y > xy) {} // always false [NOT DETECTED] +} + +void mult_overflow() { + unsigned long x, y; + // The following two numbers multiply to 2^64 + 1, which is 1 when truncated + // to 64-bit unsigned. + x = 274177UL; + y = 67280421310721UL; + if (x * y == 1) {} // always true [BUG: reported as always false] + + // This bug appears to be caused by + // `RangeAnalysisUtils::typeUpperBound(unsigned long)` having a result of + // 2**64 + 384, making the range analysis think that the multiplication can't + // overflow. The correct `typeUpperBound` would be 2**64 - 1, but we can't + // represent that with a QL float or int. We could make `typeUpperBound` + // exclusive instead of inclusive, but there is no exclusive upper bound for + // floats. +} diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.expected b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.expected index c2c6cf6f14df..6c273b985eeb 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.expected @@ -31,7 +31,7 @@ | PointlessComparison.c:126:12:126:18 | ... >= ... | Comparison is always true because a >= 20. | | PointlessComparison.c:129:12:129:16 | ... > ... | Comparison is always false because a <= 3. | | PointlessComparison.c:197:7:197:11 | ... < ... | Comparison is always false because x >= 0. | -| PointlessComparison.c:264:12:264:22 | ... >= ... | Comparison is always true because dbl >= 0 and -0 >= - .... | +| PointlessComparison.c:264:12:264:22 | ... >= ... | Comparison is always true because dbl >= 0 and 0 >= - .... | | PointlessComparison.c:273:9:273:18 | ... > ... | Comparison is always false because c <= 0. | | PointlessComparison.c:283:13:283:19 | ... >= ... | Comparison is always true because c >= 11. | | PointlessComparison.c:294:9:294:16 | ... >= ... | Comparison is always false because ui1 <= 0. | @@ -41,7 +41,10 @@ | PointlessComparison.c:372:6:372:16 | ... >= ... | Comparison is always true because ... >> ... >= 1. | | PointlessComparison.c:373:6:373:16 | ... >= ... | Comparison is always false because ... >> ... <= 1. | | PointlessComparison.c:383:6:383:17 | ... >= ... | Comparison is always false because ... & ... <= 2. | -| PointlessComparison.cpp:36:6:36:33 | ... >= ... | Comparison is always false because ... >> ... <= 9223372036854776000. | +| PointlessComparison.c:388:10:388:21 | ... > ... | Comparison is always false because ... * ... <= 408. | +| PointlessComparison.c:391:12:391:20 | ... < ... | Comparison is always false because ... * ... >= 6. | +| PointlessComparison.c:414:7:414:16 | ... == ... | Comparison is always false because ... * ... >= 18446744073709551616. | +| PointlessComparison.cpp:36:6:36:33 | ... >= ... | Comparison is always false because ... >> ... <= 9223372036854775808. | | PointlessComparison.cpp:41:6:41:29 | ... >= ... | Comparison is always false because ... >> ... <= 140737488355327.5. | | PointlessComparison.cpp:42:6:42:29 | ... >= ... | Comparison is always false because ... >> ... <= 140737488355327.5. | | PointlessComparison.cpp:43:6:43:29 | ... >= ... | Comparison is always true because ... >> ... >= 140737488355327.5. | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/RegressionTests.cpp b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/RegressionTests.cpp index ce12aa7d0efc..28d211a129ea 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/RegressionTests.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/RegressionTests.cpp @@ -79,4 +79,40 @@ int containsIfDef(int x) { #endif return result >= 0; -} \ No newline at end of file +} + +void negativeZero1(int val) { + if (val >= 0) + { + val = -val; + } + if (val == 0) // GOOD [NO LONGER REPORTED] + ; +} + +void negativeZero2(int val) { + if (val >= 0) + { + val = 0 - val; + } + if (val == 0) // GOOD + ; +} + +void negativeZero3(int val) { + if (val >= 0) + { + val *= -1; + } + if (val == 0) // GOOD [NO LONGER REPORTED] + ; +} + +void negativeZero4(int val) { + if (val >= 0) + { + val = val * -1; + } + if (val == 0) // GOOD [NO LONGER REPORTED] + ; +} diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/inconsistentLoopDirection/inconsistentLoopDirection.cpp b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/inconsistentLoopDirection/inconsistentLoopDirection.cpp index e6e743382ed8..0642eb747c41 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/inconsistentLoopDirection/inconsistentLoopDirection.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/inconsistentLoopDirection/inconsistentLoopDirection.cpp @@ -177,4 +177,43 @@ void FalseNegativeTestCases() for (int i = 100; i > 0; i += 2) {} // For comparison for (int i = 100; i > 0; i ++ ) {} // BUG -} \ No newline at end of file +} + +void IntendedOverflow(unsigned char p) +{ + const unsigned char m = 10; + unsigned char i; + signed char s; + + for (i = 63; i < 64; i--) {} // GOOD (legitimate way to count down with an unsigned) + for (i = 63; i < 128; i--) {} // DUBIOUS (could still be a typo?) + for (i = 63; i < 255; i--) {} // GOOD + + for (i = m - 1; i < m; i--) {} // GOOD + for (i = m - 2; i < m; i--) {} // DUBIOUS + for (i = m; i < m + 1; i--) {} // GOOD + + for (s = 63; s < 64; s--) {} // BAD (signed numbers don't wrap at 0 / at all) + for (s = m + 1; s < m; s--) {} // BAD (never runs) + + for (i = p - 1; i < p; i--) {} // GOOD + for (s = p - 1; s < p; s--) {} // BAD [NOT DETECTED] + + { + int n; + + n = 64; + for (i = n - 1; i < n; i--) {} // GOOD + n = 64; + for (i = n - 1; i < 64; i--) {} // GOOD + n = 64; + for (i = 63; i < n; i--) {} // GOOD + + n = 64; + for (s = n - 1; s < n; s--) {} // BAD [NOT DETECTED] + n = 64; + for (s = n - 1; s < 64; s--) {} // BAD + n = 64; + for (s = 63; s < n; s--) {} // BAD [NOT DETECTED] + } +} diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/inconsistentLoopDirection/inconsistentLoopDirection.expected b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/inconsistentLoopDirection/inconsistentLoopDirection.expected index f5ff346271f0..310a6d725f58 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/inconsistentLoopDirection/inconsistentLoopDirection.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/inconsistentLoopDirection/inconsistentLoopDirection.expected @@ -20,3 +20,6 @@ | inconsistentLoopDirection.cpp:140:5:142:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (200), but the terminal condition is lower (0). | | inconsistentLoopDirection.cpp:175:5:175:36 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (10). | | inconsistentLoopDirection.cpp:179:5:179:38 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). | +| inconsistentLoopDirection.cpp:196:5:196:32 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (63), but the terminal condition is higher (64). | +| inconsistentLoopDirection.cpp:197:5:197:34 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (... + ...), but the terminal condition is always false. | +| inconsistentLoopDirection.cpp:215:3:215:33 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (... - ...), but the terminal condition is higher (64). | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.expected b/cpp/ql/test/query-tests/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.expected index 14dedbdebed8..dcc6ba8be889 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.expected @@ -1,6 +1,4 @@ -| test.c:33:3:33:19 | call to not_yet_declared2 | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:32:3:32:3 | not_yet_declared2 | not_yet_declared2 | test.c:33:21:33:22 | ca | ca | file://:0:0:0:0 | int * | int * | test.c:76:24:76:26 | p#0 | int p#0 | | test.c:33:3:33:19 | call to not_yet_declared2 | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:32:3:32:3 | not_yet_declared2 | not_yet_declared2 | test.c:33:21:33:22 | ca | ca | file://:0:0:0:0 | int[4] | int[4] | test.c:76:24:76:26 | p#0 | int p#0 | -| test.c:33:3:33:19 | call to not_yet_declared2 | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:76:6:76:22 | not_yet_declared2 | not_yet_declared2 | test.c:33:21:33:22 | ca | ca | file://:0:0:0:0 | int * | int * | test.c:76:24:76:26 | p#0 | int p#0 | | test.c:33:3:33:19 | call to not_yet_declared2 | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:76:6:76:22 | not_yet_declared2 | not_yet_declared2 | test.c:33:21:33:22 | ca | ca | file://:0:0:0:0 | int[4] | int[4] | test.c:76:24:76:26 | p#0 | int p#0 | | test.c:40:3:40:29 | call to declared_empty_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:77:6:77:32 | declared_empty_defined_with | declared_empty_defined_with | test.c:40:31:40:32 | & ... | & ... | file://:0:0:0:0 | int * | int * | test.c:77:38:77:38 | x | int x | | test.c:44:3:44:27 | call to not_declared_defined_with | Calling $@: argument $@ of type $@ is incompatible with parameter $@. | test.c:80:6:80:30 | not_declared_defined_with | not_declared_defined_with | test.c:44:29:44:31 | 4 | 4 | file://:0:0:0:0 | long long | long long | test.c:80:36:80:36 | x | int x | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected index 17bb67fdc082..20bb97a9f663 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected @@ -4,12 +4,10 @@ edges | search.c:14:24:14:28 | query | search.c:17:8:17:12 | query | | search.c:22:24:22:28 | query | search.c:23:39:23:43 | query | | search.c:22:24:22:28 | query | search.c:23:39:23:43 | query | -| search.c:41:21:41:26 | call to getenv | search.c:45:17:45:25 | raw_query | -| search.c:41:21:41:26 | call to getenv | search.c:45:17:45:25 | raw_query | -| search.c:41:21:41:26 | call to getenv | search.c:47:17:47:25 | raw_query | -| search.c:41:21:41:26 | call to getenv | search.c:47:17:47:25 | raw_query | -| search.c:45:17:45:25 | raw_query | search.c:14:24:14:28 | query | -| search.c:47:17:47:25 | raw_query | search.c:22:24:22:28 | query | +| search.c:41:21:41:26 | call to getenv | search.c:14:24:14:28 | query | +| search.c:41:21:41:26 | call to getenv | search.c:14:24:14:28 | query | +| search.c:41:21:41:26 | call to getenv | search.c:22:24:22:28 | query | +| search.c:41:21:41:26 | call to getenv | search.c:22:24:22:28 | query | nodes | search.c:14:24:14:28 | query | semmle.label | query | | search.c:17:8:17:12 | (const char *)... | semmle.label | (const char *)... | @@ -23,8 +21,8 @@ nodes | search.c:23:39:23:43 | query | semmle.label | query | | search.c:41:21:41:26 | call to getenv | semmle.label | call to getenv | | search.c:41:21:41:26 | call to getenv | semmle.label | call to getenv | -| search.c:45:17:45:25 | raw_query | semmle.label | raw_query | -| search.c:47:17:47:25 | raw_query | semmle.label | raw_query | +| search.c:45:5:45:15 | Argument 0 | semmle.label | Argument 0 | +| search.c:47:5:47:15 | Argument 0 | semmle.label | Argument 0 | #select | search.c:17:8:17:12 | query | search.c:41:21:41:26 | call to getenv | search.c:17:8:17:12 | query | Cross-site scripting vulnerability due to $@. | search.c:41:21:41:26 | call to getenv | this query data | | search.c:23:39:23:43 | query | search.c:41:21:41:26 | call to getenv | search.c:23:39:23:43 | query | Cross-site scripting vulnerability due to $@. | search.c:41:21:41:26 | call to getenv | this query data | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected index f84c6719157a..94c1e3383fee 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected @@ -7,6 +7,22 @@ edges | test.cpp:42:18:42:34 | (const char *)... | test.cpp:24:30:24:36 | command | | test.cpp:43:18:43:23 | call to getenv | test.cpp:29:30:29:36 | command | | test.cpp:43:18:43:34 | (const char *)... | test.cpp:29:30:29:36 | command | +| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | (const char *)... | +| test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer | +| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | (const char *)... | +| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data | +| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | (const char *)... | +| test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer | +| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | (const char *)... | +| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data | +| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | (const char *)... | +| test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer | +| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | (const char *)... | +| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data | +| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | (const char *)... | +| test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | buffer | +| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | (const char *)... | +| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data | nodes | test.cpp:24:30:24:36 | command | semmle.label | command | | test.cpp:26:10:26:16 | command | semmle.label | command | @@ -16,10 +32,32 @@ nodes | test.cpp:31:10:31:16 | command | semmle.label | command | | test.cpp:31:10:31:16 | command | semmle.label | command | | test.cpp:31:10:31:16 | command | semmle.label | command | +| test.cpp:42:7:42:16 | Argument 0 | semmle.label | Argument 0 | | test.cpp:42:18:42:23 | call to getenv | semmle.label | call to getenv | | test.cpp:42:18:42:34 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:43:7:43:16 | Argument 0 | semmle.label | Argument 0 | | test.cpp:43:18:43:23 | call to getenv | semmle.label | call to getenv | | test.cpp:43:18:43:34 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:56:12:56:17 | buffer | semmle.label | buffer | +| test.cpp:56:12:56:17 | fgets output argument | semmle.label | fgets output argument | +| test.cpp:62:10:62:15 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:62:10:62:15 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:62:10:62:15 | buffer | semmle.label | buffer | +| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:63:10:63:13 | data | semmle.label | data | +| test.cpp:76:12:76:17 | buffer | semmle.label | buffer | +| test.cpp:76:12:76:17 | fgets output argument | semmle.label | fgets output argument | +| test.cpp:78:10:78:15 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:78:10:78:15 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:78:10:78:15 | buffer | semmle.label | buffer | +| test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:79:10:79:13 | data | semmle.label | data | #select | test.cpp:26:10:26:16 | command | test.cpp:42:18:42:23 | call to getenv | test.cpp:26:10:26:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:42:18:42:23 | call to getenv | call to getenv | | test.cpp:31:10:31:16 | command | test.cpp:43:18:43:23 | call to getenv | test.cpp:31:10:31:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:43:18:43:23 | call to getenv | call to getenv | +| test.cpp:62:10:62:15 | buffer | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer | +| test.cpp:63:10:63:13 | data | test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer | +| test.cpp:78:10:78:15 | buffer | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer | +| test.cpp:79:10:79:13 | data | test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/test.cpp index 83a67f44417c..eb9436bcadb3 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/test.cpp @@ -42,3 +42,42 @@ void testMyDerived() md2->doCommand2(getenv("varname")); md3->doCommand3(getenv("varname")); } + +// --- + +typedef struct {} FILE; +char *fgets(char *s, int n, FILE *stream); +FILE *stdin; + +void testReferencePointer1() +{ + char buffer[1024]; + + if (fgets(buffer, 1024, stdin) != 0) + { + char *data = buffer; + char *&dataref = data; + char *data2 = dataref; + + system(buffer); // BAD + system(data); // BAD + system(dataref); // BAD [NOT DETECTED] + system(data2); // BAD [NOT DETECTED] + } +} + +void testReferencePointer2() +{ + char buffer[1024]; + char *data = buffer; + char *&dataref = data; + char *data2 = dataref; + + if (fgets(buffer, 1024, stdin) != 0) + { + system(buffer); // BAD + system(data); // BAD + system(dataref); // BAD [NOT DETECTED] + system(data2); // BAD [NOT DETECTED] + } +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected index 291c1cb3a716..365f9ed0aa97 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected @@ -5,6 +5,10 @@ edges | tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array | | tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array | | tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array | +| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array | +| tests.c:28:22:28:25 | argv | tests.c:28:22:28:28 | access to array | +| tests.c:28:22:28:28 | access to array | tests.c:28:22:28:28 | (const char *)... | +| tests.c:28:22:28:28 | access to array | tests.c:28:22:28:28 | access to array | | tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array | | tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array | | tests.c:29:28:29:31 | argv | tests.c:29:28:29:34 | access to array | @@ -15,6 +19,10 @@ edges | tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array | | tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array | | tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array | +| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array | +| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array | +| tests.c:34:10:34:16 | access to array | tests.c:34:10:34:16 | (const char *)... | +| tests.c:34:10:34:16 | access to array | tests.c:34:10:34:16 | access to array | nodes | tests.c:28:22:28:25 | argv | semmle.label | argv | | tests.c:28:22:28:25 | argv | semmle.label | argv | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected index 1d92afdeb040..ac52436676c5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected @@ -5,6 +5,10 @@ edges | argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array | | argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array | | argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array | +| argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array | +| argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array | +| argvLocal.c:95:9:95:15 | access to array | argvLocal.c:95:9:95:15 | (const char *)... | +| argvLocal.c:95:9:95:15 | access to array | argvLocal.c:95:9:95:15 | access to array | | argvLocal.c:96:15:96:18 | argv | argvLocal.c:96:15:96:21 | access to array | | argvLocal.c:96:15:96:18 | argv | argvLocal.c:96:15:96:21 | access to array | | argvLocal.c:96:15:96:18 | argv | argvLocal.c:96:15:96:21 | access to array | @@ -35,6 +39,8 @@ edges | argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | access to array | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | access to array | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | access to array | +| argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | access to array | +| argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | access to array | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:107:15:107:19 | access to array | @@ -45,34 +51,36 @@ edges | argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... | +| argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... | +| argvLocal.c:105:14:105:17 | argv | argvLocal.c:110:9:110:11 | * ... | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... | | argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... | +| argvLocal.c:106:9:106:13 | access to array | argvLocal.c:106:9:106:13 | (const char *)... | +| argvLocal.c:106:9:106:13 | access to array | argvLocal.c:106:9:106:13 | access to array | +| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:110:9:110:11 | (const char *)... | +| argvLocal.c:110:9:110:11 | * ... | argvLocal.c:110:9:110:11 | * ... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | Argument 0 indirection | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | Argument 0 indirection | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | printWrapper output argument | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | printWrapper output argument | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | Argument 0 indirection | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | Argument 0 indirection | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | printWrapper output argument | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | printWrapper output argument | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ | @@ -81,20 +89,15 @@ edges | argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... | -| argvLocal.c:117:15:117:16 | Argument 0 indirection | argvLocal.c:117:15:117:16 | printWrapper output argument | -| argvLocal.c:117:15:117:16 | array to pointer conversion | argvLocal.c:117:15:117:16 | printWrapper output argument | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | (const char *)... | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | i4 | -| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | Argument 0 indirection | -| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 | +| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | printWrapper output argument | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... | -| argvLocal.c:122:15:122:16 | Argument 0 indirection | argvLocal.c:122:15:122:16 | printWrapper output argument | -| argvLocal.c:122:15:122:16 | i4 | argvLocal.c:122:15:122:16 | printWrapper output argument | | argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... | | argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ | | argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... | @@ -103,14 +106,12 @@ edges | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | (const char *)... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 | -| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | Argument 0 indirection | -| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | Argument 0 indirection | -| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion | -| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 | +| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | printWrapper output argument | +| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | printWrapper output argument | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | (const char *)... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | (const char *)... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... | @@ -119,8 +120,6 @@ edges | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | -| argvLocal.c:128:15:128:16 | Argument 0 indirection | argvLocal.c:128:15:128:16 | printWrapper output argument | -| argvLocal.c:128:15:128:16 | array to pointer conversion | argvLocal.c:128:15:128:16 | printWrapper output argument | | argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | (const char *)... | | argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | ... + ... | | argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | ... + ... | @@ -215,6 +214,7 @@ nodes | argvLocal.c:116:9:116:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:116:9:116:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:116:9:116:10 | i3 | semmle.label | i3 | +| argvLocal.c:117:2:117:13 | Argument 0 | semmle.label | Argument 0 | | argvLocal.c:117:15:117:16 | Argument 0 indirection | semmle.label | Argument 0 indirection | | argvLocal.c:117:15:117:16 | array to pointer conversion | semmle.label | array to pointer conversion | | argvLocal.c:117:15:117:16 | array to pointer conversion | semmle.label | array to pointer conversion | @@ -223,6 +223,7 @@ nodes | argvLocal.c:121:9:121:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:121:9:121:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:121:9:121:10 | i4 | semmle.label | i4 | +| argvLocal.c:122:2:122:13 | Argument 0 | semmle.label | Argument 0 | | argvLocal.c:122:15:122:16 | Argument 0 indirection | semmle.label | Argument 0 indirection | | argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 | | argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 | @@ -233,6 +234,7 @@ nodes | argvLocal.c:127:9:127:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:127:9:127:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:127:9:127:10 | i5 | semmle.label | i5 | +| argvLocal.c:128:2:128:13 | Argument 0 | semmle.label | Argument 0 | | argvLocal.c:128:15:128:16 | Argument 0 indirection | semmle.label | Argument 0 indirection | | argvLocal.c:128:15:128:16 | array to pointer conversion | semmle.label | array to pointer conversion | | argvLocal.c:128:15:128:16 | array to pointer conversion | semmle.label | array to pointer conversion | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected index ba735a3e140a..00f98ac48be7 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected @@ -13,6 +13,8 @@ edges | globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | | globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | | globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:11:22:11:25 | *argv | globalVars.c:12:2:12:15 | Store | +| globalVars.c:11:22:11:25 | argv | globalVars.c:11:22:11:25 | *argv | | globalVars.c:11:22:11:25 | argv | globalVars.c:12:2:12:15 | Store | | globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy | | globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store | @@ -29,10 +31,12 @@ edges nodes | globalVars.c:8:7:8:10 | copy | semmle.label | copy | | globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 | +| globalVars.c:11:22:11:25 | *argv | semmle.label | *argv | | globalVars.c:11:22:11:25 | argv | semmle.label | argv | | globalVars.c:12:2:12:15 | Store | semmle.label | Store | | globalVars.c:15:21:15:23 | val | semmle.label | val | | globalVars.c:16:2:16:12 | Store | semmle.label | Store | +| globalVars.c:24:2:24:9 | Argument 0 | semmle.label | Argument 0 | | globalVars.c:24:11:24:14 | argv | semmle.label | argv | | globalVars.c:24:11:24:14 | argv | semmle.label | argv | | globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... | @@ -43,6 +47,7 @@ nodes | globalVars.c:30:15:30:18 | copy | semmle.label | copy | | globalVars.c:30:15:30:18 | copy | semmle.label | copy | | globalVars.c:30:15:30:18 | copy | semmle.label | copy | +| globalVars.c:35:2:35:9 | Argument 0 | semmle.label | Argument 0 | | globalVars.c:35:11:35:14 | copy | semmle.label | copy | | globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... | | globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index 8aa79b5bb6d9..a60e361fc1e6 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -47,30 +47,31 @@ edges | test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s | | test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s | | test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s | +| test.cpp:227:24:227:29 | call to getenv | test.cpp:214:23:214:23 | s | +| test.cpp:227:24:227:29 | call to getenv | test.cpp:220:21:220:21 | s | | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | (size_t)... | | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:235:11:235:20 | (size_t)... | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:237:10:237:19 | (size_t)... | +| test.cpp:227:24:227:37 | (const char *)... | test.cpp:214:23:214:23 | s | +| test.cpp:227:24:227:37 | (const char *)... | test.cpp:220:21:220:21 | s | | test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | (size_t)... | | test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size | | test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:235:11:235:20 | (size_t)... | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:237:10:237:19 | (size_t)... | -| test.cpp:235:11:235:20 | (size_t)... | test.cpp:214:23:214:23 | s | -| test.cpp:237:10:237:19 | (size_t)... | test.cpp:220:21:220:21 | s | -| test.cpp:241:2:241:32 | Chi | test.cpp:279:17:279:20 | get_size output argument | -| test.cpp:241:2:241:32 | Chi | test.cpp:295:18:295:21 | get_size output argument | -| test.cpp:241:18:241:23 | call to getenv | test.cpp:241:2:241:32 | Chi | -| test.cpp:241:18:241:31 | (const char *)... | test.cpp:241:2:241:32 | Chi | +| test.cpp:241:2:241:32 | Chi [array content] | test.cpp:279:17:279:20 | get_size output argument [array content] | +| test.cpp:241:2:241:32 | Chi [array content] | test.cpp:295:18:295:21 | get_size output argument [array content] | +| test.cpp:241:2:241:32 | Store | test.cpp:241:2:241:32 | Chi [array content] | +| test.cpp:241:18:241:23 | call to getenv | test.cpp:241:2:241:32 | Store | +| test.cpp:241:18:241:31 | (const char *)... | test.cpp:241:2:241:32 | Store | | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | | test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | | test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:279:17:279:20 | get_size output argument | test.cpp:281:11:281:28 | ... * ... | -| test.cpp:279:17:279:20 | get_size output argument | test.cpp:281:11:281:28 | ... * ... | -| test.cpp:295:18:295:21 | get_size output argument | test.cpp:298:10:298:27 | ... * ... | -| test.cpp:295:18:295:21 | get_size output argument | test.cpp:298:10:298:27 | ... * ... | +| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... | +| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... | +| test.cpp:279:17:279:20 | get_size output argument [array content] | test.cpp:279:17:279:20 | Chi | +| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | +| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | +| test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | | test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | | test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | | test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | @@ -140,9 +141,10 @@ nodes | test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | | test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | | test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| test.cpp:235:11:235:20 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:237:10:237:19 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:241:2:241:32 | Chi | semmle.label | Chi | +| test.cpp:235:2:235:9 | Argument 0 | semmle.label | Argument 0 | +| test.cpp:237:2:237:8 | Argument 0 | semmle.label | Argument 0 | +| test.cpp:241:2:241:32 | Chi [array content] | semmle.label | Chi [array content] | +| test.cpp:241:2:241:32 | Store | semmle.label | Store | | test.cpp:241:18:241:23 | call to getenv | semmle.label | call to getenv | | test.cpp:241:18:241:31 | (const char *)... | semmle.label | (const char *)... | | test.cpp:249:20:249:25 | call to getenv | semmle.label | call to getenv | @@ -150,11 +152,13 @@ nodes | test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | | test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | | test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:279:17:279:20 | get_size output argument | semmle.label | get_size output argument | +| test.cpp:279:17:279:20 | Chi | semmle.label | Chi | +| test.cpp:279:17:279:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | | test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | | test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | | test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:295:18:295:21 | get_size output argument | semmle.label | get_size output argument | +| test.cpp:295:18:295:21 | Chi | semmle.label | Chi | +| test.cpp:295:18:295:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected index b74dd08dd76e..8bb25025b868 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected @@ -1,3 +1,5 @@ +| test2.cpp:14:11:14:11 | v | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | +| test2.cpp:14:11:14:11 | v | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | | test3.c:15:10:15:10 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | | test3.c:15:14:15:14 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | | test3.c:15:18:15:18 | z | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/IntegerOverflowTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/IntegerOverflowTainted.expected index d2b6499d40e2..e71cacde76ce 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/IntegerOverflowTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/IntegerOverflowTainted.expected @@ -1,6 +1,16 @@ +| test2.cpp:14:11:14:15 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | +| test2.cpp:15:11:15:19 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | +| test2.cpp:16:11:16:21 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | +| test2.cpp:17:11:17:22 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | | test3.c:12:31:12:34 | * ... | $@ flows to here and is used in an expression which might overflow negatively. | test3.c:11:15:11:18 | argv | User-provided value | | test3.c:13:16:13:19 | * ... | $@ flows to here and is used in an expression which might overflow negatively. | test3.c:11:15:11:18 | argv | User-provided value | | test4.cpp:13:17:13:20 | access to array | $@ flows to here and is used in an expression which might overflow negatively. | test4.cpp:9:13:9:16 | argv | User-provided value | | test5.cpp:10:9:10:15 | call to strtoul | $@ flows to here and is used in an expression which might overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:17:6:17:27 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:19:6:19:13 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test6.cpp:11:15:11:15 | s | $@ flows to here and is used in an expression which might overflow. | test6.cpp:39:23:39:24 | & ... | User-provided value | +| test6.cpp:16:15:16:15 | s | $@ flows to here and is used in an expression which might overflow. | test6.cpp:39:23:39:24 | & ... | User-provided value | +| test6.cpp:30:16:30:16 | s | $@ flows to here and is used in an expression which might overflow. | test6.cpp:39:23:39:24 | & ... | User-provided value | +| test.c:14:15:14:35 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:44:7:44:12 | ... -- | $@ flows to here and is used in an expression which might overflow negatively. | test.c:41:17:41:20 | argv | User-provided value | | test.c:54:7:54:12 | ... -- | $@ flows to here and is used in an expression which might overflow negatively. | test.c:51:17:51:20 | argv | User-provided value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test2.cpp new file mode 100644 index 000000000000..eabbd2747033 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test2.cpp @@ -0,0 +1,28 @@ + +typedef signed long long int s64; + +typedef struct {} FILE; +int fscanf(FILE *stream, const char *format, ...); +FILE *stdin; + +typedef struct _myStruct { + s64 val; +} MyStruct; + +void test2_sink(s64 v, MyStruct s, MyStruct &s_r, MyStruct *s_p) +{ + s64 v1 = v * 2; // bad + s64 v2 = s.val * 2; // bad + s64 v3 = s_r.val * 2; // bad + s64 v4 = s_p->val * 2; // bad +} + +void test2_source() +{ + MyStruct ms; + s64 v; + + fscanf(stdin, "%i", &v); + ms.val = v; + test2_sink(v, ms, ms, &ms); +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test6.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test6.cpp new file mode 100644 index 000000000000..c7034e6cd0ea --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test6.cpp @@ -0,0 +1,54 @@ + +typedef unsigned short u16; +typedef unsigned int u32; + +typedef struct {} FILE; +int fscanf(FILE *stream, const char *format, ...); +FILE *stdin; + +void docast1(u32 s) +{ + u16 c = (u16)s; // bad +} + +void docast2(u32 s) +{ + u16 c = (u16)s; // bad +} + +class MyBaseClass +{ +public: + virtual void docast(u32 s) = 0; +}; + +class MyDerivedClass : public MyBaseClass +{ +public: + void docast(u32 s) + { + u16 c = (u16)s; // bad + } +}; + +void test6() +{ + u32 s; + + s = -1; + fscanf(stdin, "%hd", &s); + + docast1(s); + { + void (*docast2_ptr)(u32) = &docast2; + + docast2_ptr(s); + } + { + MyBaseClass *mbc = new MyDerivedClass; + + mbc->docast(s); + + delete mbc; + } +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected index f68464b6b38f..6a8976532654 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected @@ -42,18 +42,22 @@ edges | test.cpp:8:9:8:12 | Store | test.cpp:24:11:24:18 | call to get_rand | | test.cpp:8:9:8:12 | call to rand | test.cpp:8:9:8:12 | Store | | test.cpp:8:9:8:12 | call to rand | test.cpp:8:9:8:12 | Store | -| test.cpp:13:2:13:15 | Chi | test.cpp:30:13:30:14 | get_rand2 output argument | -| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:15 | Chi | -| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:15 | Chi | -| test.cpp:18:2:18:14 | Chi | test.cpp:36:13:36:13 | get_rand3 output argument | -| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:14 | Chi | -| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:14 | Chi | +| test.cpp:13:2:13:15 | Chi [array content] | test.cpp:30:13:30:14 | get_rand2 output argument [array content] | +| test.cpp:13:2:13:15 | Store | test.cpp:13:2:13:15 | Chi [array content] | +| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:15 | Store | +| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:15 | Store | +| test.cpp:18:2:18:14 | Chi [array content] | test.cpp:36:13:36:13 | get_rand3 output argument [array content] | +| test.cpp:18:2:18:14 | Store | test.cpp:18:2:18:14 | Chi [array content] | +| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:14 | Store | +| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:14 | Store | | test.cpp:24:11:24:18 | call to get_rand | test.cpp:25:7:25:7 | r | | test.cpp:24:11:24:18 | call to get_rand | test.cpp:25:7:25:7 | r | -| test.cpp:30:13:30:14 | get_rand2 output argument | test.cpp:31:7:31:7 | r | -| test.cpp:30:13:30:14 | get_rand2 output argument | test.cpp:31:7:31:7 | r | -| test.cpp:36:13:36:13 | get_rand3 output argument | test.cpp:37:7:37:7 | r | -| test.cpp:36:13:36:13 | get_rand3 output argument | test.cpp:37:7:37:7 | r | +| test.cpp:30:13:30:14 | Chi | test.cpp:31:7:31:7 | r | +| test.cpp:30:13:30:14 | Chi | test.cpp:31:7:31:7 | r | +| test.cpp:30:13:30:14 | get_rand2 output argument [array content] | test.cpp:30:13:30:14 | Chi | +| test.cpp:36:13:36:13 | Chi | test.cpp:37:7:37:7 | r | +| test.cpp:36:13:36:13 | Chi | test.cpp:37:7:37:7 | r | +| test.cpp:36:13:36:13 | get_rand3 output argument [array content] | test.cpp:36:13:36:13 | Chi | nodes | test.c:18:13:18:16 | call to rand | semmle.label | call to rand | | test.c:18:13:18:16 | call to rand | semmle.label | call to rand | @@ -106,21 +110,25 @@ nodes | test.cpp:8:9:8:12 | Store | semmle.label | Store | | test.cpp:8:9:8:12 | call to rand | semmle.label | call to rand | | test.cpp:8:9:8:12 | call to rand | semmle.label | call to rand | -| test.cpp:13:2:13:15 | Chi | semmle.label | Chi | +| test.cpp:13:2:13:15 | Chi [array content] | semmle.label | Chi [array content] | +| test.cpp:13:2:13:15 | Store | semmle.label | Store | | test.cpp:13:10:13:13 | call to rand | semmle.label | call to rand | | test.cpp:13:10:13:13 | call to rand | semmle.label | call to rand | -| test.cpp:18:2:18:14 | Chi | semmle.label | Chi | +| test.cpp:18:2:18:14 | Chi [array content] | semmle.label | Chi [array content] | +| test.cpp:18:2:18:14 | Store | semmle.label | Store | | test.cpp:18:9:18:12 | call to rand | semmle.label | call to rand | | test.cpp:18:9:18:12 | call to rand | semmle.label | call to rand | | test.cpp:24:11:24:18 | call to get_rand | semmle.label | call to get_rand | | test.cpp:25:7:25:7 | r | semmle.label | r | | test.cpp:25:7:25:7 | r | semmle.label | r | | test.cpp:25:7:25:7 | r | semmle.label | r | -| test.cpp:30:13:30:14 | get_rand2 output argument | semmle.label | get_rand2 output argument | +| test.cpp:30:13:30:14 | Chi | semmle.label | Chi | +| test.cpp:30:13:30:14 | get_rand2 output argument [array content] | semmle.label | get_rand2 output argument [array content] | | test.cpp:31:7:31:7 | r | semmle.label | r | | test.cpp:31:7:31:7 | r | semmle.label | r | | test.cpp:31:7:31:7 | r | semmle.label | r | -| test.cpp:36:13:36:13 | get_rand3 output argument | semmle.label | get_rand3 output argument | +| test.cpp:36:13:36:13 | Chi | semmle.label | Chi | +| test.cpp:36:13:36:13 | get_rand3 output argument [array content] | semmle.label | get_rand3 output argument [array content] | | test.cpp:37:7:37:7 | r | semmle.label | r | | test.cpp:37:7:37:7 | r | semmle.label | r | | test.cpp:37:7:37:7 | r | semmle.label | r | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/OverrunWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/OverrunWrite.expected index 0467d3509c95..6c978fc972ce 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/OverrunWrite.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/OverrunWrite.expected @@ -2,3 +2,4 @@ | tests.cpp:259:2:259:8 | call to sprintf | This 'call to sprintf' operation requires 17 bytes but the destination is only 10 bytes. | | tests.cpp:272:2:272:8 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. | | tests.cpp:273:2:273:8 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. | +| tests.cpp:308:3:308:9 | call to sprintf | This 'call to sprintf' operation requires 9 bytes but the destination is only 8 bytes. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/tests.cpp index 5cf53554a272..72b0d1afec70 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/tests.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-242/semmle/tests/tests.cpp @@ -289,3 +289,22 @@ void test5(va_list args, float f) vsprintf(buffer4, "123", args); // GOOD vsprintf(buffer4, "1234", args); // BAD: buffer overflow [NOT DETECTED] } + +namespace custom_sprintf_impl { + int sprintf(char *buf, const char *format, ...) + { + __builtin_va_list args; + int i; + + __builtin_va_start(args, format); + i = vsprintf(buf, format, args); + __builtin_va_end(args); + return i; + } + + void regression_test1() + { + char buffer8[8]; + sprintf(buffer8, "12345678"); // BAD: potential buffer overflow + } +} \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/block/emptyblock/emptyblock01.ql b/cpp/ql/test/successor-tests/block/emptyblock/emptyblock01.ql index ecadf57692ec..e2a1da4e2ed5 100644 --- a/cpp/ql/test/successor-tests/block/emptyblock/emptyblock01.ql +++ b/cpp/ql/test/successor-tests/block/emptyblock/emptyblock01.ql @@ -1,6 +1,6 @@ import cpp -from Block s, int i, Stmt f, boolean succ +from BlockStmt s, int i, Stmt f, boolean succ where s.getParentStmt().hasChild(s, i) and s.getParentStmt().hasChild(f, i + 1) and diff --git a/cpp/ql/test/successor-tests/dostmt/dostmt04.ql b/cpp/ql/test/successor-tests/dostmt/dostmt04.ql index 61c1bb80f265..6b7a8a071788 100644 --- a/cpp/ql/test/successor-tests/dostmt/dostmt04.ql +++ b/cpp/ql/test/successor-tests/dostmt/dostmt04.ql @@ -8,7 +8,7 @@ import cpp from DoStmt ds, ExprStmt last, Expr succ where ds.getEnclosingFunction().hasName("normal") and - last = ds.getStmt().(Block).getLastStmt() and + last = ds.getStmt().(BlockStmt).getLastStmt() and succ = last.getExpr().getASuccessor() and succ = ds.getCondition().getAChild*() and count(last.getExpr().getASuccessor()) = 1 diff --git a/cpp/ql/test/successor-tests/forstmt/shortforstmt/shortforstmt04.ql b/cpp/ql/test/successor-tests/forstmt/shortforstmt/shortforstmt04.ql index a33bcb898086..3e5f4bfbbbef 100644 --- a/cpp/ql/test/successor-tests/forstmt/shortforstmt/shortforstmt04.ql +++ b/cpp/ql/test/successor-tests/forstmt/shortforstmt/shortforstmt04.ql @@ -9,7 +9,7 @@ import cpp from ForStmt fs, ExprStmt last, Expr succ where fs.getEnclosingFunction().hasName("normal") and - last = fs.getStmt().(Block).getLastStmt() and + last = fs.getStmt().(BlockStmt).getLastStmt() and succ = fs.getCondition().getAChild*() and succ = last.getExpr().getASuccessor() and count(last.getExpr().getASuccessor()) = 1 diff --git a/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt04.ql b/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt04.ql index c6fbf3c99f71..73f49a135c4e 100644 --- a/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt04.ql +++ b/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt04.ql @@ -10,7 +10,7 @@ where is.getEnclosingFunction().hasName("normal") and is.getParentStmt().hasChild(is, k) and is.getParentStmt().hasChild(l3, k + 1) and - last = is.getThen().(Block).getLastStmt() and + last = is.getThen().(BlockStmt).getLastStmt() and l3 = last.getASuccessor() and count(last.getASuccessor()) = 1 select last, l3.getName() diff --git a/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt06.ql b/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt06.ql index fc6ae7d3bc87..31f213558f09 100644 --- a/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt06.ql +++ b/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt06.ql @@ -10,7 +10,7 @@ where is.getEnclosingFunction().hasName("normal") and is.getParentStmt().hasChild(is, k) and is.getParentStmt().hasChild(l3, k + 1) and - last = is.getElse().(Block).getLastStmt() and + last = is.getElse().(BlockStmt).getLastStmt() and l3 = last.getASuccessor() and count(last.getASuccessor()) = 1 select last, l3.getName() diff --git a/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt02.ql b/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt02.ql index 90cfc707daa4..d3dc2521aa77 100644 --- a/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt02.ql +++ b/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt02.ql @@ -5,7 +5,7 @@ import cpp -from IfStmt is, Block t +from IfStmt is, BlockStmt t where is.getEnclosingFunction().hasName("normal") and t = is.getThen() and diff --git a/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt04.ql b/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt04.ql index f203b5f0ea29..7051192159cc 100644 --- a/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt04.ql +++ b/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt04.ql @@ -10,7 +10,7 @@ where is.getEnclosingFunction().hasName("normal") and is.getParentStmt().hasChild(is, k) and is.getParentStmt().hasChild(l2, k + 1) and - last = is.getThen().(Block).getLastStmt() and + last = is.getThen().(BlockStmt).getLastStmt() and l2 = last.getASuccessor() and count(last.getASuccessor()) = 1 select last, l2.getName() diff --git a/cpp/ql/test/successor-tests/stackvariables/stackvariables/graphable.expected b/cpp/ql/test/successor-tests/stackvariables/stackvariables/graphable.expected index 03f49c4b623b..04ab93f13b6a 100644 --- a/cpp/ql/test/successor-tests/stackvariables/stackvariables/graphable.expected +++ b/cpp/ql/test/successor-tests/stackvariables/stackvariables/graphable.expected @@ -1,512 +1,513 @@ -| BoxedInt::BoxedInt | false | 196 | 196 | BoxedInt | -| BoxedInt::BoxedInt | false | 484 | 484 | BoxedInt | -| BoxedInt::BoxedInt | false | 527 | 527 | this | -| BoxedInt::BoxedInt | false | 528 | 528 | m_ptr | -| BoxedInt::BoxedInt | false | 532 | 532 | x | -| BoxedInt::BoxedInt | false | 534 | 534 | new | -| BoxedInt::BoxedInt | false | 536 | 536 | ... = ... | -| BoxedInt::BoxedInt | false | 538 | 538 | ExprStmt | -| BoxedInt::BoxedInt | false | 540 | 540 | return ... | -| BoxedInt::BoxedInt | false | 542 | 542 | { ... } | -| BoxedInt::BoxedInt | true | 527 | 528 | | -| BoxedInt::BoxedInt | true | 528 | 536 | | -| BoxedInt::BoxedInt | true | 532 | 534 | | -| BoxedInt::BoxedInt | true | 534 | 527 | | -| BoxedInt::BoxedInt | true | 536 | 540 | | -| BoxedInt::BoxedInt | true | 538 | 532 | | -| BoxedInt::BoxedInt | true | 540 | 196 | | -| BoxedInt::BoxedInt | true | 542 | 538 | | -| BoxedInt::operator int | false | 204 | 204 | operator int | -| BoxedInt::operator int | false | 494 | 494 | this | -| BoxedInt::operator int | false | 495 | 495 | m_ptr | -| BoxedInt::operator int | false | 497 | 497 | * ... | -| BoxedInt::operator int | false | 499 | 499 | return ... | -| BoxedInt::operator int | false | 501 | 501 | { ... } | -| BoxedInt::operator int | true | 494 | 495 | | -| BoxedInt::operator int | true | 495 | 497 | | -| BoxedInt::operator int | true | 497 | 204 | | -| BoxedInt::operator int | true | 499 | 494 | | -| BoxedInt::operator int | true | 501 | 499 | | -| BoxedInt::operator= | false | 477 | 477 | operator= | -| BoxedInt::~BoxedInt | false | 254 | 254 | ~BoxedInt | -| BoxedInt::~BoxedInt | false | 509 | 509 | this | -| BoxedInt::~BoxedInt | false | 510 | 510 | m_ptr | -| BoxedInt::~BoxedInt | false | 512 | 512 | delete | -| BoxedInt::~BoxedInt | false | 514 | 514 | ExprStmt | -| BoxedInt::~BoxedInt | false | 516 | 516 | return ... | -| BoxedInt::~BoxedInt | false | 518 | 518 | { ... } | -| BoxedInt::~BoxedInt | true | 509 | 510 | | -| BoxedInt::~BoxedInt | true | 510 | 512 | | -| BoxedInt::~BoxedInt | true | 512 | 516 | | -| BoxedInt::~BoxedInt | true | 514 | 509 | | -| BoxedInt::~BoxedInt | true | 516 | 254 | | -| BoxedInt::~BoxedInt | true | 518 | 514 | | -| NonTrivial::operator= | false | 875 | 875 | operator= | -| NonTrivial::~NonTrivial | false | 649 | 649 | ~NonTrivial | -| NonTrivial::~NonTrivial | false | 886 | 886 | return ... | -| NonTrivial::~NonTrivial | false | 888 | 888 | { ... } | -| NonTrivial::~NonTrivial | true | 886 | 649 | | -| NonTrivial::~NonTrivial | true | 888 | 886 | | -| __va_list_tag::operator= | false | 95 | 95 | operator= | -| __va_list_tag::operator= | false | 102 | 102 | operator= | -| early_return | false | 777 | 777 | early_return | -| early_return | false | 785 | 785 | declaration | -| early_return | false | 787 | 787 | x | -| early_return | false | 789 | 789 | (bool)... | -| early_return | false | 793 | 793 | declaration | -| early_return | false | 795 | 795 | return ... | -| early_return | false | 797 | 797 | { ... } | -| early_return | false | 799 | 799 | if (...) ... | -| early_return | false | 801 | 801 | declaration | -| early_return | false | 803 | 803 | return ... | -| early_return | false | 805 | 805 | { ... } | -| early_return | false | 807 | 807 | before | -| early_return | false | 809 | 809 | call to before.~NonTrivial | -| early_return | false | 810 | 810 | after | -| early_return | false | 811 | 811 | call to after.~NonTrivial | -| early_return | false | 812 | 812 | inner | -| early_return | false | 814 | 814 | call to inner.~NonTrivial | -| early_return | false | 815 | 815 | inner | -| early_return | false | 816 | 816 | call to inner.~NonTrivial | -| early_return | true | 785 | 799 | | -| early_return | true | 787 | 797 | T | -| early_return | true | 787 | 801 | F | -| early_return | true | 793 | 795 | | -| early_return | true | 795 | 815 | | -| early_return | true | 797 | 793 | | -| early_return | true | 799 | 787 | | -| early_return | true | 801 | 803 | | -| early_return | true | 803 | 810 | | -| early_return | true | 805 | 785 | | -| early_return | true | 807 | 809 | | -| early_return | true | 809 | 777 | | -| early_return | true | 810 | 811 | | -| early_return | true | 811 | 807 | | -| early_return | true | 812 | 814 | | -| early_return | true | 814 | 801 | | -| early_return | true | 815 | 816 | | -| early_return | true | 816 | 807 | | -| early_throw | false | 727 | 727 | early_throw | -| early_throw | false | 735 | 735 | declaration | -| early_throw | false | 737 | 737 | x | -| early_throw | false | 739 | 739 | (bool)... | -| early_throw | false | 743 | 743 | declaration | -| early_throw | false | 745 | 745 | re-throw exception | -| early_throw | false | 747 | 747 | ExprStmt | -| early_throw | false | 749 | 749 | { ... } | -| early_throw | false | 751 | 751 | if (...) ... | -| early_throw | false | 753 | 753 | declaration | -| early_throw | false | 755 | 755 | return ... | -| early_throw | false | 757 | 757 | { ... } | -| early_throw | false | 759 | 759 | before | -| early_throw | false | 761 | 761 | call to before.~NonTrivial | -| early_throw | false | 762 | 762 | after | -| early_throw | false | 763 | 763 | call to after.~NonTrivial | -| early_throw | false | 764 | 764 | inner | -| early_throw | false | 766 | 766 | call to inner.~NonTrivial | -| early_throw | false | 767 | 767 | before | -| early_throw | false | 768 | 768 | call to before.~NonTrivial | -| early_throw | false | 769 | 769 | inner | -| early_throw | false | 770 | 770 | call to inner.~NonTrivial | -| early_throw | true | 735 | 751 | | -| early_throw | true | 737 | 749 | T | -| early_throw | true | 737 | 753 | F | -| early_throw | true | 743 | 747 | | -| early_throw | true | 745 | 769 | | -| early_throw | true | 747 | 745 | | -| early_throw | true | 749 | 743 | | -| early_throw | true | 751 | 737 | | -| early_throw | true | 753 | 755 | | -| early_throw | true | 755 | 762 | | -| early_throw | true | 757 | 735 | | -| early_throw | true | 759 | 761 | | -| early_throw | true | 761 | 727 | | -| early_throw | true | 762 | 763 | | -| early_throw | true | 763 | 759 | | -| early_throw | true | 764 | 766 | | -| early_throw | true | 766 | 753 | | -| early_throw | true | 767 | 768 | | -| early_throw | true | 768 | 727 | | -| early_throw | true | 769 | 770 | | -| early_throw | true | 770 | 767 | | -| for_decl_bind | false | 185 | 185 | for_decl_bind | -| for_decl_bind | false | 194 | 194 | call to BoxedInt | -| for_decl_bind | false | 197 | 197 | x | -| for_decl_bind | false | 199 | 199 | - ... | -| for_decl_bind | false | 201 | 201 | initializer for init | -| for_decl_bind | false | 208 | 208 | call to operator int | -| for_decl_bind | false | 210 | 210 | call to BoxedInt | -| for_decl_bind | false | 212 | 212 | x | -| for_decl_bind | false | 214 | 214 | initializer for bi | -| for_decl_bind | false | 217 | 217 | bi | -| for_decl_bind | false | 219 | 219 | (bool)... | -| for_decl_bind | false | 220 | 220 | (condition decl) | -| for_decl_bind | false | 222 | 222 | x | -| for_decl_bind | false | 224 | 224 | ++ ... | -| for_decl_bind | false | 226 | 226 | ExprStmt | -| for_decl_bind | false | 228 | 228 | { ... } | -| for_decl_bind | false | 230 | 230 | declaration | -| for_decl_bind | false | 232 | 232 | x | -| for_decl_bind | false | 236 | 236 | 2 | -| for_decl_bind | false | 237 | 237 | ... *= ... | -| for_decl_bind | false | 239 | 239 | for(...;...;...) ... | -| for_decl_bind | false | 241 | 241 | x | -| for_decl_bind | false | 243 | 243 | -- ... | -| for_decl_bind | false | 245 | 245 | ExprStmt | -| for_decl_bind | false | 247 | 247 | return ... | -| for_decl_bind | false | 249 | 249 | { ... } | -| for_decl_bind | false | 251 | 251 | init | -| for_decl_bind | false | 253 | 253 | call to init.~BoxedInt | -| for_decl_bind | false | 255 | 255 | bi | -| for_decl_bind | false | 257 | 257 | call to bi.~BoxedInt | -| for_decl_bind | false | 258 | 258 | bi | -| for_decl_bind | false | 259 | 259 | call to bi.~BoxedInt | -| for_decl_bind | true | 194 | 212 | | -| for_decl_bind | true | 197 | 199 | | -| for_decl_bind | true | 199 | 194 | | -| for_decl_bind | true | 201 | 197 | | -| for_decl_bind | true | 210 | 220 | | -| for_decl_bind | true | 212 | 210 | | -| for_decl_bind | true | 220 | 228 | T | -| for_decl_bind | true | 220 | 255 | F | -| for_decl_bind | true | 222 | 224 | | -| for_decl_bind | true | 224 | 232 | | -| for_decl_bind | true | 226 | 222 | | -| for_decl_bind | true | 228 | 226 | | -| for_decl_bind | true | 230 | 201 | | -| for_decl_bind | true | 232 | 236 | | -| for_decl_bind | true | 236 | 237 | | -| for_decl_bind | true | 237 | 258 | | -| for_decl_bind | true | 239 | 230 | | -| for_decl_bind | true | 241 | 243 | | -| for_decl_bind | true | 243 | 247 | | -| for_decl_bind | true | 245 | 241 | | -| for_decl_bind | true | 247 | 185 | | -| for_decl_bind | true | 249 | 239 | | -| for_decl_bind | true | 251 | 253 | | -| for_decl_bind | true | 253 | 245 | | -| for_decl_bind | true | 255 | 257 | | -| for_decl_bind | true | 257 | 251 | | -| for_decl_bind | true | 258 | 259 | | -| for_decl_bind | true | 259 | 212 | | -| for_loop_scope | false | 676 | 676 | for_loop_scope | -| for_loop_scope | false | 684 | 684 | declaration | -| for_loop_scope | false | 689 | 689 | x | -| for_loop_scope | false | 693 | 693 | 10 | -| for_loop_scope | false | 694 | 694 | ... < ... | -| for_loop_scope | false | 699 | 699 | declaration | -| for_loop_scope | false | 701 | 701 | { ... } | -| for_loop_scope | false | 703 | 703 | declaration | -| for_loop_scope | false | 705 | 705 | x | -| for_loop_scope | false | 707 | 707 | ++ ... | -| for_loop_scope | false | 709 | 709 | for(...;...;...) ... | -| for_loop_scope | false | 711 | 711 | return ... | -| for_loop_scope | false | 713 | 713 | { ... } | -| for_loop_scope | false | 715 | 715 | outer_scope | -| for_loop_scope | false | 717 | 717 | call to outer_scope.~NonTrivial | -| for_loop_scope | false | 718 | 718 | for_scope | -| for_loop_scope | false | 720 | 720 | call to for_scope.~NonTrivial | -| for_loop_scope | false | 721 | 721 | inner_scope | -| for_loop_scope | false | 723 | 723 | call to inner_scope.~NonTrivial | -| for_loop_scope | true | 684 | 709 | | -| for_loop_scope | true | 689 | 693 | | -| for_loop_scope | true | 693 | 694 | | -| for_loop_scope | true | 694 | 701 | T | -| for_loop_scope | true | 694 | 718 | F | -| for_loop_scope | true | 699 | 721 | | -| for_loop_scope | true | 701 | 699 | | -| for_loop_scope | true | 703 | 689 | | -| for_loop_scope | true | 705 | 707 | | -| for_loop_scope | true | 707 | 689 | | -| for_loop_scope | true | 709 | 703 | | -| for_loop_scope | true | 711 | 715 | | -| for_loop_scope | true | 713 | 684 | | -| for_loop_scope | true | 715 | 717 | | -| for_loop_scope | true | 717 | 676 | | -| for_loop_scope | true | 718 | 720 | | -| for_loop_scope | true | 720 | 711 | | -| for_loop_scope | true | 721 | 723 | | -| for_loop_scope | true | 723 | 705 | | -| gotos | false | 585 | 585 | gotos | -| gotos | false | 593 | 593 | declaration | -| gotos | false | 595 | 595 | x | -| gotos | false | 597 | 597 | (bool)... | -| gotos | false | 598 | 598 | goto ... | -| gotos | false | 600 | 600 | if (...) ... | -| gotos | false | 602 | 602 | x | -| gotos | false | 604 | 604 | ++ ... | -| gotos | false | 606 | 606 | initializer for y | -| gotos | false | 617 | 617 | declaration | -| gotos | false | 619 | 619 | label ...: | -| gotos | false | 621 | 621 | declaration | -| gotos | false | 623 | 623 | y | -| gotos | false | 625 | 625 | (bool)... | -| gotos | false | 626 | 626 | goto ... | -| gotos | false | 628 | 628 | if (...) ... | -| gotos | false | 630 | 630 | declaration | -| gotos | false | 632 | 632 | { ... } | -| gotos | false | 634 | 634 | label ...: | -| gotos | false | 636 | 636 | x | -| gotos | false | 638 | 638 | -- ... | -| gotos | false | 640 | 640 | ExprStmt | -| gotos | false | 642 | 642 | return ... | -| gotos | false | 644 | 644 | { ... } | -| gotos | false | 646 | 646 | nt1 | -| gotos | false | 648 | 648 | call to nt1.~NonTrivial | -| gotos | false | 650 | 650 | nt2 | -| gotos | false | 652 | 652 | call to nt2.~NonTrivial | -| gotos | false | 653 | 653 | nt3 | -| gotos | false | 654 | 654 | call to nt3.~NonTrivial | -| gotos | false | 655 | 655 | nt2 | -| gotos | false | 656 | 656 | call to nt2.~NonTrivial | -| gotos | true | 593 | 600 | | -| gotos | true | 595 | 598 | T | -| gotos | true | 595 | 632 | F | -| gotos | true | 598 | 619 | | -| gotos | true | 600 | 595 | | -| gotos | true | 602 | 604 | | -| gotos | true | 604 | 619 | | -| gotos | true | 606 | 602 | | -| gotos | true | 617 | 606 | | -| gotos | true | 617 | 619 | | -| gotos | true | 619 | 621 | | -| gotos | true | 621 | 628 | | -| gotos | true | 623 | 626 | T | -| gotos | true | 623 | 630 | F | -| gotos | true | 626 | 655 | | -| gotos | true | 628 | 623 | | -| gotos | true | 630 | 653 | | -| gotos | true | 632 | 617 | | -| gotos | true | 634 | 640 | | -| gotos | true | 636 | 638 | | -| gotos | true | 638 | 642 | | -| gotos | true | 640 | 636 | | -| gotos | true | 642 | 646 | | -| gotos | true | 644 | 593 | | -| gotos | true | 646 | 648 | | -| gotos | true | 648 | 585 | | -| gotos | true | 650 | 652 | | -| gotos | true | 652 | 634 | | -| gotos | true | 653 | 654 | | -| gotos | true | 654 | 650 | | -| gotos | true | 655 | 656 | | -| gotos | true | 656 | 634 | | -| if_decl_bind | false | 407 | 407 | if_decl_bind | -| if_decl_bind | false | 416 | 416 | call to operator int | -| if_decl_bind | false | 418 | 418 | call to BoxedInt | -| if_decl_bind | false | 420 | 420 | x | -| if_decl_bind | false | 422 | 422 | initializer for bi | -| if_decl_bind | false | 425 | 425 | bi | -| if_decl_bind | false | 427 | 427 | (bool)... | -| if_decl_bind | false | 428 | 428 | (condition decl) | -| if_decl_bind | false | 430 | 430 | bi | -| if_decl_bind | false | 432 | 432 | m_ptr | -| if_decl_bind | false | 434 | 434 | * ... | -| if_decl_bind | false | 436 | 436 | ++ ... | -| if_decl_bind | false | 438 | 438 | ExprStmt | -| if_decl_bind | false | 440 | 440 | { ... } | -| if_decl_bind | false | 442 | 442 | bi | -| if_decl_bind | false | 444 | 444 | m_ptr | -| if_decl_bind | false | 446 | 446 | * ... | -| if_decl_bind | false | 448 | 448 | -- ... | -| if_decl_bind | false | 450 | 450 | ExprStmt | -| if_decl_bind | false | 452 | 452 | { ... } | -| if_decl_bind | false | 454 | 454 | if (...) ... | -| if_decl_bind | false | 456 | 456 | x | -| if_decl_bind | false | 460 | 460 | 1 | -| if_decl_bind | false | 461 | 461 | ... = ... | -| if_decl_bind | false | 463 | 463 | ExprStmt | -| if_decl_bind | false | 465 | 465 | return ... | -| if_decl_bind | false | 467 | 467 | { ... } | -| if_decl_bind | false | 469 | 469 | bi | -| if_decl_bind | false | 471 | 471 | call to bi.~BoxedInt | -| if_decl_bind | true | 418 | 428 | | -| if_decl_bind | true | 420 | 418 | | -| if_decl_bind | true | 428 | 440 | T | -| if_decl_bind | true | 428 | 452 | F | -| if_decl_bind | true | 430 | 432 | | -| if_decl_bind | true | 432 | 434 | | -| if_decl_bind | true | 434 | 436 | | -| if_decl_bind | true | 436 | 469 | | -| if_decl_bind | true | 438 | 430 | | -| if_decl_bind | true | 440 | 438 | | -| if_decl_bind | true | 442 | 444 | | -| if_decl_bind | true | 444 | 446 | | -| if_decl_bind | true | 446 | 448 | | -| if_decl_bind | true | 448 | 469 | | -| if_decl_bind | true | 450 | 442 | | -| if_decl_bind | true | 452 | 450 | | -| if_decl_bind | true | 454 | 420 | | -| if_decl_bind | true | 456 | 461 | | -| if_decl_bind | true | 460 | 456 | | -| if_decl_bind | true | 461 | 465 | | -| if_decl_bind | true | 463 | 460 | | -| if_decl_bind | true | 465 | 407 | | -| if_decl_bind | true | 467 | 454 | | -| if_decl_bind | true | 469 | 471 | | -| if_decl_bind | true | 471 | 463 | | -| never_destructs | false | 660 | 660 | never_destructs | -| never_destructs | false | 665 | 665 | declaration | -| never_destructs | false | 667 | 667 | label ...: | -| never_destructs | false | 669 | 669 | goto ... | -| never_destructs | false | 671 | 671 | { ... } | -| never_destructs | true | 665 | 667 | | -| never_destructs | true | 667 | 669 | | -| never_destructs | true | 669 | 667 | | -| never_destructs | true | 671 | 665 | | -| operator delete | false | 507 | 507 | operator delete | -| operator new | false | 530 | 530 | operator new | -| simple | false | 847 | 847 | simple | -| simple | false | 852 | 852 | declaration | -| simple | false | 854 | 854 | return ... | -| simple | false | 856 | 856 | { ... } | -| simple | false | 858 | 858 | nt | -| simple | false | 860 | 860 | call to nt.~NonTrivial | -| simple | true | 852 | 854 | | -| simple | true | 854 | 858 | | -| simple | true | 856 | 852 | | -| simple | true | 858 | 860 | | -| simple | true | 860 | 847 | | -| simple2 | false | 823 | 823 | simple2 | -| simple2 | false | 828 | 828 | declaration | -| simple2 | false | 830 | 830 | declaration | -| simple2 | false | 832 | 832 | return ... | -| simple2 | false | 834 | 834 | { ... } | -| simple2 | false | 836 | 836 | one | -| simple2 | false | 838 | 838 | call to one.~NonTrivial | -| simple2 | false | 839 | 839 | two | -| simple2 | false | 840 | 840 | call to two.~NonTrivial | -| simple2 | true | 828 | 830 | | -| simple2 | true | 830 | 832 | | -| simple2 | true | 832 | 839 | | -| simple2 | true | 834 | 828 | | -| simple2 | true | 836 | 838 | | -| simple2 | true | 838 | 823 | | -| simple2 | true | 839 | 840 | | -| simple2 | true | 840 | 836 | | -| switch_decl_bind | false | 308 | 308 | switch_decl_bind | -| switch_decl_bind | false | 317 | 317 | call to operator int | -| switch_decl_bind | false | 319 | 319 | call to BoxedInt | -| switch_decl_bind | false | 321 | 321 | x | -| switch_decl_bind | false | 323 | 323 | initializer for bi | -| switch_decl_bind | false | 326 | 326 | bi | -| switch_decl_bind | false | 328 | 328 | (condition decl) | -| switch_decl_bind | false | 332 | 332 | 0 | -| switch_decl_bind | false | 333 | 333 | case ...: | -| switch_decl_bind | false | 336 | 336 | bi | -| switch_decl_bind | false | 339 | 339 | m_ptr | -| switch_decl_bind | false | 341 | 341 | * ... | -| switch_decl_bind | false | 343 | 343 | -- ... | -| switch_decl_bind | false | 345 | 345 | ExprStmt | -| switch_decl_bind | false | 347 | 347 | break; | -| switch_decl_bind | false | 351 | 351 | 1 | -| switch_decl_bind | false | 352 | 352 | case ...: | -| switch_decl_bind | false | 354 | 354 | bi | -| switch_decl_bind | false | 356 | 356 | m_ptr | -| switch_decl_bind | false | 358 | 358 | * ... | -| switch_decl_bind | false | 360 | 360 | ++ ... | -| switch_decl_bind | false | 362 | 362 | ExprStmt | -| switch_decl_bind | false | 364 | 364 | break; | -| switch_decl_bind | false | 366 | 366 | default: | -| switch_decl_bind | false | 368 | 368 | bi | -| switch_decl_bind | false | 370 | 370 | m_ptr | -| switch_decl_bind | false | 372 | 372 | * ... | -| switch_decl_bind | false | 376 | 376 | 2 | -| switch_decl_bind | false | 377 | 377 | ... /= ... | -| switch_decl_bind | false | 379 | 379 | ExprStmt | -| switch_decl_bind | false | 381 | 381 | { ... } | -| switch_decl_bind | false | 383 | 383 | switch (...) ... | -| switch_decl_bind | false | 385 | 385 | label ...: | -| switch_decl_bind | false | 387 | 387 | x | -| switch_decl_bind | false | 391 | 391 | 1 | -| switch_decl_bind | false | 392 | 392 | ... = ... | -| switch_decl_bind | false | 394 | 394 | ExprStmt | -| switch_decl_bind | false | 396 | 396 | return ... | -| switch_decl_bind | false | 398 | 398 | { ... } | -| switch_decl_bind | false | 400 | 400 | bi | -| switch_decl_bind | false | 402 | 402 | call to bi.~BoxedInt | -| switch_decl_bind | false | 403 | 403 | bi | -| switch_decl_bind | false | 404 | 404 | call to bi.~BoxedInt | -| switch_decl_bind | false | 405 | 405 | bi | -| switch_decl_bind | false | 406 | 406 | call to bi.~BoxedInt | -| switch_decl_bind | true | 319 | 328 | | -| switch_decl_bind | true | 321 | 319 | | -| switch_decl_bind | true | 328 | 381 | | -| switch_decl_bind | true | 333 | 345 | | -| switch_decl_bind | true | 336 | 339 | | -| switch_decl_bind | true | 339 | 341 | | -| switch_decl_bind | true | 341 | 343 | | -| switch_decl_bind | true | 343 | 347 | | -| switch_decl_bind | true | 345 | 336 | | -| switch_decl_bind | true | 347 | 405 | | -| switch_decl_bind | true | 352 | 362 | | -| switch_decl_bind | true | 354 | 356 | | -| switch_decl_bind | true | 356 | 358 | | -| switch_decl_bind | true | 358 | 360 | | +| BoxedInt::BoxedInt | false | 153 | 153 | BoxedInt | +| BoxedInt::BoxedInt | false | 571 | 571 | BoxedInt | +| BoxedInt::BoxedInt | false | 625 | 625 | ExprStmt | +| BoxedInt::BoxedInt | false | 628 | 628 | this | +| BoxedInt::BoxedInt | false | 630 | 630 | m_ptr | +| BoxedInt::BoxedInt | false | 635 | 635 | x | +| BoxedInt::BoxedInt | false | 638 | 638 | new | +| BoxedInt::BoxedInt | false | 641 | 641 | ... = ... | +| BoxedInt::BoxedInt | false | 644 | 644 | return ... | +| BoxedInt::BoxedInt | false | 647 | 647 | { ... } | +| BoxedInt::BoxedInt | true | 625 | 635 | | +| BoxedInt::BoxedInt | true | 628 | 630 | | +| BoxedInt::BoxedInt | true | 630 | 641 | | +| BoxedInt::BoxedInt | true | 635 | 638 | | +| BoxedInt::BoxedInt | true | 638 | 628 | | +| BoxedInt::BoxedInt | true | 641 | 644 | | +| BoxedInt::BoxedInt | true | 644 | 153 | | +| BoxedInt::BoxedInt | true | 647 | 625 | | +| BoxedInt::operator int | false | 165 | 165 | operator int | +| BoxedInt::operator int | false | 581 | 581 | return ... | +| BoxedInt::operator int | false | 584 | 584 | this | +| BoxedInt::operator int | false | 586 | 586 | m_ptr | +| BoxedInt::operator int | false | 589 | 589 | * ... | +| BoxedInt::operator int | false | 592 | 592 | { ... } | +| BoxedInt::operator int | true | 581 | 584 | | +| BoxedInt::operator int | true | 584 | 586 | | +| BoxedInt::operator int | true | 586 | 589 | | +| BoxedInt::operator int | true | 589 | 165 | | +| BoxedInt::operator int | true | 592 | 581 | | +| BoxedInt::operator= | false | 562 | 562 | operator= | +| BoxedInt::~BoxedInt | false | 236 | 236 | ~BoxedInt | +| BoxedInt::~BoxedInt | false | 599 | 599 | ExprStmt | +| BoxedInt::~BoxedInt | false | 604 | 604 | this | +| BoxedInt::~BoxedInt | false | 606 | 606 | m_ptr | +| BoxedInt::~BoxedInt | false | 609 | 609 | delete | +| BoxedInt::~BoxedInt | false | 612 | 612 | return ... | +| BoxedInt::~BoxedInt | false | 615 | 615 | { ... } | +| BoxedInt::~BoxedInt | true | 599 | 604 | | +| BoxedInt::~BoxedInt | true | 604 | 606 | | +| BoxedInt::~BoxedInt | true | 606 | 609 | | +| BoxedInt::~BoxedInt | true | 609 | 612 | | +| BoxedInt::~BoxedInt | true | 612 | 236 | | +| BoxedInt::~BoxedInt | true | 615 | 599 | | +| NonTrivial::NonTrivial | false | 707 | 707 | NonTrivial | +| NonTrivial::operator= | false | 686 | 686 | operator= | +| NonTrivial::~NonTrivial | false | 695 | 695 | ~NonTrivial | +| NonTrivial::~NonTrivial | false | 702 | 702 | return ... | +| NonTrivial::~NonTrivial | false | 705 | 705 | { ... } | +| NonTrivial::~NonTrivial | true | 702 | 695 | | +| NonTrivial::~NonTrivial | true | 705 | 702 | | +| __va_list_tag::operator= | false | 65 | 65 | operator= | +| __va_list_tag::operator= | false | 71 | 71 | operator= | +| early_return | false | 1025 | 1025 | early_return | +| early_return | false | 1034 | 1034 | declaration | +| early_return | false | 1041 | 1041 | if (...) ... | +| early_return | false | 1044 | 1044 | x | +| early_return | false | 1047 | 1047 | (bool)... | +| early_return | false | 1053 | 1053 | declaration | +| early_return | false | 1056 | 1056 | return ... | +| early_return | false | 1059 | 1059 | { ... } | +| early_return | false | 1062 | 1062 | declaration | +| early_return | false | 1069 | 1069 | return ... | +| early_return | false | 1072 | 1072 | { ... } | +| early_return | false | 1075 | 1075 | inner | +| early_return | false | 1078 | 1078 | call to inner.~NonTrivial | +| early_return | false | 1080 | 1080 | before | +| early_return | false | 1083 | 1083 | call to before.~NonTrivial | +| early_return | false | 1085 | 1085 | inner | +| early_return | false | 1087 | 1087 | call to inner.~NonTrivial | +| early_return | false | 1089 | 1089 | after | +| early_return | false | 1091 | 1091 | call to after.~NonTrivial | +| early_return | true | 1034 | 1041 | | +| early_return | true | 1041 | 1044 | | +| early_return | true | 1044 | 1059 | T | +| early_return | true | 1044 | 1062 | F | +| early_return | true | 1053 | 1056 | | +| early_return | true | 1056 | 1085 | | +| early_return | true | 1059 | 1053 | | +| early_return | true | 1062 | 1069 | | +| early_return | true | 1069 | 1089 | | +| early_return | true | 1072 | 1034 | | +| early_return | true | 1075 | 1078 | | +| early_return | true | 1078 | 1062 | | +| early_return | true | 1080 | 1083 | | +| early_return | true | 1083 | 1025 | | +| early_return | true | 1085 | 1087 | | +| early_return | true | 1087 | 1080 | | +| early_return | true | 1089 | 1091 | | +| early_return | true | 1091 | 1080 | | +| early_throw | false | 951 | 951 | early_throw | +| early_throw | false | 960 | 960 | declaration | +| early_throw | false | 967 | 967 | if (...) ... | +| early_throw | false | 970 | 970 | x | +| early_throw | false | 973 | 973 | (bool)... | +| early_throw | false | 979 | 979 | declaration | +| early_throw | false | 982 | 982 | ExprStmt | +| early_throw | false | 985 | 985 | re-throw exception | +| early_throw | false | 988 | 988 | { ... } | +| early_throw | false | 991 | 991 | declaration | +| early_throw | false | 998 | 998 | return ... | +| early_throw | false | 1001 | 1001 | { ... } | +| early_throw | false | 1004 | 1004 | inner | +| early_throw | false | 1007 | 1007 | call to inner.~NonTrivial | +| early_throw | false | 1009 | 1009 | before | +| early_throw | false | 1012 | 1012 | call to before.~NonTrivial | +| early_throw | false | 1014 | 1014 | inner | +| early_throw | false | 1016 | 1016 | call to inner.~NonTrivial | +| early_throw | false | 1018 | 1018 | before | +| early_throw | false | 1020 | 1020 | call to before.~NonTrivial | +| early_throw | false | 1022 | 1022 | after | +| early_throw | false | 1024 | 1024 | call to after.~NonTrivial | +| early_throw | true | 960 | 967 | | +| early_throw | true | 967 | 970 | | +| early_throw | true | 970 | 988 | T | +| early_throw | true | 970 | 991 | F | +| early_throw | true | 979 | 982 | | +| early_throw | true | 982 | 985 | | +| early_throw | true | 985 | 1014 | | +| early_throw | true | 988 | 979 | | +| early_throw | true | 991 | 998 | | +| early_throw | true | 998 | 1022 | | +| early_throw | true | 1001 | 960 | | +| early_throw | true | 1004 | 1007 | | +| early_throw | true | 1007 | 991 | | +| early_throw | true | 1009 | 1012 | | +| early_throw | true | 1012 | 951 | | +| early_throw | true | 1014 | 1016 | | +| early_throw | true | 1016 | 1009 | | +| early_throw | true | 1018 | 1020 | | +| early_throw | true | 1020 | 951 | | +| early_throw | true | 1022 | 1024 | | +| early_throw | true | 1024 | 1018 | | +| for_decl_bind | false | 137 | 137 | for_decl_bind | +| for_decl_bind | false | 146 | 146 | for(...;...;...) ... | +| for_decl_bind | false | 151 | 151 | call to BoxedInt | +| for_decl_bind | false | 155 | 155 | x | +| for_decl_bind | false | 158 | 158 | - ... | +| for_decl_bind | false | 161 | 161 | initializer for init | +| for_decl_bind | false | 169 | 169 | call to operator int | +| for_decl_bind | false | 173 | 173 | call to BoxedInt | +| for_decl_bind | false | 176 | 176 | x | +| for_decl_bind | false | 179 | 179 | initializer for bi | +| for_decl_bind | false | 183 | 183 | bi | +| for_decl_bind | false | 186 | 186 | (bool)... | +| for_decl_bind | false | 188 | 188 | (condition decl) | +| for_decl_bind | false | 191 | 191 | ExprStmt | +| for_decl_bind | false | 194 | 194 | x | +| for_decl_bind | false | 197 | 197 | ++ ... | +| for_decl_bind | false | 200 | 200 | { ... } | +| for_decl_bind | false | 203 | 203 | declaration | +| for_decl_bind | false | 206 | 206 | x | +| for_decl_bind | false | 212 | 212 | 2 | +| for_decl_bind | false | 214 | 214 | ... *= ... | +| for_decl_bind | false | 217 | 217 | ExprStmt | +| for_decl_bind | false | 220 | 220 | x | +| for_decl_bind | false | 223 | 223 | -- ... | +| for_decl_bind | false | 226 | 226 | return ... | +| for_decl_bind | false | 229 | 229 | { ... } | +| for_decl_bind | false | 232 | 232 | init | +| for_decl_bind | false | 235 | 235 | call to init.~BoxedInt | +| for_decl_bind | false | 238 | 238 | bi | +| for_decl_bind | false | 241 | 241 | call to bi.~BoxedInt | +| for_decl_bind | false | 243 | 243 | bi | +| for_decl_bind | false | 245 | 245 | call to bi.~BoxedInt | +| for_decl_bind | true | 146 | 203 | | +| for_decl_bind | true | 151 | 176 | | +| for_decl_bind | true | 155 | 158 | | +| for_decl_bind | true | 158 | 151 | | +| for_decl_bind | true | 161 | 155 | | +| for_decl_bind | true | 173 | 188 | | +| for_decl_bind | true | 176 | 173 | | +| for_decl_bind | true | 188 | 200 | T | +| for_decl_bind | true | 188 | 238 | F | +| for_decl_bind | true | 191 | 194 | | +| for_decl_bind | true | 194 | 197 | | +| for_decl_bind | true | 197 | 206 | | +| for_decl_bind | true | 200 | 191 | | +| for_decl_bind | true | 203 | 161 | | +| for_decl_bind | true | 206 | 212 | | +| for_decl_bind | true | 212 | 214 | | +| for_decl_bind | true | 214 | 243 | | +| for_decl_bind | true | 217 | 220 | | +| for_decl_bind | true | 220 | 223 | | +| for_decl_bind | true | 223 | 226 | | +| for_decl_bind | true | 226 | 137 | | +| for_decl_bind | true | 229 | 146 | | +| for_decl_bind | true | 232 | 235 | | +| for_decl_bind | true | 235 | 217 | | +| for_decl_bind | true | 238 | 241 | | +| for_decl_bind | true | 241 | 232 | | +| for_decl_bind | true | 243 | 245 | | +| for_decl_bind | true | 245 | 176 | | +| for_loop_scope | false | 878 | 878 | for_loop_scope | +| for_loop_scope | false | 887 | 887 | declaration | +| for_loop_scope | false | 894 | 894 | for(...;...;...) ... | +| for_loop_scope | false | 901 | 901 | x | +| for_loop_scope | false | 907 | 907 | 10 | +| for_loop_scope | false | 909 | 909 | ... < ... | +| for_loop_scope | false | 916 | 916 | declaration | +| for_loop_scope | false | 919 | 919 | { ... } | +| for_loop_scope | false | 922 | 922 | declaration | +| for_loop_scope | false | 925 | 925 | x | +| for_loop_scope | false | 928 | 928 | ++ ... | +| for_loop_scope | false | 931 | 931 | return ... | +| for_loop_scope | false | 934 | 934 | { ... } | +| for_loop_scope | false | 937 | 937 | for_scope | +| for_loop_scope | false | 940 | 940 | call to for_scope.~NonTrivial | +| for_loop_scope | false | 942 | 942 | inner_scope | +| for_loop_scope | false | 945 | 945 | call to inner_scope.~NonTrivial | +| for_loop_scope | false | 947 | 947 | outer_scope | +| for_loop_scope | false | 950 | 950 | call to outer_scope.~NonTrivial | +| for_loop_scope | true | 887 | 894 | | +| for_loop_scope | true | 894 | 922 | | +| for_loop_scope | true | 901 | 907 | | +| for_loop_scope | true | 907 | 909 | | +| for_loop_scope | true | 909 | 919 | T | +| for_loop_scope | true | 909 | 937 | F | +| for_loop_scope | true | 916 | 942 | | +| for_loop_scope | true | 919 | 916 | | +| for_loop_scope | true | 922 | 901 | | +| for_loop_scope | true | 925 | 928 | | +| for_loop_scope | true | 928 | 901 | | +| for_loop_scope | true | 931 | 947 | | +| for_loop_scope | true | 934 | 887 | | +| for_loop_scope | true | 937 | 940 | | +| for_loop_scope | true | 940 | 931 | | +| for_loop_scope | true | 942 | 945 | | +| for_loop_scope | true | 945 | 925 | | +| for_loop_scope | true | 947 | 950 | | +| for_loop_scope | true | 950 | 878 | | +| gotos | false | 748 | 748 | gotos | +| gotos | false | 757 | 757 | declaration | +| gotos | false | 764 | 764 | if (...) ... | +| gotos | false | 767 | 767 | x | +| gotos | false | 770 | 770 | (bool)... | +| gotos | false | 772 | 772 | goto ... | +| gotos | false | 775 | 775 | x | +| gotos | false | 778 | 778 | ++ ... | +| gotos | false | 781 | 781 | initializer for y | +| gotos | false | 796 | 796 | declaration | +| gotos | false | 799 | 799 | label ...: | +| gotos | false | 802 | 802 | declaration | +| gotos | false | 805 | 805 | if (...) ... | +| gotos | false | 808 | 808 | y | +| gotos | false | 811 | 811 | (bool)... | +| gotos | false | 813 | 813 | goto ... | +| gotos | false | 816 | 816 | declaration | +| gotos | false | 819 | 819 | { ... } | +| gotos | false | 822 | 822 | label ...: | +| gotos | false | 825 | 825 | ExprStmt | +| gotos | false | 828 | 828 | x | +| gotos | false | 831 | 831 | -- ... | +| gotos | false | 834 | 834 | return ... | +| gotos | false | 837 | 837 | { ... } | +| gotos | false | 840 | 840 | nt2 | +| gotos | false | 843 | 843 | call to nt2.~NonTrivial | +| gotos | false | 845 | 845 | nt3 | +| gotos | false | 847 | 847 | call to nt3.~NonTrivial | +| gotos | false | 849 | 849 | nt2 | +| gotos | false | 851 | 851 | call to nt2.~NonTrivial | +| gotos | false | 853 | 853 | nt1 | +| gotos | false | 856 | 856 | call to nt1.~NonTrivial | +| gotos | true | 757 | 764 | | +| gotos | true | 764 | 767 | | +| gotos | true | 767 | 772 | T | +| gotos | true | 767 | 819 | F | +| gotos | true | 772 | 799 | | +| gotos | true | 775 | 778 | | +| gotos | true | 778 | 799 | | +| gotos | true | 781 | 775 | | +| gotos | true | 796 | 781 | | +| gotos | true | 796 | 799 | | +| gotos | true | 799 | 802 | | +| gotos | true | 802 | 805 | | +| gotos | true | 805 | 808 | | +| gotos | true | 808 | 813 | T | +| gotos | true | 808 | 816 | F | +| gotos | true | 813 | 849 | | +| gotos | true | 816 | 845 | | +| gotos | true | 819 | 796 | | +| gotos | true | 822 | 825 | | +| gotos | true | 825 | 828 | | +| gotos | true | 828 | 831 | | +| gotos | true | 831 | 834 | | +| gotos | true | 834 | 853 | | +| gotos | true | 837 | 757 | | +| gotos | true | 840 | 843 | | +| gotos | true | 843 | 822 | | +| gotos | true | 845 | 847 | | +| gotos | true | 847 | 840 | | +| gotos | true | 849 | 851 | | +| gotos | true | 851 | 822 | | +| gotos | true | 853 | 856 | | +| gotos | true | 856 | 748 | | +| if_decl_bind | false | 464 | 464 | if_decl_bind | +| if_decl_bind | false | 473 | 473 | if (...) ... | +| if_decl_bind | false | 477 | 477 | call to operator int | +| if_decl_bind | false | 481 | 481 | call to BoxedInt | +| if_decl_bind | false | 484 | 484 | x | +| if_decl_bind | false | 487 | 487 | initializer for bi | +| if_decl_bind | false | 491 | 491 | bi | +| if_decl_bind | false | 494 | 494 | (bool)... | +| if_decl_bind | false | 496 | 496 | (condition decl) | +| if_decl_bind | false | 499 | 499 | ExprStmt | +| if_decl_bind | false | 502 | 502 | bi | +| if_decl_bind | false | 505 | 505 | m_ptr | +| if_decl_bind | false | 508 | 508 | * ... | +| if_decl_bind | false | 511 | 511 | ++ ... | +| if_decl_bind | false | 514 | 514 | { ... } | +| if_decl_bind | false | 517 | 517 | ExprStmt | +| if_decl_bind | false | 520 | 520 | bi | +| if_decl_bind | false | 523 | 523 | m_ptr | +| if_decl_bind | false | 526 | 526 | * ... | +| if_decl_bind | false | 529 | 529 | -- ... | +| if_decl_bind | false | 532 | 532 | { ... } | +| if_decl_bind | false | 535 | 535 | ExprStmt | +| if_decl_bind | false | 538 | 538 | x | +| if_decl_bind | false | 544 | 544 | 1 | +| if_decl_bind | false | 546 | 546 | ... = ... | +| if_decl_bind | false | 549 | 549 | return ... | +| if_decl_bind | false | 552 | 552 | { ... } | +| if_decl_bind | false | 555 | 555 | bi | +| if_decl_bind | false | 558 | 558 | call to bi.~BoxedInt | +| if_decl_bind | true | 473 | 484 | | +| if_decl_bind | true | 481 | 496 | | +| if_decl_bind | true | 484 | 481 | | +| if_decl_bind | true | 496 | 514 | T | +| if_decl_bind | true | 496 | 532 | F | +| if_decl_bind | true | 499 | 502 | | +| if_decl_bind | true | 502 | 505 | | +| if_decl_bind | true | 505 | 508 | | +| if_decl_bind | true | 508 | 511 | | +| if_decl_bind | true | 511 | 555 | | +| if_decl_bind | true | 514 | 499 | | +| if_decl_bind | true | 517 | 520 | | +| if_decl_bind | true | 520 | 523 | | +| if_decl_bind | true | 523 | 526 | | +| if_decl_bind | true | 526 | 529 | | +| if_decl_bind | true | 529 | 555 | | +| if_decl_bind | true | 532 | 517 | | +| if_decl_bind | true | 535 | 544 | | +| if_decl_bind | true | 538 | 546 | | +| if_decl_bind | true | 544 | 538 | | +| if_decl_bind | true | 546 | 549 | | +| if_decl_bind | true | 549 | 464 | | +| if_decl_bind | true | 552 | 473 | | +| if_decl_bind | true | 555 | 558 | | +| if_decl_bind | true | 558 | 535 | | +| never_destructs | false | 857 | 857 | never_destructs | +| never_destructs | false | 863 | 863 | declaration | +| never_destructs | false | 870 | 870 | label ...: | +| never_destructs | false | 873 | 873 | goto ... | +| never_destructs | false | 876 | 876 | { ... } | +| never_destructs | true | 863 | 870 | | +| never_destructs | true | 870 | 873 | | +| never_destructs | true | 873 | 870 | | +| never_destructs | true | 876 | 863 | | +| operator delete | false | 601 | 601 | operator delete | +| operator new | false | 632 | 632 | operator new | +| simple | false | 1126 | 1126 | simple | +| simple | false | 1132 | 1132 | declaration | +| simple | false | 1139 | 1139 | return ... | +| simple | false | 1142 | 1142 | { ... } | +| simple | false | 1145 | 1145 | nt | +| simple | false | 1148 | 1148 | call to nt.~NonTrivial | +| simple | true | 1132 | 1139 | | +| simple | true | 1139 | 1145 | | +| simple | true | 1142 | 1132 | | +| simple | true | 1145 | 1148 | | +| simple | true | 1148 | 1126 | | +| simple2 | false | 1092 | 1092 | simple2 | +| simple2 | false | 1098 | 1098 | declaration | +| simple2 | false | 1105 | 1105 | declaration | +| simple2 | false | 1112 | 1112 | return ... | +| simple2 | false | 1115 | 1115 | { ... } | +| simple2 | false | 1118 | 1118 | one | +| simple2 | false | 1121 | 1121 | call to one.~NonTrivial | +| simple2 | false | 1123 | 1123 | two | +| simple2 | false | 1125 | 1125 | call to two.~NonTrivial | +| simple2 | true | 1098 | 1105 | | +| simple2 | true | 1105 | 1112 | | +| simple2 | true | 1112 | 1123 | | +| simple2 | true | 1115 | 1098 | | +| simple2 | true | 1118 | 1121 | | +| simple2 | true | 1121 | 1092 | | +| simple2 | true | 1123 | 1125 | | +| simple2 | true | 1125 | 1118 | | +| switch_decl_bind | false | 316 | 316 | switch_decl_bind | +| switch_decl_bind | false | 325 | 325 | switch (...) ... | +| switch_decl_bind | false | 329 | 329 | call to operator int | +| switch_decl_bind | false | 333 | 333 | call to BoxedInt | +| switch_decl_bind | false | 336 | 336 | x | +| switch_decl_bind | false | 339 | 339 | initializer for bi | +| switch_decl_bind | false | 343 | 343 | bi | +| switch_decl_bind | false | 346 | 346 | (condition decl) | +| switch_decl_bind | false | 349 | 349 | case ...: | +| switch_decl_bind | false | 355 | 355 | 0 | +| switch_decl_bind | false | 357 | 357 | ExprStmt | +| switch_decl_bind | false | 360 | 360 | bi | +| switch_decl_bind | false | 364 | 364 | m_ptr | +| switch_decl_bind | false | 368 | 368 | * ... | +| switch_decl_bind | false | 371 | 371 | -- ... | +| switch_decl_bind | false | 374 | 374 | break; | +| switch_decl_bind | false | 377 | 377 | case ...: | +| switch_decl_bind | false | 383 | 383 | 1 | +| switch_decl_bind | false | 385 | 385 | ExprStmt | +| switch_decl_bind | false | 388 | 388 | bi | +| switch_decl_bind | false | 391 | 391 | m_ptr | +| switch_decl_bind | false | 394 | 394 | * ... | +| switch_decl_bind | false | 397 | 397 | ++ ... | +| switch_decl_bind | false | 400 | 400 | break; | +| switch_decl_bind | false | 403 | 403 | default: | +| switch_decl_bind | false | 406 | 406 | ExprStmt | +| switch_decl_bind | false | 409 | 409 | bi | +| switch_decl_bind | false | 412 | 412 | m_ptr | +| switch_decl_bind | false | 415 | 415 | * ... | +| switch_decl_bind | false | 421 | 421 | 2 | +| switch_decl_bind | false | 423 | 423 | ... /= ... | +| switch_decl_bind | false | 426 | 426 | { ... } | +| switch_decl_bind | false | 429 | 429 | label ...: | +| switch_decl_bind | false | 432 | 432 | ExprStmt | +| switch_decl_bind | false | 435 | 435 | x | +| switch_decl_bind | false | 441 | 441 | 1 | +| switch_decl_bind | false | 443 | 443 | ... = ... | +| switch_decl_bind | false | 446 | 446 | return ... | +| switch_decl_bind | false | 449 | 449 | { ... } | +| switch_decl_bind | false | 452 | 452 | bi | +| switch_decl_bind | false | 455 | 455 | call to bi.~BoxedInt | +| switch_decl_bind | false | 457 | 457 | bi | +| switch_decl_bind | false | 459 | 459 | call to bi.~BoxedInt | +| switch_decl_bind | false | 461 | 461 | bi | +| switch_decl_bind | false | 463 | 463 | call to bi.~BoxedInt | +| switch_decl_bind | true | 325 | 336 | | +| switch_decl_bind | true | 333 | 346 | | +| switch_decl_bind | true | 336 | 333 | | +| switch_decl_bind | true | 346 | 426 | | +| switch_decl_bind | true | 349 | 357 | | +| switch_decl_bind | true | 357 | 360 | | | switch_decl_bind | true | 360 | 364 | | -| switch_decl_bind | true | 362 | 354 | | -| switch_decl_bind | true | 364 | 403 | | -| switch_decl_bind | true | 366 | 379 | | -| switch_decl_bind | true | 368 | 370 | | -| switch_decl_bind | true | 370 | 372 | | -| switch_decl_bind | true | 372 | 376 | | -| switch_decl_bind | true | 376 | 377 | | -| switch_decl_bind | true | 377 | 400 | | -| switch_decl_bind | true | 379 | 368 | | -| switch_decl_bind | true | 381 | 333 | | -| switch_decl_bind | true | 381 | 352 | | -| switch_decl_bind | true | 381 | 366 | | -| switch_decl_bind | true | 383 | 321 | | -| switch_decl_bind | true | 385 | 394 | | -| switch_decl_bind | true | 387 | 392 | | -| switch_decl_bind | true | 391 | 387 | | -| switch_decl_bind | true | 392 | 396 | | -| switch_decl_bind | true | 394 | 391 | | -| switch_decl_bind | true | 396 | 308 | | -| switch_decl_bind | true | 398 | 383 | | -| switch_decl_bind | true | 400 | 402 | | -| switch_decl_bind | true | 402 | 385 | | -| switch_decl_bind | true | 403 | 404 | | -| switch_decl_bind | true | 404 | 385 | | -| switch_decl_bind | true | 405 | 406 | | -| switch_decl_bind | true | 406 | 385 | | -| while_decl_bind | false | 260 | 260 | while_decl_bind | -| while_decl_bind | false | 269 | 269 | call to operator int | -| while_decl_bind | false | 271 | 271 | call to BoxedInt | -| while_decl_bind | false | 273 | 273 | x | -| while_decl_bind | false | 275 | 275 | initializer for bi | -| while_decl_bind | false | 278 | 278 | bi | -| while_decl_bind | false | 280 | 280 | (bool)... | -| while_decl_bind | false | 281 | 281 | (condition decl) | -| while_decl_bind | false | 283 | 283 | x | -| while_decl_bind | false | 285 | 285 | -- ... | -| while_decl_bind | false | 287 | 287 | ExprStmt | -| while_decl_bind | false | 289 | 289 | { ... } | -| while_decl_bind | false | 291 | 291 | while (...) ... | -| while_decl_bind | false | 293 | 293 | x | -| while_decl_bind | false | 295 | 295 | ++ ... | -| while_decl_bind | false | 297 | 297 | ExprStmt | -| while_decl_bind | false | 299 | 299 | return ... | -| while_decl_bind | false | 301 | 301 | { ... } | -| while_decl_bind | false | 303 | 303 | bi | -| while_decl_bind | false | 305 | 305 | call to bi.~BoxedInt | -| while_decl_bind | false | 306 | 306 | bi | -| while_decl_bind | false | 307 | 307 | call to bi.~BoxedInt | -| while_decl_bind | true | 271 | 281 | | -| while_decl_bind | true | 273 | 271 | | -| while_decl_bind | true | 281 | 289 | T | -| while_decl_bind | true | 281 | 303 | F | -| while_decl_bind | true | 283 | 285 | | -| while_decl_bind | true | 285 | 306 | | -| while_decl_bind | true | 287 | 283 | | -| while_decl_bind | true | 289 | 287 | | -| while_decl_bind | true | 291 | 273 | | -| while_decl_bind | true | 293 | 295 | | -| while_decl_bind | true | 295 | 299 | | -| while_decl_bind | true | 297 | 293 | | -| while_decl_bind | true | 299 | 260 | | -| while_decl_bind | true | 301 | 291 | | -| while_decl_bind | true | 303 | 305 | | -| while_decl_bind | true | 305 | 297 | | -| while_decl_bind | true | 306 | 307 | | -| while_decl_bind | true | 307 | 273 | | +| switch_decl_bind | true | 364 | 368 | | +| switch_decl_bind | true | 368 | 371 | | +| switch_decl_bind | true | 371 | 374 | | +| switch_decl_bind | true | 374 | 457 | | +| switch_decl_bind | true | 377 | 385 | | +| switch_decl_bind | true | 385 | 388 | | +| switch_decl_bind | true | 388 | 391 | | +| switch_decl_bind | true | 391 | 394 | | +| switch_decl_bind | true | 394 | 397 | | +| switch_decl_bind | true | 397 | 400 | | +| switch_decl_bind | true | 400 | 461 | | +| switch_decl_bind | true | 403 | 406 | | +| switch_decl_bind | true | 406 | 409 | | +| switch_decl_bind | true | 409 | 412 | | +| switch_decl_bind | true | 412 | 415 | | +| switch_decl_bind | true | 415 | 421 | | +| switch_decl_bind | true | 421 | 423 | | +| switch_decl_bind | true | 423 | 452 | | +| switch_decl_bind | true | 426 | 349 | | +| switch_decl_bind | true | 426 | 377 | | +| switch_decl_bind | true | 426 | 403 | | +| switch_decl_bind | true | 429 | 432 | | +| switch_decl_bind | true | 432 | 441 | | +| switch_decl_bind | true | 435 | 443 | | +| switch_decl_bind | true | 441 | 435 | | +| switch_decl_bind | true | 443 | 446 | | +| switch_decl_bind | true | 446 | 316 | | +| switch_decl_bind | true | 449 | 325 | | +| switch_decl_bind | true | 452 | 455 | | +| switch_decl_bind | true | 455 | 429 | | +| switch_decl_bind | true | 457 | 459 | | +| switch_decl_bind | true | 459 | 429 | | +| switch_decl_bind | true | 461 | 463 | | +| switch_decl_bind | true | 463 | 429 | | +| while_decl_bind | false | 246 | 246 | while_decl_bind | +| while_decl_bind | false | 255 | 255 | while (...) ... | +| while_decl_bind | false | 259 | 259 | call to operator int | +| while_decl_bind | false | 263 | 263 | call to BoxedInt | +| while_decl_bind | false | 266 | 266 | x | +| while_decl_bind | false | 269 | 269 | initializer for bi | +| while_decl_bind | false | 273 | 273 | bi | +| while_decl_bind | false | 276 | 276 | (bool)... | +| while_decl_bind | false | 278 | 278 | (condition decl) | +| while_decl_bind | false | 281 | 281 | ExprStmt | +| while_decl_bind | false | 284 | 284 | x | +| while_decl_bind | false | 287 | 287 | -- ... | +| while_decl_bind | false | 290 | 290 | { ... } | +| while_decl_bind | false | 293 | 293 | ExprStmt | +| while_decl_bind | false | 296 | 296 | x | +| while_decl_bind | false | 299 | 299 | ++ ... | +| while_decl_bind | false | 302 | 302 | return ... | +| while_decl_bind | false | 305 | 305 | { ... } | +| while_decl_bind | false | 308 | 308 | bi | +| while_decl_bind | false | 311 | 311 | call to bi.~BoxedInt | +| while_decl_bind | false | 313 | 313 | bi | +| while_decl_bind | false | 315 | 315 | call to bi.~BoxedInt | +| while_decl_bind | true | 255 | 266 | | +| while_decl_bind | true | 263 | 278 | | +| while_decl_bind | true | 266 | 263 | | +| while_decl_bind | true | 278 | 290 | T | +| while_decl_bind | true | 278 | 308 | F | +| while_decl_bind | true | 281 | 284 | | +| while_decl_bind | true | 284 | 287 | | +| while_decl_bind | true | 287 | 313 | | +| while_decl_bind | true | 290 | 281 | | +| while_decl_bind | true | 293 | 296 | | +| while_decl_bind | true | 296 | 299 | | +| while_decl_bind | true | 299 | 302 | | +| while_decl_bind | true | 302 | 246 | | +| while_decl_bind | true | 305 | 255 | | +| while_decl_bind | true | 308 | 311 | | +| while_decl_bind | true | 311 | 293 | | +| while_decl_bind | true | 313 | 315 | | +| while_decl_bind | true | 315 | 266 | | diff --git a/cpp/ql/test/successor-tests/whilestmt/whilestmt04.ql b/cpp/ql/test/successor-tests/whilestmt/whilestmt04.ql index 1ae9f4931a57..1e7c43de9931 100644 --- a/cpp/ql/test/successor-tests/whilestmt/whilestmt04.ql +++ b/cpp/ql/test/successor-tests/whilestmt/whilestmt04.ql @@ -8,7 +8,7 @@ import cpp from WhileStmt ws, ExprStmt last, Expr succ where ws.getEnclosingFunction().hasName("normal") and - last = ws.getStmt().(Block).getLastStmt() and + last = ws.getStmt().(BlockStmt).getLastStmt() and succ = last.getExpr().getASuccessor() and succ = ws.getCondition().getAChild*() and count(last.getExpr().getASuccessor()) = 1 diff --git a/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/old.dbscheme b/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/old.dbscheme new file mode 100644 index 000000000000..025827d85c3f --- /dev/null +++ b/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/old.dbscheme @@ -0,0 +1,2109 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; +successors( + int from: @cfgnode ref, + int to: @cfgnode ref +); + +truecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +falsecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/semmlecode.cpp.dbscheme b/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/semmlecode.cpp.dbscheme new file mode 100644 index 000000000000..098850d25c4e --- /dev/null +++ b/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/semmlecode.cpp.dbscheme @@ -0,0 +1,2110 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; +successors( + int from: @cfgnode ref, + int to: @cfgnode ref +); + +truecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +falsecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/static_asserts.ql b/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/static_asserts.ql new file mode 100644 index 000000000000..e5ec84fd76e4 --- /dev/null +++ b/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/static_asserts.ql @@ -0,0 +1,21 @@ +class StaticAssert extends @static_assert { + string toString() { none() } +} + +class Expr extends @expr { + string toString() { none() } +} + +class Location extends @location_default { + string toString() { none() } +} + +class NameSpace extends @namespace { + string toString() { none() } +} + +from StaticAssert sa, Expr condition, string message, Location loc, NameSpace ns +where + static_asserts(sa, condition, message, loc) and + namespaces(ns, "") +select sa, condition, message, loc, ns diff --git a/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/upgrade.properties b/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/upgrade.properties new file mode 100644 index 000000000000..ca1074f5a4de --- /dev/null +++ b/cpp/upgrades/025827d85c3f44a7fd52d4fad636e9a4141f12dd/upgrade.properties @@ -0,0 +1,4 @@ +description: Give static_assert's an enclosing element +compatibility: partial +static_asserts.rel: run static_asserts.qlo + diff --git a/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/old.dbscheme b/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/old.dbscheme new file mode 100644 index 000000000000..098850d25c4e --- /dev/null +++ b/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/old.dbscheme @@ -0,0 +1,2110 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; +successors( + int from: @cfgnode ref, + int to: @cfgnode ref +); + +truecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +falsecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/semmlecode.cpp.dbscheme b/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/semmlecode.cpp.dbscheme new file mode 100644 index 000000000000..75da61c94e19 --- /dev/null +++ b/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/semmlecode.cpp.dbscheme @@ -0,0 +1,2096 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/upgrade.properties b/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/upgrade.properties new file mode 100644 index 000000000000..474d82826f82 --- /dev/null +++ b/cpp/upgrades/098850d25c4e9d417eb74c1bef9deb2f9d2dc417/upgrade.properties @@ -0,0 +1,3 @@ +description: Remove the old CFG tables +compatibility: full + diff --git a/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/member_function_this_type.ql b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/member_function_this_type.ql new file mode 100644 index 000000000000..2e99f1ed5f00 --- /dev/null +++ b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/member_function_this_type.ql @@ -0,0 +1,48 @@ +/* + * Upgrade script to populate the member_function_this_type table. It's a rough + * approximation of what the extractor would do - for a member function C::f, + * we say its type is C* (if that pointer type exists in the database). + * CV-qualifiers are ignored. + */ + +class Class extends @usertype { + Class() { + usertypes(this, _, 1) or + usertypes(this, _, 2) or + usertypes(this, _, 3) or + usertypes(this, _, 6) or + usertypes(this, _, 10) or + usertypes(this, _, 11) or + usertypes(this, _, 12) + } + + string toString() { usertypes(this, result, _) } +} + +class ClassPointerType extends @derivedtype { + ClassPointerType() { derivedtypes(this, _, 1, _) } + + Class getBaseType() { derivedtypes(this, _, _, result) } + + string toString() { result = getBaseType().toString() + "*" } +} + +class DefinedMemberFunction extends @function { + DefinedMemberFunction() { + exists(@fun_decl fd | + fun_def(fd) and + ( + fun_decls(fd, this, _, _, _) + or + exists(@function f | function_instantiation(this, f) and fun_decls(fd, f, _, _, _)) + ) + ) + } + + ClassPointerType getTypeOfThis() { member(result.getBaseType(), _, this) } + + string toString() { functions(this, result, _) } +} + +from DefinedMemberFunction f +select f, f.getTypeOfThis() diff --git a/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/old.dbscheme b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/old.dbscheme new file mode 100644 index 000000000000..282c13bfdbcb --- /dev/null +++ b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/old.dbscheme @@ -0,0 +1,2109 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + + + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; +successors( + int from: @cfgnode ref, + int to: @cfgnode ref +); + +truecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +falsecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/semmlecode.cpp.dbscheme b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/semmlecode.cpp.dbscheme new file mode 100644 index 000000000000..025827d85c3f --- /dev/null +++ b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/semmlecode.cpp.dbscheme @@ -0,0 +1,2109 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; +successors( + int from: @cfgnode ref, + int to: @cfgnode ref +); + +truecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +falsecond( + unique int from: @cfgnode ref, + int to: @cfgnode ref +); + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/upgrade.properties b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/upgrade.properties new file mode 100644 index 000000000000..018a42e6dc19 --- /dev/null +++ b/cpp/upgrades/282c13bfdbcbd57a887972b47a471342a4ad5507/upgrade.properties @@ -0,0 +1,3 @@ +description: Add table relating a member function to the type of `this`. +compatibility: partial +member_function_this_type.rel: run member_function_this_type.qlo diff --git a/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/old.dbscheme b/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/old.dbscheme new file mode 100644 index 000000000000..75da61c94e19 --- /dev/null +++ b/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/old.dbscheme @@ -0,0 +1,2096 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/semmlecode.cpp.dbscheme b/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/semmlecode.cpp.dbscheme new file mode 100644 index 000000000000..b5fa4fb0283c --- /dev/null +++ b/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/semmlecode.cpp.dbscheme @@ -0,0 +1,2101 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/upgrade.properties b/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/upgrade.properties new file mode 100644 index 000000000000..c935a87cb40c --- /dev/null +++ b/cpp/upgrades/75da61c94e19ae80a142f03a877ab9d0728752bc/upgrade.properties @@ -0,0 +1,3 @@ +description: Add some coroutines types (@co_await, @co_yield, @stmt_co_return) +compatibility: backwards + diff --git a/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/old.dbscheme b/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/old.dbscheme new file mode 100644 index 000000000000..b5fa4fb0283c --- /dev/null +++ b/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/old.dbscheme @@ -0,0 +1,2101 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/semmlecode.cpp.dbscheme b/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/semmlecode.cpp.dbscheme new file mode 100644 index 000000000000..ef73d8cf906d --- /dev/null +++ b/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/semmlecode.cpp.dbscheme @@ -0,0 +1,2123 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +/** If `function` is a coroutine, then this gives the + std::experimental::resumable_traits instance associated with it, + and the variables representing the `handle` and `promise` for it. */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +decltypes( + unique int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/upgrade.properties b/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/upgrade.properties new file mode 100644 index 000000000000..d089781baafc --- /dev/null +++ b/cpp/upgrades/b5fa4fb0283c4accf2d85d559aeb2bba914c102b/upgrade.properties @@ -0,0 +1,3 @@ +description: Add coroutines metadata tables +compatibility: backwards + diff --git a/csharp/.editorconfig b/csharp/.editorconfig new file mode 100644 index 000000000000..3705b7144e33 --- /dev/null +++ b/csharp/.editorconfig @@ -0,0 +1,273 @@ +# Taken as is from https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2019 +# Naming rules are removed. +# Customizations are added at the bottom of the file. + +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = {cr|lf|crlf} # we should use what git uses +insert_final_newline = true + +#### .NET Coding Conventions #### + +# Organize usings - not specified +# dotnet_separate_import_directive_groups = false +# dotnet_sort_system_directives_first = false + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + + +#### Naming rules #### + +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# Private fields are camelCase +dotnet_naming_rule.private_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.private_fields_should_be_pascal_case.symbols = private_fields +dotnet_naming_rule.private_fields_should_be_pascal_case.style = camel_case_style + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +# Locals and parameters are camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style + +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local + +# Interfaces are PascalCase, prefixed with I + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = * + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +# Type parameters are PascalCase, prefixed with T + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = type_parameter +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_t + +dotnet_naming_symbols.type_parameter.applicable_kinds = type_parameter +dotnet_naming_symbols.type_parameter.applicable_accessibilities = * + +dotnet_naming_style.begins_with_t.required_prefix = T +dotnet_naming_style.begins_with_t.capitalization = pascal_case + +# By default, name items with PascalCase +dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members +dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.all_members.applicable_kinds = * + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +#### End naming rules #### + + + +## The below are taken from Roslyn: https://github.com/dotnet/roslyn/blob/master/.editorconfig + +# IDE0055: Fix formatting +dotnet_diagnostic.IDE0055.severity = warning + +# IDE0011: Add braces +csharp_prefer_braces = when_multiline:warning +# NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201 +dotnet_diagnostic.IDE0011.severity = warning + +# IDE0040: Add accessibility modifiers +dotnet_diagnostic.IDE0040.severity = warning + +# CONSIDER: Are IDE0051 and IDE0052 too noisy to be warnings for IDE editing scenarios? Should they be made build-only warnings? +# IDE0051: Remove unused private member +dotnet_diagnostic.IDE0051.severity = warning + +# IDE0052: Remove unread private member +dotnet_diagnostic.IDE0052.severity = warning + +# IDE0059: Unnecessary assignment to a value +dotnet_diagnostic.IDE0059.severity = warning + +# IDE0060: Remove unused parameter +dotnet_diagnostic.IDE0060.severity = warning + +# CA1822: Make member static +dotnet_diagnostic.CA1822.severity = warning + +# IDE0035: Remove unreachable code +dotnet_diagnostic.IDE0035.severity = warning + +# IDE0036: Order modifiers +dotnet_diagnostic.IDE0036.severity = warning + +# IDE0043: Format string contains invalid placeholder +dotnet_diagnostic.IDE0043.severity = warning + +# IDE0044: Make field readonly +dotnet_diagnostic.IDE0044.severity = warning + +# Prefer "var" everywhere +dotnet_diagnostic.IDE0007.severity = suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# +# Customizations +# + +[extractor/Semmle.Extraction/Tuples.cs, + extractor/Semmle.Extraction.CSharp/Tuples.cs, + extractor/Semmle.Extraction.CIL/Tuples.cs] +dotnet_naming_rule.members_should_be_pascal_case.severity = none \ No newline at end of file diff --git a/csharp/.gitignore b/csharp/.gitignore index f81ecc73dffa..0701c11fe1d8 100644 --- a/csharp/.gitignore +++ b/csharp/.gitignore @@ -10,4 +10,5 @@ csharp.log **/bin/Release *.tlog .vs -*.user \ No newline at end of file +*.user +.vscode/launch.json \ No newline at end of file diff --git a/csharp/.vscode/extensions.json b/csharp/.vscode/extensions.json new file mode 100644 index 000000000000..f05b9cdf715c --- /dev/null +++ b/csharp/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "github.vscode-codeql", + "ms-dotnettools.csharp", + "formulahendry.dotnet-test-explorer", + "hbenl.vscode-test-explorer" + ], + "unwantedRecommendations": [] +} \ No newline at end of file diff --git a/csharp/.vscode/settings.json b/csharp/.vscode/settings.json new file mode 100644 index 000000000000..dd6b2b3baf88 --- /dev/null +++ b/csharp/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "dotnet-test-explorer.enableTelemetry": false, + "dotnet-test-explorer.testProjectPath": "**/*Tests.@(csproj|vbproj|fsproj)", + "dotnet-test-explorer.testArguments": "/property:GenerateTargetFrameworkAttribute=false", + "csharp.suppressDotnetRestoreNotification": true, + "[csharp]": { + "editor.defaultFormatter": "ms-dotnettools.csharp" + }, + "omnisharp.enableEditorConfigSupport": true, + "omnisharp.enableRoslynAnalyzers": true +} \ No newline at end of file diff --git a/csharp/.vscode/tasks.json b/csharp/.vscode/tasks.json new file mode 100644 index 000000000000..d3a0553dc440 --- /dev/null +++ b/csharp/.vscode/tasks.json @@ -0,0 +1,53 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "dotnet build", + "command": "dotnet", + "type": "shell", + "args": [ + "build", + // Ask dotnet build to generate full paths for file names. + "/property:GenerateFullPaths=true", + // Do not generate summary otherwise it leads to duplicate errors in Problems panel + "/consoleloggerparameters:NoSummary" + ], + "group": "build", + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "dotnet rebuild", + "command": "dotnet", + "type": "shell", + "args": [ + "build", + "--no-incremental", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "group": "build", + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "dotnet test", + "command": "dotnet", + "type": "shell", + "args": [ + "test", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "group": "test", + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/csharp/CSharp.sln b/csharp/CSharp.sln index 78d853a5bbea..7762dd469b90 100644 --- a/csharp/CSharp.sln +++ b/csharp/CSharp.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CIL", "extractor\Semmle.Extraction.CIL\Semmle.Extraction.CIL.csproj", "{399A1579-68F0-40F4-9A23-F241BA697F9C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Autobuild", "autobuilder\Semmle.Autobuild\Semmle.Autobuild.csproj", "{5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CSharp.Standalone", "extractor\Semmle.Extraction.CSharp.Standalone\Semmle.Extraction.CSharp.Standalone.csproj", "{D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.CIL.Driver", "extractor\Semmle.Extraction.CIL.Driver\Semmle.Extraction.CIL.Driver.csproj", "{EFA400B3-C1CE-446F-A4E2-8B44E61EF47C}" @@ -23,7 +21,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Extraction.Tests", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Util.Tests", "extractor\Semmle.Util.Tests\Semmle.Util.Tests.csproj", "{55A620F0-23F6-440D-A5BA-0567613B3C0F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semmle.Autobuild.Tests", "autobuilder\Semmle.Autobuild.Tests\Semmle.Autobuild.Tests.csproj", "{CE267461-D762-4F53-A275-685A0A4EC48D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.Shared", "autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj", "{133F2B5B-FD25-4BD9-B34C-062CC6BB4178}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.CSharp", "autobuilder\Semmle.Autobuild.CSharp\Semmle.Autobuild.CSharp.csproj", "{F3C07863-3759-4A0B-B777-8A0E0FDB1A41}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semmle.Autobuild.CSharp.Tests", "autobuilder\Semmle.Autobuild.CSharp.Tests\Semmle.Autobuild.CSharp.Tests.csproj", "{34256E8F-866A-46C1-800E-3DF69FD1DCB7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -47,10 +49,6 @@ Global {399A1579-68F0-40F4-9A23-F241BA697F9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {399A1579-68F0-40F4-9A23-F241BA697F9C}.Release|Any CPU.ActiveCfg = Release|Any CPU {399A1579-68F0-40F4-9A23-F241BA697F9C}.Release|Any CPU.Build.0 = Release|Any CPU - {5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5131EF00-0BA9-4436-A3B0-C5CDAB4B194C}.Release|Any CPU.Build.0 = Release|Any CPU {D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}.Debug|Any CPU.Build.0 = Debug|Any CPU {D00E7D25-0FA0-48EC-B048-CD60CE1B30D8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -69,10 +67,18 @@ Global {55A620F0-23F6-440D-A5BA-0567613B3C0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {55A620F0-23F6-440D-A5BA-0567613B3C0F}.Debug|Any CPU.Build.0 = Debug|Any CPU {55A620F0-23F6-440D-A5BA-0567613B3C0F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE267461-D762-4F53-A275-685A0A4EC48D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE267461-D762-4F53-A275-685A0A4EC48D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE267461-D762-4F53-A275-685A0A4EC48D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE267461-D762-4F53-A275-685A0A4EC48D}.Release|Any CPU.Build.0 = Release|Any CPU + {133F2B5B-FD25-4BD9-B34C-062CC6BB4178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {133F2B5B-FD25-4BD9-B34C-062CC6BB4178}.Debug|Any CPU.Build.0 = Debug|Any CPU + {133F2B5B-FD25-4BD9-B34C-062CC6BB4178}.Release|Any CPU.ActiveCfg = Release|Any CPU + {133F2B5B-FD25-4BD9-B34C-062CC6BB4178}.Release|Any CPU.Build.0 = Release|Any CPU + {F3C07863-3759-4A0B-B777-8A0E0FDB1A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3C07863-3759-4A0B-B777-8A0E0FDB1A41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3C07863-3759-4A0B-B777-8A0E0FDB1A41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3C07863-3759-4A0B-B777-8A0E0FDB1A41}.Release|Any CPU.Build.0 = Release|Any CPU + {34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34256E8F-866A-46C1-800E-3DF69FD1DCB7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs new file mode 100644 index 000000000000..7b9f2e788811 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -0,0 +1,1130 @@ +īģŋusing Xunit; +using Semmle.Autobuild.Shared; +using System.Collections.Generic; +using System; +using System.Linq; +using Microsoft.Build.Construction; +using System.Xml; +using System.IO; + +namespace Semmle.Autobuild.CSharp.Tests +{ + /// + /// Test class to script Autobuilder scenarios. + /// For most methods, it uses two fields: + /// - an IList to capture the the arguments passed to it + /// - an IDictionary of possible return values. + /// + internal class TestActions : IBuildActions + { + /// + /// List of strings passed to FileDelete. + /// + public IList FileDeleteIn { get; } = new List(); + + void IBuildActions.FileDelete(string file) + { + FileDeleteIn.Add(file); + } + + public IList FileExistsIn { get; } = new List(); + public IDictionary FileExists { get; } = new Dictionary(); + + bool IBuildActions.FileExists(string file) + { + FileExistsIn.Add(file); + if (FileExists.TryGetValue(file, out var ret)) + return ret; + + if (FileExists.TryGetValue(Path.GetFileName(file), out ret)) + return ret; + + throw new ArgumentException("Missing FileExists " + file); + } + + public IList RunProcessIn { get; } = new List(); + public IDictionary RunProcess { get; } = new Dictionary(); + public IDictionary RunProcessOut { get; } = new Dictionary(); + public IDictionary RunProcessWorkingDirectory { get; } = new Dictionary(); + public HashSet CreateDirectories { get; } = new HashSet(); + public HashSet<(string, string)> DownloadFiles { get; } = new HashSet<(string, string)>(); + + int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env, out IList stdOut) + { + var pattern = cmd + " " + args; + RunProcessIn.Add(pattern); + + if (!RunProcessOut.TryGetValue(pattern, out var str)) + throw new ArgumentException("Missing RunProcessOut " + pattern); + + stdOut = str.Split("\n"); + + RunProcessWorkingDirectory.TryGetValue(pattern, out var wd); + + if (wd != workingDirectory) + throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern); + + if (!RunProcess.TryGetValue(pattern, out var ret)) + throw new ArgumentException("Missing RunProcess " + pattern); + + return ret; + } + + int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env) + { + var pattern = cmd + " " + args; + RunProcessIn.Add(pattern); + RunProcessWorkingDirectory.TryGetValue(pattern, out var wd); + + if (wd != workingDirectory) + throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern); + + if (!RunProcess.TryGetValue(pattern, out var ret)) + throw new ArgumentException("Missing RunProcess " + pattern); + + return ret; + } + + public IList DirectoryDeleteIn { get; } = new List(); + + void IBuildActions.DirectoryDelete(string dir, bool recursive) + { + DirectoryDeleteIn.Add(dir); + } + + public IDictionary DirectoryExists { get; } = new Dictionary(); + + bool IBuildActions.DirectoryExists(string dir) + { + if (!DirectoryExists.TryGetValue(dir, out var ret)) + throw new ArgumentException("Missing DirectoryExists " + dir); + + return ret; + } + + public IDictionary GetEnvironmentVariable { get; } = new Dictionary(); + + string? IBuildActions.GetEnvironmentVariable(string name) + { + if (!GetEnvironmentVariable.TryGetValue(name, out var ret)) + throw new ArgumentException("Missing GetEnvironmentVariable " + name); + + return ret; + } + + public string GetCurrentDirectory { get; set; } = ""; + + string IBuildActions.GetCurrentDirectory() + { + return GetCurrentDirectory; + } + + public IDictionary EnumerateFiles { get; } = new Dictionary(); + + IEnumerable IBuildActions.EnumerateFiles(string dir) + { + if (!EnumerateFiles.TryGetValue(dir, out var str)) + throw new ArgumentException("Missing EnumerateFiles " + dir); + + return str.Split("\n").Select(p => PathCombine(dir, p)); + } + + public IDictionary EnumerateDirectories { get; } = new Dictionary(); + + IEnumerable IBuildActions.EnumerateDirectories(string dir) + { + if (!EnumerateDirectories.TryGetValue(dir, out var str)) + throw new ArgumentException("Missing EnumerateDirectories " + dir); + + return string.IsNullOrEmpty(str) + ? Enumerable.Empty() + : str.Split("\n").Select(p => PathCombine(dir, p)); + } + + public bool IsWindows { get; set; } + + bool IBuildActions.IsWindows() => IsWindows; + + public string PathCombine(params string[] parts) + { + return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p))); + } + + string IBuildActions.GetFullPath(string path) => path; + + string? IBuildActions.GetFileName(string? path) => Path.GetFileName(path?.Replace('\\', '/')); + + public string? GetDirectoryName(string? path) + { + var dir = Path.GetDirectoryName(path?.Replace('\\', '/')); + return dir is null ? path : path?.Substring(0, dir.Length); + } + + void IBuildActions.WriteAllText(string filename, string contents) + { + } + + public IDictionary LoadXml { get; } = new Dictionary(); + + XmlDocument IBuildActions.LoadXml(string filename) + { + if (!LoadXml.TryGetValue(filename, out var xml)) + throw new ArgumentException("Missing LoadXml " + filename); + return xml; + } + + public string EnvironmentExpandEnvironmentVariables(string s) + { + foreach (var kvp in GetEnvironmentVariable) + s = s.Replace($"%{kvp.Key}%", kvp.Value); + + return s; + } + + public void CreateDirectory(string path) + { + if (!CreateDirectories.Contains(path)) + throw new ArgumentException($"Missing CreateDirectory, {path}"); + } + + public void DownloadFile(string address, string fileName) + { + if (!DownloadFiles.Contains((address, fileName))) + throw new ArgumentException($"Missing DownloadFile, {address}, {fileName}"); + } + } + + /// + /// A fake solution to build. + /// + internal class TestSolution : ISolution + { + public IEnumerable Configurations => throw new NotImplementedException(); + + public string DefaultConfigurationName => "Release"; + + public string DefaultPlatformName => "x86"; + + public string FullPath { get; set; } + + public Version ToolsVersion => new Version("14.0"); + + public IEnumerable IncludedProjects => throw new NotImplementedException(); + + public TestSolution(string path) + { + FullPath = path; + } + } + + public class BuildScriptTests + { + private readonly TestActions actions = new TestActions(); + + // Records the arguments passed to StartCallback. + private readonly IList startCallbackIn = new List(); + + private void StartCallback(string s, bool silent) + { + startCallbackIn.Add(s); + } + + // Records the arguments passed to EndCallback + private readonly IList endCallbackIn = new List(); + private readonly IList endCallbackReturn = new List(); + + private void EndCallback(int ret, string s, bool silent) + { + endCallbackReturn.Add(ret); + endCallbackIn.Add(s); + } + + [Fact] + public void TestBuildCommand() + { + var cmd = BuildScript.Create("abc", "def ghi", false, null, null); + + actions.RunProcess["abc def ghi"] = 1; + cmd.Run(actions, StartCallback, EndCallback); + Assert.Equal("abc def ghi", actions.RunProcessIn[0]); + Assert.Equal("abc def ghi", startCallbackIn[0]); + Assert.Equal("", endCallbackIn[0]); + Assert.Equal(1, endCallbackReturn[0]); + } + + [Fact] + public void TestAnd1() + { + var cmd = BuildScript.Create("abc", "def ghi", false, null, null) & BuildScript.Create("odasa", null, false, null, null); + + actions.RunProcess["abc def ghi"] = 1; + cmd.Run(actions, StartCallback, EndCallback); + + Assert.Equal("abc def ghi", actions.RunProcessIn[0]); + Assert.Equal("abc def ghi", startCallbackIn[0]); + Assert.Equal("", endCallbackIn[0]); + Assert.Equal(1, endCallbackReturn[0]); + } + + [Fact] + public void TestAnd2() + { + var cmd = BuildScript.Create("odasa", null, false, null, null) & BuildScript.Create("abc", "def ghi", false, null, null); + + actions.RunProcess["abc def ghi"] = 1; + actions.RunProcess["odasa "] = 0; + cmd.Run(actions, StartCallback, EndCallback); + + Assert.Equal("odasa ", actions.RunProcessIn[0]); + Assert.Equal("odasa ", startCallbackIn[0]); + Assert.Equal("", endCallbackIn[0]); + Assert.Equal(0, endCallbackReturn[0]); + + Assert.Equal("abc def ghi", actions.RunProcessIn[1]); + Assert.Equal("abc def ghi", startCallbackIn[1]); + Assert.Equal("", endCallbackIn[1]); + Assert.Equal(1, endCallbackReturn[1]); + } + + [Fact] + public void TestOr1() + { + var cmd = BuildScript.Create("odasa", null, false, null, null) | BuildScript.Create("abc", "def ghi", false, null, null); + + actions.RunProcess["abc def ghi"] = 1; + actions.RunProcess["odasa "] = 0; + cmd.Run(actions, StartCallback, EndCallback); + + Assert.Equal("odasa ", actions.RunProcessIn[0]); + Assert.Equal("odasa ", startCallbackIn[0]); + Assert.Equal("", endCallbackIn[0]); + Assert.Equal(0, endCallbackReturn[0]); + Assert.Equal(1, endCallbackReturn.Count); + } + + [Fact] + public void TestOr2() + { + var cmd = BuildScript.Create("abc", "def ghi", false, null, null) | BuildScript.Create("odasa", null, false, null, null); + + actions.RunProcess["abc def ghi"] = 1; + actions.RunProcess["odasa "] = 0; + cmd.Run(actions, StartCallback, EndCallback); + + Assert.Equal("abc def ghi", actions.RunProcessIn[0]); + Assert.Equal("abc def ghi", startCallbackIn[0]); + Assert.Equal("", endCallbackIn[0]); + Assert.Equal(1, endCallbackReturn[0]); + + Assert.Equal("odasa ", actions.RunProcessIn[1]); + Assert.Equal("odasa ", startCallbackIn[1]); + Assert.Equal("", endCallbackIn[1]); + Assert.Equal(0, endCallbackReturn[1]); + } + + [Fact] + public void TestSuccess() + { + Assert.Equal(0, BuildScript.Success.Run(actions, StartCallback, EndCallback)); + } + + [Fact] + public void TestFailure() + { + Assert.NotEqual(0, BuildScript.Failure.Run(actions, StartCallback, EndCallback)); + } + + [Fact] + public void TestDeleteDirectorySuccess() + { + actions.DirectoryExists["trap"] = true; + Assert.Equal(0, BuildScript.DeleteDirectory("trap").Run(actions, StartCallback, EndCallback)); + Assert.Equal("trap", actions.DirectoryDeleteIn[0]); + } + + [Fact] + public void TestDeleteDirectoryFailure() + { + actions.DirectoryExists["trap"] = false; + Assert.NotEqual(0, BuildScript.DeleteDirectory("trap").Run(actions, StartCallback, EndCallback)); + } + + [Fact] + public void TestDeleteFileSuccess() + { + actions.FileExists["csharp.log"] = true; + Assert.Equal(0, BuildScript.DeleteFile("csharp.log").Run(actions, StartCallback, EndCallback)); + Assert.Equal("csharp.log", actions.FileExistsIn[0]); + Assert.Equal("csharp.log", actions.FileDeleteIn[0]); + } + + [Fact] + public void TestDeleteFileFailure() + { + actions.FileExists["csharp.log"] = false; + Assert.NotEqual(0, BuildScript.DeleteFile("csharp.log").Run(actions, StartCallback, EndCallback)); + Assert.Equal("csharp.log", actions.FileExistsIn[0]); + } + + [Fact] + public void TestTry() + { + Assert.Equal(0, BuildScript.Try(BuildScript.Failure).Run(actions, StartCallback, EndCallback)); + } + + private CSharpAutobuilder CreateAutoBuilder(bool isWindows, + string? buildless = null, string? solution = null, string? buildCommand = null, string? ignoreErrors = null, + string? msBuildArguments = null, string? msBuildPlatform = null, string? msBuildConfiguration = null, string? msBuildTarget = null, + string? dotnetArguments = null, string? dotnetVersion = null, string? vsToolsVersion = null, + string? nugetRestore = null, string? allSolutions = null, + string cwd = @"C:\Project") + { + var codeqlUpperLanguage = Language.CSharp.UpperCaseName; + actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = ""; + actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}"; + actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java"; + actions.GetEnvironmentVariable["CODEQL_PLATFORM"] = isWindows ? "win64" : "linux64"; + actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa"; + actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java"; + actions.GetEnvironmentVariable["SEMMLE_PLATFORM_TOOLS"] = @"C:\odasa\tools"; + actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion; + actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_ARGUMENTS"] = msBuildArguments; + actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_PLATFORM"] = msBuildPlatform; + actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_CONFIGURATION"] = msBuildConfiguration; + actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_TARGET"] = msBuildTarget; + actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_ARGUMENTS"] = dotnetArguments; + actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_VERSION"] = dotnetVersion; + actions.GetEnvironmentVariable["LGTM_INDEX_BUILD_COMMAND"] = buildCommand; + actions.GetEnvironmentVariable["LGTM_INDEX_SOLUTION"] = solution; + actions.GetEnvironmentVariable["LGTM_INDEX_IGNORE_ERRORS"] = ignoreErrors; + actions.GetEnvironmentVariable["LGTM_INDEX_BUILDLESS"] = buildless; + actions.GetEnvironmentVariable["LGTM_INDEX_ALL_SOLUTIONS"] = allSolutions; + actions.GetEnvironmentVariable["LGTM_INDEX_NUGET_RESTORE"] = nugetRestore; + actions.GetEnvironmentVariable["ProgramFiles(x86)"] = isWindows ? @"C:\Program Files (x86)" : null; + actions.GetCurrentDirectory = cwd; + actions.IsWindows = isWindows; + + var options = new AutobuildOptions(actions, Language.CSharp); + return new CSharpAutobuilder(actions, options); + } + + [Fact] + public void TestDefaultCSharpAutoBuilder() + { + actions.RunProcess["cmd.exe /C dotnet --info"] = 0; + actions.RunProcess[@"cmd.exe /C dotnet clean C:\Project\test.csproj"] = 0; + actions.RunProcess[@"cmd.exe /C dotnet restore C:\Project\test.csproj"] = 0; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental C:\Project\test.csproj"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Project\test.csproj"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbar.cs\ntest.csproj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + var xml = new XmlDocument(); + xml.LoadXml(@" + + Exe + netcoreapp2.1 + + +"); + actions.LoadXml[@"C:\Project\test.csproj"] = xml; + + var autobuilder = CreateAutoBuilder(true); + TestAutobuilderScript(autobuilder, 0, 4); + } + + [Fact] + public void TestLinuxCSharpAutoBuilder() + { + actions.RunProcess["dotnet --list-runtimes"] = 0; + actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] +Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; + actions.RunProcess["dotnet --info"] = 0; + actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; + actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Project/test.csproj"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + var xml = new XmlDocument(); + xml.LoadXml(@" + + Exe + netcoreapp2.1 + + +"); + actions.LoadXml[@"C:\Project/test.csproj"] = xml; + + var autobuilder = CreateAutoBuilder(false); + TestAutobuilderScript(autobuilder, 0, 5); + } + + [Fact] + public void TestLinuxCSharpAutoBuilderExtractorFailed() + { + actions.FileExists["csharp.log"] = false; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(false); + TestAutobuilderScript(autobuilder, 1, 0); + } + + [Fact] + public void TestVsWhereSucceeded() + { + actions.IsWindows = true; + actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true; + actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 0; + actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "C:\\VS1\nC:\\VS2\nC:\\VS3"; + actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0; + actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = "10.0\n11.0\n16.0"; + + var candidates = BuildTools.GetCandidateVcVarsFiles(actions).ToArray(); + Assert.Equal("C:\\VS1\\VC\\vcvarsall.bat", candidates[0].Path); + Assert.Equal(10, candidates[0].ToolsVersion); + Assert.Equal("C:\\VS2\\VC\\vcvarsall.bat", candidates[1].Path); + Assert.Equal(11, candidates[1].ToolsVersion); + Assert.Equal(@"C:\VS3\VC\Auxiliary\Build\vcvars32.bat", candidates[2].Path); + Assert.Equal(16, candidates[2].ToolsVersion); + Assert.Equal(@"C:\VS3\VC\Auxiliary\Build\vcvars64.bat", candidates[3].Path); + Assert.Equal(16, candidates[3].ToolsVersion); + Assert.Equal(@"C:\VS3\Common7\Tools\VsDevCmd.bat", candidates[4].Path); + Assert.Equal(16, candidates[4].ToolsVersion); + Assert.Equal(5, candidates.Length); + } + + [Fact] + public void TestVsWhereNotExist() + { + actions.IsWindows = true; + actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; + + var candidates = BuildTools.GetCandidateVcVarsFiles(actions).ToArray(); + Assert.Equal(4, candidates.Length); + } + + [Fact] + public void TestVcVarsAllBatFiles() + { + actions.IsWindows = true; + actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = false; + + var vcvarsfiles = BuildTools.VcVarsAllBatFiles(actions).ToArray(); + Assert.Equal(2, vcvarsfiles.Length); + } + + [Fact] + public void TestLinuxBuildlessExtractionSuccess() + { + actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone --references:."] = 0; + actions.FileExists["csharp.log"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(false, buildless: "true"); + TestAutobuilderScript(autobuilder, 0, 1); + } + + [Fact] + public void TestLinuxBuildlessExtractionFailed() + { + actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone --references:."] = 10; + actions.FileExists["csharp.log"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(false, buildless: "true"); + TestAutobuilderScript(autobuilder, 10, 1); + } + + [Fact] + public void TestLinuxBuildlessExtractionSolution() + { + actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0; + actions.FileExists["csharp.log"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(false, buildless: "true", solution: "foo.sln"); + TestAutobuilderScript(autobuilder, 0, 1); + } + + private void SkipVsWhere() + { + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = false; + } + + private void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun) + { + Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(actions, StartCallback, EndCallback)); + + // Check expected commands actually ran + Assert.Equal(commandsRun, startCallbackIn.Count); + Assert.Equal(commandsRun, endCallbackIn.Count); + Assert.Equal(commandsRun, endCallbackReturn.Count); + + var action = actions.RunProcess.GetEnumerator(); + for (var cmd = 0; cmd < commandsRun; ++cmd) + { + Assert.True(action.MoveNext()); + + Assert.Equal(action.Current.Key, startCallbackIn[cmd]); + Assert.Equal(action.Current.Value, endCallbackReturn[cmd]); + } + } + + [Fact] + public void TestLinuxBuildCommand() + { + actions.RunProcess["dotnet --list-runtimes"] = 1; + actions.RunProcessOut["dotnet --list-runtimes"] = ""; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0; + actions.FileExists["csharp.log"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + SkipVsWhere(); + + var autobuilder = CreateAutoBuilder(false, buildCommand: "./build.sh --skip-tests"); + TestAutobuilderScript(autobuilder, 0, 2); + } + + [Fact] + public void TestLinuxBuildSh() + { + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild/build.sh"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.RunProcess[@"/bin/chmod u+x C:\Project/build/build.sh"] = 0; + actions.RunProcess["dotnet --list-runtimes"] = 1; + actions.RunProcessOut["dotnet --list-runtimes"] = ""; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = 0; + actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = @"C:\Project/build"; + actions.FileExists["csharp.log"] = true; + + var autobuilder = CreateAutoBuilder(false); + TestAutobuilderScript(autobuilder, 0, 3); + } + + [Fact] + public void TestLinuxBuildShCSharpLogMissing() + { + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.sh"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + + actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0; + actions.RunProcess["dotnet --list-runtimes"] = 1; + actions.RunProcessOut["dotnet --list-runtimes"] = ""; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 0; + actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project"; + actions.FileExists["csharp.log"] = false; + + var autobuilder = CreateAutoBuilder(false); + TestAutobuilderScript(autobuilder, 1, 3); + } + + [Fact] + public void TestLinuxBuildShFailed() + { + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.sh"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + + actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0; + actions.RunProcess["dotnet --list-runtimes"] = 1; + actions.RunProcessOut["dotnet --list-runtimes"] = ""; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 5; + actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project"; + actions.FileExists["csharp.log"] = true; + + var autobuilder = CreateAutoBuilder(false); + TestAutobuilderScript(autobuilder, 1, 3); + } + + [Fact] + public void TestWindowsBuildBat() + { + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.bat"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = 0; + actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = @"C:\Project"; + actions.FileExists["csharp.log"] = true; + + var autobuilder = CreateAutoBuilder(true); + TestAutobuilderScript(autobuilder, 0, 1); + } + + [Fact] + public void TestWindowsBuildBatIgnoreErrors() + { + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.bat"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = 1; + actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = @"C:\Project"; + actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0; + actions.FileExists["csharp.log"] = true; + + var autobuilder = CreateAutoBuilder(true, ignoreErrors: "true"); + TestAutobuilderScript(autobuilder, 1, 1); + } + + [Fact] + public void TestWindowsCmdIgnoreErrors() + { + actions.RunProcess["cmd.exe /C C:\\odasa\\tools\\odasa index --auto ^\"build.cmd --skip-tests^\""] = 3; + actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0; + actions.FileExists["csharp.log"] = true; + SkipVsWhere(); + + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(true, buildCommand: "build.cmd --skip-tests", ignoreErrors: "true"); + TestAutobuilderScript(autobuilder, 3, 1); + } + + [Fact] + public void TestWindowCSharpMsBuild() + { + actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test1.sln -DisableParallelProcessing"] = 0; + actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; + actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test2.sln -DisableParallelProcessing"] = 0; + actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; + + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; + actions.EnumerateFiles[@"C:\Project\.nuget"] = "nuget.exe"; + actions.EnumerateDirectories[@"C:\Project"] = @".nuget"; + actions.EnumerateDirectories[@"C:\Project\.nuget"] = ""; + + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", + vsToolsVersion: "12", allSolutions: "true"); + var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); + var testSolution2 = new TestSolution(@"C:\Project\test2.sln"); + autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1); + autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2); + + TestAutobuilderScript(autobuilder, 0, 4); + } + + [Fact] + public void TestWindowCSharpMsBuildMultipleSolutions() + { + actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.csproj -DisableParallelProcessing"] = 0; + actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; + actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test2.csproj -DisableParallelProcessing"] = 0; + actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Project\test1.csproj"] = true; + actions.FileExists[@"C:\Project\test2.csproj"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; + + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "test1.csproj\ntest2.csproj\ntest1.cs\ntest2.cs"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var csproj1 = new XmlDocument(); + csproj1.LoadXml(@" + + + + + "); + actions.LoadXml[@"C:\Project\test1.csproj"] = csproj1; + + var csproj2 = new XmlDocument(); + csproj2.LoadXml(@" + + + + + "); + actions.LoadXml[@"C:\Project\test2.csproj"] = csproj2; + + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", + vsToolsVersion: "12"); + + TestAutobuilderScript(autobuilder, 0, 4); + } + + [Fact] + public void TestWindowCSharpMsBuildFailed() + { + actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.sln -DisableParallelProcessing"] = 0; + actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 1; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", + vsToolsVersion: "12", allSolutions: "true"); + var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); + var testSolution2 = new TestSolution(@"C:\Project\test2.sln"); + autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1); + autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2); + + TestAutobuilderScript(autobuilder, 1, 2); + } + + + [Fact] + public void TestSkipNugetMsBuild() + { + actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; + actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", + msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12", + allSolutions: "true", nugetRestore: "false"); + var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); + var testSolution2 = new TestSolution(@"C:\Project\test2.sln"); + autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1); + autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2); + + TestAutobuilderScript(autobuilder, 0, 2); + } + + [Fact] + public void TestSkipNugetBuildless() + { + actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0; + actions.FileExists["csharp.log"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var autobuilder = CreateAutoBuilder(false, buildless: "true", solution: "foo.sln", nugetRestore: "false"); + TestAutobuilderScript(autobuilder, 0, 1); + } + + + [Fact] + public void TestSkipNugetDotnet() + { + actions.RunProcess["dotnet --list-runtimes"] = 0; + actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] +Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; + actions.RunProcess["dotnet --info"] = 0; + actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; + actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore C:\Project/test.csproj"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Project/test.csproj"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + var xml = new XmlDocument(); + xml.LoadXml(@" + + Exe + netcoreapp2.1 + + +"); + actions.LoadXml[@"C:\Project/test.csproj"] = xml; + + var autobuilder = CreateAutoBuilder(false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now. + TestAutobuilderScript(autobuilder, 0, 5); + } + + [Fact] + public void TestDotnetVersionNotInstalled() + { + actions.RunProcess["dotnet --list-sdks"] = 0; + actions.RunProcessOut["dotnet --list-sdks"] = "2.1.2 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; + actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; + actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; + actions.RunProcess[@"rm dotnet-install.sh"] = 0; + actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; + actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] +Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; + actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; + actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; + actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental C:\Project/test.csproj"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists["test.csproj"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + var xml = new XmlDocument(); + xml.LoadXml(@" + + Exe + netcoreapp2.1 + + +"); + actions.LoadXml[@"C:\Project/test.csproj"] = xml; + actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh")); + + var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); + TestAutobuilderScript(autobuilder, 0, 9); + } + + [Fact] + public void TestDotnetVersionAlreadyInstalled() + { + actions.RunProcess["dotnet --list-sdks"] = 0; + actions.RunProcessOut["dotnet --list-sdks"] = @"2.1.3 [C:\Program Files\dotnet\sdks] +2.1.4 [C:\Program Files\dotnet\sdks]"; + actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; + actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; + actions.RunProcess[@"rm dotnet-install.sh"] = 0; + actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; + actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] +Microsoft.AspNetCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] +Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] +Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; + actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; + actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; + actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists["test.csproj"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbar.cs\ntest.csproj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + var xml = new XmlDocument(); + xml.LoadXml(@" + + Exe + netcoreapp2.1 + + +"); + actions.LoadXml[@"C:\Project/test.csproj"] = xml; + actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh")); + + var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); + TestAutobuilderScript(autobuilder, 0, 9); + } + + [Fact] + public void TestDotnetVersionWindows() + { + actions.RunProcess["cmd.exe /C dotnet --list-sdks"] = 0; + actions.RunProcessOut["cmd.exe /C dotnet --list-sdks"] = "2.1.3 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; + actions.RunProcess[@"cmd.exe /C powershell -NoProfile -ExecutionPolicy unrestricted -file C:\Project\install-dotnet.ps1 -Version 2.1.3 -InstallDir C:\Project\.dotnet"] = 0; + actions.RunProcess[@"cmd.exe /C del C:\Project\install-dotnet.ps1"] = 0; + actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet --info"] = 0; + actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean C:\Project\test.csproj"] = 0; + actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore C:\Project\test.csproj"] = 0; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental C:\Project\test.csproj"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Project\test.csproj"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; + actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + var xml = new XmlDocument(); + xml.LoadXml(@" + + Exe + netcoreapp2.1 + + +"); + actions.LoadXml[@"C:\Project\test.csproj"] = xml; + + var autobuilder = CreateAutoBuilder(true, dotnetVersion: "2.1.3"); + TestAutobuilderScript(autobuilder, 0, 7); + } + + [Fact] + public void TestDirsProjWindows() + { + actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\dirs.proj -DisableParallelProcessing"] = 1; + actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\dirs.proj -DisableParallelProcessing"] = 0; + actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\dirs.proj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Project\a\test.csproj"] = true; + actions.FileExists[@"C:\Project\dirs.proj"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; + actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; + + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "a\\test.cs\na\\test.csproj\ndirs.proj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + actions.CreateDirectories.Add(@"C:\Project\.nuget"); + actions.DownloadFiles.Add(("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", @"C:\Project\.nuget\nuget.exe")); + + var csproj = new XmlDocument(); + csproj.LoadXml(@" + + + + + "); + actions.LoadXml[@"C:\Project\a\test.csproj"] = csproj; + + var dirsproj = new XmlDocument(); + dirsproj.LoadXml(@" + + + +"); + actions.LoadXml[@"C:\Project\dirs.proj"] = dirsproj; + + var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", + vsToolsVersion: "12", allSolutions: "true"); + TestAutobuilderScript(autobuilder, 0, 3); + } + + [Fact] + public void TestDirsProjLinux() + { + actions.RunProcess[@"nuget restore C:\Project/dirs.proj -DisableParallelProcessing"] = 1; + actions.RunProcess[@"mono C:\Project/.nuget/nuget.exe restore C:\Project/dirs.proj -DisableParallelProcessing"] = 0; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto msbuild C:\Project/dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0; + actions.FileExists["csharp.log"] = true; + actions.FileExists[@"C:\Project/a/test.csproj"] = true; + actions.FileExists[@"C:\Project/dirs.proj"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.EnumerateFiles[@"C:\Project"] = "a/test.cs\na/test.csproj\ndirs.proj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + actions.CreateDirectories.Add(@"C:\Project/.nuget"); + actions.DownloadFiles.Add(("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", @"C:\Project/.nuget/nuget.exe")); + + var csproj = new XmlDocument(); + csproj.LoadXml(@" + + + + + "); + actions.LoadXml[@"C:\Project/a/test.csproj"] = csproj; + + var dirsproj = new XmlDocument(); + dirsproj.LoadXml(@" + + + +"); + actions.LoadXml[@"C:\Project/dirs.proj"] = dirsproj; + + var autobuilder = CreateAutoBuilder(false); + TestAutobuilderScript(autobuilder, 0, 3); + } + + [Fact] + public void TestCyclicDirsProj() + { + actions.FileExists["dirs.proj"] = true; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; + actions.FileExists["csharp.log"] = false; + actions.EnumerateFiles[@"C:\Project"] = "dirs.proj"; + actions.EnumerateDirectories[@"C:\Project"] = ""; + + var dirsproj1 = new XmlDocument(); + dirsproj1.LoadXml(@" + + + +"); + actions.LoadXml[@"C:\Project/dirs.proj"] = dirsproj1; + + var autobuilder = CreateAutoBuilder(false); + TestAutobuilderScript(autobuilder, 1, 0); + } + + [Fact] + public void TestAsStringWithExpandedEnvVarsWindows() + { + actions.IsWindows = true; + actions.GetEnvironmentVariable["LGTM_SRC"] = @"C:\repo"; + Assert.Equal(@"C:\repo\test", @"%LGTM_SRC%\test".AsStringWithExpandedEnvVars(actions)); + } + + [Fact] + public void TestAsStringWithExpandedEnvVarsLinux() + { + actions.IsWindows = false; + actions.GetEnvironmentVariable["LGTM_SRC"] = "/tmp/repo"; + Assert.Equal("/tmp/repo/test", "$LGTM_SRC/test".AsStringWithExpandedEnvVars(actions)); + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj new file mode 100644 index 000000000000..ee3324eb6398 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj @@ -0,0 +1,25 @@ + + + + Exe + netcoreapp3.1 + false + win-x64;linux-x64;osx-x64 + enable + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs new file mode 100644 index 000000000000..5f29771c11b2 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs @@ -0,0 +1,129 @@ +īģŋusing Semmle.Extraction.CSharp; +using Semmle.Util.Logging; +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.CSharp +{ + public class CSharpAutobuilder : Autobuilder + { + public CSharpAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { } + + public override BuildScript GetBuildScript() + { + /// + /// A script that checks that the C# extractor has been executed. + /// + BuildScript CheckExtractorRun(bool warnOnFailure) => + BuildScript.Create(actions => + { + if (actions.FileExists(Extractor.GetCSharpLogPath())) + return 0; + + if (warnOnFailure) + Log(Severity.Error, "No C# code detected during build."); + + return 1; + }); + + var attempt = BuildScript.Failure; + switch (GetCSharpBuildStrategy()) + { + case CSharpBuildStrategy.CustomBuildCommand: + attempt = new BuildCommandRule(DotNetRule.WithDotNet).Analyse(this, false) & CheckExtractorRun(true); + break; + case CSharpBuildStrategy.Buildless: + // No need to check that the extractor has been executed in buildless mode + attempt = new StandaloneBuildRule().Analyse(this, false); + break; + case CSharpBuildStrategy.MSBuild: + attempt = new MsBuildRule().Analyse(this, false) & CheckExtractorRun(true); + break; + case CSharpBuildStrategy.DotNet: + attempt = new DotNetRule().Analyse(this, false) & CheckExtractorRun(true); + break; + case CSharpBuildStrategy.Auto: + var cleanTrapFolder = + BuildScript.DeleteDirectory(TrapDir); + var cleanSourceArchive = + BuildScript.DeleteDirectory(SourceArchiveDir); + var tryCleanExtractorArgsLogs = + BuildScript.Create(actions => + { + foreach (var file in Extractor.GetCSharpArgsLogs()) + { + try + { + actions.FileDelete(file); + } + catch // lgtm[cs/catch-of-all-exceptions] lgtm[cs/empty-catch-block] + { } + } + + return 0; + }); + var attemptExtractorCleanup = + BuildScript.Try(cleanTrapFolder) & + BuildScript.Try(cleanSourceArchive) & + tryCleanExtractorArgsLogs & + BuildScript.DeleteFile(Extractor.GetCSharpLogPath()); + + /// + /// Execute script `s` and check that the C# extractor has been executed. + /// If either fails, attempt to cleanup any artifacts produced by the extractor, + /// and exit with code 1, in order to proceed to the next attempt. + /// + BuildScript IntermediateAttempt(BuildScript s) => + (s & CheckExtractorRun(false)) | + (attemptExtractorCleanup & BuildScript.Failure); + + attempt = + // First try .NET Core + IntermediateAttempt(new DotNetRule().Analyse(this, true)) | + // Then MSBuild + (() => IntermediateAttempt(new MsBuildRule().Analyse(this, true))) | + // And finally look for a script that might be a build script + (() => new BuildCommandAutoRule(DotNetRule.WithDotNet).Analyse(this, true) & CheckExtractorRun(true)) | + // All attempts failed: print message + AutobuildFailure(); + break; + } + + return attempt; + } + + /// + /// Gets the build strategy that the autobuilder should apply, based on the + /// options in the `lgtm.yml` file. + /// + private CSharpBuildStrategy GetCSharpBuildStrategy() + { + if (Options.BuildCommand != null) + return CSharpBuildStrategy.CustomBuildCommand; + + if (Options.Buildless) + return CSharpBuildStrategy.Buildless; + + if (Options.MsBuildArguments != null + || Options.MsBuildConfiguration != null + || Options.MsBuildPlatform != null + || Options.MsBuildTarget != null) + { + return CSharpBuildStrategy.MSBuild; + } + + if (Options.DotNetArguments != null || Options.DotNetVersion != null) + return CSharpBuildStrategy.DotNet; + + return CSharpBuildStrategy.Auto; + } + + private enum CSharpBuildStrategy + { + CustomBuildCommand, + Buildless, + MSBuild, + DotNet, + Auto + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs similarity index 83% rename from csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs rename to csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index 21215af84345..0af1026e35ac 100644 --- a/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -6,14 +6,15 @@ using System.IO; using Semmle.Util; using System.Text.RegularExpressions; +using Semmle.Autobuild.Shared; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.CSharp { /// /// A build rule where the build command is of the form "dotnet build". /// Currently unused because the tracer does not work with dotnet. /// - class DotNetRule : IBuildRule + internal class DotNetRule : IBuildRule { public BuildScript Analyse(Autobuilder builder, bool auto) { @@ -22,10 +23,10 @@ public BuildScript Analyse(Autobuilder builder, bool auto) if (auto) { - var notDotNetProject = builder.ProjectsOrSolutionsToBuild. - SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects)). - OfType(). - FirstOrDefault(p => !p.DotNetProject); + var notDotNetProject = builder.ProjectsOrSolutionsToBuild + .SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects)) + .OfType() + .FirstOrDefault(p => !p.DotNetProject); if (notDotNetProject != null) { builder.Log(Severity.Info, "Not using .NET Core because of incompatible project {0}", notDotNetProject); @@ -56,9 +57,9 @@ public BuildScript Analyse(Autobuilder builder, bool auto) }); } - static BuildScript WithDotNet(Autobuilder builder, Func?, bool, BuildScript> f) + private static BuildScript WithDotNet(Autobuilder builder, Func?, bool, BuildScript> f) { - string? installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet"); + var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet"); var installScript = DownloadDotNet(builder, installDir); return BuildScript.Bind(installScript, installed => { @@ -93,11 +94,11 @@ static BuildScript WithDotNet(Autobuilder builder, Func regex.Match(runtime)). - Where(m => m.Success). - Select(m => m.Groups[1].Value). - Any(m => Version.TryParse(m, out var v) && v >= minimumVersion); + compatibleClr = runtimes + .Select(runtime => regex.Match(runtime)) + .Where(m => m.Success) + .Select(m => m.Groups[1].Value) + .Any(m => Version.TryParse(m, out var v) && v >= minimumVersion); } if (!compatibleClr) @@ -128,7 +129,7 @@ public static BuildScript WithDotNet(Autobuilder builder, FuncinstallDir /// (provided that the script succeeds). /// - static BuildScript DownloadDotNet(Autobuilder builder, string installDir) + private static BuildScript DownloadDotNet(Autobuilder builder, string installDir) { if (!string.IsNullOrEmpty(builder.Options.DotNetVersion)) // Specific version supplied in configuration: always use that @@ -165,11 +166,11 @@ static BuildScript DownloadDotNet(Autobuilder builder, string installDir) /// /// See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script. /// - static BuildScript DownloadDotNetVersion(Autobuilder builder, string path, string version) + private static BuildScript DownloadDotNetVersion(Autobuilder builder, string path, string version) { return BuildScript.Bind(GetInstalledSdksScript(builder.Actions), (sdks, sdksRet) => { - if (sdksRet == 0 && sdks.Count() == 1 && sdks[0].StartsWith(version + " ", StringComparison.Ordinal)) + if (sdksRet == 0 && sdks.Count == 1 && sdks[0].StartsWith(version + " ", StringComparison.Ordinal)) // The requested SDK is already installed (and no other SDKs are installed), so // no need to reinstall return BuildScript.Failure; @@ -228,11 +229,10 @@ public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certifi } else { - var curl = new CommandBuilder(builder.Actions). - RunCommand("curl"). - Argument("-L"). - Argument("-sO"). - Argument("https://dot.net/v1/dotnet-install.sh"); + var downloadDotNetInstallSh = BuildScript.DownloadFile( + "https://dot.net/v1/dotnet-install.sh", + "dotnet-install.sh", + e => builder.Log(Severity.Warning, $"Failed to download 'dotnet-install.sh': {e.Message}")); var chmod = new CommandBuilder(builder.Actions). RunCommand("chmod"). @@ -252,12 +252,12 @@ public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certifi RunCommand("rm"). Argument("dotnet-install.sh"); - return curl.Script & chmod.Script & install.Script & BuildScript.Try(removeScript.Script); + return downloadDotNetInstallSh & chmod.Script & install.Script & BuildScript.Try(removeScript.Script); } }); } - static BuildScript GetInstalledSdksScript(IBuildActions actions) + private static BuildScript GetInstalledSdksScript(IBuildActions actions) { var listSdks = new CommandBuilder(actions, silent: true). RunCommand("dotnet"). @@ -265,10 +265,10 @@ static BuildScript GetInstalledSdksScript(IBuildActions actions) return listSdks.Script; } - static string DotNetCommand(IBuildActions actions, string? dotNetPath) => + private static string DotNetCommand(IBuildActions actions, string? dotNetPath) => dotNetPath != null ? actions.PathCombine(dotNetPath, "dotnet") : "dotnet"; - BuildScript GetInfoCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) + private static BuildScript GetInfoCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) { var info = new CommandBuilder(actions, null, environment). RunCommand(DotNetCommand(actions, dotNetPath)). @@ -276,7 +276,7 @@ BuildScript GetInfoCommand(IBuildActions actions, string? dotNetPath, IDictionar return info.Script; } - CommandBuilder GetCleanCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) + private static CommandBuilder GetCleanCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) { var clean = new CommandBuilder(actions, null, environment). RunCommand(DotNetCommand(actions, dotNetPath)). @@ -284,7 +284,7 @@ CommandBuilder GetCleanCommand(IBuildActions actions, string? dotNetPath, IDicti return clean; } - CommandBuilder GetRestoreCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) + private static CommandBuilder GetRestoreCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) { var restore = new CommandBuilder(actions, null, environment). RunCommand(DotNetCommand(actions, dotNetPath)). @@ -292,7 +292,7 @@ CommandBuilder GetRestoreCommand(IBuildActions actions, string? dotNetPath, IDic return restore; } - static BuildScript GetInstalledRuntimesScript(IBuildActions actions, string? dotNetPath, IDictionary? environment) + private static BuildScript GetInstalledRuntimesScript(IBuildActions actions, string? dotNetPath, IDictionary? environment) { var listSdks = new CommandBuilder(actions, environment: environment, silent: true). RunCommand(DotNetCommand(actions, dotNetPath)). @@ -309,7 +309,7 @@ static BuildScript GetInstalledRuntimesScript(IBuildActions actions, string? dot /// hence the need for CLR tracing), by adding a /// `/p:UseSharedCompilation=false` argument. /// - BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, bool compatibleClr, string projOrSln) + private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, bool compatibleClr, string projOrSln) { var build = new CommandBuilder(builder.Actions, null, environment); var script = builder.MaybeIndex(build, DotNetCommand(builder.Actions, dotNetPath)). diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs new file mode 100644 index 000000000000..2233557af16d --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs @@ -0,0 +1,33 @@ +īģŋusing System; +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.CSharp +{ + public static class Program + { + public static int Main() + { + + try + { + var actions = SystemBuildActions.Instance; + var options = new AutobuildOptions(actions, Language.CSharp); + try + { + Console.WriteLine("CodeQL C# autobuilder"); + var builder = new CSharpAutobuilder(actions, options); + return builder.AttemptBuild(); + } + catch (InvalidEnvironmentException ex) + { + Console.WriteLine("The environment is invalid: {0}", ex.Message); + } + } + catch (ArgumentOutOfRangeException ex) + { + Console.WriteLine("The value \"{0}\" for parameter \"{1}\" is invalid", ex.ActualValue, ex.ParamName); + } + return 1; + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Properties/AssemblyInfo.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..7314539ace49 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +īģŋusing System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Semmle.Autobuild.CSharp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("GitHub")] +[assembly: AssemblyProduct("CodeQL autobuilder for C#")] +[assembly: AssemblyCopyright("Copyright Š GitHub 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj new file mode 100644 index 000000000000..82ca05504bca --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj @@ -0,0 +1,30 @@ + + + + netcoreapp3.1 + Semmle.Autobuild.CSharp + Semmle.Autobuild.CSharp + + Exe + + false + win-x64;linux-x64;osx-x64 + enable + + + + + + + + + + + + + + + + + + diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs new file mode 100644 index 000000000000..997d1db55228 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs @@ -0,0 +1,58 @@ +īģŋusing System.Linq; +using Semmle.Autobuild.Shared; + +namespace Semmle.Autobuild.CSharp +{ + /// + /// Build using standalone extraction. + /// + internal class StandaloneBuildRule : IBuildRule + { + public BuildScript Analyse(Autobuilder builder, bool auto) + { + BuildScript GetCommand(string? solution) + { + string standalone; + if (builder.CodeQLExtractorLangRoot is object && builder.CodeQlPlatform is object) + { + standalone = builder.Actions.PathCombine(builder.CodeQLExtractorLangRoot, "tools", builder.CodeQlPlatform, "Semmle.Extraction.CSharp.Standalone"); + } + else if (builder.SemmlePlatformTools is object) + { + standalone = builder.Actions.PathCombine(builder.SemmlePlatformTools, "csharp", "Semmle.Extraction.CSharp.Standalone"); + } + else + { + return BuildScript.Failure; + } + + var cmd = new CommandBuilder(builder.Actions); + cmd.RunCommand(standalone); + + if (solution != null) + cmd.QuoteArgument(solution); + + cmd.Argument("--references:."); + + if (!builder.Options.NugetRestore) + { + cmd.Argument("--skip-nuget"); + } + + return cmd.Script; + } + + if (!builder.Options.Buildless) + return BuildScript.Failure; + + if (!builder.Options.Solution.Any()) + return GetCommand(null); + + var script = BuildScript.Success; + foreach (var solution in builder.Options.Solution) + script &= GetCommand(solution); + + return script; + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs new file mode 100644 index 000000000000..0bcffa684962 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs @@ -0,0 +1,107 @@ +īģŋusing System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Semmle.Autobuild.Shared +{ + /// + /// Encapsulates build options. + /// + public class AutobuildOptions + { + private const string prefix = "LGTM_INDEX_"; + + public int SearchDepth { get; } = 3; + public string RootDirectory { get; } + public string? VsToolsVersion { get; } + public string? MsBuildArguments { get; } + public string? MsBuildPlatform { get; } + public string? MsBuildConfiguration { get; } + public string? MsBuildTarget { get; } + public string? DotNetArguments { get; } + public string? DotNetVersion { get; } + public string? BuildCommand { get; } + public IEnumerable Solution { get; } + public bool IgnoreErrors { get; } + public bool Buildless { get; } + public bool AllSolutions { get; } + public bool NugetRestore { get; } + public Language Language { get; } + + /// + /// Reads options from environment variables. + /// Throws ArgumentOutOfRangeException for invalid arguments. + /// + public AutobuildOptions(IBuildActions actions, Language language) + { + RootDirectory = actions.GetCurrentDirectory(); + VsToolsVersion = actions.GetEnvironmentVariable(prefix + "VSTOOLS_VERSION"); + MsBuildArguments = actions.GetEnvironmentVariable(prefix + "MSBUILD_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions); + MsBuildPlatform = actions.GetEnvironmentVariable(prefix + "MSBUILD_PLATFORM"); + MsBuildConfiguration = actions.GetEnvironmentVariable(prefix + "MSBUILD_CONFIGURATION"); + MsBuildTarget = actions.GetEnvironmentVariable(prefix + "MSBUILD_TARGET"); + DotNetArguments = actions.GetEnvironmentVariable(prefix + "DOTNET_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions); + DotNetVersion = actions.GetEnvironmentVariable(prefix + "DOTNET_VERSION"); + BuildCommand = actions.GetEnvironmentVariable(prefix + "BUILD_COMMAND"); + Solution = actions.GetEnvironmentVariable(prefix + "SOLUTION").AsListWithExpandedEnvVars(actions, Array.Empty()); + + IgnoreErrors = actions.GetEnvironmentVariable(prefix + "IGNORE_ERRORS").AsBool("ignore_errors", false); + Buildless = actions.GetEnvironmentVariable(prefix + "BUILDLESS").AsBool("buildless", false); + AllSolutions = actions.GetEnvironmentVariable(prefix + "ALL_SOLUTIONS").AsBool("all_solutions", false); + NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true); + + Language = language; + } + } + + public static class OptionsExtensions + { + public static bool AsBool(this string? value, string param, bool defaultValue) + { + if (value == null) + return defaultValue; + + switch (value.ToLower()) + { + case "on": + case "yes": + case "true": + case "enabled": + return true; + case "off": + case "no": + case "false": + case "disabled": + return false; + default: + throw new ArgumentOutOfRangeException(param, value, "The Boolean value is invalid."); + } + } + + public static string[] AsListWithExpandedEnvVars(this string? value, IBuildActions actions, string[] defaultValue) + { + if (value == null) + return defaultValue; + + return value. + Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries). + Select(s => AsStringWithExpandedEnvVars(s, actions)).ToArray(); + } + + private static readonly Regex linuxEnvRegEx = new Regex(@"\$([a-zA-Z_][a-zA-Z_0-9]*)", RegexOptions.Compiled); + + public static string AsStringWithExpandedEnvVars(this string value, IBuildActions actions) + { + if (string.IsNullOrEmpty(value)) + return value; + + // `Environment.ExpandEnvironmentVariables` only works with Windows-style + // environment variables + var windowsStyle = actions.IsWindows() + ? value + : linuxEnvRegEx.Replace(value, m => $"%{m.Groups[1].Value}%"); + return actions.EnvironmentExpandEnvironmentVariables(windowsStyle); + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs new file mode 100644 index 000000000000..29465bde90aa --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs @@ -0,0 +1,297 @@ +using Semmle.Util.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Semmle.Autobuild.Shared +{ + /// + /// A build rule analyses the files in "builder" and outputs a build script. + /// + public interface IBuildRule + { + /// + /// Analyse the files and produce a build script. + /// + /// The files and options relating to the build. + /// Whether this build rule is being automatically applied. + BuildScript Analyse(Autobuilder builder, bool auto); + } + + /// + /// A delegate used to wrap a build script in an environment where an appropriate + /// version of .NET Core is automatically installed. + /// + public delegate BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f); + + /// + /// Exception indicating that environment variables are missing or invalid. + /// + public class InvalidEnvironmentException : Exception + { + public InvalidEnvironmentException(string m) : base(m) { } + } + + /// + /// Main application logic, containing all data + /// gathered from the project and filesystem. + /// + /// The overall design is intended to be extensible so that in theory, + /// it should be possible to add new build rules without touching this code. + /// + public abstract class Autobuilder + { + /// + /// Full file paths of files found in the project directory, as well as + /// their distance from the project root folder. The list is sorted + /// by distance in ascending order. + /// + public IEnumerable<(string, int)> Paths => pathsLazy.Value; + private readonly Lazy> pathsLazy; + + /// + /// Gets a list of paths matching a set of extensions (including the "."), + /// as well as their distance from the project root folder. + /// The list is sorted by distance in ascending order. + /// + /// The extensions to find. + /// The files matching the extension. + public IEnumerable<(string, int)> GetExtensions(params string[] extensions) => + Paths.Where(p => extensions.Contains(Path.GetExtension(p.Item1))); + + /// + /// Gets all paths matching a particular filename, as well as + /// their distance from the project root folder. The list is sorted + /// by distance in ascending order. + /// + /// The filename to find. + /// Possibly empty sequence of paths with the given filename. + public IEnumerable<(string, int)> GetFilename(string name) => + Paths.Where(p => Actions.GetFileName(p.Item1) == name); + + /// + /// Holds if a given path, relative to the root of the source directory + /// was found. + /// + /// The relative path. + /// True iff the path was found. + public bool HasRelativePath(string path) => HasPath(Actions.PathCombine(RootDirectory, path)); + + /// + /// List of project/solution files to build. + /// + public IList ProjectsOrSolutionsToBuild => projectsOrSolutionsToBuildLazy.Value; + private readonly Lazy> projectsOrSolutionsToBuildLazy; + + /// + /// Holds if a given path was found. + /// + /// The path of the file. + /// True iff the path was found. + public bool HasPath(string path) => Paths.Any(p => path == p.Item1); + + private void FindFiles(string dir, int depth, int maxDepth, IList<(string, int)> results) + { + foreach (var f in Actions.EnumerateFiles(dir)) + { + results.Add((f, depth)); + } + + if (depth < maxDepth) + { + foreach (var d in Actions.EnumerateDirectories(dir)) + { + FindFiles(d, depth + 1, maxDepth, results); + } + } + } + + /// + /// The root of the source directory. + /// + private string RootDirectory => Options.RootDirectory; + + /// + /// Gets the supplied build configuration. + /// + public AutobuildOptions Options { get; } + + /// + /// The set of build actions used during the autobuilder. + /// Could be real system operations, or a stub for testing. + /// + public IBuildActions Actions { get; } + + private IEnumerable? FindFiles(string extension, Func create) + { + var matchingFiles = GetExtensions(extension) + .Select(p => (ProjectOrSolution: create(p.Item1), DistanceFromRoot: p.Item2)) + .Where(p => p.ProjectOrSolution.HasLanguage(this.Options.Language)) + .ToArray(); + + if (matchingFiles.Length == 0) + return null; + + if (Options.AllSolutions) + return matchingFiles.Select(p => p.ProjectOrSolution); + + return matchingFiles + .Where(f => f.DistanceFromRoot == matchingFiles[0].DistanceFromRoot) + .Select(f => f.ProjectOrSolution); + } + + /// + /// Find all the relevant files and picks the best + /// solution file and tools. + /// + /// The command line options. + protected Autobuilder(IBuildActions actions, AutobuildOptions options) + { + Actions = actions; + Options = options; + + pathsLazy = new Lazy>(() => + { + var files = new List<(string, int)>(); + FindFiles(options.RootDirectory, 0, options.SearchDepth, files); + return files.OrderBy(f => f.Item2).ToArray(); + }); + + projectsOrSolutionsToBuildLazy = new Lazy>(() => + { + List? ret; + if (options.Solution.Any()) + { + ret = new List(); + foreach (var solution in options.Solution) + { + if (actions.FileExists(solution)) + ret.Add(new Solution(this, solution, true)); + else + Log(Severity.Error, $"The specified project or solution file {solution} was not found"); + } + return ret; + } + + // First look for `.proj` files + ret = FindFiles(".proj", f => new Project(this, f))?.ToList(); + if (ret != null) + return ret; + + // Then look for `.sln` files + ret = FindFiles(".sln", f => new Solution(this, f, false))?.ToList(); + if (ret != null) + return ret; + + // Finally look for language specific project files, e.g. `.csproj` files + ret = FindFiles(this.Options.Language.ProjectExtension, f => new Project(this, f))?.ToList(); + return ret ?? new List(); + }); + + CodeQLExtractorLangRoot = Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT"); + SemmlePlatformTools = Actions.GetEnvironmentVariable("SEMMLE_PLATFORM_TOOLS"); + + CodeQlPlatform = Actions.GetEnvironmentVariable("CODEQL_PLATFORM"); + + TrapDir = + Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR") ?? + Actions.GetEnvironmentVariable("TRAP_FOLDER") ?? + throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR or TRAP_FOLDER has not been set."); + + SourceArchiveDir = + Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR") ?? + Actions.GetEnvironmentVariable("SOURCE_ARCHIVE") ?? + throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR or SOURCE_ARCHIVE has not been set."); + } + + protected string TrapDir { get; } + + protected string SourceArchiveDir { get; } + + private readonly ILogger logger = new ConsoleLogger(Verbosity.Info); + + /// + /// Log a given build event to the console. + /// + /// The format string. + /// Inserts to the format string. + public void Log(Severity severity, string format, params object[] args) + { + logger.Log(severity, format, args); + } + + /// + /// Attempt to build this project. + /// + /// The exit code, 0 for success and non-zero for failures. + public int AttemptBuild() + { + Log(Severity.Info, $"Working directory: {Options.RootDirectory}"); + + var script = GetBuildScript(); + + if (Options.IgnoreErrors) + script |= BuildScript.Success; + + void startCallback(string s, bool silent) + { + Log(silent ? Severity.Debug : Severity.Info, $"\nRunning {s}"); + } + + void exitCallback(int ret, string msg, bool silent) + { + Log(silent ? Severity.Debug : Severity.Info, $"Exit code {ret}{(string.IsNullOrEmpty(msg) ? "" : $": {msg}")}"); + } + + return script.Run(Actions, startCallback, exitCallback); + } + + /// + /// Returns the build script to use for this project. + /// + public abstract BuildScript GetBuildScript(); + + protected BuildScript AutobuildFailure() => + BuildScript.Create(actions => + { + Log(Severity.Error, "Could not auto-detect a suitable build method"); + return 1; + }); + + /// + /// Value of CODEQL_EXTRACTOR__ROOT environment variable. + /// + public string? CodeQLExtractorLangRoot { get; } + + /// + /// Value of SEMMLE_PLATFORM_TOOLS environment variable. + /// + public string? SemmlePlatformTools { get; } + + /// + /// Value of CODEQL_PLATFORM environment variable. + /// + public string? CodeQlPlatform { get; } + + /// + /// The absolute path of the odasa executable. + /// null if we are running in CodeQL. + /// + public string? Odasa + { + get + { + var semmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST"); + return semmleDist is null ? null : Actions.PathCombine(semmleDist, "tools", "odasa"); + } + } + + /// + /// Construct a command that executed the given wrapped in + /// an odasa --index, unless indexing has been disabled, in which case + /// is run directly. + /// + public CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Odasa is null ? builder.RunCommand(cmd) : builder.IndexCommand(Odasa, cmd); + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildActions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs similarity index 77% rename from csharp/autobuilder/Semmle.Autobuild/BuildActions.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs index 7bc4b9b7591b..b9a5c9c17c35 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildActions.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs @@ -4,8 +4,11 @@ using System.Diagnostics; using System.IO; using System.Xml; +using System.Net.Http; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// Wrapper around system calls so that the build scripts can be unit-tested. @@ -58,6 +61,11 @@ public interface IBuildActions /// void DirectoryDelete(string dir, bool recursive); + /// + /// Creates all directories and subdirectories in the specified path unless they already exist. + /// + void CreateDirectory(string path); + /// /// Gets an environment variable, Environment.GetEnvironmentVariable(). /// @@ -102,6 +110,17 @@ public interface IBuildActions /// string GetFullPath(string path); + /// + /// Returns the file name and extension of the specified path string. + /// + [return: NotNullIfNotNull("path")] + string? GetFileName(string? path); + + /// + /// Returns the directory information for the specified path string. + /// + string? GetDirectoryName(string? path); + /// /// Writes contents to file, File.WriteAllText(). /// @@ -114,23 +133,24 @@ public interface IBuildActions /// XmlDocument LoadXml(string filename); + string EnvironmentExpandEnvironmentVariables(string s); + /// - /// Expand all Windows-style environment variables in , - /// Environment.ExpandEnvironmentVariables() + /// Downloads the resource with the specified URI to a local file. /// - string EnvironmentExpandEnvironmentVariables(string s); + void DownloadFile(string address, string fileName); } /// /// An implementation of IBuildActions that actually performs the requested operations. /// - class SystemBuildActions : IBuildActions + public class SystemBuildActions : IBuildActions { void IBuildActions.FileDelete(string file) => File.Delete(file); bool IBuildActions.FileExists(string file) => File.Exists(file); - ProcessStartInfo GetProcessStartInfo(string exe, string arguments, string? workingDirectory, IDictionary? environment, bool redirectStandardOutput) + private static ProcessStartInfo GetProcessStartInfo(string exe, string arguments, string? workingDirectory, IDictionary? environment, bool redirectStandardOutput) { var pi = new ProcessStartInfo(exe, arguments) { @@ -149,11 +169,9 @@ ProcessStartInfo GetProcessStartInfo(string exe, string arguments, string? worki int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? environment) { var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment, false); - using (var p = Process.Start(pi)) - { - p.WaitForExit(); - return p.ExitCode; - } + using var p = Process.Start(pi); + p.WaitForExit(); + return p.ExitCode; } int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? environment, out IList stdOut) @@ -166,6 +184,8 @@ int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, bool IBuildActions.DirectoryExists(string dir) => Directory.Exists(dir); + void IBuildActions.CreateDirectory(string path) => Directory.CreateDirectory(path); + string? IBuildActions.GetEnvironmentVariable(string name) => Environment.GetEnvironmentVariable(name); string IBuildActions.GetCurrentDirectory() => Directory.GetCurrentDirectory(); @@ -189,8 +209,24 @@ XmlDocument IBuildActions.LoadXml(string filename) string IBuildActions.GetFullPath(string path) => Path.GetFullPath(path); + string? IBuildActions.GetFileName(string? path) => Path.GetFileName(path); + + string? IBuildActions.GetDirectoryName(string? path) => Path.GetDirectoryName(path); + public string EnvironmentExpandEnvironmentVariables(string s) => Environment.ExpandEnvironmentVariables(s); - public static readonly IBuildActions Instance = new SystemBuildActions(); + private static async Task DownloadFileAsync(string address, string filename) + { + using var httpClient = new HttpClient(); + using var request = new HttpRequestMessage(HttpMethod.Get, address); + using var contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(); + using var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true); + await contentStream.CopyToAsync(stream); + } + + public void DownloadFile(string address, string fileName) => + DownloadFileAsync(address, fileName).Wait(); + + public static IBuildActions Instance { get; } = new SystemBuildActions(); } } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs new file mode 100644 index 000000000000..dfbed17b2d75 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs @@ -0,0 +1,66 @@ +īģŋusing System.Collections.Generic; +using System.Linq; +using Semmle.Util.Logging; + +namespace Semmle.Autobuild.Shared +{ + /// + /// Auto-detection of build scripts. + /// + public class BuildCommandAutoRule : IBuildRule + { + private readonly WithDotNet withDotNet; + + public BuildCommandAutoRule(WithDotNet withDotNet) + { + this.withDotNet = withDotNet; + } + + private readonly IEnumerable winExtensions = new List { + ".bat", + ".cmd", + ".exe" + }; + + private readonly IEnumerable linuxExtensions = new List { + "", + ".sh" + }; + + private readonly IEnumerable buildScripts = new List { + "build" + }; + + public BuildScript Analyse(Autobuilder builder, bool auto) + { + builder.Log(Severity.Info, "Attempting to locate build script"); + + var extensions = builder.Actions.IsWindows() ? winExtensions : linuxExtensions; + var scripts = buildScripts.SelectMany(s => extensions.Select(e => s + e)); + var scriptPath = builder.Paths.Where(p => scripts.Any(p.Item1.ToLower().EndsWith)).OrderBy(p => p.Item2).Select(p => p.Item1).FirstOrDefault(); + + if (scriptPath == null) + return BuildScript.Failure; + + var chmod = new CommandBuilder(builder.Actions); + chmod.RunCommand("/bin/chmod", $"u+x {scriptPath}"); + var chmodScript = builder.Actions.IsWindows() ? BuildScript.Success : BuildScript.Try(chmod.Script); + + var dir = builder.Actions.GetDirectoryName(scriptPath); + + // A specific .NET Core version may be required + return chmodScript & withDotNet(builder, environment => + { + var command = new CommandBuilder(builder.Actions, dir, environment); + + // A specific Visual Studio version may be required + var vsTools = MsBuildRule.GetVcVarsBatFile(builder); + if (vsTools != null) + command.CallBatFile(vsTools.Path); + + builder.MaybeIndex(command, scriptPath); + return command.Script; + }); + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs new file mode 100644 index 000000000000..daccb41d96c5 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs @@ -0,0 +1,35 @@ +īģŋnamespace Semmle.Autobuild.Shared +{ + /// + /// Execute the build_command rule. + /// + public class BuildCommandRule : IBuildRule + { + private readonly WithDotNet withDotNet; + + public BuildCommandRule(WithDotNet withDotNet) + { + this.withDotNet = withDotNet; + } + + public BuildScript Analyse(Autobuilder builder, bool auto) + { + if (builder.Options.BuildCommand == null) + return BuildScript.Failure; + + // Custom build commands may require a specific .NET Core version + return withDotNet(builder, environment => + { + var command = new CommandBuilder(builder.Actions, null, environment); + + // Custom build commands may require a specific Visual Studio version + var vsTools = MsBuildRule.GetVcVarsBatFile(builder); + if (vsTools != null) + command.CallBatFile(vsTools.Path); + builder.MaybeIndex(command, builder.Options.BuildCommand); + + return command.Script; + }); + } + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildScript.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs similarity index 84% rename from csharp/autobuilder/Semmle.Autobuild/BuildScript.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs index e441030ff77e..954befd2c05e 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildScript.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A build script. @@ -46,12 +46,12 @@ public abstract class BuildScript /// The exit code from this build script. public abstract int Run(IBuildActions actions, Action startCallback, Action exitCallBack, out IList stdout); - class BuildCommand : BuildScript + private class BuildCommand : BuildScript { - readonly string exe, arguments; - readonly string? workingDirectory; - readonly IDictionary? environment; - readonly bool silent; + private readonly string exe, arguments; + private readonly string? workingDirectory; + private readonly IDictionary? environment; + private readonly bool silent; /// /// Create a simple build command. @@ -104,7 +104,7 @@ public override int Run(IBuildActions actions, Action startCallbac when (ex is System.ComponentModel.Win32Exception || ex is FileNotFoundException) { retMessage = ex.Message; - stdout = new string[0]; + stdout = Array.Empty(); } exitCallBack(ret, retMessage, silent); return ret; @@ -112,9 +112,9 @@ public override int Run(IBuildActions actions, Action startCallbac } - class ReturnBuildCommand : BuildScript + private class ReturnBuildCommand : BuildScript { - readonly Func func; + private readonly Func func; public ReturnBuildCommand(Func func) { this.func = func; @@ -124,16 +124,17 @@ public ReturnBuildCommand(Func func) public override int Run(IBuildActions actions, Action startCallback, Action exitCallBack, out IList stdout) { - stdout = new string[0]; + stdout = Array.Empty(); return func(actions); } } - class BindBuildScript : BuildScript + private class BindBuildScript : BuildScript { - readonly BuildScript s1; - readonly Func, int, BuildScript>? s2a; - readonly Func? s2b; + private readonly BuildScript s1; + private readonly Func, int, BuildScript>? s2a; + private readonly Func? s2b; + public BindBuildScript(BuildScript s1, Func, int, BuildScript> s2) { this.s1 = s1; @@ -192,6 +193,26 @@ public static BuildScript Create(string exe, string? argumentsOpt, bool silent, public static BuildScript Create(Func func) => new ReturnBuildCommand(func); + /// + /// Creates a build script that downloads the specified file. + /// + public static BuildScript DownloadFile(string address, string fileName, Action exceptionCallback) => + Create(actions => + { + if (actions.GetDirectoryName(fileName) is string dir && !string.IsNullOrWhiteSpace(dir)) + actions.CreateDirectory(dir); + try + { + actions.DownloadFile(address, fileName); + return 0; + } + catch (Exception e) + { + exceptionCallback(e); + return 1; + } + }); + /// /// Creates a build script that runs , followed by running the script /// produced by on the exit code from . @@ -207,19 +228,19 @@ public static BuildScript Bind(BuildScript s1, Func s2) => public static BuildScript Bind(BuildScript s1, Func, int, BuildScript> s2) => new BindBuildScript(s1, s2); - const int SuccessCode = 0; + private const int successCode = 0; /// /// The empty build script that always returns exit code 0. /// - public static readonly BuildScript Success = Create(actions => SuccessCode); + public static BuildScript Success { get; } = Create(actions => successCode); - const int FailureCode = 1; + private const int failureCode = 1; /// /// The empty build script that always returns exit code 1. /// - public static readonly BuildScript Failure = Create(actions => FailureCode); + public static BuildScript Failure { get; } = Create(actions => failureCode); - static bool Succeeded(int i) => i == SuccessCode; + private static bool Succeeded(int i) => i == successCode; public static BuildScript operator &(BuildScript s1, BuildScript s2) => new BindBuildScript(s1, ret1 => Succeeded(ret1) ? s2 : Create(actions => ret1)); @@ -246,7 +267,7 @@ public static BuildScript DeleteDirectory(string dir) => Create(actions => { if (string.IsNullOrEmpty(dir) || !actions.DirectoryExists(dir)) - return FailureCode; + return failureCode; try { @@ -254,9 +275,9 @@ public static BuildScript DeleteDirectory(string dir) => } catch // lgtm[cs/catch-of-all-exceptions] { - return FailureCode; + return failureCode; } - return SuccessCode; + return successCode; }); /// @@ -266,7 +287,7 @@ public static BuildScript DeleteFile(string file) => Create(actions => { if (string.IsNullOrEmpty(file) || !actions.FileExists(file)) - return FailureCode; + return failureCode; try { @@ -274,9 +295,9 @@ public static BuildScript DeleteFile(string file) => } catch // lgtm[cs/catch-of-all-exceptions] { - return FailureCode; + return failureCode; } - return SuccessCode; + return successCode; }); } } diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildTools.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs similarity index 83% rename from csharp/autobuilder/Semmle.Autobuild/BuildTools.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs index ca9285d2c9c1..79367c90d8e6 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildTools.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A BAT file used to initialise the appropriate @@ -9,15 +9,13 @@ namespace Semmle.Autobuild /// public class VcVarsBatFile { - public readonly int ToolsVersion; - public readonly string Path; - public readonly string[] Platform; + public int ToolsVersion { get; } + public string Path { get; } - public VcVarsBatFile(string path, int version, params string[] platform) + public VcVarsBatFile(string path, int version) { Path = path; ToolsVersion = version; - Platform = platform; } }; @@ -33,12 +31,12 @@ public static IEnumerable GetCandidateVcVarsFiles(IBuildActions a yield break; // Attempt to use vswhere to find installations of Visual Studio - string vswhere = actions.PathCombine(programFilesx86, "Microsoft Visual Studio", "Installer", "vswhere.exe"); + var vswhere = actions.PathCombine(programFilesx86, "Microsoft Visual Studio", "Installer", "vswhere.exe"); if (actions.FileExists(vswhere)) { - int exitCode1 = actions.RunProcess(vswhere, "-prerelease -legacy -property installationPath", null, null, out var installationList); - int exitCode2 = actions.RunProcess(vswhere, "-prerelease -legacy -property installationVersion", null, null, out var versionList); + var exitCode1 = actions.RunProcess(vswhere, "-prerelease -legacy -property installationPath", null, null, out var installationList); + var exitCode2 = actions.RunProcess(vswhere, "-prerelease -legacy -property installationVersion", null, null, out var versionList); if (exitCode1 == 0 && exitCode2 == 0 && versionList.Count == installationList.Count) { @@ -47,16 +45,19 @@ public static IEnumerable GetCandidateVcVarsFiles(IBuildActions a { var dot = vsInstallation.Version.IndexOf('.'); var majorVersionString = dot == -1 ? vsInstallation.Version : vsInstallation.Version.Substring(0, dot); - if (int.TryParse(majorVersionString, out int majorVersion)) + if (int.TryParse(majorVersionString, out var majorVersion)) { if (majorVersion < 15) { - yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\vcvarsall.bat"), majorVersion, "x86"); + // Visual Studio 2015 and below + yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\vcvarsall.bat"), majorVersion); } else { - yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars32.bat"), majorVersion, "x86"); - yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars64.bat"), majorVersion, "x64"); + // Visual Studio 2017 and above + yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars32.bat"), majorVersion); + yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars64.bat"), majorVersion); + yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"Common7\Tools\VsDevCmd.bat"), majorVersion); } } // else: Skip installation without a version @@ -66,10 +67,10 @@ public static IEnumerable GetCandidateVcVarsFiles(IBuildActions a } // vswhere not installed or didn't run correctly - return legacy Visual Studio versions - yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 14.0\VC\vcvarsall.bat"), 14, "x86"); - yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 12.0\VC\vcvarsall.bat"), 12, "x86"); - yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 11.0\VC\vcvarsall.bat"), 11, "x86"); - yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 10.0\VC\vcvarsall.bat"), 10, "x86"); + yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 14.0\VC\vcvarsall.bat"), 14); + yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 12.0\VC\vcvarsall.bat"), 12); + yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 11.0\VC\vcvarsall.bat"), 11); + yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 10.0\VC\vcvarsall.bat"), 10); } /// diff --git a/csharp/autobuilder/Semmle.Autobuild/CommandBuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs similarity index 83% rename from csharp/autobuilder/Semmle.Autobuild/CommandBuilder.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs index 353f132c6ecc..a5f49d4f4358 100644 --- a/csharp/autobuilder/Semmle.Autobuild/CommandBuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs @@ -2,22 +2,22 @@ using System.Linq; using System.Text; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// Utility to construct a build command. /// - class CommandBuilder + public class CommandBuilder { - enum EscapeMode { Process, Cmd }; + private enum EscapeMode { Process, Cmd }; - readonly StringBuilder arguments; - bool firstCommand; - string? executable; - readonly EscapeMode escapingMode; - readonly string? workingDirectory; - readonly IDictionary? environment; - readonly bool silent; + private readonly StringBuilder arguments; + private bool firstCommand; + private string? executable; + private readonly EscapeMode escapingMode; + private readonly string? workingDirectory; + private readonly IDictionary? environment; + private readonly bool silent; /// /// Initializes a new instance of the class. @@ -45,7 +45,7 @@ public CommandBuilder(IBuildActions actions, string? workingDirectory = null, ID this.silent = silent; } - void OdasaIndex(string odasa) + private void OdasaIndex(string odasa) { RunCommand(odasa, "index --auto"); } @@ -74,8 +74,8 @@ public CommandBuilder IndexCommand(string odasa, string command, string? argumen return this; } - static readonly char[] specialChars = { ' ', '\t', '\n', '\v', '\"' }; - static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|' }; + private static readonly char[] specialChars = { ' ', '\t', '\n', '\v', '\"' }; + private static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|' }; /// /// Appends the given argument to the command line. @@ -88,9 +88,9 @@ public CommandBuilder IndexCommand(string odasa, string command, string? argumen /// This implementation is copied from /// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ /// - void ArgvQuote(string argument, bool force) + private void ArgvQuote(string argument, bool force) { - bool cmd = escapingMode == EscapeMode.Cmd; + var cmd = escapingMode == EscapeMode.Cmd; if (!force && !string.IsNullOrEmpty(argument) && argument.IndexOfAny(specialChars) == -1) @@ -99,9 +99,12 @@ void ArgvQuote(string argument, bool force) } else { - if (cmd) arguments.Append('^'); + if (cmd) + arguments.Append('^'); + arguments.Append('\"'); - for (int it = 0; ; ++it) + + for (var it = 0; ; ++it) { var numBackslashes = 0; while (it != argument.Length && argument[it] == '\\') @@ -115,10 +118,12 @@ void ArgvQuote(string argument, bool force) arguments.Append('\\', numBackslashes * 2); break; } - else if (argument[it] == '\"') + + if (argument[it] == '\"') { arguments.Append('\\', numBackslashes * 2 + 1); - if (cmd) arguments.Append('^'); + if (cmd) + arguments.Append('^'); arguments.Append(arguments[it]); } else @@ -130,7 +135,10 @@ void ArgvQuote(string argument, bool force) arguments.Append(argument[it]); } } - if (cmd) arguments.Append('^'); + + if (cmd) + arguments.Append('^'); + arguments.Append('\"'); } } @@ -145,7 +153,7 @@ public CommandBuilder QuoteArgument(string argumentsOpt) return this; } - void NextArgument() + private void NextArgument() { if (arguments.Length > 0) arguments.Append(' '); @@ -161,7 +169,7 @@ public CommandBuilder Argument(string? argumentsOpt) return this; } - void NextCommand() + private void NextCommand() { if (firstCommand) firstCommand = false; diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Language.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Language.cs new file mode 100644 index 000000000000..c53eb7e592d3 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Language.cs @@ -0,0 +1,23 @@ +īģŋnamespace Semmle.Autobuild.Shared +{ + public sealed class Language + { + public static Language Cpp { get; } = new Language(".vcxproj", "CPP"); + public static Language CSharp { get; } = new Language(".csproj", "CSHARP"); + + public bool ProjectFileHasThisLanguage(string path) => + System.IO.Path.GetExtension(path) == ProjectExtension; + + public string ProjectExtension { get; } + public string UpperCaseName { get; } + + private Language(string extension, string name) + { + ProjectExtension = extension; + UpperCaseName = name; + } + + public override string ToString() => + ProjectExtension == Cpp.ProjectExtension ? "C/C++" : "C#"; + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs new file mode 100644 index 000000000000..30b7e0476132 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs @@ -0,0 +1,178 @@ +using Semmle.Util.Logging; +using System.Linq; + +namespace Semmle.Autobuild.Shared +{ + /// + /// A build rule using msbuild. + /// + public class MsBuildRule : IBuildRule + { + /// + /// The name of the msbuild command. + /// + private const string msBuild = "msbuild"; + + public BuildScript Analyse(Autobuilder builder, bool auto) + { + if (!builder.ProjectsOrSolutionsToBuild.Any()) + return BuildScript.Failure; + + if (auto) + builder.Log(Severity.Info, "Attempting to build using MSBuild"); + + var vsTools = GetVcVarsBatFile(builder); + + if (vsTools == null && builder.ProjectsOrSolutionsToBuild.Any()) + { + var firstSolution = builder.ProjectsOrSolutionsToBuild.OfType().FirstOrDefault(); + vsTools = firstSolution != null + ? BuildTools.FindCompatibleVcVars(builder.Actions, firstSolution) + : BuildTools.VcVarsAllBatFiles(builder.Actions).OrderByDescending(b => b.ToolsVersion).FirstOrDefault(); + } + + if (vsTools == null && builder.Actions.IsWindows()) + { + builder.Log(Severity.Warning, "Could not find a suitable version of VsDevCmd.bat/vcvarsall.bat"); + } + + // Use `nuget.exe` from source code repo, if present, otherwise first attempt with global + // `nuget` command, and if that fails, attempt to download `nuget.exe` from nuget.org + var nuget = builder.GetFilename("nuget.exe").Select(t => t.Item1).FirstOrDefault() ?? "nuget"; + var nugetDownload = builder.Actions.PathCombine(builder.Options.RootDirectory, ".nuget", "nuget.exe"); + var nugetDownloaded = false; + + var ret = BuildScript.Success; + + foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild) + { + if (builder.Options.NugetRestore) + { + BuildScript GetNugetRestoreScript() => + new CommandBuilder(builder.Actions). + RunCommand(nuget). + Argument("restore"). + QuoteArgument(projectOrSolution.FullPath). + Argument("-DisableParallelProcessing"). + Script; + var nugetRestore = GetNugetRestoreScript(); + var msbuildRestoreCommand = new CommandBuilder(builder.Actions). + RunCommand(msBuild). + Argument("/t:restore"). + QuoteArgument(projectOrSolution.FullPath); + + if (nugetDownloaded) + { + ret &= BuildScript.Try(nugetRestore | msbuildRestoreCommand.Script); + } + else + { + // If `nuget restore` fails, and we have not already attempted to download `nuget.exe`, + // download it and reattempt `nuget restore`. + var nugetDownloadAndRestore = + BuildScript.Bind(DownloadNugetExe(builder, nugetDownload), exitCode => + { + nugetDownloaded = true; + if (exitCode != 0) + return BuildScript.Failure; + + nuget = nugetDownload; + return GetNugetRestoreScript(); + }); + ret &= BuildScript.Try(nugetRestore | nugetDownloadAndRestore | msbuildRestoreCommand.Script); + } + } + + var command = new CommandBuilder(builder.Actions); + + if (vsTools != null) + { + command.CallBatFile(vsTools.Path); + // `vcvarsall.bat` sets a default Platform environment variable, + // which may not be compatible with the supported platforms of the + // given project/solution. Unsetting it means that the default platform + // of the project/solution is used instead. + command.RunCommand("set Platform=&& type NUL", quoteExe: false); + } + + builder.MaybeIndex(command, msBuild); + command.QuoteArgument(projectOrSolution.FullPath); + + command.Argument("/p:UseSharedCompilation=false"); + + var target = builder.Options.MsBuildTarget ?? "rebuild"; + var platform = builder.Options.MsBuildPlatform ?? (projectOrSolution is ISolution s1 ? s1.DefaultPlatformName : null); + var configuration = builder.Options.MsBuildConfiguration ?? (projectOrSolution is ISolution s2 ? s2.DefaultConfigurationName : null); + + command.Argument("/t:" + target); + if (platform != null) + command.Argument(string.Format("/p:Platform=\"{0}\"", platform)); + if (configuration != null) + command.Argument(string.Format("/p:Configuration=\"{0}\"", configuration)); + command.Argument("/p:MvcBuildViews=true"); + + command.Argument(builder.Options.MsBuildArguments); + + ret &= command.Script; + } + + return ret; + } + + /// + /// Gets the BAT file used to initialize the appropriate Visual Studio + /// version/platform, as specified by the `vstools_version` property in + /// lgtm.yml. + /// + /// Returns null when no version is specified. + /// + public static VcVarsBatFile? GetVcVarsBatFile(Autobuilder builder) + { + VcVarsBatFile? vsTools = null; + + if (builder.Options.VsToolsVersion != null) + { + if (int.TryParse(builder.Options.VsToolsVersion, out var msToolsVersion)) + { + foreach (var b in BuildTools.VcVarsAllBatFiles(builder.Actions)) + { + builder.Log(Severity.Info, "Found {0} version {1}", b.Path, b.ToolsVersion); + } + + vsTools = BuildTools.FindCompatibleVcVars(builder.Actions, msToolsVersion); + if (vsTools == null) + builder.Log(Severity.Warning, "Could not find build tools matching version {0}", msToolsVersion); + else + builder.Log(Severity.Info, "Setting Visual Studio tools to {0}", vsTools.Path); + } + else + { + builder.Log(Severity.Error, "The format of vstools_version is incorrect. Please specify an integer."); + } + } + + return vsTools; + } + + /// + /// Returns a script for downloading `nuget.exe` from nuget.org. + /// + private static BuildScript DownloadNugetExe(Autobuilder builder, string path) => + BuildScript.Create(_ => + { + builder.Log(Severity.Info, "Attempting to download nuget.exe"); + return 0; + }) + & + BuildScript.DownloadFile( + "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", + path, + e => builder.Log(Severity.Warning, $"Failed to download 'nuget.exe': {e.Message}")) + & + BuildScript.Create(_ => + { + builder.Log(Severity.Info, $"Successfully downloaded {path}"); + return 0; + }); + } +} diff --git a/csharp/autobuilder/Semmle.Autobuild/Project.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs similarity index 97% rename from csharp/autobuilder/Semmle.Autobuild/Project.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs index 415ddcbc0f0b..aaaa7cdac566 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Project.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs @@ -5,7 +5,7 @@ using System.Xml; using Semmle.Util.Logging; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// Representation of a .proj file, a .csproj file (C#), or a .vcxproj file (C++). @@ -23,7 +23,7 @@ public class Project : ProjectOrSolution public Version ToolsVersion { get; private set; } - readonly Lazy> includedProjectsLazy; + private readonly Lazy> includedProjectsLazy; public override IEnumerable IncludedProjects => includedProjectsLazy.Value; public Project(Autobuilder builder, string path) : base(builder, path) diff --git a/csharp/autobuilder/Semmle.Autobuild/ProjectOrSolution.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/ProjectOrSolution.cs similarity index 90% rename from csharp/autobuilder/Semmle.Autobuild/ProjectOrSolution.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/ProjectOrSolution.cs index 13859a8c0eb6..5e4d6c05248a 100644 --- a/csharp/autobuilder/Semmle.Autobuild/ProjectOrSolution.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/ProjectOrSolution.cs @@ -1,8 +1,7 @@ īģŋusing System.Collections.Generic; -using System.IO; using System.Linq; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A file that can be the target in an invocation of `msbuild` or `dotnet build`. @@ -23,13 +22,14 @@ public interface IProjectOrSolution public abstract class ProjectOrSolution : IProjectOrSolution { - public string FullPath { get; private set; } + public string FullPath { get; } - public string DirectoryName => Path.GetDirectoryName(FullPath) ?? ""; + public string DirectoryName { get; } protected ProjectOrSolution(Autobuilder builder, string path) { FullPath = builder.Actions.GetFullPath(path); + DirectoryName = builder.Actions.GetDirectoryName(path) ?? ""; } public abstract IEnumerable IncludedProjects { get; } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Properties/AssemblyInfo.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..2eecfca8f52b --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +īģŋusing System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Semmle.Autobuild.Shared")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("GitHub")] +[assembly: AssemblyProduct("CodeQL autobuilder")] +[assembly: AssemblyCopyright("Copyright Š GitHub 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1d9920ad-7b00-4df1-8b01-9ff5b687828e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj new file mode 100644 index 000000000000..6663c428b036 --- /dev/null +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.1 + Semmle.Autobuild.Shared + Semmle.Autobuild.Shared + false + win-x64;linux-x64;osx-x64 + enable + + + + + + + + + + + + + + + diff --git a/csharp/autobuilder/Semmle.Autobuild/Solution.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs similarity index 77% rename from csharp/autobuilder/Semmle.Autobuild/Solution.cs rename to csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs index 0429b9f420cc..2f16872dd749 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Solution.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs @@ -3,11 +3,10 @@ using System; using System.Collections.Generic; using System.Linq; -using Semmle.Util; using System.IO; using Semmle.Util.Logging; -namespace Semmle.Autobuild +namespace Semmle.Autobuild.Shared { /// /// A solution file, extension .sln. @@ -41,11 +40,11 @@ public interface ISolution : IProjectOrSolution /// /// A solution file on the filesystem, read using Microsoft.Build. /// - class Solution : ProjectOrSolution, ISolution + internal class Solution : ProjectOrSolution, ISolution { - readonly SolutionFile? solution; + private readonly SolutionFile? solution; - readonly IEnumerable includedProjects; + private readonly IEnumerable includedProjects; public override IEnumerable IncludedProjects => includedProjects; public IEnumerable Configurations => @@ -74,19 +73,18 @@ public Solution(Autobuilder builder, string path, bool allowProject) : base(buil } builder.Log(Severity.Info, $"Unable to read solution file {path}."); - includedProjects = new Project[0]; + includedProjects = Array.Empty(); return; } - includedProjects = - solution.ProjectsInOrder. - Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat). - Select(p => builder.Actions.PathCombine(DirectoryName, builder.Actions.PathCombine(p.RelativePath.Split('\\', StringSplitOptions.RemoveEmptyEntries)))). - Select(p => new Project(builder, p)). - ToArray(); + includedProjects = solution.ProjectsInOrder + .Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat) + .Select(p => builder.Actions.PathCombine(DirectoryName, builder.Actions.PathCombine(p.RelativePath.Split('\\', StringSplitOptions.RemoveEmptyEntries)))) + .Select(p => new Project(builder, p)) + .ToArray(); } - IEnumerable ToolsVersions => includedProjects.Where(p => p.ValidToolsVersion).Select(p => p.ToolsVersion); + private IEnumerable ToolsVersions => includedProjects.Where(p => p.ValidToolsVersion).Select(p => p.ToolsVersion); public Version ToolsVersion => ToolsVersions.Any() ? ToolsVersions.Max() : new Version(); } diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs deleted file mode 100644 index 93b498912258..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs +++ /dev/null @@ -1,1147 +0,0 @@ -īģŋusing Xunit; -using Semmle.Autobuild; -using System.Collections.Generic; -using System; -using System.Linq; -using Microsoft.Build.Construction; -using System.Xml; - -namespace Semmle.Extraction.Tests -{ - /// - /// Test class to script Autobuilder scenarios. - /// For most methods, it uses two fields: - /// - an IList to capture the the arguments passed to it - /// - an IDictionary of possible return values. - /// - class TestActions : IBuildActions - { - /// - /// List of strings passed to FileDelete. - /// - public IList FileDeleteIn = new List(); - - void IBuildActions.FileDelete(string file) - { - FileDeleteIn.Add(file); - } - - public IList FileExistsIn = new List(); - public IDictionary FileExists = new Dictionary(); - - bool IBuildActions.FileExists(string file) - { - FileExistsIn.Add(file); - if (FileExists.TryGetValue(file, out var ret)) - return ret; - if (FileExists.TryGetValue(System.IO.Path.GetFileName(file), out ret)) - return ret; - throw new ArgumentException("Missing FileExists " + file); - } - - public IList RunProcessIn = new List(); - public IDictionary RunProcess = new Dictionary(); - public IDictionary RunProcessOut = new Dictionary(); - public IDictionary RunProcessWorkingDirectory = new Dictionary(); - - int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env, out IList stdOut) - { - var pattern = cmd + " " + args; - RunProcessIn.Add(pattern); - if (RunProcessOut.TryGetValue(pattern, out var str)) - stdOut = str.Split("\n"); - else - throw new ArgumentException("Missing RunProcessOut " + pattern); - RunProcessWorkingDirectory.TryGetValue(pattern, out var wd); - if (wd != workingDirectory) - throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern); - if (RunProcess.TryGetValue(pattern, out var ret)) - return ret; - throw new ArgumentException("Missing RunProcess " + pattern); - } - - int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env) - { - var pattern = cmd + " " + args; - RunProcessIn.Add(pattern); - RunProcessWorkingDirectory.TryGetValue(pattern, out var wd); - if (wd != workingDirectory) - throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern); - if (RunProcess.TryGetValue(pattern, out var ret)) - return ret; - throw new ArgumentException("Missing RunProcess " + pattern); - } - - public IList DirectoryDeleteIn = new List(); - - void IBuildActions.DirectoryDelete(string dir, bool recursive) - { - DirectoryDeleteIn.Add(dir); - } - - public IDictionary DirectoryExists = new Dictionary(); - public IList DirectoryExistsIn = new List(); - - bool IBuildActions.DirectoryExists(string dir) - { - DirectoryExistsIn.Add(dir); - if (DirectoryExists.TryGetValue(dir, out var ret)) - return ret; - throw new ArgumentException("Missing DirectoryExists " + dir); - } - - public IDictionary GetEnvironmentVariable = new Dictionary(); - - string? IBuildActions.GetEnvironmentVariable(string name) - { - if (GetEnvironmentVariable.TryGetValue(name, out var ret)) - return ret; - throw new ArgumentException("Missing GetEnvironmentVariable " + name); - } - - public string GetCurrentDirectory = ""; - - string IBuildActions.GetCurrentDirectory() - { - return GetCurrentDirectory; - } - - public IDictionary EnumerateFiles = new Dictionary(); - - IEnumerable IBuildActions.EnumerateFiles(string dir) - { - if (EnumerateFiles.TryGetValue(dir, out var str)) - return str.Split("\n"); - throw new ArgumentException("Missing EnumerateFiles " + dir); - } - - public IDictionary EnumerateDirectories = new Dictionary(); - - IEnumerable IBuildActions.EnumerateDirectories(string dir) - { - if (EnumerateDirectories.TryGetValue(dir, out var str)) - return string.IsNullOrEmpty(str) ? Enumerable.Empty() : str.Split("\n"); - throw new ArgumentException("Missing EnumerateDirectories " + dir); - } - - public bool IsWindows; - - bool IBuildActions.IsWindows() => IsWindows; - - string IBuildActions.PathCombine(params string[] parts) - { - return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p))); - } - - string IBuildActions.GetFullPath(string path) => path; - - void IBuildActions.WriteAllText(string filename, string contents) - { - } - - public IDictionary LoadXml = new Dictionary(); - XmlDocument IBuildActions.LoadXml(string filename) - { - if (LoadXml.TryGetValue(filename, out var xml)) - return xml; - throw new ArgumentException("Missing LoadXml " + filename); - } - - public string EnvironmentExpandEnvironmentVariables(string s) - { - foreach (var kvp in GetEnvironmentVariable) - s = s.Replace($"%{kvp.Key}%", kvp.Value); - return s; - } - } - - /// - /// A fake solution to build. - /// - class TestSolution : ISolution - { - public IEnumerable Configurations => throw new NotImplementedException(); - - public string DefaultConfigurationName => "Release"; - - public string DefaultPlatformName => "x86"; - - public string FullPath { get; set; } - - public Version ToolsVersion => new Version("14.0"); - - public IEnumerable IncludedProjects => throw new NotImplementedException(); - - public TestSolution(string path) - { - FullPath = path; - } - } - - public class BuildScriptTests - { - TestActions Actions = new TestActions(); - - // Records the arguments passed to StartCallback. - IList StartCallbackIn = new List(); - - void StartCallback(string s, bool silent) - { - StartCallbackIn.Add(s); - } - - // Records the arguments passed to EndCallback - IList EndCallbackIn = new List(); - IList EndCallbackReturn = new List(); - - void EndCallback(int ret, string s, bool silent) - { - EndCallbackReturn.Add(ret); - EndCallbackIn.Add(s); - } - - [Fact] - public void TestBuildCommand() - { - var cmd = BuildScript.Create("abc", "def ghi", false, null, null); - - Actions.RunProcess["abc def ghi"] = 1; - cmd.Run(Actions, StartCallback, EndCallback); - Assert.Equal("abc def ghi", Actions.RunProcessIn[0]); - Assert.Equal("abc def ghi", StartCallbackIn[0]); - Assert.Equal("", EndCallbackIn[0]); - Assert.Equal(1, EndCallbackReturn[0]); - } - - [Fact] - public void TestAnd1() - { - var cmd = BuildScript.Create("abc", "def ghi", false, null, null) & BuildScript.Create("odasa", null, false, null, null); - - Actions.RunProcess["abc def ghi"] = 1; - cmd.Run(Actions, StartCallback, EndCallback); - - Assert.Equal("abc def ghi", Actions.RunProcessIn[0]); - Assert.Equal("abc def ghi", StartCallbackIn[0]); - Assert.Equal("", EndCallbackIn[0]); - Assert.Equal(1, EndCallbackReturn[0]); - } - - [Fact] - public void TestAnd2() - { - var cmd = BuildScript.Create("odasa", null, false, null, null) & BuildScript.Create("abc", "def ghi", false, null, null); - - Actions.RunProcess["abc def ghi"] = 1; - Actions.RunProcess["odasa "] = 0; - cmd.Run(Actions, StartCallback, EndCallback); - - Assert.Equal("odasa ", Actions.RunProcessIn[0]); - Assert.Equal("odasa ", StartCallbackIn[0]); - Assert.Equal("", EndCallbackIn[0]); - Assert.Equal(0, EndCallbackReturn[0]); - - Assert.Equal("abc def ghi", Actions.RunProcessIn[1]); - Assert.Equal("abc def ghi", StartCallbackIn[1]); - Assert.Equal("", EndCallbackIn[1]); - Assert.Equal(1, EndCallbackReturn[1]); - } - - [Fact] - public void TestOr1() - { - var cmd = BuildScript.Create("odasa", null, false, null, null) | BuildScript.Create("abc", "def ghi", false, null, null); - - Actions.RunProcess["abc def ghi"] = 1; - Actions.RunProcess["odasa "] = 0; - cmd.Run(Actions, StartCallback, EndCallback); - - Assert.Equal("odasa ", Actions.RunProcessIn[0]); - Assert.Equal("odasa ", StartCallbackIn[0]); - Assert.Equal("", EndCallbackIn[0]); - Assert.Equal(0, EndCallbackReturn[0]); - Assert.Equal(1, EndCallbackReturn.Count); - } - - [Fact] - public void TestOr2() - { - var cmd = BuildScript.Create("abc", "def ghi", false, null, null) | BuildScript.Create("odasa", null, false, null, null); - - Actions.RunProcess["abc def ghi"] = 1; - Actions.RunProcess["odasa "] = 0; - cmd.Run(Actions, StartCallback, EndCallback); - - Assert.Equal("abc def ghi", Actions.RunProcessIn[0]); - Assert.Equal("abc def ghi", StartCallbackIn[0]); - Assert.Equal("", EndCallbackIn[0]); - Assert.Equal(1, EndCallbackReturn[0]); - - Assert.Equal("odasa ", Actions.RunProcessIn[1]); - Assert.Equal("odasa ", StartCallbackIn[1]); - Assert.Equal("", EndCallbackIn[1]); - Assert.Equal(0, EndCallbackReturn[1]); - } - - [Fact] - public void TestSuccess() - { - Assert.Equal(0, BuildScript.Success.Run(Actions, StartCallback, EndCallback)); - } - - [Fact] - public void TestFailure() - { - Assert.NotEqual(0, BuildScript.Failure.Run(Actions, StartCallback, EndCallback)); - } - - [Fact] - public void TestDeleteDirectorySuccess() - { - Actions.DirectoryExists["trap"] = true; - Assert.Equal(0, BuildScript.DeleteDirectory("trap").Run(Actions, StartCallback, EndCallback)); - Assert.Equal("trap", Actions.DirectoryDeleteIn[0]); - } - - [Fact] - public void TestDeleteDirectoryFailure() - { - Actions.DirectoryExists["trap"] = false; - Assert.NotEqual(0, BuildScript.DeleteDirectory("trap").Run(Actions, StartCallback, EndCallback)); - } - - [Fact] - public void TestDeleteFileSuccess() - { - Actions.FileExists["csharp.log"] = true; - Assert.Equal(0, BuildScript.DeleteFile("csharp.log").Run(Actions, StartCallback, EndCallback)); - Assert.Equal("csharp.log", Actions.FileExistsIn[0]); - Assert.Equal("csharp.log", Actions.FileDeleteIn[0]); - } - - [Fact] - public void TestDeleteFileFailure() - { - Actions.FileExists["csharp.log"] = false; - Assert.NotEqual(0, BuildScript.DeleteFile("csharp.log").Run(Actions, StartCallback, EndCallback)); - Assert.Equal("csharp.log", Actions.FileExistsIn[0]); - } - - [Fact] - public void TestTry() - { - Assert.Equal(0, BuildScript.Try(BuildScript.Failure).Run(Actions, StartCallback, EndCallback)); - } - - Autobuilder CreateAutoBuilder(string lgtmLanguage, bool isWindows, - string? buildless = null, string? solution = null, string? buildCommand = null, string? ignoreErrors = null, - string? msBuildArguments = null, string? msBuildPlatform = null, string? msBuildConfiguration = null, string? msBuildTarget = null, - string? dotnetArguments = null, string? dotnetVersion = null, string? vsToolsVersion = null, - string? nugetRestore = null, string? allSolutions = null, - string cwd = @"C:\Project") - { - Actions.GetEnvironmentVariable["CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING"] = "false"; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_ROOT"] = @"C:\codeql\csharp"; - Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java"; - Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa"; - Actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java"; - Actions.GetEnvironmentVariable["LGTM_PROJECT_LANGUAGE"] = lgtmLanguage; - Actions.GetEnvironmentVariable["SEMMLE_PLATFORM_TOOLS"] = @"C:\odasa\tools"; - Actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion; - Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_ARGUMENTS"] = msBuildArguments; - Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_PLATFORM"] = msBuildPlatform; - Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_CONFIGURATION"] = msBuildConfiguration; - Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_TARGET"] = msBuildTarget; - Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_ARGUMENTS"] = dotnetArguments; - Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_VERSION"] = dotnetVersion; - Actions.GetEnvironmentVariable["LGTM_INDEX_BUILD_COMMAND"] = buildCommand; - Actions.GetEnvironmentVariable["LGTM_INDEX_SOLUTION"] = solution; - Actions.GetEnvironmentVariable["LGTM_INDEX_IGNORE_ERRORS"] = ignoreErrors; - Actions.GetEnvironmentVariable["LGTM_INDEX_BUILDLESS"] = buildless; - Actions.GetEnvironmentVariable["LGTM_INDEX_ALL_SOLUTIONS"] = allSolutions; - Actions.GetEnvironmentVariable["LGTM_INDEX_NUGET_RESTORE"] = nugetRestore; - Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = isWindows ? @"C:\Program Files (x86)" : null; - Actions.GetCurrentDirectory = cwd; - Actions.IsWindows = isWindows; - - var options = new AutobuildOptions(Actions); - return new Autobuilder(Actions, options); - } - - [Fact] - public void TestDefaultCSharpAutoBuilder() - { - Actions.RunProcess["cmd.exe /C dotnet --info"] = 0; - Actions.RunProcess["cmd.exe /C dotnet clean test.csproj"] = 0; - Actions.RunProcess["cmd.exe /C dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbar.cs\ntest.csproj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - var xml = new XmlDocument(); - xml.LoadXml(@" - - Exe - netcoreapp2.1 - - -"); - Actions.LoadXml["test.csproj"] = xml; - - var autobuilder = CreateAutoBuilder("csharp", true); - TestAutobuilderScript(autobuilder, 0, 6); - } - - [Fact] - public void TestLinuxCSharpAutoBuilder() - { - Actions.RunProcess["dotnet --list-runtimes"] = 0; - Actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; - Actions.RunProcess["dotnet --info"] = 0; - Actions.RunProcess["dotnet clean test.csproj"] = 0; - Actions.RunProcess["dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - var xml = new XmlDocument(); - xml.LoadXml(@" - - Exe - netcoreapp2.1 - - -"); - Actions.LoadXml["test.csproj"] = xml; - - var autobuilder = CreateAutoBuilder("csharp", false); - TestAutobuilderScript(autobuilder, 0, 7); - } - - [Fact] - public void TestLinuxCSharpAutoBuilderExtractorFailed() - { - Actions.FileExists["csharp.log"] = false; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", false); - TestAutobuilderScript(autobuilder, 1, 0); - } - - - [Fact] - public void TestDefaultCppAutobuilder() - { - Actions.EnumerateFiles[@"C:\Project"] = ""; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("cpp", true); - var script = autobuilder.GetBuildScript(); - - // Fails due to no solutions present. - Assert.NotEqual(0, script.Run(Actions, StartCallback, EndCallback)); - } - - [Fact] - public void TestCppAutobuilderSuccess() - { - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test.sln"] = 1; - Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /p:UseSharedCompilation=false /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0; - Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = ""; - Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1; - Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0; - Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = ""; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.slx"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("cpp", true); - var solution = new TestSolution(@"C:\Project\test.sln"); - autobuilder.ProjectsOrSolutionsToBuild.Add(solution); - TestAutobuilderScript(autobuilder, 0, 2); - } - - [Fact] - public void TestVsWhereSucceeded() - { - Actions.IsWindows = true; - Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true; - Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 0; - Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "C:\\VS1\nC:\\VS2"; - Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = "10.0\n11.0"; - Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0; - - var candidates = BuildTools.GetCandidateVcVarsFiles(Actions).ToArray(); - Assert.Equal("C:\\VS1\\VC\\vcvarsall.bat", candidates[0].Path); - Assert.Equal(10, candidates[0].ToolsVersion); - Assert.Equal("C:\\VS2\\VC\\vcvarsall.bat", candidates[1].Path); - Assert.Equal(11, candidates[1].ToolsVersion); - } - - [Fact] - public void TestVsWhereNotExist() - { - Actions.IsWindows = true; - Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; - - var candidates = BuildTools.GetCandidateVcVarsFiles(Actions).ToArray(); - Assert.Equal(4, candidates.Length); - } - - [Fact] - public void TestVcVarsAllBatFiles() - { - Actions.IsWindows = true; - Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = false; - - var vcvarsfiles = BuildTools.VcVarsAllBatFiles(Actions).ToArray(); - Assert.Equal(2, vcvarsfiles.Length); - } - - [Fact] - public void TestLinuxBuildlessExtractionSuccess() - { - Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone --references:."] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", false, buildless: "true"); - TestAutobuilderScript(autobuilder, 0, 3); - } - - [Fact] - public void TestLinuxBuildlessExtractionFailed() - { - Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone --references:."] = 10; - Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", false, buildless: "true"); - TestAutobuilderScript(autobuilder, 10, 1); - } - - [Fact] - public void TestLinuxBuildlessExtractionSolution() - { - Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", false, buildless: "true", solution: "foo.sln"); - TestAutobuilderScript(autobuilder, 0, 3); - } - - void SkipVsWhere() - { - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = false; - } - - void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun) - { - Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(Actions, StartCallback, EndCallback)); - - // Check expected commands actually ran - Assert.Equal(commandsRun, StartCallbackIn.Count); - Assert.Equal(commandsRun, EndCallbackIn.Count); - Assert.Equal(commandsRun, EndCallbackReturn.Count); - - var action = Actions.RunProcess.GetEnumerator(); - for (int cmd = 0; cmd < commandsRun; ++cmd) - { - Assert.True(action.MoveNext()); - - Assert.Equal(action.Current.Key, StartCallbackIn[cmd]); - Assert.Equal(action.Current.Value, EndCallbackReturn[cmd]); - } - } - - [Fact] - public void TestLinuxBuildCommand() - { - Actions.RunProcess["dotnet --list-runtimes"] = 1; - Actions.RunProcessOut["dotnet --list-runtimes"] = ""; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - SkipVsWhere(); - - var autobuilder = CreateAutoBuilder("csharp", false, buildCommand: "./build.sh --skip-tests"); - TestAutobuilderScript(autobuilder, 0, 4); - } - - [Fact] - public void TestLinuxBuildSh() - { - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild/build.sh"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.RunProcess["/bin/chmod u+x build/build.sh"] = 0; - Actions.RunProcess["dotnet --list-runtimes"] = 1; - Actions.RunProcessOut["dotnet --list-runtimes"] = ""; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build/build.sh"] = 0; - Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build/build.sh"] = "build"; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - - var autobuilder = CreateAutoBuilder("csharp", false); - TestAutobuilderScript(autobuilder, 0, 5); - } - - [Fact] - public void TestLinuxBuildShCSharpLogMissing() - { - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.sh"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - - Actions.RunProcess["/bin/chmod u+x build.sh"] = 0; - Actions.RunProcess["dotnet --list-runtimes"] = 1; - Actions.RunProcessOut["dotnet --list-runtimes"] = ""; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build.sh"] = 0; - Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build.sh"] = ""; - Actions.FileExists["csharp.log"] = false; - - var autobuilder = CreateAutoBuilder("csharp", false); - TestAutobuilderScript(autobuilder, 1, 3); - } - - [Fact] - public void TestLinuxBuildShFailed() - { - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.sh"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - - Actions.RunProcess["/bin/chmod u+x build.sh"] = 0; - Actions.RunProcess["dotnet --list-runtimes"] = 1; - Actions.RunProcessOut["dotnet --list-runtimes"] = ""; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build.sh"] = 5; - Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build.sh"] = ""; - Actions.FileExists["csharp.log"] = true; - - var autobuilder = CreateAutoBuilder("csharp", false); - TestAutobuilderScript(autobuilder, 1, 3); - } - - [Fact] - public void TestWindowsBuildBat() - { - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.bat"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto build.bat"] = 0; - Actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto build.bat"] = ""; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - - var autobuilder = CreateAutoBuilder("csharp", true); - TestAutobuilderScript(autobuilder, 0, 3); - } - - [Fact] - public void TestWindowsBuildBatIgnoreErrors() - { - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.bat"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto build.bat"] = 1; - Actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto build.bat"] = ""; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0; - Actions.FileExists["csharp.log"] = true; - - var autobuilder = CreateAutoBuilder("csharp", true, ignoreErrors: "true"); - TestAutobuilderScript(autobuilder, 1, 1); - } - - [Fact] - public void TestWindowsCmdIgnoreErrors() - { - Actions.RunProcess["cmd.exe /C C:\\odasa\\tools\\odasa index --auto ^\"build.cmd --skip-tests^\""] = 3; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0; - Actions.FileExists["csharp.log"] = true; - SkipVsWhere(); - - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", true, buildCommand: "build.cmd --skip-tests", ignoreErrors: "true"); - TestAutobuilderScript(autobuilder, 3, 1); - } - - [Fact] - public void TestWindowCSharpMsBuild() - { - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test1.sln"] = 0; - Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test2.sln"] = 0; - Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", - vsToolsVersion: "12", allSolutions: "true"); - var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); - var testSolution2 = new TestSolution(@"C:\Project\test2.sln"); - autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1); - autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2); - - TestAutobuilderScript(autobuilder, 0, 6); - } - - [Fact] - public void TestWindowCSharpMsBuildMultipleSolutions() - { - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore test1.csproj"] = 0; - Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild test1.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore test2.csproj"] = 0; - Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild test2.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists[@"test1.csproj"] = true; - Actions.FileExists[@"test2.csproj"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "test1.csproj\ntest2.csproj\ntest1.cs\ntest2.cs"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var csproj1 = new XmlDocument(); - csproj1.LoadXml(@" - - - - - "); - Actions.LoadXml["test1.csproj"] = csproj1; - - var csproj2 = new XmlDocument(); - csproj2.LoadXml(@" - - - - - "); - Actions.LoadXml["test2.csproj"] = csproj2; - - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", - vsToolsVersion: "12"); - - TestAutobuilderScript(autobuilder, 0, 6); - } - - [Fact] - public void TestWindowCSharpMsBuildFailed() - { - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test1.sln"] = 0; - Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 1; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", - vsToolsVersion: "12", allSolutions: "true"); - var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); - var testSolution2 = new TestSolution(@"C:\Project\test2.sln"); - autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1); - autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2); - - TestAutobuilderScript(autobuilder, 1, 2); - } - - - [Fact] - public void TestSkipNugetMsBuild() - { - Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", - msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12", - allSolutions: "true", nugetRestore: "false"); - var testSolution1 = new TestSolution(@"C:\Project\test1.sln"); - var testSolution2 = new TestSolution(@"C:\Project\test2.sln"); - autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1); - autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2); - - TestAutobuilderScript(autobuilder, 0, 4); - } - - [Fact] - public void TestSkipNugetBuildless() - { - Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var autobuilder = CreateAutoBuilder("csharp", false, buildless: "true", solution: "foo.sln", nugetRestore: "false"); - TestAutobuilderScript(autobuilder, 0, 3); - } - - - [Fact] - public void TestSkipNugetDotnet() - { - Actions.RunProcess["dotnet --list-runtimes"] = 0; - Actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; - Actions.RunProcess["dotnet --info"] = 0; - Actions.RunProcess["dotnet clean test.csproj"] = 0; - Actions.RunProcess["dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore test.csproj"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - var xml = new XmlDocument(); - xml.LoadXml(@" - - Exe - netcoreapp2.1 - - -"); - Actions.LoadXml["test.csproj"] = xml; - - var autobuilder = CreateAutoBuilder("csharp", false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now. - TestAutobuilderScript(autobuilder, 0, 7); - } - - [Fact] - public void TestDotnetVersionNotInstalled() - { - Actions.RunProcess["dotnet --list-sdks"] = 0; - Actions.RunProcessOut["dotnet --list-sdks"] = "2.1.2 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; - Actions.RunProcess[@"curl -L -sO https://dot.net/v1/dotnet-install.sh"] = 0; - Actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; - Actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; - Actions.RunProcess[@"rm dotnet-install.sh"] = 0; - Actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; - Actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; - Actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; - Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean test.csproj"] = 0; - Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental test.csproj"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - var xml = new XmlDocument(); - xml.LoadXml(@" - - Exe - netcoreapp2.1 - - -"); - Actions.LoadXml["test.csproj"] = xml; - - var autobuilder = CreateAutoBuilder("csharp", false, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 12); - } - - [Fact] - public void TestDotnetVersionAlreadyInstalled() - { - Actions.RunProcess["dotnet --list-sdks"] = 0; - Actions.RunProcessOut["dotnet --list-sdks"] = @"2.1.3 [C:\Program Files\dotnet\sdks] -2.1.4 [C:\Program Files\dotnet\sdks]"; - Actions.RunProcess[@"curl -L -sO https://dot.net/v1/dotnet-install.sh"] = 0; - Actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; - Actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; - Actions.RunProcess[@"rm dotnet-install.sh"] = 0; - Actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; - Actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.AspNetCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] -Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; - Actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; - Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean test.csproj"] = 0; - Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbar.cs\ntest.csproj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - var xml = new XmlDocument(); - xml.LoadXml(@" - - Exe - netcoreapp2.1 - - -"); - Actions.LoadXml["test.csproj"] = xml; - - var autobuilder = CreateAutoBuilder("csharp", false, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 12); - } - - [Fact] - public void TestDotnetVersionWindows() - { - Actions.RunProcess["cmd.exe /C dotnet --list-sdks"] = 0; - Actions.RunProcessOut["cmd.exe /C dotnet --list-sdks"] = "2.1.3 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; - Actions.RunProcess[@"cmd.exe /C powershell -NoProfile -ExecutionPolicy unrestricted -file C:\Project\install-dotnet.ps1 -Version 2.1.3 -InstallDir C:\Project\.dotnet"] = 0; - Actions.RunProcess[@"cmd.exe /C del C:\Project\install-dotnet.ps1"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet --info"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; - Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - var xml = new XmlDocument(); - xml.LoadXml(@" - - Exe - netcoreapp2.1 - - -"); - Actions.LoadXml["test.csproj"] = xml; - - var autobuilder = CreateAutoBuilder("csharp", true, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 9); - } - - [Fact] - public void TestDirsProjWindows() - { - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore dirs.proj"] = 1; - Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild dirs.proj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists[@"a\test.csproj"] = true; - Actions.FileExists["dirs.proj"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; - Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "a\\test.cs\na\\test.csproj\ndirs.proj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var csproj = new XmlDocument(); - csproj.LoadXml(@" - - - - - "); - Actions.LoadXml["a\\test.csproj"] = csproj; - - var dirsproj = new XmlDocument(); - dirsproj.LoadXml(@" - - - -"); - Actions.LoadXml["dirs.proj"] = dirsproj; - - var autobuilder = CreateAutoBuilder("csharp", true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", - vsToolsVersion: "12", allSolutions: "true"); - TestAutobuilderScript(autobuilder, 0, 4); - } - - [Fact] - public void TestDirsProjLinux() - { - Actions.RunProcess[@"mono C:\odasa\tools/csharp/nuget/nuget.exe restore dirs.proj"] = 1; - Actions.RunProcess[@"C:\odasa/tools/odasa index --auto msbuild dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; - Actions.FileExists["csharp.log"] = true; - Actions.FileExists["a/test.csproj"] = true; - Actions.FileExists["dirs.proj"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.EnumerateFiles[@"C:\Project"] = "a/test.cs\na/test.csproj\ndirs.proj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var csproj = new XmlDocument(); - csproj.LoadXml(@" - - - - - "); - Actions.LoadXml["a/test.csproj"] = csproj; - - var dirsproj = new XmlDocument(); - dirsproj.LoadXml(@" - - - -"); - Actions.LoadXml["dirs.proj"] = dirsproj; - - var autobuilder = CreateAutoBuilder("csharp", false); - TestAutobuilderScript(autobuilder, 0, 4); - } - - [Fact] - public void TestCyclicDirsProj() - { - Actions.FileExists["dirs.proj"] = true; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; - Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; - Actions.FileExists["csharp.log"] = false; - Actions.EnumerateFiles[@"C:\Project"] = "dirs.proj"; - Actions.EnumerateDirectories[@"C:\Project"] = ""; - - var dirsproj1 = new XmlDocument(); - dirsproj1.LoadXml(@" - - - -"); - Actions.LoadXml["dirs.proj"] = dirsproj1; - - var autobuilder = CreateAutoBuilder("csharp", false); - TestAutobuilderScript(autobuilder, 1, 0); - } - - [Fact] - public void TestAsStringWithExpandedEnvVarsWindows() - { - Actions.IsWindows = true; - Actions.GetEnvironmentVariable["LGTM_SRC"] = @"C:\repo"; - Assert.Equal(@"C:\repo\test", @"%LGTM_SRC%\test".AsStringWithExpandedEnvVars(Actions)); - } - - [Fact] - public void TestAsStringWithExpandedEnvVarsLinux() - { - Actions.IsWindows = false; - Actions.GetEnvironmentVariable["LGTM_SRC"] = "/tmp/repo"; - Assert.Equal("/tmp/repo/test", "$LGTM_SRC/test".AsStringWithExpandedEnvVars(Actions)); - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/Properties/AssemblyInfo.cs b/csharp/autobuilder/Semmle.Autobuild.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 778d6305fc5d..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -īģŋusing System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Semmle.Autobuild.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Semmle.Extraction.Tests")] -[assembly: AssemblyCopyright("Copyright Š 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/Semmle.Autobuild.Tests.csproj b/csharp/autobuilder/Semmle.Autobuild.Tests/Semmle.Autobuild.Tests.csproj deleted file mode 100644 index 1f0016fc9b0b..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/Semmle.Autobuild.Tests.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - Exe - netcoreapp3.0 - false - win-x64;linux-x64;osx-x64 - enable - - - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - - - diff --git a/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs deleted file mode 100644 index f9c690c273b8..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs +++ /dev/null @@ -1,21 +0,0 @@ -īģŋnamespace Semmle.Autobuild -{ - /// - /// ASP extraction. - /// - class AspBuildRule : IBuildRule - { - public BuildScript Analyse(Autobuilder builder, bool auto) - { - var javaHome = builder.JavaHome; - var dist = builder.Distribution; - - var command = new CommandBuilder(builder.Actions). - RunCommand(builder.Actions.PathCombine(javaHome, "bin", "java")). - Argument("-jar"). - QuoteArgument(builder.Actions.PathCombine(dist, "tools", "extractor-asp.jar")). - Argument("."); - return command.Script; - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs deleted file mode 100644 index 9786e4dcca65..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs +++ /dev/null @@ -1,123 +0,0 @@ -īģŋusing System; -using System.Linq; -using System.Text.RegularExpressions; - -namespace Semmle.Autobuild -{ - /// - /// Encapsulates build options. - /// - public class AutobuildOptions - { - public readonly int SearchDepth = 3; - public readonly string RootDirectory; - private const string prefix = "LGTM_INDEX_"; - - public readonly string? VsToolsVersion; - public readonly string? MsBuildArguments; - public readonly string? MsBuildPlatform; - public readonly string? MsBuildConfiguration; - public readonly string? MsBuildTarget; - public readonly string? DotNetArguments; - public readonly string? DotNetVersion; - public readonly string? BuildCommand; - public readonly string[] Solution; - - public readonly bool IgnoreErrors; - public readonly bool Buildless; - public readonly bool AllSolutions; - public readonly bool NugetRestore; - - public readonly Language Language; - public readonly bool Indexing; - - /// - /// Reads options from environment variables. - /// Throws ArgumentOutOfRangeException for invalid arguments. - /// - public AutobuildOptions(IBuildActions actions) - { - RootDirectory = actions.GetCurrentDirectory(); - VsToolsVersion = actions.GetEnvironmentVariable(prefix + "VSTOOLS_VERSION"); - MsBuildArguments = actions.GetEnvironmentVariable(prefix + "MSBUILD_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions); - MsBuildPlatform = actions.GetEnvironmentVariable(prefix + "MSBUILD_PLATFORM"); - MsBuildConfiguration = actions.GetEnvironmentVariable(prefix + "MSBUILD_CONFIGURATION"); - MsBuildTarget = actions.GetEnvironmentVariable(prefix + "MSBUILD_TARGET"); - DotNetArguments = actions.GetEnvironmentVariable(prefix + "DOTNET_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions); - DotNetVersion = actions.GetEnvironmentVariable(prefix + "DOTNET_VERSION"); - BuildCommand = actions.GetEnvironmentVariable(prefix + "BUILD_COMMAND"); - Solution = actions.GetEnvironmentVariable(prefix + "SOLUTION").AsListWithExpandedEnvVars(actions, new string[0]); - - IgnoreErrors = actions.GetEnvironmentVariable(prefix + "IGNORE_ERRORS").AsBool("ignore_errors", false); - Buildless = actions.GetEnvironmentVariable(prefix + "BUILDLESS").AsBool("buildless", false); - AllSolutions = actions.GetEnvironmentVariable(prefix + "ALL_SOLUTIONS").AsBool("all_solutions", false); - NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true); - - Language = actions.GetEnvironmentVariable("LGTM_PROJECT_LANGUAGE").AsLanguage(); - Indexing = !actions.GetEnvironmentVariable("CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING").AsBool("no_indexing", false); - } - } - - public static class OptionsExtensions - { - public static bool AsBool(this string? value, string param, bool defaultValue) - { - if (value == null) return defaultValue; - switch (value.ToLower()) - { - case "on": - case "yes": - case "true": - case "enabled": - return true; - case "off": - case "no": - case "false": - case "disabled": - return false; - default: - throw new ArgumentOutOfRangeException(param, value, "The Boolean value is invalid."); - } - } - - public static Language AsLanguage(this string? key) - { - switch (key) - { - case null: - throw new ArgumentException("Environment variable required: LGTM_PROJECT_LANGUAGE"); - case "csharp": - return Language.CSharp; - case "cpp": - return Language.Cpp; - default: - throw new ArgumentException("Language key not understood: '" + key + "'"); - } - } - - public static string[] AsListWithExpandedEnvVars(this string? value, IBuildActions actions, string[] defaultValue) - { - if (value == null) - return defaultValue; - - return value. - Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries). - Select(s => AsStringWithExpandedEnvVars(s, actions)).ToArray(); - } - - static readonly Regex linuxEnvRegEx = new Regex(@"\$([a-zA-Z_][a-zA-Z_0-9]*)", RegexOptions.Compiled); - - public static string AsStringWithExpandedEnvVars(this string value, IBuildActions actions) - { - if (string.IsNullOrEmpty(value)) - return value; - - // `Environment.ExpandEnvironmentVariables` only works with Windows-style - // environment variables - var windowsStyle = actions.IsWindows() - ? value - : linuxEnvRegEx.Replace(value, m => $"%{m.Groups[1].Value}%"); - return actions.EnvironmentExpandEnvironmentVariables(windowsStyle); - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs deleted file mode 100644 index a882f5bb80c7..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs +++ /dev/null @@ -1,431 +0,0 @@ -īģŋusing Semmle.Extraction.CSharp; -using Semmle.Util.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Semmle.Autobuild -{ - /// - /// A build rule analyses the files in "builder" and outputs a build script. - /// - interface IBuildRule - { - /// - /// Analyse the files and produce a build script. - /// - /// The files and options relating to the build. - /// Whether this build rule is being automatically applied. - BuildScript Analyse(Autobuilder builder, bool auto); - } - - /// - /// Exception indicating that environment variables are missing or invalid. - /// - class InvalidEnvironmentException : Exception - { - public InvalidEnvironmentException(string m) : base(m) { } - } - - /// - /// Main application logic, containing all data - /// gathered from the project and filesystem. - /// - /// The overall design is intended to be extensible so that in theory, - /// it should be possible to add new build rules without touching this code. - /// - public class Autobuilder - { - /// - /// Full file paths of files found in the project directory, as well as - /// their distance from the project root folder. The list is sorted - /// by distance in ascending order. - /// - public IEnumerable<(string, int)> Paths => pathsLazy.Value; - readonly Lazy> pathsLazy; - - /// - /// Gets a list of paths matching a set of extensions (including the "."), - /// as well as their distance from the project root folder. - /// The list is sorted by distance in ascending order. - /// - /// The extensions to find. - /// The files matching the extension. - public IEnumerable<(string, int)> GetExtensions(params string[] extensions) => - Paths.Where(p => extensions.Contains(Path.GetExtension(p.Item1))); - - /// - /// Gets all paths matching a particular filename, as well as - /// their distance from the project root folder. The list is sorted - /// by distance in ascending order. - /// - /// The filename to find. - /// Possibly empty sequence of paths with the given filename. - public IEnumerable<(string, int)> GetFilename(string name) => - Paths.Where(p => Path.GetFileName(p.Item1) == name); - - /// - /// Holds if a given path, relative to the root of the source directory - /// was found. - /// - /// The relative path. - /// True iff the path was found. - public bool HasRelativePath(string path) => HasPath(Actions.PathCombine(RootDirectory, path)); - - /// - /// List of project/solution files to build. - /// - public IList ProjectsOrSolutionsToBuild => projectsOrSolutionsToBuildLazy.Value; - private readonly Lazy> projectsOrSolutionsToBuildLazy; - - /// - /// Holds if a given path was found. - /// - /// The path of the file. - /// True iff the path was found. - public bool HasPath(string path) => Paths.Any(p => path == p.Item1); - - void FindFiles(string dir, int depth, int maxDepth, IList<(string, int)> results) - { - foreach (var f in Actions.EnumerateFiles(dir)) - { - results.Add((f, depth)); - } - - if (depth < maxDepth) - { - foreach (var d in Actions.EnumerateDirectories(dir)) - { - FindFiles(d, depth + 1, maxDepth, results); - } - } - } - - /// - /// The root of the source directory. - /// - string RootDirectory => Options.RootDirectory; - - /// - /// Gets the supplied build configuration. - /// - public AutobuildOptions Options { get; } - - /// - /// The set of build actions used during the autobuilder. - /// Could be real system operations, or a stub for testing. - /// - public IBuildActions Actions { get; } - - IEnumerable? FindFiles(string extension, Func create) - { - var matchingFiles = GetExtensions(extension). - Select(p => (ProjectOrSolution: create(p.Item1), DistanceFromRoot: p.Item2)). - Where(p => p.ProjectOrSolution.HasLanguage(this.Options.Language)). - ToArray(); - - if (matchingFiles.Length == 0) - return null; - - if (Options.AllSolutions) - return matchingFiles.Select(p => p.ProjectOrSolution); - - return matchingFiles. - Where(f => f.DistanceFromRoot == matchingFiles[0].DistanceFromRoot). - Select(f => f.ProjectOrSolution); - } - - /// - /// Find all the relevant files and picks the best - /// solution file and tools. - /// - /// The command line options. - public Autobuilder(IBuildActions actions, AutobuildOptions options) - { - Actions = actions; - Options = options; - - pathsLazy = new Lazy>(() => - { - var files = new List<(string, int)>(); - FindFiles(options.RootDirectory, 0, options.SearchDepth, files); - return files.OrderBy(f => f.Item2).ToArray(); - }); - - projectsOrSolutionsToBuildLazy = new Lazy>(() => - { - List? ret; - if (options.Solution.Any()) - { - ret = new List(); - foreach (var solution in options.Solution) - { - if (actions.FileExists(solution)) - ret.Add(new Solution(this, solution, true)); - else - Log(Severity.Error, $"The specified project or solution file {solution} was not found"); - } - return ret; - } - - // First look for `.proj` files - ret = FindFiles(".proj", f => new Project(this, f))?.ToList(); - if (ret != null) - return ret; - - // Then look for `.sln` files - ret = FindFiles(".sln", f => new Solution(this, f, false))?.ToList(); - if (ret != null) - return ret; - - // Finally look for language specific project files, e.g. `.csproj` files - ret = FindFiles(this.Options.Language.ProjectExtension, f => new Project(this, f))?.ToList(); - return ret ?? new List(); - }); - - CodeQLExtractorCSharpRoot = Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_ROOT"); - SemmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST"); - SemmlePlatformTools = Actions.GetEnvironmentVariable("SEMMLE_PLATFORM_TOOLS"); - - JavaHome = - Actions.GetEnvironmentVariable("CODEQL_JAVA_HOME") ?? - Actions.GetEnvironmentVariable("SEMMLE_JAVA_HOME") ?? - throw new InvalidEnvironmentException("The environment variable CODEQL_JAVA_HOME or SEMMLE_JAVA_HOME has not been set."); - - Distribution = - CodeQLExtractorCSharpRoot ?? - SemmleDist ?? - throw new InvalidEnvironmentException("The environment variable CODEQL_EXTRACTOR_CSHARP_ROOT or SEMMLE_DIST has not been set."); - - TrapDir = - Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_TRAP_DIR") ?? - Actions.GetEnvironmentVariable("TRAP_FOLDER") ?? - throw new InvalidEnvironmentException("The environment variable CODEQL_EXTRACTOR_CSHARP_TRAP_DIR or TRAP_FOLDER has not been set."); - - SourceArchiveDir = - Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR") ?? - Actions.GetEnvironmentVariable("SOURCE_ARCHIVE") ?? - throw new InvalidEnvironmentException("The environment variable CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR or SOURCE_ARCHIVE has not been set."); - } - - private string TrapDir { get; } - - private string SourceArchiveDir { get; } - - readonly ILogger logger = new ConsoleLogger(Verbosity.Info); - - /// - /// Log a given build event to the console. - /// - /// The format string. - /// Inserts to the format string. - public void Log(Severity severity, string format, params object[] args) - { - logger.Log(severity, format, args); - } - - /// - /// Attempt to build this project. - /// - /// The exit code, 0 for success and non-zero for failures. - public int AttemptBuild() - { - Log(Severity.Info, $"Working directory: {Options.RootDirectory}"); - - var script = GetBuildScript(); - if (Options.IgnoreErrors) - script |= BuildScript.Success; - - void startCallback(string s, bool silent) - { - Log(silent ? Severity.Debug : Severity.Info, $"\nRunning {s}"); - } - - void exitCallback(int ret, string msg, bool silent) - { - Log(silent ? Severity.Debug : Severity.Info, $"Exit code {ret}{(string.IsNullOrEmpty(msg) ? "" : $": {msg}")}"); - } - - return script.Run(Actions, startCallback, exitCallback); - } - - /// - /// Returns the build script to use for this project. - /// - public BuildScript GetBuildScript() - { - var isCSharp = Options.Language == Language.CSharp; - return isCSharp ? GetCSharpBuildScript() : GetCppBuildScript(); - } - - BuildScript GetCSharpBuildScript() - { - /// - /// A script that checks that the C# extractor has been executed. - /// - BuildScript CheckExtractorRun(bool warnOnFailure) => - BuildScript.Create(actions => - { - if (actions.FileExists(Extractor.GetCSharpLogPath())) - return 0; - - if (warnOnFailure) - Log(Severity.Error, "No C# code detected during build."); - - return 1; - }); - - var attempt = BuildScript.Failure; - switch (GetCSharpBuildStrategy()) - { - case CSharpBuildStrategy.CustomBuildCommand: - attempt = new BuildCommandRule().Analyse(this, false) & CheckExtractorRun(true); - break; - case CSharpBuildStrategy.Buildless: - // No need to check that the extractor has been executed in buildless mode - attempt = new StandaloneBuildRule().Analyse(this, false); - break; - case CSharpBuildStrategy.MSBuild: - attempt = new MsBuildRule().Analyse(this, false) & CheckExtractorRun(true); - break; - case CSharpBuildStrategy.DotNet: - attempt = new DotNetRule().Analyse(this, false) & CheckExtractorRun(true); - break; - case CSharpBuildStrategy.Auto: - var cleanTrapFolder = - BuildScript.DeleteDirectory(TrapDir); - var cleanSourceArchive = - BuildScript.DeleteDirectory(SourceArchiveDir); - var tryCleanExtractorArgsLogs = - BuildScript.Create(actions => - { - foreach (var file in Extractor.GetCSharpArgsLogs()) - try - { - actions.FileDelete(file); - } - catch // lgtm[cs/catch-of-all-exceptions] lgtm[cs/empty-catch-block] - { } - return 0; - }); - var attemptExtractorCleanup = - BuildScript.Try(cleanTrapFolder) & - BuildScript.Try(cleanSourceArchive) & - tryCleanExtractorArgsLogs & - BuildScript.DeleteFile(Extractor.GetCSharpLogPath()); - - /// - /// Execute script `s` and check that the C# extractor has been executed. - /// If either fails, attempt to cleanup any artifacts produced by the extractor, - /// and exit with code 1, in order to proceed to the next attempt. - /// - BuildScript IntermediateAttempt(BuildScript s) => - (s & CheckExtractorRun(false)) | - (attemptExtractorCleanup & BuildScript.Failure); - - attempt = - // First try .NET Core - IntermediateAttempt(new DotNetRule().Analyse(this, true)) | - // Then MSBuild - (() => IntermediateAttempt(new MsBuildRule().Analyse(this, true))) | - // And finally look for a script that might be a build script - (() => new BuildCommandAutoRule().Analyse(this, true) & CheckExtractorRun(true)) | - // All attempts failed: print message - AutobuildFailure(); - break; - } - - return - attempt & - (() => new AspBuildRule().Analyse(this, false)) & - (() => new XmlBuildRule().Analyse(this, false)); - } - - /// - /// Gets the build strategy that the autobuilder should apply, based on the - /// options in the `lgtm.yml` file. - /// - CSharpBuildStrategy GetCSharpBuildStrategy() - { - if (Options.BuildCommand != null) - return CSharpBuildStrategy.CustomBuildCommand; - - if (Options.Buildless) - return CSharpBuildStrategy.Buildless; - - if (Options.MsBuildArguments != null - || Options.MsBuildConfiguration != null - || Options.MsBuildPlatform != null - || Options.MsBuildTarget != null) - return CSharpBuildStrategy.MSBuild; - - if (Options.DotNetArguments != null || Options.DotNetVersion != null) - return CSharpBuildStrategy.DotNet; - - return CSharpBuildStrategy.Auto; - } - - enum CSharpBuildStrategy - { - CustomBuildCommand, - Buildless, - MSBuild, - DotNet, - Auto - } - - BuildScript GetCppBuildScript() - { - if (Options.BuildCommand != null) - return new BuildCommandRule().Analyse(this, false); - - return - // First try MSBuild - new MsBuildRule().Analyse(this, true) | - // Then look for a script that might be a build script - (() => new BuildCommandAutoRule().Analyse(this, true)) | - // All attempts failed: print message - AutobuildFailure(); - } - - BuildScript AutobuildFailure() => - BuildScript.Create(actions => - { - Log(Severity.Error, "Could not auto-detect a suitable build method"); - return 1; - }); - - /// - /// Value of CODEQL_EXTRACTOR_CSHARP_ROOT environment variable. - /// - private string? CodeQLExtractorCSharpRoot { get; } - - /// - /// Value of SEMMLE_DIST environment variable. - /// - private string? SemmleDist { get; } - - public string Distribution { get; } - - public string JavaHome { get; } - - /// - /// Value of SEMMLE_PLATFORM_TOOLS environment variable. - /// - public string? SemmlePlatformTools { get; } - - /// - /// The absolute path of the odasa executable. - /// null if we are running in CodeQL. - /// - public string? Odasa => SemmleDist is null ? null : Actions.PathCombine(SemmleDist, "tools", "odasa"); - - /// - /// Construct a command that executed the given wrapped in - /// an odasa --index, unless indexing has been disabled, in which case - /// is run directly. - /// - internal CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Options.Indexing && !(Odasa is null) ? builder.IndexCommand(Odasa, cmd) : builder.RunCommand(cmd); - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs b/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs deleted file mode 100644 index 80a819f403e4..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs +++ /dev/null @@ -1,60 +0,0 @@ -īģŋusing System.Collections.Generic; -using System.IO; -using System.Linq; -using Semmle.Util.Logging; - -namespace Semmle.Autobuild -{ - /// - /// Auto-detection of build scripts. - /// - class BuildCommandAutoRule : IBuildRule - { - readonly IEnumerable winExtensions = new List { - ".bat", - ".cmd", - ".exe" - }; - - readonly IEnumerable linuxExtensions = new List { - "", - ".sh" - }; - - readonly IEnumerable buildScripts = new List { - "build" - }; - - public BuildScript Analyse(Autobuilder builder, bool auto) - { - builder.Log(Severity.Info, "Attempting to locate build script"); - - var extensions = builder.Actions.IsWindows() ? winExtensions : linuxExtensions; - var scripts = buildScripts.SelectMany(s => extensions.Select(e => s + e)); - var scriptPath = builder.Paths.Where(p => scripts.Any(p.Item1.ToLower().EndsWith)).OrderBy(p => p.Item2).Select(p => p.Item1).FirstOrDefault(); - - if (scriptPath == null) - return BuildScript.Failure; - - var chmod = new CommandBuilder(builder.Actions); - chmod.RunCommand("/bin/chmod", $"u+x {scriptPath}"); - var chmodScript = builder.Actions.IsWindows() ? BuildScript.Success : BuildScript.Try(chmod.Script); - - string? dir = Path.GetDirectoryName(scriptPath); - - // A specific .NET Core version may be required - return chmodScript & DotNetRule.WithDotNet(builder, environment => - { - var command = new CommandBuilder(builder.Actions, dir, environment); - - // A specific Visual Studio version may be required - var vsTools = MsBuildRule.GetVcVarsBatFile(builder); - if (vsTools != null) - command.CallBatFile(vsTools.Path); - - builder.MaybeIndex(command, scriptPath); - return command.Script; - }); - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs b/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs deleted file mode 100644 index fe91503ec8fb..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs +++ /dev/null @@ -1,28 +0,0 @@ -īģŋnamespace Semmle.Autobuild -{ - /// - /// Execute the build_command rule. - /// - class BuildCommandRule : IBuildRule - { - public BuildScript Analyse(Autobuilder builder, bool auto) - { - if (builder.Options.BuildCommand == null) - return BuildScript.Failure; - - // Custom build commands may require a specific .NET Core version - return DotNetRule.WithDotNet(builder, environment => - { - var command = new CommandBuilder(builder.Actions, null, environment); - - // Custom build commands may require a specific Visual Studio version - var vsTools = MsBuildRule.GetVcVarsBatFile(builder); - if (vsTools != null) - command.CallBatFile(vsTools.Path); - builder.MaybeIndex(command, builder.Options.BuildCommand); - - return command.Script; - }); - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/Language.cs b/csharp/autobuilder/Semmle.Autobuild/Language.cs deleted file mode 100644 index 5049506be57a..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/Language.cs +++ /dev/null @@ -1,21 +0,0 @@ -īģŋnamespace Semmle.Autobuild -{ - public sealed class Language - { - public static readonly Language Cpp = new Language(".vcxproj"); - public static readonly Language CSharp = new Language(".csproj"); - - public bool ProjectFileHasThisLanguage(string path) => - System.IO.Path.GetExtension(path) == ProjectExtension; - - public readonly string ProjectExtension; - - private Language(string extension) - { - ProjectExtension = extension; - } - - public override string ToString() => - ProjectExtension == Cpp.ProjectExtension ? "C/C++" : "C#"; - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs deleted file mode 100644 index 569f9f183eba..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs +++ /dev/null @@ -1,134 +0,0 @@ -using Semmle.Util.Logging; -using System.Linq; - -namespace Semmle.Autobuild -{ - /// - /// A build rule using msbuild. - /// - class MsBuildRule : IBuildRule - { - /// - /// The name of the msbuild command. - /// - const string MsBuild = "msbuild"; - - public BuildScript Analyse(Autobuilder builder, bool auto) - { - if (!builder.ProjectsOrSolutionsToBuild.Any()) - return BuildScript.Failure; - - if (auto) - builder.Log(Severity.Info, "Attempting to build using MSBuild"); - - var vsTools = GetVcVarsBatFile(builder); - - if (vsTools == null && builder.ProjectsOrSolutionsToBuild.Any()) - { - var firstSolution = builder.ProjectsOrSolutionsToBuild.OfType().FirstOrDefault(); - vsTools = firstSolution != null - ? BuildTools.FindCompatibleVcVars(builder.Actions, firstSolution) - : BuildTools.VcVarsAllBatFiles(builder.Actions).OrderByDescending(b => b.ToolsVersion).FirstOrDefault(); - } - - if (vsTools == null && builder.Actions.IsWindows()) - { - builder.Log(Severity.Warning, "Could not find a suitable version of vcvarsall.bat"); - } - - var nuget = - builder.SemmlePlatformTools != null ? - builder.Actions.PathCombine(builder.SemmlePlatformTools, "csharp", "nuget", "nuget.exe") : - "nuget"; - - var ret = BuildScript.Success; - - foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild) - { - if (builder.Options.NugetRestore) - { - var nugetCommand = new CommandBuilder(builder.Actions). - RunCommand(nuget). - Argument("restore"). - QuoteArgument(projectOrSolution.FullPath); - ret &= BuildScript.Try(nugetCommand.Script); - } - - var command = new CommandBuilder(builder.Actions); - - if (vsTools != null) - { - command.CallBatFile(vsTools.Path); - // `vcvarsall.bat` sets a default Platform environment variable, - // which may not be compatible with the supported platforms of the - // given project/solution. Unsetting it means that the default platform - // of the project/solution is used instead. - command.RunCommand("set Platform=&& type NUL", quoteExe: false); - } - - builder.MaybeIndex(command, MsBuild); - command.QuoteArgument(projectOrSolution.FullPath); - - command.Argument("/p:UseSharedCompilation=false"); - - string target = builder.Options.MsBuildTarget != null - ? builder.Options.MsBuildTarget - : "rebuild"; - string? platform = builder.Options.MsBuildPlatform != null - ? builder.Options.MsBuildPlatform - : projectOrSolution is ISolution s1 ? s1.DefaultPlatformName : null; - string? configuration = builder.Options.MsBuildConfiguration != null - ? builder.Options.MsBuildConfiguration - : projectOrSolution is ISolution s2 ? s2.DefaultConfigurationName : null; - - command.Argument("/t:" + target); - if (platform != null) - command.Argument(string.Format("/p:Platform=\"{0}\"", platform)); - if (configuration != null) - command.Argument(string.Format("/p:Configuration=\"{0}\"", configuration)); - command.Argument("/p:MvcBuildViews=true"); - - command.Argument(builder.Options.MsBuildArguments); - - ret &= command.Script; - } - - return ret; - } - - /// - /// Gets the BAT file used to initialize the appropriate Visual Studio - /// version/platform, as specified by the `vstools_version` property in - /// lgtm.yml. - /// - /// Returns null when no version is specified. - /// - public static VcVarsBatFile? GetVcVarsBatFile(Autobuilder builder) - { - VcVarsBatFile? vsTools = null; - - if (builder.Options.VsToolsVersion != null) - { - if (int.TryParse(builder.Options.VsToolsVersion, out var msToolsVersion)) - { - foreach (var b in BuildTools.VcVarsAllBatFiles(builder.Actions)) - { - builder.Log(Severity.Info, "Found {0} version {1}", b.Path, b.ToolsVersion); - } - - vsTools = BuildTools.FindCompatibleVcVars(builder.Actions, msToolsVersion); - if (vsTools == null) - builder.Log(Severity.Warning, "Could not find build tools matching version {0}", msToolsVersion); - else - builder.Log(Severity.Info, "Setting Visual Studio tools to {0}", vsTools.Path); - } - else - { - builder.Log(Severity.Error, "The format of vstools_version is incorrect. Please specify an integer."); - } - } - - return vsTools; - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/Program.cs b/csharp/autobuilder/Semmle.Autobuild/Program.cs deleted file mode 100644 index e4bccb0e626e..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/Program.cs +++ /dev/null @@ -1,32 +0,0 @@ -īģŋusing System; - -namespace Semmle.Autobuild -{ - class Program - { - static int Main() - { - - try - { - var actions = SystemBuildActions.Instance; - var options = new AutobuildOptions(actions); - try - { - Console.WriteLine($"Semmle autobuilder for {options.Language}"); - var builder = new Autobuilder(actions, options); - return builder.AttemptBuild(); - } - catch(InvalidEnvironmentException ex) - { - Console.WriteLine("The environment is invalid: {0}", ex.Message); - } - } - catch (ArgumentOutOfRangeException ex) - { - Console.WriteLine("The value \"{0}\" for parameter \"{1}\" is invalid", ex.ActualValue, ex.ParamName); - } - return 1; - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/Properties/AssemblyInfo.cs b/csharp/autobuilder/Semmle.Autobuild/Properties/AssemblyInfo.cs deleted file mode 100644 index e3da7ca22e9e..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -īģŋusing System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Semmle.Autobuild")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Semmle")] -[assembly: AssemblyProduct("Semmle Visual Studio Autobuild")] -[assembly: AssemblyCopyright("Copyright Š Semmle 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("1d9920ad-7b00-4df1-8b01-9ff5b687828e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/csharp/autobuilder/Semmle.Autobuild/Semmle.Autobuild.csproj b/csharp/autobuilder/Semmle.Autobuild/Semmle.Autobuild.csproj deleted file mode 100644 index 63aab3b29fbb..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/Semmle.Autobuild.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - netcoreapp3.0 - Semmle.Autobuild - Semmle.Autobuild - - Exe - - false - win-x64;linux-x64;osx-x64 - enable - - - - - - - - - - - - - - - - - diff --git a/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs deleted file mode 100644 index 26bc84bb6015..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs +++ /dev/null @@ -1,47 +0,0 @@ -īģŋnamespace Semmle.Autobuild -{ - /// - /// Build using standalone extraction. - /// - class StandaloneBuildRule : IBuildRule - { - public BuildScript Analyse(Autobuilder builder, bool auto) - { - BuildScript GetCommand(string? solution) - { - if (builder.SemmlePlatformTools is null) - return BuildScript.Failure; - - var standalone = builder.Actions.PathCombine(builder.SemmlePlatformTools, "csharp", "Semmle.Extraction.CSharp.Standalone"); - var cmd = new CommandBuilder(builder.Actions); - cmd.RunCommand(standalone); - - if (solution != null) - cmd.QuoteArgument(solution); - - cmd.Argument("--references:."); - - if (!builder.Options.NugetRestore) - { - cmd.Argument("--skip-nuget"); - } - - return cmd.Script; - } - - if (!builder.Options.Buildless) - return BuildScript.Failure; - - var solutions = builder.Options.Solution.Length; - - if (solutions == 0) - return GetCommand(null); - - var script = BuildScript.Success; - foreach (var solution in builder.Options.Solution) - script &= GetCommand(solution); - - return script; - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs deleted file mode 100644 index d9b05dbe0a99..000000000000 --- a/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs +++ /dev/null @@ -1,19 +0,0 @@ -īģŋnamespace Semmle.Autobuild -{ - /// - /// XML extraction. - /// - class XmlBuildRule : IBuildRule - { - public BuildScript Analyse(Autobuilder builder, bool auto) - { - if (!builder.Options.Indexing || builder.Odasa is null) - return BuildScript.Success; - - var command = new CommandBuilder(builder.Actions). - RunCommand(builder.Odasa). - Argument("index --xml --extensions config csproj props xml"); - return command.Script; - } - } -} diff --git a/csharp/codeql-extractor.yml b/csharp/codeql-extractor.yml new file mode 100644 index 000000000000..f346663157a1 --- /dev/null +++ b/csharp/codeql-extractor.yml @@ -0,0 +1,18 @@ +name: "csharp" +display_name: "C#" +version: 1.22.1 +column_kind: "utf16" +extra_env_vars: + DOTNET_GENERATE_ASPNET_CERTIFICATE: "false" + COR_ENABLE_PROFILING: "1" + COR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}" + COR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}" + CORECLR_ENABLE_PROFILING: "1" + CORECLR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}" + CORECLR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}" +file_types: + - name: cs + display_name: C# sources + extensions: + - .cs +legacy_qltest_extraction: true diff --git a/csharp/extractor/Semmle.Extraction.CIL.Driver/ExtractorOptions.cs b/csharp/extractor/Semmle.Extraction.CIL.Driver/ExtractorOptions.cs index 2c032c699f34..9964ff5bf0cd 100644 --- a/csharp/extractor/Semmle.Extraction.CIL.Driver/ExtractorOptions.cs +++ b/csharp/extractor/Semmle.Extraction.CIL.Driver/ExtractorOptions.cs @@ -14,11 +14,11 @@ namespace Semmle.Extraction.CIL.Driver /// Information about a single assembly. /// In particular, provides references between assemblies. /// - class AssemblyInfo + internal class AssemblyInfo { - public override string ToString() => filename; + public override string ToString() => Filename; - static AssemblyName CreateAssemblyName(MetadataReader mdReader, StringHandle name, System.Version version, StringHandle culture) + private static AssemblyName CreateAssemblyName(MetadataReader mdReader, StringHandle name, System.Version version, StringHandle culture) { var cultureString = mdReader.GetString(culture); @@ -34,7 +34,7 @@ static AssemblyName CreateAssemblyName(MetadataReader mdReader, StringHandle nam return assemblyName; } - static AssemblyName CreateAssemblyName(MetadataReader mdReader, AssemblyReference ar) + private static AssemblyName CreateAssemblyName(MetadataReader mdReader, AssemblyReference ar) { var an = CreateAssemblyName(mdReader, ar.Name, ar.Version, ar.Culture); if (!ar.PublicKeyOrToken.IsNil) @@ -42,7 +42,7 @@ static AssemblyName CreateAssemblyName(MetadataReader mdReader, AssemblyReferenc return an; } - static AssemblyName CreateAssemblyName(MetadataReader mdReader, AssemblyDefinition ad) + private static AssemblyName CreateAssemblyName(MetadataReader mdReader, AssemblyDefinition ad) { var an = CreateAssemblyName(mdReader, ad.Name, ad.Version, ad.Culture); if (!ad.PublicKey.IsNil) @@ -50,47 +50,51 @@ static AssemblyName CreateAssemblyName(MetadataReader mdReader, AssemblyDefiniti return an; } + /// + /// Initializes a new instance of the class. + /// + /// Path of the assembly. + /// + /// Thrown when the input file is not a valid assembly. + /// public AssemblyInfo(string path) { - filename = path; + Filename = path; // Attempt to open the file and see if it's a valid assembly. - using (var stream = File.OpenRead(path)) - using (var peReader = new PEReader(stream)) + using var stream = File.OpenRead(path); + using var peReader = new PEReader(stream); + try { - try - { - isAssembly = peReader.HasMetadata; - if (!isAssembly) return; + if (!peReader.HasMetadata) + throw new InvalidAssemblyException(); - var mdReader = peReader.GetMetadataReader(); + var mdReader = peReader.GetMetadataReader(); - isAssembly = mdReader.IsAssembly; - if (!mdReader.IsAssembly) return; + if (!mdReader.IsAssembly) + throw new InvalidAssemblyException(); - // Get our own assembly name - name = CreateAssemblyName(mdReader, mdReader.GetAssemblyDefinition()); + // Get our own assembly name + Name = CreateAssemblyName(mdReader, mdReader.GetAssemblyDefinition()); - references = mdReader.AssemblyReferences. - Select(r => mdReader.GetAssemblyReference(r)). - Select(ar => CreateAssemblyName(mdReader, ar)). - ToArray(); - } - catch (System.BadImageFormatException) - { - // This failed on one of the Roslyn tests that includes - // a deliberately malformed assembly. - // In this case, we just skip the extraction of this assembly. - isAssembly = false; - } + References = mdReader.AssemblyReferences + .Select(r => mdReader.GetAssemblyReference(r)) + .Select(ar => CreateAssemblyName(mdReader, ar)) + .ToArray(); + } + catch (System.BadImageFormatException) + { + // This failed on one of the Roslyn tests that includes + // a deliberately malformed assembly. + // In this case, we just skip the extraction of this assembly. + throw new InvalidAssemblyException(); } } - public readonly AssemblyName name; - public readonly string filename; - public bool extract; - public readonly bool isAssembly; - public readonly AssemblyName[] references; + public AssemblyName Name { get; } + public string Filename { get; } + public bool Extract { get; set; } + public AssemblyName[] References { get; } } /// @@ -98,37 +102,42 @@ public AssemblyInfo(string path) /// Resolves references between assemblies and determines which /// additional assemblies need to be extracted. /// - class AssemblyList + internal class AssemblyList { - class AssemblyNameComparer : IEqualityComparer + private class AssemblyNameComparer : IEqualityComparer { - bool IEqualityComparer.Equals(AssemblyName x, AssemblyName y) => - x.Name == y.Name && x.Version == y.Version; + bool IEqualityComparer.Equals(AssemblyName? x, AssemblyName? y) => + object.ReferenceEquals(x, y) || + x?.Name == y?.Name && x?.Version == y?.Version; int IEqualityComparer.GetHashCode(AssemblyName obj) => - obj.Name.GetHashCode() + 7 * obj.Version.GetHashCode(); + (obj.Name, obj.Version).GetHashCode(); } - readonly Dictionary assembliesRead = new Dictionary(new AssemblyNameComparer()); + private readonly Dictionary assembliesRead = new Dictionary(new AssemblyNameComparer()); public void AddFile(string assemblyPath, bool extractAll) { if (!filesAnalyzed.Contains(assemblyPath)) { filesAnalyzed.Add(assemblyPath); - var info = new AssemblyInfo(assemblyPath); - if (info.isAssembly) + try { - info.extract = extractAll; - if (!assembliesRead.ContainsKey(info.name)) - assembliesRead.Add(info.name, info); + var info = new AssemblyInfo(assemblyPath) + { + Extract = extractAll + }; + if (!assembliesRead.ContainsKey(info.Name)) + assembliesRead.Add(info.Name, info); } + catch (InvalidAssemblyException) + { } } } - public IEnumerable AssembliesToExtract => assembliesRead.Values.Where(info => info.extract); + public IEnumerable AssembliesToExtract => assembliesRead.Values.Where(info => info.Extract); - IEnumerable AssembliesToReference => AssembliesToExtract.SelectMany(info => info.references); + private IEnumerable AssembliesToReference => AssembliesToExtract.SelectMany(info => info.References); public void ResolveReferences() { @@ -137,33 +146,47 @@ public void ResolveReferences() while (assembliesToReference.Any()) { var item = assembliesToReference.Pop(); - AssemblyInfo info; - if (assembliesRead.TryGetValue(item, out info)) + if (assembliesRead.TryGetValue(item, out var info)) { - if (!info.extract) + if (!info.Extract) { - info.extract = true; - foreach (var reference in info.references) + info.Extract = true; + foreach (var reference in info.References) assembliesToReference.Push(reference); } } else { - missingReferences.Add(item); + MissingReferences.Add(item); } } } - readonly HashSet filesAnalyzed = new HashSet(); - public readonly HashSet missingReferences = new HashSet(); + private readonly HashSet filesAnalyzed = new HashSet(); + public HashSet MissingReferences {get;} = new HashSet(); } /// /// Parses the command line and collates a list of DLLs/EXEs to extract. /// - class ExtractorOptions + internal class ExtractorOptions { - readonly AssemblyList assemblyList = new AssemblyList(); + private readonly AssemblyList assemblyList = new AssemblyList(); + + public ExtractorOptions(string[] args) + { + Verbosity = Verbosity.Info; + Threads = System.Environment.ProcessorCount; + PDB = true; + TrapCompression = TrapWriter.CompressionMode.Gzip; + + ParseArgs(args); + + AddFrameworkDirectories(false); + + assemblyList.ResolveReferences(); + AssembliesToExtract = assemblyList.AssembliesToExtract.ToArray(); + } public void AddDirectory(string directory, bool extractAll) { @@ -175,7 +198,7 @@ public void AddDirectory(string directory, bool extractAll) } } - void AddFrameworkDirectories(bool extractAll) + private void AddFrameworkDirectories(bool extractAll) { AddDirectory(RuntimeEnvironment.GetRuntimeDirectory(), extractAll); } @@ -186,13 +209,18 @@ void AddFrameworkDirectories(bool extractAll) public bool PDB { get; private set; } public TrapWriter.CompressionMode TrapCompression { get; private set; } - void AddFileOrDirectory(string path) + private void AddFileOrDirectory(string path) { path = Path.GetFullPath(path); if (File.Exists(path)) { assemblyList.AddFile(path, true); - AddDirectory(Path.GetDirectoryName(path), false); + var directory = Path.GetDirectoryName(path); + if (directory is null) + { + throw new InternalError($"Directory of path '{path}' is null"); + } + AddDirectory(directory, false); } else if (Directory.Exists(path)) { @@ -200,70 +228,52 @@ void AddFileOrDirectory(string path) } } - void ResolveReferences() - { - assemblyList.ResolveReferences(); - AssembliesToExtract = assemblyList.AssembliesToExtract.ToArray(); - } - - public IEnumerable AssembliesToExtract { get; private set; } + public IEnumerable AssembliesToExtract { get; } /// /// Gets the assemblies that were referenced but were not available to be /// extracted. This is not an error, it just means that the database is not /// as complete as it could be. /// - public IEnumerable MissingReferences => assemblyList.missingReferences; + public IEnumerable MissingReferences => assemblyList.MissingReferences; - public static ExtractorOptions ParseCommandLine(string[] args) + private void ParseArgs(string[] args) { - var options = new ExtractorOptions(); - options.Verbosity = Verbosity.Info; - options.Threads = System.Environment.ProcessorCount; - options.PDB = true; - options.TrapCompression = TrapWriter.CompressionMode.Gzip; - foreach (var arg in args) { if (arg == "--verbose") { - options.Verbosity = Verbosity.All; + Verbosity = Verbosity.All; } else if (arg == "--silent") { - options.Verbosity = Verbosity.Off; + Verbosity = Verbosity.Off; } else if (arg.StartsWith("--verbosity:")) { - options.Verbosity = (Verbosity)int.Parse(arg.Substring(12)); + Verbosity = (Verbosity)int.Parse(arg.Substring(12)); } else if (arg == "--dotnet") { - options.AddFrameworkDirectories(true); + AddFrameworkDirectories(true); } else if (arg == "--nocache") { - options.NoCache = true; + NoCache = true; } else if (arg.StartsWith("--threads:")) { - options.Threads = int.Parse(arg.Substring(10)); + Threads = int.Parse(arg.Substring(10)); } else if (arg == "--no-pdb") { - options.PDB = false; + PDB = false; } else { - options.AddFileOrDirectory(arg); + AddFileOrDirectory(arg); } } - - options.AddFrameworkDirectories(false); - options.ResolveReferences(); - - return options; } - } } diff --git a/csharp/extractor/Semmle.Extraction.CIL.Driver/InvalidAssemblyException.cs b/csharp/extractor/Semmle.Extraction.CIL.Driver/InvalidAssemblyException.cs new file mode 100644 index 000000000000..2686442a08d5 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CIL.Driver/InvalidAssemblyException.cs @@ -0,0 +1,7 @@ +using System; + +namespace Semmle.Extraction.CIL.Driver +{ + public class InvalidAssemblyException : Exception + { } +} diff --git a/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs b/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs index 5a63425ea5e4..53542c970e9d 100644 --- a/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs +++ b/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs @@ -1,15 +1,14 @@ īģŋusing System; using System.Linq; using System.Threading.Tasks; -using System.IO; using Semmle.Util.Logging; using System.Diagnostics; namespace Semmle.Extraction.CIL.Driver { - class Program + public static class Program { - static void DisplayHelp() + private static void DisplayHelp() { Console.WriteLine("CIL command line extractor"); Console.WriteLine(); @@ -21,18 +20,16 @@ static void DisplayHelp() Console.WriteLine(" path A directory/dll/exe to analyze"); } - static void ExtractAssembly(Layout layout, string assemblyPath, ILogger logger, bool nocache, bool extractPdbs, TrapWriter.CompressionMode trapCompression) + private static void ExtractAssembly(Layout layout, string assemblyPath, ILogger logger, bool nocache, bool extractPdbs, TrapWriter.CompressionMode trapCompression) { - string trapFile; - bool extracted; var sw = new Stopwatch(); sw.Start(); - Entities.Assembly.ExtractCIL(layout, assemblyPath, logger, nocache, extractPdbs, trapCompression, out trapFile, out extracted); + Entities.Assembly.ExtractCIL(layout, assemblyPath, logger, nocache, extractPdbs, trapCompression, out _, out _); sw.Stop(); logger.Log(Severity.Info, " {0} ({1})", assemblyPath, sw.Elapsed); } - static void Main(string[] args) + public static void Main(string[] args) { if (args.Length == 0) { @@ -40,14 +37,15 @@ static void Main(string[] args) return; } - var options = ExtractorOptions.ParseCommandLine(args); + var options = new ExtractorOptions(args); var layout = new Layout(); - var logger = new ConsoleLogger(options.Verbosity); + using var logger = new ConsoleLogger(options.Verbosity); - var actions = options. - AssembliesToExtract.Select(asm => asm.filename). - Select(filename => () => ExtractAssembly(layout, filename, logger, options.NoCache, options.PDB, options.TrapCompression)). - ToArray(); + var actions = options.AssembliesToExtract + .Select(asm => asm.Filename) + .Select(filename => + () => ExtractAssembly(layout, filename, logger, options.NoCache, options.PDB, options.TrapCompression)) + .ToArray(); foreach (var missingRef in options.MissingReferences) logger.Log(Severity.Info, " Missing assembly " + missingRef); diff --git a/csharp/extractor/Semmle.Extraction.CIL.Driver/Semmle.Extraction.CIL.Driver.csproj b/csharp/extractor/Semmle.Extraction.CIL.Driver/Semmle.Extraction.CIL.Driver.csproj index e7f87c7b36b3..67e40dae2d87 100644 --- a/csharp/extractor/Semmle.Extraction.CIL.Driver/Semmle.Extraction.CIL.Driver.csproj +++ b/csharp/extractor/Semmle.Extraction.CIL.Driver/Semmle.Extraction.CIL.Driver.csproj @@ -2,11 +2,12 @@ Exe - netcoreapp3.0 + netcoreapp3.1 Semmle.Extraction.CIL.Driver Semmle.Extraction.CIL.Driver false win-x64;linux-x64;osx-x64 + enable diff --git a/csharp/extractor/Semmle.Extraction.CIL/CachedFunction.cs b/csharp/extractor/Semmle.Extraction.CIL/CachedFunction.cs index 3bbc386a691e..4f7ce5a7ef18 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/CachedFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/CachedFunction.cs @@ -7,21 +7,21 @@ namespace Semmle.Extraction.CIL /// A factory and a cache for mapping source entities to target entities. /// Could be considered as a memoizer. /// - /// The type of the source. - /// The type of the generated object. - public class CachedFunction + /// The type of the source. + /// The type of the generated object. + public class CachedFunction where TSrc : notnull { - readonly Func generator; - readonly Dictionary cache; + private readonly Func generator; + private readonly Dictionary cache; /// /// Initializes the factory with a given mapping. /// /// The mapping. - public CachedFunction(Func g) + public CachedFunction(Func g) { generator = g; - cache = new Dictionary(); + cache = new Dictionary(); } /// @@ -30,12 +30,11 @@ public CachedFunction(Func g) /// /// The source object. /// The created object. - public TargetType this[SrcType src] + public TTarget this[TSrc src] { get { - TargetType result; - if (!cache.TryGetValue(src, out result)) + if (!cache.TryGetValue(src, out var result)) { result = generator(src); cache[src] = result; @@ -48,22 +47,22 @@ public TargetType this[SrcType src] /// /// A factory for mapping a pair of source entities to a target entity. /// - /// Source entity type 1. - /// Source entity type 2. - /// The target type. - public class CachedFunction + /// Source entity type 1. + /// Source entity type 2. + /// The target type. + public class CachedFunction { - readonly CachedFunction<(Src1, Src2), Target> factory; + private readonly CachedFunction<(TSrcEntity1, TSrcEntity2), TTarget> factory; /// /// Initializes the factory with a given mapping. /// /// The mapping. - public CachedFunction(Func g) + public CachedFunction(Func g) { - factory = new CachedFunction<(Src1, Src2), Target>(p => g(p.Item1, p.Item2)); + factory = new CachedFunction<(TSrcEntity1, TSrcEntity2), TTarget>(p => g(p.Item1, p.Item2)); } - public Target this[Src1 s1, Src2 s2] => factory[(s1, s2)]; + public TTarget this[TSrcEntity1 s1, TSrcEntity2 s2] => factory[(s1, s2)]; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Context.cs b/csharp/extractor/Semmle.Extraction.CIL/Context.cs index 79fb8b7a8d49..ac6a5f52dd96 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Context.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Context.cs @@ -12,51 +12,57 @@ namespace Semmle.Extraction.CIL /// Adds additional context that is specific for CIL extraction. /// One context = one DLL/EXE. /// - partial class Context : IDisposable + public sealed partial class Context : IDisposable { - public Extraction.Context cx; - readonly FileStream stream; - public readonly MetadataReader mdReader; - public readonly PEReader peReader; - public readonly string assemblyPath; - public Entities.Assembly assembly; - public PDB.IPdb pdb; + private readonly FileStream stream; + private Entities.Assembly? assemblyNull; + + public Extraction.Context Cx { get; } + public MetadataReader MdReader { get; } + public PEReader PeReader { get; } + public string AssemblyPath { get; } + public Entities.Assembly Assembly + { + get { return assemblyNull!; } + set { assemblyNull = value; } + } + public PDB.IPdb? Pdb { get; } public Context(Extraction.Context cx, string assemblyPath, bool extractPdbs) { - this.cx = cx; - this.assemblyPath = assemblyPath; + this.Cx = cx; + this.AssemblyPath = assemblyPath; stream = File.OpenRead(assemblyPath); - peReader = new PEReader(stream, PEStreamOptions.PrefetchEntireImage); - mdReader = peReader.GetMetadataReader(); + PeReader = new PEReader(stream, PEStreamOptions.PrefetchEntireImage); + MdReader = PeReader.GetMetadataReader(); TypeSignatureDecoder = new Entities.TypeSignatureDecoder(this); globalNamespace = new Lazy(() => Populate(new Entities.Namespace(this, "", null))); systemNamespace = new Lazy(() => Populate(new Entities.Namespace(this, "System"))); genericHandleFactory = new CachedFunction(CreateGenericHandle); - namespaceFactory = new CachedFunction(n => CreateNamespace(mdReader.GetString(n))); + namespaceFactory = new CachedFunction(n => CreateNamespace(MdReader.GetString(n))); namespaceDefinitionFactory = new CachedFunction(CreateNamespace); sourceFiles = new CachedFunction(path => new Entities.PdbSourceFile(this, path)); - folders = new CachedFunction(path => new Entities.Folder(this, path)); + folders = new CachedFunction(path => new Entities.Folder(this, path)); sourceLocations = new CachedFunction(location => new Entities.PdbSourceLocation(this, location)); defaultGenericContext = new EmptyContext(this); if (extractPdbs) { - pdb = PDB.PdbReader.Create(assemblyPath, peReader); - if (pdb != null) + Pdb = PDB.PdbReader.Create(assemblyPath, PeReader); + if (Pdb != null) { cx.Extractor.Logger.Log(Util.Logging.Severity.Info, string.Format("Found PDB information for {0}", assemblyPath)); } } } - void IDisposable.Dispose() + public void Dispose() { - if (pdb != null) - pdb.Dispose(); - peReader.Dispose(); + if (Pdb != null) + Pdb.Dispose(); + PeReader.Dispose(); stream.Dispose(); } @@ -74,14 +80,14 @@ public void Extract(IExtractedEntity entity) public void WriteAssemblyPrefix(TextWriter trapFile) { - var def = mdReader.GetAssemblyDefinition(); + var def = MdReader.GetAssemblyDefinition(); trapFile.Write(GetString(def.Name)); trapFile.Write('_'); trapFile.Write(def.Version.ToString()); trapFile.Write("::"); } - public readonly Entities.TypeSignatureDecoder TypeSignatureDecoder; + public Entities.TypeSignatureDecoder TypeSignatureDecoder { get; } /// /// A type used to signify something we can't handle yet. @@ -105,9 +111,9 @@ public Entities.Type ErrorType /// /// The handle of the method. /// The debugging information, or null if the information could not be located. - public PDB.IMethod GetMethodDebugInformation(MethodDefinitionHandle handle) + public PDB.IMethod? GetMethodDebugInformation(MethodDefinitionHandle handle) { - return pdb == null ? null : pdb.GetMethod(handle.ToDebugInformationHandle()); + return Pdb?.GetMethod(handle.ToDebugInformationHandle()); } } @@ -117,15 +123,16 @@ public PDB.IMethod GetMethodDebugInformation(MethodDefinitionHandle handle) /// public abstract class GenericContext { - public Context cx; + public Context Cx { get; } - public GenericContext(Context cx) + protected GenericContext(Context cx) { - this.cx = cx; + this.Cx = cx; } /// - /// The list of generic type parameters. + /// The list of generic type parameters, including type parameters of + /// containing types. /// public abstract IEnumerable TypeParameters { get; } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs index 255d37699f24..6842577ce879 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs @@ -5,6 +5,7 @@ using System; using Semmle.Extraction.Entities; using System.IO; +using Semmle.Util; namespace Semmle.Extraction.CIL.Entities { @@ -12,7 +13,7 @@ public interface ILocation : IEntity { } - interface IAssembly : ILocation + internal interface IAssembly : ILocation { } @@ -21,68 +22,70 @@ interface IAssembly : ILocation /// public class Assembly : LabelledEntity, IAssembly { - readonly File file; - readonly AssemblyName assemblyName; + private readonly File file; + private readonly AssemblyName assemblyName; public Assembly(Context cx) : base(cx) { - cx.assembly = this; - var def = cx.mdReader.GetAssemblyDefinition(); + cx.Assembly = this; + var def = cx.MdReader.GetAssemblyDefinition(); - assemblyName = new AssemblyName(); - assemblyName.Name = cx.mdReader.GetString(def.Name); - assemblyName.Version = def.Version; - assemblyName.CultureInfo = new CultureInfo(cx.mdReader.GetString(def.Culture)); + assemblyName = new AssemblyName + { + Name = cx.MdReader.GetString(def.Name), + Version = def.Version, + CultureInfo = new CultureInfo(cx.MdReader.GetString(def.Culture)) + }; if (!def.PublicKey.IsNil) - assemblyName.SetPublicKey(cx.mdReader.GetBlobBytes(def.PublicKey)); + assemblyName.SetPublicKey(cx.MdReader.GetBlobBytes(def.PublicKey)); - file = new File(cx, cx.assemblyPath); + file = new File(cx, cx.AssemblyPath); } public override void WriteId(TextWriter trapFile) { trapFile.Write(FullName); trapFile.Write("#file:///"); - trapFile.Write(cx.assemblyPath.Replace("\\", "/")); + trapFile.Write(Cx.AssemblyPath.Replace("\\", "/")); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - return GetType() == obj.GetType() && Equals(file, ((Assembly)obj).file); + return GetType() == obj?.GetType() && Equals(file, ((Assembly)obj).file); } public override int GetHashCode() => 7 * file.GetHashCode(); public override string IdSuffix => ";assembly"; - string FullName => assemblyName.GetPublicKey() is null ? assemblyName.FullName + ", PublicKeyToken=null" : assemblyName.FullName; + private string FullName => assemblyName.GetPublicKey() is null ? assemblyName.FullName + ", PublicKeyToken=null" : assemblyName.FullName; public override IEnumerable Contents { get { yield return file; - yield return Tuples.assemblies(this, file, FullName, assemblyName.Name, assemblyName.Version.ToString()); + yield return Tuples.assemblies(this, file, FullName, assemblyName.Name ?? string.Empty, assemblyName.Version?.ToString() ?? string.Empty); - if (cx.pdb != null) + if (Cx.Pdb != null) { - foreach (var f in cx.pdb.SourceFiles) + foreach (var f in Cx.Pdb.SourceFiles) { - yield return cx.CreateSourceFile(f); + yield return Cx.CreateSourceFile(f); } } - foreach (var handle in cx.mdReader.TypeDefinitions) + foreach (var handle in Cx.MdReader.TypeDefinitions) { - IExtractionProduct product = null; + IExtractionProduct? product = null; try { - product = cx.Create(handle); + product = Cx.Create(handle); } catch (InternalError e) { - cx.cx.ExtractionError("Error processing type definition", e.Message, GeneratedLocation.Create(cx.cx), e.StackTrace); + Cx.Cx.ExtractionError("Error processing type definition", e.Message, GeneratedLocation.Create(Cx.Cx), e.StackTrace); } // Limitation of C#: Cannot yield return inside a try-catch. @@ -90,16 +93,16 @@ public override IEnumerable Contents yield return product; } - foreach (var handle in cx.mdReader.MethodDefinitions) + foreach (var handle in Cx.MdReader.MethodDefinitions) { - IExtractionProduct product = null; + IExtractionProduct? product = null; try { - product = cx.Create(handle); + product = Cx.Create(handle); } catch (InternalError e) { - cx.cx.ExtractionError("Error processing bytecode", e.Message, GeneratedLocation.Create(cx.cx), e.StackTrace); + Cx.Cx.ExtractionError("Error processing bytecode", e.Message, GeneratedLocation.Create(Cx.Cx), e.StackTrace); } if (product != null) @@ -108,13 +111,11 @@ public override IEnumerable Contents } } - static void ExtractCIL(Extraction.Context cx, string assemblyPath, bool extractPdbs) + private static void ExtractCIL(Extraction.Context cx, string assemblyPath, bool extractPdbs) { - using (var cilContext = new Context(cx, assemblyPath, extractPdbs)) - { - cilContext.Populate(new Assembly(cilContext)); - cilContext.cx.PopulateAll(); - } + using var cilContext = new Context(cx, assemblyPath, extractPdbs); + cilContext.Populate(new Assembly(cilContext)); + cilContext.Cx.PopulateAll(); } /// @@ -134,17 +135,18 @@ public static void ExtractCIL(Layout layout, string assemblyPath, ILogger logger extracted = false; try { - var extractor = new Extractor(false, assemblyPath, logger); - var project = layout.LookupProjectOrDefault(assemblyPath); - using (var trapWriter = project.CreateTrapWriter(logger, assemblyPath + ".cil", true, trapCompression)) + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); + var pathTransformer = new PathTransformer(canonicalPathCache); + var extractor = new Extractor(false, assemblyPath, logger, pathTransformer); + var transformedAssemblyPath = pathTransformer.Transform(assemblyPath); + var project = layout.LookupProjectOrDefault(transformedAssemblyPath); + using var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), true, trapCompression); + trapFile = trapWriter.TrapFile; + if (nocache || !System.IO.File.Exists(trapFile)) { - trapFile = trapWriter.TrapFile; - if (nocache || !System.IO.File.Exists(trapFile)) - { - var cx = extractor.CreateContext(null, trapWriter, null); - ExtractCIL(cx, assemblyPath, extractPdbs); - extracted = true; - } + var cx = extractor.CreateContext(null, trapWriter, null, false); + ExtractCIL(cx, assemblyPath, extractPdbs); + extracted = true; } } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs index ed3e866c16fb..d199fb31195a 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs @@ -7,27 +7,27 @@ namespace Semmle.Extraction.CIL.Entities /// /// A CIL attribute. /// - interface IAttribute : IExtractedEntity + internal interface IAttribute : IExtractedEntity { } /// /// Entity representing a CIL attribute. /// - sealed class Attribute : UnlabelledEntity, IAttribute + internal sealed class Attribute : UnlabelledEntity, IAttribute { - readonly CustomAttributeHandle handle; - readonly CustomAttribute attrib; - readonly IEntity @object; + private readonly CustomAttributeHandle handle; + private readonly CustomAttribute attrib; + private readonly IEntity @object; public Attribute(Context cx, IEntity @object, CustomAttributeHandle handle) : base(cx) { - attrib = cx.mdReader.GetCustomAttribute(handle); + attrib = cx.MdReader.GetCustomAttribute(handle); this.handle = handle; this.@object = @object; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Attribute attribute && handle.Equals(attribute.handle); } @@ -38,7 +38,7 @@ public override IEnumerable Contents { get { - var constructor = (Method)cx.Create(attrib.Constructor); + var constructor = (Method)Cx.Create(attrib.Constructor); yield return constructor; yield return Tuples.cil_attribute(this, @object, constructor); @@ -47,7 +47,7 @@ public override IEnumerable Contents try { - decoded = attrib.DecodeValue(new CustomAttributeDecoder(cx)); + decoded = attrib.DecodeValue(new CustomAttributeDecoder(Cx)); } catch (NotImplementedException) { @@ -55,16 +55,18 @@ public override IEnumerable Contents yield break; } - for (int index = 0; index < decoded.FixedArguments.Length; ++index) + for (var index = 0; index < decoded.FixedArguments.Length; ++index) { - object value = decoded.FixedArguments[index].Value; - yield return Tuples.cil_attribute_positional_argument(this, index, value == null ? "null" : value.ToString()); + var value = decoded.FixedArguments[index].Value; + var stringValue = value?.ToString(); + yield return Tuples.cil_attribute_positional_argument(this, index, stringValue ?? "null"); } foreach (var p in decoded.NamedArguments) { - object value = p.Value; - yield return Tuples.cil_attribute_named_argument(this, p.Name, value == null ? "null" : value.ToString()); + var value = p.Value; + var stringValue = value?.ToString(); + yield return Tuples.cil_attribute_named_argument(this, p.Name, stringValue ?? "null"); } } } @@ -82,9 +84,9 @@ public static IEnumerable Populate(Context cx, IEntity @obje /// Helper class to decode the attribute structure. /// Note that there are some unhandled cases that should be fixed in due course. /// - class CustomAttributeDecoder : ICustomAttributeTypeProvider + internal class CustomAttributeDecoder : ICustomAttributeTypeProvider { - readonly Context cx; + private readonly Context cx; public CustomAttributeDecoder(Context cx) { this.cx = cx; } public Type GetPrimitiveType(PrimitiveTypeCode typeCode) => cx.Create(typeCode); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs index da33b9a63d5c..e9a87c3c96c1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs @@ -7,36 +7,36 @@ namespace Semmle.Extraction.CIL.Entities /// /// An event. /// - interface IEvent : IExtractedEntity + internal interface IEvent : IExtractedEntity { } /// /// An event entity. /// - sealed class Event : LabelledEntity, IEvent + internal sealed class Event : LabelledEntity, IEvent { - readonly EventDefinitionHandle handle; - readonly Type parent; - readonly EventDefinition ed; + private readonly EventDefinitionHandle handle; + private readonly Type parent; + private readonly EventDefinition ed; public Event(Context cx, Type parent, EventDefinitionHandle handle) : base(cx) { this.handle = handle; this.parent = parent; - ed = cx.mdReader.GetEventDefinition(handle); + ed = cx.MdReader.GetEventDefinition(handle); } public override void WriteId(TextWriter trapFile) { parent.WriteId(trapFile); trapFile.Write('.'); - trapFile.Write(cx.ShortName(ed.Name)); + trapFile.Write(Cx.ShortName(ed.Name)); } public override string IdSuffix => ";cil-event"; - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Event e && handle.Equals(e.handle); } @@ -47,34 +47,34 @@ public override IEnumerable Contents { get { - var signature = (Type)cx.CreateGeneric(parent, ed.Type); + var signature = (Type)Cx.CreateGeneric(parent, ed.Type); yield return signature; - yield return Tuples.cil_event(this, parent, cx.ShortName(ed.Name), signature); + yield return Tuples.cil_event(this, parent, Cx.ShortName(ed.Name), signature); var accessors = ed.GetAccessors(); if (!accessors.Adder.IsNil) { - var adder = (Method)cx.CreateGeneric(parent, accessors.Adder); + var adder = (Method)Cx.CreateGeneric(parent, accessors.Adder); yield return adder; yield return Tuples.cil_adder(this, adder); } if (!accessors.Remover.IsNil) { - var remover = (Method)cx.CreateGeneric(parent, accessors.Remover); + var remover = (Method)Cx.CreateGeneric(parent, accessors.Remover); yield return remover; yield return Tuples.cil_remover(this, remover); } if (!accessors.Raiser.IsNil) { - var raiser = (Method)cx.CreateGeneric(parent, accessors.Raiser); + var raiser = (Method)Cx.CreateGeneric(parent, accessors.Raiser); yield return raiser; yield return Tuples.cil_raiser(this, raiser); } - foreach (var c in Attribute.Populate(cx, this, ed.GetCustomAttributes())) + foreach (var c in Attribute.Populate(Cx, this, ed.GetCustomAttributes())) yield return c; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs index 57035d993af0..e775ba56dd48 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs @@ -2,22 +2,22 @@ namespace Semmle.Extraction.CIL.Entities { - interface IExceptionRegion : IExtractedEntity + internal interface IExceptionRegion : IExtractedEntity { } /// /// An exception region entity. /// - class ExceptionRegion : UnlabelledEntity, IExceptionRegion + internal class ExceptionRegion : UnlabelledEntity, IExceptionRegion { - readonly GenericContext gc; - readonly MethodImplementation method; - readonly int index; - readonly System.Reflection.Metadata.ExceptionRegion r; - readonly Dictionary jump_table; + private readonly GenericContext gc; + private readonly MethodImplementation method; + private readonly int index; + private readonly System.Reflection.Metadata.ExceptionRegion r; + private readonly Dictionary jump_table; - public ExceptionRegion(GenericContext gc, MethodImplementation method, int index, System.Reflection.Metadata.ExceptionRegion r, Dictionary jump_table) : base(gc.cx) + public ExceptionRegion(GenericContext gc, MethodImplementation method, int index, System.Reflection.Metadata.ExceptionRegion r, Dictionary jump_table) : base(gc.Cx) { this.gc = gc; this.method = method; @@ -30,22 +30,19 @@ public override IEnumerable Contents { get { - IInstruction try_start, try_end, handler_start; - if (!jump_table.TryGetValue(r.TryOffset, out try_start)) + if (!jump_table.TryGetValue(r.TryOffset, out var try_start)) throw new InternalError("Failed to retrieve handler"); - if (!jump_table.TryGetValue(r.TryOffset + r.TryLength, out try_end)) + if (!jump_table.TryGetValue(r.TryOffset + r.TryLength, out var try_end)) throw new InternalError("Failed to retrieve handler"); - if (!jump_table.TryGetValue(r.HandlerOffset, out handler_start)) + if (!jump_table.TryGetValue(r.HandlerOffset, out var handler_start)) throw new InternalError("Failed to retrieve handler"); - yield return Tuples.cil_handler(this, method, index, (int)r.Kind, try_start, try_end, handler_start); if (r.FilterOffset != -1) { - IInstruction filter_start; - if (!jump_table.TryGetValue(r.FilterOffset, out filter_start)) + if (!jump_table.TryGetValue(r.FilterOffset, out var filter_start)) throw new InternalError("ExceptionRegion filter clause"); yield return Tuples.cil_handler_filter(this, filter_start); @@ -53,7 +50,7 @@ public override IEnumerable Contents if (!r.CatchType.IsNil) { - var catchType = (Type)cx.CreateGeneric(gc, r.CatchType); + var catchType = (Type)Cx.CreateGeneric(gc, r.CatchType); yield return catchType; yield return Tuples.cil_handler_type(this, catchType); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs index 400b48fc07ea..de34a28e0d4a 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs @@ -13,28 +13,26 @@ namespace Semmle.Extraction.CIL.Entities /// An entity represting a member. /// Used to type tuples correctly. /// - interface IMember : IExtractedEntity + internal interface IMember : IExtractedEntity { } /// /// An entity representing a field. /// - interface IField : IMember + internal interface IField : IMember { } /// /// An entity representing a field. /// - abstract class Field : GenericContext, IField + internal abstract class Field : GenericContext, IField { protected Field(Context cx) : base(cx) { } - public bool NeedsPopulation { get { return true; } } - public Label Label { get; set; } public void WriteId(TextWriter trapFile) @@ -48,11 +46,11 @@ public void WriteQuotedId(TextWriter trapFile) { trapFile.Write("@\""); WriteId(trapFile); - trapFile.Write(IdSuffix); + trapFile.Write(idSuffix); trapFile.Write('\"'); } - public string IdSuffix => ";cil-field"; + private const string idSuffix = ";cil-field"; public abstract string Name { get; } @@ -60,7 +58,7 @@ public void WriteQuotedId(TextWriter trapFile) public Location ReportingLocation => throw new NotImplementedException(); - abstract public Type Type { get; } + public abstract Type Type { get; } public virtual IEnumerable Contents { @@ -78,20 +76,18 @@ public void Extract(Context cx2) TrapStackBehaviour IEntity.TrapStackBehaviour => TrapStackBehaviour.NoLabel; } - sealed class DefinitionField : Field + internal sealed class DefinitionField : Field { - readonly Handle handle; - readonly FieldDefinition fd; - readonly GenericContext gc; + private readonly Handle handle; + private readonly FieldDefinition fd; - public DefinitionField(GenericContext gc, FieldDefinitionHandle handle) : base(gc.cx) + public DefinitionField(Context cx, FieldDefinitionHandle handle) : base(cx) { this.handle = handle; - this.gc = gc; - fd = cx.mdReader.GetFieldDefinition(handle); + fd = Cx.MdReader.GetFieldDefinition(handle); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DefinitionField field && handle.Equals(field.handle); } @@ -102,7 +98,7 @@ public override IEnumerable Contents { get { - yield return Tuples.metadata_handle(this, cx.assembly, MetadataTokens.GetToken(handle)); + yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle)); foreach (var c in base.Contents) yield return c; @@ -122,52 +118,52 @@ public override IEnumerable Contents if (fd.Attributes.HasFlag(FieldAttributes.Assembly)) yield return Tuples.cil_internal(this); - foreach (var c in Attribute.Populate(cx, this, fd.GetCustomAttributes())) + foreach (var c in Attribute.Populate(Cx, this, fd.GetCustomAttributes())) yield return c; } } - public override string Name => cx.GetString(fd.Name); + public override string Name => Cx.GetString(fd.Name); - public override Type DeclaringType => (Type)cx.Create(fd.GetDeclaringType()); + public override Type DeclaringType => (Type)Cx.Create(fd.GetDeclaringType()); - public override Type Type => fd.DecodeSignature(cx.TypeSignatureDecoder, DeclaringType); + public override Type Type => fd.DecodeSignature(Cx.TypeSignatureDecoder, DeclaringType); public override IEnumerable TypeParameters => throw new NotImplementedException(); public override IEnumerable MethodParameters => throw new NotImplementedException(); } - sealed class MemberReferenceField : Field + internal sealed class MemberReferenceField : Field { - readonly MemberReferenceHandle Handle; - readonly MemberReference mr; - readonly GenericContext gc; - readonly Type declType; + private readonly MemberReferenceHandle handle; + private readonly MemberReference mr; + private readonly GenericContext gc; + private readonly Type declType; - public MemberReferenceField(GenericContext gc, MemberReferenceHandle handle) : base(gc.cx) + public MemberReferenceField(GenericContext gc, MemberReferenceHandle handle) : base(gc.Cx) { - Handle = handle; + this.handle = handle; this.gc = gc; - mr = cx.mdReader.GetMemberReference(handle); - declType = (Type)cx.CreateGeneric(gc, mr.Parent); + mr = Cx.MdReader.GetMemberReference(handle); + declType = (Type)Cx.CreateGeneric(gc, mr.Parent); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - return obj is MemberReferenceField field && Handle.Equals(field.Handle); + return obj is MemberReferenceField field && handle.Equals(field.handle); } public override int GetHashCode() { - return Handle.GetHashCode(); + return handle.GetHashCode(); } - public override string Name => cx.GetString(mr.Name); + public override string Name => Cx.GetString(mr.Name); public override Type DeclaringType => declType; - public override Type Type => mr.DecodeFieldSignature(cx.TypeSignatureDecoder, this); + public override Type Type => mr.DecodeFieldSignature(Cx.TypeSignatureDecoder, this); public override IEnumerable TypeParameters => gc.TypeParameters.Concat(declType.TypeParameters); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs index bc8c4c8c76dd..02bba3f04270 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs @@ -1,45 +1,50 @@ -īģŋusing System.Collections.Generic; +using System.Collections.Generic; using System.IO; namespace Semmle.Extraction.CIL.Entities { - interface IFileOrFolder : IEntity + internal interface IFileOrFolder : IEntity { } - interface IFile : IFileOrFolder + internal interface IFile : IFileOrFolder { } public class File : LabelledEntity, IFile { - protected readonly string path; + protected string OriginalPath { get; } + protected PathTransformer.ITransformedPath TransformedPath { get; } public File(Context cx, string path) : base(cx) { - this.path = Semmle.Extraction.Entities.File.PathAsDatabaseString(path); + this.OriginalPath = path; + TransformedPath = cx.Cx.Extractor.PathTransformer.Transform(OriginalPath); } public override void WriteId(TextWriter trapFile) { - trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path)); + trapFile.Write(TransformedPath.DatabaseId); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - return GetType() == obj.GetType() && path == ((File)obj).path; + return GetType() == obj?.GetType() && OriginalPath == ((File)obj).OriginalPath; } - public override int GetHashCode() => 11 * path.GetHashCode(); + public override int GetHashCode() => 11 * OriginalPath.GetHashCode(); public override IEnumerable Contents { get { - var parent = cx.CreateFolder(System.IO.Path.GetDirectoryName(path)); - yield return parent; - yield return Tuples.containerparent(parent, this); - yield return Tuples.files(this, path, System.IO.Path.GetFileNameWithoutExtension(path), System.IO.Path.GetExtension(path).Substring(1)); + if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath dir) + { + var parent = Cx.CreateFolder(dir); + yield return parent; + yield return Tuples.containerparent(parent, this); + } + yield return Tuples.files(this, TransformedPath.Value, TransformedPath.NameWithoutExtension, TransformedPath.Extension); } } @@ -48,7 +53,7 @@ public override IEnumerable Contents public class PdbSourceFile : File { - readonly PDB.ISourceFile file; + private readonly PDB.ISourceFile file; public PdbSourceFile(Context cx, PDB.ISourceFile file) : base(cx, file.Path) { @@ -65,9 +70,9 @@ public override IEnumerable Contents var text = file.Contents; if (text == null) - cx.cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, string.Format("PDB source file {0} could not be found", path)); + Cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, string.Format("PDB source file {0} could not be found", OriginalPath)); else - cx.cx.TrapWriter.Archive(path, text); + Cx.Cx.TrapWriter.Archive(TransformedPath, text); yield return Tuples.file_extraction_mode(this, 2); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs index 48ebe6a19d1d..98a30dd0ad19 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs @@ -3,22 +3,22 @@ namespace Semmle.Extraction.CIL.Entities { - interface IFolder : IFileOrFolder + internal interface IFolder : IFileOrFolder { } public sealed class Folder : LabelledEntity, IFolder { - readonly string path; + private readonly PathTransformer.ITransformedPath transformedPath; - public Folder(Context cx, string path) : base(cx) + public Folder(Context cx, PathTransformer.ITransformedPath path) : base(cx) { - this.path = path; + this.transformedPath = path; } public override void WriteId(TextWriter trapFile) { - trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path)); + trapFile.Write(transformedPath.DatabaseId); } public override string IdSuffix => ";folder"; @@ -27,25 +27,21 @@ public override IEnumerable Contents { get { - // On Posix, we could get a Windows directory of the form "C:" - bool windowsDriveLetter = path.Length == 2 && char.IsLetter(path[0]) && path[1] == ':'; - - var parent = Path.GetDirectoryName(path); - if (parent != null && !windowsDriveLetter) + if (transformedPath.ParentDirectory is PathTransformer.ITransformedPath parent) { - var parentFolder = cx.CreateFolder(parent); + var parentFolder = Cx.CreateFolder(parent); yield return parentFolder; yield return Tuples.containerparent(parentFolder, this); } - yield return Tuples.folders(this, Semmle.Extraction.Entities.File.PathAsDatabaseString(path), Path.GetFileName(path)); + yield return Tuples.folders(this, transformedPath.Value, transformedPath.NameWithoutExtension); } } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - return obj is Folder folder && path == folder.path; + return obj is Folder folder && transformedPath == folder.transformedPath; } - public override int GetHashCode() => path.GetHashCode(); + public override int GetHashCode() => transformedPath.GetHashCode(); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs index 925dea6e7110..9718e01be808 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs @@ -8,7 +8,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// A CIL instruction. /// - interface IInstruction : IExtractedEntity + internal interface IInstruction : IExtractedEntity { /// /// Gets the extraction products for branches. @@ -21,7 +21,7 @@ interface IInstruction : IExtractedEntity /// /// A CIL instruction. /// - class Instruction : UnlabelledEntity, IInstruction + internal class Instruction : UnlabelledEntity, IInstruction { /// /// The additional data following the opcode, if any. @@ -38,7 +38,7 @@ public enum Payload /// /// For each Payload, how many additional bytes in the bytestream need to be read. /// - internal static readonly int[] payloadSizes = { + private static readonly int[] payloadSizes = { 0, 4, 4, 1, 4, 4, 1, 1, 4, 1, 2, 4, 8, 4, 8, @@ -46,7 +46,7 @@ public enum Payload 4, 2, 1, 4, 2, 4 }; // Maps opcodes to payloads for each instruction. - public static readonly Dictionary opPayload = new Dictionary() + private static readonly Dictionary opPayload = new Dictionary() { { ILOpCode.Nop, Payload.None }, { ILOpCode.Break, Payload.None }, @@ -268,19 +268,18 @@ public enum Payload { ILOpCode.Readonly, Payload.None } }; - public readonly DefinitionMethod Method; - public readonly ILOpCode OpCode; - public readonly int Offset; - public readonly int Index; - readonly int PayloadValue; - readonly uint UnsignedPayloadValue; + public DefinitionMethod Method { get; } + public ILOpCode OpCode { get; } + public int Offset { get; } + public int Index { get; } + private readonly int payloadValue; + private readonly uint unsignedPayloadValue; public Payload PayloadType { get { - Payload result; - if (!opPayload.TryGetValue(OpCode, out result)) + if (!opPayload.TryGetValue(OpCode, out var result)) throw new InternalError("Unknown op code " + OpCode); return result; } @@ -296,7 +295,8 @@ public int Width { get { - if (OpCode == ILOpCode.Switch) return 5 + 4 * PayloadValue; + if (OpCode == ILOpCode.Switch) + return 5 + 4 * payloadValue; return ((int)OpCode > 255 ? 2 : 1) + PayloadSize; } @@ -307,10 +307,9 @@ Label IEntity.Label get; set; } + private readonly byte[] data; - readonly byte[] data; - - int PayloadSize => payloadSizes[(int)PayloadType]; + private int PayloadSize => payloadSizes[(int)PayloadType]; /// /// Reads the instruction from a byte stream. @@ -338,19 +337,19 @@ public Instruction(Context cx, DefinitionMethod method, byte[] data, int offset, switch (PayloadSize) { case 0: - PayloadValue = 0; + payloadValue = 0; break; case 1: - PayloadValue = (sbyte)data[offset]; - UnsignedPayloadValue = data[offset]; + payloadValue = (sbyte)data[offset]; + unsignedPayloadValue = data[offset]; break; case 2: - PayloadValue = BitConverter.ToInt16(data, offset); - UnsignedPayloadValue = BitConverter.ToUInt16(data, offset); + payloadValue = BitConverter.ToInt16(data, offset); + unsignedPayloadValue = BitConverter.ToUInt16(data, offset); break; case -1: // Switch case 4: - PayloadValue = BitConverter.ToInt32(data, offset); + payloadValue = BitConverter.ToInt32(data, offset); break; case 8: // Not handled here. break; @@ -363,14 +362,19 @@ public override IEnumerable Contents { get { - int offset = Offset; + var offset = Offset; + + if (Method.Implementation is null) + { + yield break; + } yield return Tuples.cil_instruction(this, (int)OpCode, Index, Method.Implementation); switch (PayloadType) { case Payload.String: - yield return Tuples.cil_value(this, cx.mdReader.GetUserString(MetadataTokens.UserStringHandle(PayloadValue))); + yield return Tuples.cil_value(this, Cx.MdReader.GetUserString(MetadataTokens.UserStringHandle(payloadValue))); break; case Payload.Float32: yield return Tuples.cil_value(this, BitConverter.ToSingle(data, offset).ToString()); @@ -400,8 +404,8 @@ public override IEnumerable Contents case Payload.Field: case Payload.ValueType: // A generic EntityHandle. - var handle = MetadataTokens.EntityHandle(PayloadValue); - var target = cx.CreateGeneric(Method, handle); + var handle = MetadataTokens.EntityHandle(payloadValue); + var target = Cx.CreateGeneric(Method, handle); yield return target; if (target != null) { @@ -414,11 +418,17 @@ public override IEnumerable Contents break; case Payload.Arg8: case Payload.Arg16: - yield return Tuples.cil_access(this, Method.Parameters[(int)UnsignedPayloadValue]); + if (Method.Parameters is object) + { + yield return Tuples.cil_access(this, Method.Parameters[(int)unsignedPayloadValue]); + } break; case Payload.Local8: case Payload.Local16: - yield return Tuples.cil_access(this, Method.LocalVariables[(int)UnsignedPayloadValue]); + if (Method.LocalVariables is object) + { + yield return Tuples.cil_access(this, Method.LocalVariables[(int)unsignedPayloadValue]); + } break; case Payload.None: case Payload.Target8: @@ -439,27 +449,32 @@ public override IEnumerable Contents public IEnumerable JumpContents(Dictionary jump_table) { int target; - IInstruction inst; + IInstruction? inst; switch (PayloadType) { case Payload.Target8: - target = Offset + PayloadValue + 2; + target = Offset + payloadValue + 2; break; case Payload.Target32: - target = Offset + PayloadValue + 5; + target = Offset + payloadValue + 5; break; case Payload.Switch: - int end = Offset + Width; + var end = Offset + Width; - int offset = Offset + 5; + var offset = Offset + 5; - for (int b = 0; b < PayloadValue; ++b, offset += 4) + for (var b = 0; b < payloadValue; ++b, offset += 4) { target = BitConverter.ToInt32(data, offset) + end; - if (!jump_table.TryGetValue(target, out inst)) + if (jump_table.TryGetValue(target, out inst)) + { + yield return Tuples.cil_switch(this, b, inst); + } + else + { throw new InternalError("Invalid jump target"); - yield return Tuples.cil_switch(this, b, inst); + } } yield break; @@ -479,7 +494,7 @@ public IEnumerable JumpContents(Dictionary /// A method entity. /// - interface IMethod : IMember + internal interface IMethod : IMember { } /// /// A method entity. /// - abstract class Method : TypeContainer, IMethod + internal abstract class Method : TypeContainer, IMethod { - protected Method(GenericContext gc) : base(gc.cx) + protected MethodTypeParameter[]? genericParams; + protected GenericContext gc; + protected MethodSignature signature; + + protected Method(GenericContext gc) : base(gc.Cx) { this.gc = gc; } - public override IEnumerable TypeParameters => gc.TypeParameters.Concat(declaringType.TypeParameters); + public override IEnumerable TypeParameters => gc.TypeParameters.Concat(DeclaringType.TypeParameters); public override IEnumerable MethodParameters => genericParams == null ? gc.MethodParameters : gc.MethodParameters.Concat(genericParams); public int GenericParameterCount => signature.GenericParameterCount; - public virtual Method SourceDeclaration => this; + public virtual Method? SourceDeclaration => this; public abstract Type DeclaringType { get; } public abstract string Name { get; } - public virtual IList LocalVariables => throw new NotImplementedException(); - public IList Parameters { get; private set; } + public virtual IList? LocalVariables => throw new NotImplementedException(); + public IList? Parameters { get; protected set; } public override void WriteId(TextWriter trapFile) => WriteMethodId(trapFile, DeclaringType, NameLabel); public abstract string NameLabel { get; } - internal protected void WriteMethodId(TextWriter trapFile, Type parent, string methodName) + protected internal void WriteMethodId(TextWriter trapFile, Type parent, string methodName) { signature.ReturnType.WriteId(trapFile, this); trapFile.Write(' '); @@ -61,7 +64,7 @@ internal protected void WriteMethodId(TextWriter trapFile, Type parent, string m trapFile.Write(signature.GenericParameterCount); } trapFile.Write('('); - int index = 0; + var index = 0; foreach (var param in signature.ParameterTypes) { trapFile.WriteSeparator(",", ref index); @@ -70,19 +73,8 @@ internal protected void WriteMethodId(TextWriter trapFile, Type parent, string m trapFile.Write(')'); } - protected MethodTypeParameter[] genericParams; - protected Type declaringType; - protected GenericContext gc; - protected MethodSignature signature; - protected string name; - public override string IdSuffix => ";cil-method"; - protected void PopulateParameters(IEnumerable parameterTypes) - { - Parameters = MakeParameters(parameterTypes).ToArray(); - } - protected IEnumerable PopulateFlags { get @@ -94,24 +86,24 @@ protected IEnumerable PopulateFlags public abstract bool IsStatic { get; } - private IEnumerable MakeParameters(IEnumerable parameterTypes) + protected IEnumerable MakeParameters(IEnumerable parameterTypes) { - int i = 0; + var i = 0; if (!IsStatic) { - yield return cx.Populate(new Parameter(cx, this, i++, DeclaringType)); + yield return Cx.Populate(new Parameter(Cx, this, i++, DeclaringType)); } foreach (var p in parameterTypes) - yield return cx.Populate(new Parameter(cx, this, i++, p)); + yield return Cx.Populate(new Parameter(Cx, this, i++, p)); } } /// /// A method implementation entity. /// - interface IMethodImplementation : IExtractedEntity + internal interface IMethodImplementation : IExtractedEntity { } @@ -119,11 +111,11 @@ interface IMethodImplementation : IExtractedEntity /// A method implementation entity. /// In the database, the same method could in principle have multiple implementations. /// - class MethodImplementation : UnlabelledEntity, IMethodImplementation + internal class MethodImplementation : UnlabelledEntity, IMethodImplementation { - readonly Method m; + private readonly Method m; - public MethodImplementation(Method m) : base(m.cx) + public MethodImplementation(Method m) : base(m.Cx) { this.m = m; } @@ -132,7 +124,7 @@ public override IEnumerable Contents { get { - yield return Tuples.cil_method_implementation(this, m, cx.assembly); + yield return Tuples.cil_method_implementation(this, m, Cx.Assembly); } } } @@ -141,33 +133,35 @@ public override IEnumerable Contents /// /// A definition method - a method defined in the current assembly. /// - sealed class DefinitionMethod : Method, IMember + internal sealed class DefinitionMethod : Method, IMember { - readonly Handle handle; - readonly MethodDefinition md; - readonly PDB.IMethod methodDebugInformation; + private readonly Handle handle; + private readonly MethodDefinition md; + private readonly PDB.IMethod? methodDebugInformation; + private readonly Type declaringType; - LocalVariable[] locals; + private readonly string name; + private LocalVariable[]? locals; - public MethodImplementation Implementation { get; private set; } + public MethodImplementation? Implementation { get; private set; } - public override IList LocalVariables => locals; + public override IList? LocalVariables => locals; public DefinitionMethod(GenericContext gc, MethodDefinitionHandle handle) : base(gc) { - md = cx.mdReader.GetMethodDefinition(handle); + md = Cx.MdReader.GetMethodDefinition(handle); this.gc = gc; this.handle = handle; - name = cx.GetString(md.Name); + name = Cx.GetString(md.Name); - declaringType = (Type)cx.CreateGeneric(this, md.GetDeclaringType()); + declaringType = (Type)Cx.CreateGeneric(this, md.GetDeclaringType()); signature = md.DecodeSignature(new SignatureDecoder(), this); - methodDebugInformation = cx.GetMethodDebugInformation(handle); + methodDebugInformation = Cx.GetMethodDebugInformation(handle); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DefinitionMethod method && handle.Equals(method.handle); } @@ -178,7 +172,7 @@ public override bool Equals(object obj) public override Type DeclaringType => declaringType; - public override string Name => cx.ShortName(md.Name); + public override string Name => Cx.ShortName(md.Name); public override string NameLabel => name; @@ -196,17 +190,17 @@ public override IEnumerable Contents // We need to perform a 2-phase population because some type parameters can // depend on other type parameters (as a constraint). genericParams = new MethodTypeParameter[md.GetGenericParameters().Count]; - for (int i = 0; i < genericParams.Length; ++i) - genericParams[i] = cx.Populate(new MethodTypeParameter(this, this, i)); - for (int i = 0; i < genericParams.Length; ++i) - genericParams[i].PopulateHandle(this, md.GetGenericParameters()[i]); + for (var i = 0; i < genericParams.Length; ++i) + genericParams[i] = Cx.Populate(new MethodTypeParameter(this, this, i)); + for (var i = 0; i < genericParams.Length; ++i) + genericParams[i].PopulateHandle(md.GetGenericParameters()[i]); foreach (var p in genericParams) yield return p; } - var typeSignature = md.DecodeSignature(cx.TypeSignatureDecoder, this); + var typeSignature = md.DecodeSignature(Cx.TypeSignatureDecoder, this); - PopulateParameters(typeSignature.ParameterTypes); + Parameters = MakeParameters(typeSignature.ParameterTypes).ToArray(); foreach (var c in Parameters) yield return c; @@ -214,38 +208,38 @@ public override IEnumerable Contents foreach (var c in PopulateFlags) yield return c; - foreach (var p in md.GetParameters().Select(h => cx.mdReader.GetParameter(h)).Where(p => p.SequenceNumber > 0)) + foreach (var p in md.GetParameters().Select(h => Cx.MdReader.GetParameter(h)).Where(p => p.SequenceNumber > 0)) { var pe = Parameters[IsStatic ? p.SequenceNumber - 1 : p.SequenceNumber]; if (p.Attributes.HasFlag(ParameterAttributes.Out)) yield return Tuples.cil_parameter_out(pe); if (p.Attributes.HasFlag(ParameterAttributes.In)) yield return Tuples.cil_parameter_in(pe); - Attribute.Populate(cx, pe, p.GetCustomAttributes()); + Attribute.Populate(Cx, pe, p.GetCustomAttributes()); } - yield return Tuples.metadata_handle(this, cx.assembly, MetadataTokens.GetToken(handle)); + yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle)); yield return Tuples.cil_method(this, Name, declaringType, typeSignature.ReturnType); yield return Tuples.cil_method_source_declaration(this, this); - yield return Tuples.cil_method_location(this, cx.assembly); + yield return Tuples.cil_method_location(this, Cx.Assembly); if (HasBytecode) { Implementation = new MethodImplementation(this); yield return Implementation; - var body = cx.peReader.GetMethodBody(md.RelativeVirtualAddress); + var body = Cx.PeReader.GetMethodBody(md.RelativeVirtualAddress); if (!body.LocalSignature.IsNil) { - var locals = cx.mdReader.GetStandaloneSignature(body.LocalSignature); - var localVariableTypes = locals.DecodeLocalSignature(cx.TypeSignatureDecoder, this); + var locals = Cx.MdReader.GetStandaloneSignature(body.LocalSignature); + var localVariableTypes = locals.DecodeLocalSignature(Cx.TypeSignatureDecoder, this); this.locals = new LocalVariable[localVariableTypes.Length]; - for (int l = 0; l < this.locals.Length; ++l) + for (var l = 0; l < this.locals.Length; ++l) { - this.locals[l] = cx.Populate(new LocalVariable(cx, Implementation, l, localVariableTypes[l])); + this.locals[l] = Cx.Populate(new LocalVariable(Cx, Implementation, l, localVariableTypes[l])); yield return this.locals[l]; } } @@ -255,7 +249,7 @@ public override IEnumerable Contents foreach (var c in Decode(body.GetILBytes(), jump_table)) yield return c; - int filter_index = 0; + var filter_index = 0; foreach (var region in body.ExceptionRegions) { yield return new ExceptionRegion(this, Implementation, filter_index++, region, jump_table); @@ -265,7 +259,7 @@ public override IEnumerable Contents if (methodDebugInformation != null) { - var sourceLocation = cx.CreateSourceLocation(methodDebugInformation.Location); + var sourceLocation = Cx.CreateSourceLocation(methodDebugInformation.Location); yield return sourceLocation; yield return Tuples.cil_method_location(this, sourceLocation); } @@ -304,26 +298,26 @@ public override IEnumerable Contents yield return Tuples.cil_newslot(this); // Populate attributes - Attribute.Populate(cx, this, md.GetCustomAttributes()); + Attribute.Populate(Cx, this, md.GetCustomAttributes()); } } - IEnumerable Decode(byte[] ilbytes, Dictionary jump_table) + private IEnumerable Decode(byte[] ilbytes, Dictionary jump_table) { // Sequence points are stored in order of offset. // We use an enumerator to locate the correct sequence point for each instruction. // The sequence point gives the location of each instruction. // The location of an instruction is given by the sequence point *after* the // instruction. - IEnumerator nextSequencePoint = null; - PdbSourceLocation instructionLocation = null; + IEnumerator? nextSequencePoint = null; + PdbSourceLocation? instructionLocation = null; if (methodDebugInformation != null) { nextSequencePoint = methodDebugInformation.SequencePoints.GetEnumerator(); if (nextSequencePoint.MoveNext()) { - instructionLocation = cx.CreateSourceLocation(nextSequencePoint.Current.Location); + instructionLocation = Cx.CreateSourceLocation(nextSequencePoint.Current.Location); yield return instructionLocation; } else @@ -332,15 +326,15 @@ IEnumerable Decode(byte[] ilbytes, Dictionary= nextSequencePoint.Current.Offset) { - instructionLocation = cx.CreateSourceLocation(nextSequencePoint.Current.Location); + instructionLocation = Cx.CreateSourceLocation(nextSequencePoint.Current.Location); yield return instructionLocation; if (!nextSequencePoint.MoveNext()) nextSequencePoint = null; @@ -370,17 +364,17 @@ public IEnumerable DebugInstructions { if (md.ImplAttributes == MethodImplAttributes.IL && md.RelativeVirtualAddress != 0) { - var body = cx.peReader.GetMethodBody(md.RelativeVirtualAddress); + var body = Cx.PeReader.GetMethodBody(md.RelativeVirtualAddress); var ilbytes = body.GetILBytes(); - int child = 0; - for (int offset = 0; offset < ilbytes.Length;) + var child = 0; + for (var offset = 0; offset < ilbytes.Length;) { Instruction decoded; try { - decoded = new Instruction(cx, this, ilbytes, offset, child++); + decoded = new Instruction(Cx, this, ilbytes, offset, child++); offset += decoded.Width; } catch // lgtm[cs/catch-of-all-exceptions] @@ -397,41 +391,43 @@ public IEnumerable DebugInstructions /// /// This is a late-bound reference to a method. /// - sealed class MemberReferenceMethod : Method + internal sealed class MemberReferenceMethod : Method { - readonly MemberReferenceHandle handle; - readonly MemberReference mr; - readonly Type declType; - readonly GenericContext parent; - readonly Method sourceDeclaration; + private readonly MemberReferenceHandle handle; + private readonly MemberReference mr; + private readonly Type declaringType; + private readonly GenericContext parent; + private readonly Method? sourceDeclaration; public MemberReferenceMethod(GenericContext gc, MemberReferenceHandle handle) : base(gc) { this.handle = handle; this.gc = gc; - mr = cx.mdReader.GetMemberReference(handle); + mr = Cx.MdReader.GetMemberReference(handle); signature = mr.DecodeMethodSignature(new SignatureDecoder(), gc); - parent = (GenericContext)cx.CreateGeneric(gc, mr.Parent); + parent = (GenericContext)Cx.CreateGeneric(gc, mr.Parent); - var parentMethod = parent as Method; - nameLabel = cx.GetString(mr.Name); - - declType = parentMethod == null ? parent as Type : parentMethod.DeclaringType; + var declType = parent is Method parentMethod + ? parentMethod.DeclaringType + : parent as Type; if (declType is null) throw new InternalError("Parent context of method is not a type"); - var typeSourceDeclaration = declType.SourceDeclaration; - sourceDeclaration = typeSourceDeclaration == declType ? (Method)this : typeSourceDeclaration.LookupMethod(mr.Name, mr.Signature); + declaringType = declType; + nameLabel = Cx.GetString(mr.Name); + + var typeSourceDeclaration = declaringType.SourceDeclaration; + sourceDeclaration = typeSourceDeclaration == declaringType ? (Method)this : typeSourceDeclaration.LookupMethod(mr.Name, mr.Signature); } private readonly string nameLabel; public override string NameLabel => nameLabel; - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is MemberReferenceMethod method && handle.Equals(method.handle); } @@ -441,13 +437,13 @@ public override int GetHashCode() return handle.GetHashCode(); } - public override Method SourceDeclaration => sourceDeclaration; + public override Method? SourceDeclaration => sourceDeclaration; public override bool IsStatic => !signature.Header.IsInstance; - public override Type DeclaringType => declType; + public override Type DeclaringType => declaringType; - public override string Name => cx.ShortName(mr.Name); + public override string Name => Cx.ShortName(mr.Name); public override IEnumerable TypeParameters => parent.TypeParameters.Concat(gc.TypeParameters); @@ -456,15 +452,15 @@ public override IEnumerable Contents get { genericParams = new MethodTypeParameter[signature.GenericParameterCount]; - for (int p = 0; p < genericParams.Length; ++p) - genericParams[p] = cx.Populate(new MethodTypeParameter(this, this, p)); + for (var p = 0; p < genericParams.Length; ++p) + genericParams[p] = Cx.Populate(new MethodTypeParameter(this, this, p)); foreach (var p in genericParams) yield return p; - var typeSignature = mr.DecodeMethodSignature(cx.TypeSignatureDecoder, this); + var typeSignature = mr.DecodeMethodSignature(Cx.TypeSignatureDecoder, this); - PopulateParameters(typeSignature.ParameterTypes); + Parameters = MakeParameters(typeSignature.ParameterTypes).ToArray(); foreach (var p in Parameters) yield return p; foreach (var f in PopulateFlags) yield return f; @@ -480,28 +476,27 @@ public override IEnumerable Contents /// /// A constructed method. /// - sealed class MethodSpecificationMethod : Method + internal sealed class MethodSpecificationMethod : Method { - readonly MethodSpecificationHandle handle; - readonly MethodSpecification ms; - readonly Method unboundMethod; - readonly ImmutableArray typeParams; + private readonly MethodSpecificationHandle handle; + private readonly MethodSpecification ms; + private readonly Method unboundMethod; + private readonly ImmutableArray typeParams; public MethodSpecificationMethod(GenericContext gc, MethodSpecificationHandle handle) : base(gc) { this.handle = handle; - ms = cx.mdReader.GetMethodSpecification(handle); - typeParams = ms.DecodeSignature(cx.TypeSignatureDecoder, gc); - unboundMethod = (Method)cx.CreateGeneric(gc, ms.Method); - declaringType = unboundMethod.DeclaringType; + ms = Cx.MdReader.GetMethodSpecification(handle); + typeParams = ms.DecodeSignature(Cx.TypeSignatureDecoder, gc); + unboundMethod = (Method)Cx.CreateGeneric(gc, ms.Method); } public override void WriteId(TextWriter trapFile) { unboundMethod.WriteId(trapFile); trapFile.Write('<'); - int index = 0; - foreach(var param in typeParams) + var index = 0; + foreach (var param in typeParams) { trapFile.WriteSeparator(",", ref index); trapFile.WriteSubId(param); @@ -511,7 +506,7 @@ public override void WriteId(TextWriter trapFile) public override string NameLabel => throw new NotImplementedException(); - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is MethodSpecificationMethod method && handle.Equals(method.handle) && typeParams.SequenceEqual(method.typeParams); } @@ -536,18 +531,18 @@ public override IEnumerable Contents switch (ms.Method.Kind) { case HandleKind.MemberReference: - var mr = cx.mdReader.GetMemberReference((MemberReferenceHandle)ms.Method); - constructedTypeSignature = mr.DecodeMethodSignature(cx.TypeSignatureDecoder, this); + var mr = Cx.MdReader.GetMemberReference((MemberReferenceHandle)ms.Method); + constructedTypeSignature = mr.DecodeMethodSignature(Cx.TypeSignatureDecoder, this); break; case HandleKind.MethodDefinition: - var md = cx.mdReader.GetMethodDefinition((MethodDefinitionHandle)ms.Method); - constructedTypeSignature = md.DecodeSignature(cx.TypeSignatureDecoder, this); + var md = Cx.MdReader.GetMethodDefinition((MethodDefinitionHandle)ms.Method); + constructedTypeSignature = md.DecodeSignature(Cx.TypeSignatureDecoder, this); break; default: throw new InternalError($"Unexpected constructed method handle kind {ms.Method.Kind}"); } - PopulateParameters(constructedTypeSignature.ParameterTypes); + Parameters = MakeParameters(constructedTypeSignature.ParameterTypes).ToArray(); foreach (var p in Parameters) yield return p; @@ -557,10 +552,10 @@ public override IEnumerable Contents yield return Tuples.cil_method(this, Name, DeclaringType, constructedTypeSignature.ReturnType); yield return Tuples.cil_method_source_declaration(this, SourceDeclaration); - if (typeParams.Count() != unboundMethod.GenericParameterCount) + if (typeParams.Length != unboundMethod.GenericParameterCount) throw new InternalError("Method type parameter mismatch"); - for (int p = 0; p < typeParams.Length; ++p) + for (var p = 0; p < typeParams.Length; ++p) { yield return Tuples.cil_type_argument(this, p, typeParams[p]); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs index 6a7943c8bd1d..2e19eb3dd4c6 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs @@ -1,14 +1,13 @@ īģŋusing System; using System.Collections.Generic; using System.IO; -using Semmle.Extraction.Entities; namespace Semmle.Extraction.CIL.Entities { /// /// A namespace. /// - interface INamespace : ITypeContainer + internal interface INamespace : ITypeContainer { } @@ -17,10 +16,10 @@ interface INamespace : ITypeContainer /// public sealed class Namespace : TypeContainer, INamespace { - public Namespace ParentNamespace; - public readonly string Name; + public Namespace? ParentNamespace { get; } + public string Name { get; } - public bool IsGlobalNamespace => ParentNamespace == null; + public bool IsGlobalNamespace => ParentNamespace is null; public override string IdSuffix => ";namespace"; @@ -35,7 +34,7 @@ public override void WriteId(TextWriter trapFile) trapFile.Write(Name); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is Namespace ns && Name == ns.Name) { @@ -49,7 +48,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - int h = ParentNamespace is null ? 19 : ParentNamespace.GetHashCode(); + var h = ParentNamespace is null ? 19 : ParentNamespace.GetHashCode(); return 13 * h + Name.GetHashCode(); } @@ -57,15 +56,16 @@ public override int GetHashCode() public override IEnumerable MethodParameters => throw new NotImplementedException(); - static string parseNamespaceName(string fqn) + private static string parseNamespaceName(string fqn) { var i = fqn.LastIndexOf('.'); return i == -1 ? fqn : fqn.Substring(i + 1); } - static Namespace createParentNamespace(Context cx, string fqn) + private static Namespace? createParentNamespace(Context cx, string fqn) { - if (fqn == "") return null; + if (fqn.Length == 0) + return null; var i = fqn.LastIndexOf('.'); return i == -1 ? cx.GlobalNamespace : cx.Populate(new Namespace(cx, fqn.Substring(0, i))); } @@ -74,7 +74,7 @@ public Namespace(Context cx, string fqn) : this(cx, parseNamespaceName(fqn), cre { } - public Namespace(Context cx, string name, Namespace parent) : base(cx) + public Namespace(Context cx, string name, Namespace? parent) : base(cx) { Name = name; ParentNamespace = parent; @@ -85,7 +85,7 @@ public override IEnumerable Contents get { yield return Tuples.namespaces(this, Name); - if (!IsGlobalNamespace) + if (ParentNamespace is object) yield return Tuples.parent_namespace(this, ParentNamespace); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs index f48a88bcf82e..9295327de083 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs @@ -6,18 +6,18 @@ namespace Semmle.Extraction.CIL.Entities /// /// A parameter entity. /// - interface IParameter : IExtractedEntity + internal interface IParameter : IExtractedEntity { } /// /// A parameter entity. /// - sealed class Parameter : LabelledEntity, IParameter + internal sealed class Parameter : LabelledEntity, IParameter { - readonly Method method; - readonly int index; - readonly Type type; + private readonly Method method; + private readonly int index; + private readonly Type type; public Parameter(Context cx, Method m, int i, Type t) : base(cx) { @@ -33,7 +33,7 @@ public override void WriteId(TextWriter trapFile) trapFile.Write(index); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Parameter param && method.Equals(param.method) && index == param.index; } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs index 4149baf5e429..65c5dc4cc3b1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs @@ -1,6 +1,5 @@ īģŋusing System.Collections.Generic; using System.Reflection.Metadata; -using System.Linq; using System.Reflection.Metadata.Ecma335; using System.IO; @@ -9,26 +8,26 @@ namespace Semmle.Extraction.CIL.Entities /// /// A property. /// - interface IProperty : IExtractedEntity + internal interface IProperty : IExtractedEntity { } /// /// A property. /// - sealed class Property : LabelledEntity, IProperty + internal sealed class Property : LabelledEntity, IProperty { - readonly Handle handle; - readonly Type type; - readonly PropertyDefinition pd; + private readonly Handle handle; + private readonly Type type; + private readonly PropertyDefinition pd; public override string IdSuffix => ";cil-property"; - readonly GenericContext gc; + private readonly GenericContext gc; - public Property(GenericContext gc, Type type, PropertyDefinitionHandle handle) : base(gc.cx) + public Property(GenericContext gc, Type type, PropertyDefinitionHandle handle) : base(gc.Cx) { this.gc = gc; this.handle = handle; - pd = cx.mdReader.GetPropertyDefinition(handle); + pd = Cx.MdReader.GetPropertyDefinition(handle); this.type = type; } @@ -36,9 +35,9 @@ public override void WriteId(TextWriter trapFile) { trapFile.WriteSubId(type); trapFile.Write('.'); - trapFile.Write(cx.GetString(pd.Name)); + trapFile.Write(Cx.GetString(pd.Name)); trapFile.Write("("); - int index=0; + var index = 0; var signature = pd.DecodeSignature(new SignatureDecoder(), gc); foreach (var param in signature.ParameterTypes) { @@ -48,7 +47,7 @@ public override void WriteId(TextWriter trapFile) trapFile.Write(")"); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Property property && Equals(handle, property.handle); } @@ -59,27 +58,27 @@ public override IEnumerable Contents { get { - yield return Tuples.metadata_handle(this, cx.assembly, MetadataTokens.GetToken(handle)); - var sig = pd.DecodeSignature(cx.TypeSignatureDecoder, type); + yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle)); + var sig = pd.DecodeSignature(Cx.TypeSignatureDecoder, type); - yield return Tuples.cil_property(this, type, cx.ShortName(pd.Name), sig.ReturnType); + yield return Tuples.cil_property(this, type, Cx.ShortName(pd.Name), sig.ReturnType); var accessors = pd.GetAccessors(); if (!accessors.Getter.IsNil) { - var getter = (Method)cx.CreateGeneric(type, accessors.Getter); + var getter = (Method)Cx.CreateGeneric(type, accessors.Getter); yield return getter; yield return Tuples.cil_getter(this, getter); } if (!accessors.Setter.IsNil) { - var setter = (Method)cx.CreateGeneric(type, accessors.Setter); + var setter = (Method)Cx.CreateGeneric(type, accessors.Setter); yield return setter; yield return Tuples.cil_setter(this, setter); } - foreach (var c in Attribute.Populate(cx, this, pd.GetCustomAttributes())) + foreach (var c in Attribute.Populate(Cx, this, pd.GetCustomAttributes())) yield return c; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs index ade644fbb1c9..230a856ed2d1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs @@ -10,8 +10,8 @@ public interface ISourceLocation : ILocation public sealed class PdbSourceLocation : LabelledEntity, ISourceLocation { - readonly Location location; - readonly PdbSourceFile file; + private readonly Location location; + private readonly PdbSourceFile file; public PdbSourceLocation(Context cx, PDB.Location location) : base(cx) { @@ -32,7 +32,7 @@ public override void WriteId(TextWriter trapFile) trapFile.Write(location.EndColumn); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is PdbSourceLocation l && location.Equals(l.location); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs index 04b25553b6b7..d3378b7c3f12 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs @@ -7,21 +7,21 @@ using System.Reflection; using Semmle.Util; using System.IO; -using System.Text; +using System.Diagnostics.CodeAnalysis; namespace Semmle.Extraction.CIL.Entities { /// /// A type. /// - interface IType : IEntity + internal interface IType : IEntity { } /// /// An array type. /// - interface IArrayType : IType + internal interface IArrayType : IType { } @@ -39,18 +39,17 @@ public enum CilTypeKind /// /// A type container (namespace/types/method). /// - interface ITypeContainer : IExtractedEntity + internal interface ITypeContainer : IExtractedEntity { } /// /// Base class for all type containers (namespaces, types, methods). /// - abstract public class TypeContainer : GenericContext, ITypeContainer + public abstract class TypeContainer : GenericContext, ITypeContainer { protected TypeContainer(Context cx) : base(cx) { - this.cx = cx; } public virtual Label Label { get; set; } @@ -75,11 +74,9 @@ public void WriteQuotedId(TextWriter trapFile) public override string ToString() { - using (var writer = new StringWriter()) - { - WriteQuotedId(writer); - return writer.ToString(); - } + using var writer = new StringWriter(); + WriteQuotedId(writer); + return writer.ToString(); } TrapStackBehaviour IEntity.TrapStackBehaviour => TrapStackBehaviour.NoLabel; @@ -103,7 +100,7 @@ public abstract class Type : TypeContainer, IMember, IType /// shortcut to comparing the signature bytes since handles are unique. /// /// The method, or 'null' if not found or not supported. - internal virtual Method LookupMethod(StringHandle methodName, BlobHandle signature) + internal virtual Method? LookupMethod(StringHandle methodName, BlobHandle signature) { return null; } @@ -113,9 +110,10 @@ public IEnumerable TypeArguments get { if (ContainingType != null) + { foreach (var t in ContainingType.TypeArguments) yield return t; - + } foreach (var t in ThisTypeArguments) yield return t; @@ -160,7 +158,7 @@ public abstract CilTypeKind Kind get; } - public virtual TypeContainer Parent => (TypeContainer)ContainingType ?? Namespace; + public virtual TypeContainer Parent => (TypeContainer?)ContainingType ?? Namespace!; public override IEnumerable Contents { @@ -177,9 +175,9 @@ public override IEnumerable Contents public abstract string Name { get; } - public abstract Namespace Namespace { get; } + public abstract Namespace? Namespace { get; } - public abstract Type ContainingType { get; } + public abstract Type? ContainingType { get; } public abstract Type Construct(IEnumerable typeArguments); @@ -213,8 +211,10 @@ public virtual IEnumerable GenericArguments get { if (ContainingType != null) + { foreach (var t in ContainingType.GenericArguments) yield return t; + } foreach (var t in ThisGenericArguments) yield return t; } @@ -233,23 +233,21 @@ public void PrimitiveTypeId(TextWriter trapFile) /// /// The resulting primitive type, or null. /// True if this type is a primitive type. - public bool TryGetPrimitiveType(out PrimitiveType t) + public bool TryGetPrimitiveType([NotNullWhen(true)] out PrimitiveType? t) { if (TryGetPrimitiveTypeCode(out var code)) { - t = cx.Create(code); + t = Cx.Create(code); return true; } - else - { - t = null; - return false; - } + + t = null; + return false; } private bool TryGetPrimitiveTypeCode(out PrimitiveTypeCode code) { - if (ContainingType == null && Namespace.Name == cx.SystemNamespace.Name) + if (ContainingType == null && Namespace?.Name == Cx.SystemNamespace.Name) { switch (Name) { @@ -317,7 +315,7 @@ private bool TryGetPrimitiveTypeCode(out PrimitiveTypeCode code) protected bool IsPrimitiveType => TryGetPrimitiveTypeCode(out _); public static Type DecodeType(GenericContext gc, TypeSpecificationHandle handle) => - gc.cx.mdReader.GetTypeSpecification(handle).DecodeSignature(gc.cx.TypeSignatureDecoder, gc); + gc.Cx.MdReader.GetTypeSpecification(handle).DecodeSignature(gc.Cx.TypeSignatureDecoder, gc); } /// @@ -325,12 +323,12 @@ public static Type DecodeType(GenericContext gc, TypeSpecificationHandle handle) /// public sealed class TypeDefinitionType : Type { - readonly Handle handle; - readonly TypeDefinition td; + private readonly Handle handle; + private readonly TypeDefinition td; public TypeDefinitionType(Context cx, TypeDefinitionHandle handle) : base(cx) { - td = cx.mdReader.GetTypeDefinition(handle); + td = cx.MdReader.GetTypeDefinition(handle); this.handle = handle; declType = @@ -341,7 +339,7 @@ public TypeDefinitionType(Context cx, TypeDefinitionHandle handle) : base(cx) typeParams = new Lazy>(MakeTypeParameters); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is TypeDefinitionType t && handle.Equals(t.handle); } @@ -356,7 +354,7 @@ public override void WriteId(TextWriter trapFile, bool inContext) return; } - var name = cx.GetString(td.Name); + var name = Cx.GetString(td.Name); if (ContainingType != null) { @@ -382,17 +380,17 @@ public override string Name { get { - var name = cx.GetString(td.Name); + var name = Cx.GetString(td.Name); var tick = name.IndexOf('`'); return tick == -1 ? name : name.Substring(0, tick); } } - public override Namespace Namespace => cx.Create(td.NamespaceDefinition); + public override Namespace Namespace => Cx.Create(td.NamespaceDefinition); - readonly Type declType; + private readonly Type? declType; - public override Type ContainingType => declType; + public override Type? ContainingType => declType; public override int ThisTypeParameters { @@ -400,7 +398,7 @@ public override int ThisTypeParameters { var containingType = td.GetDeclaringType(); var parentTypeParameters = containingType.IsNil ? 0 : - cx.mdReader.GetTypeDefinition(containingType).GetGenericParameters().Count; + Cx.MdReader.GetTypeDefinition(containingType).GetGenericParameters().Count; return td.GetGenericParameters().Count - parentTypeParameters; } @@ -410,38 +408,38 @@ public override int ThisTypeParameters public override Type Construct(IEnumerable typeArguments) { - return cx.Populate(new ConstructedType(cx, this, typeArguments)); + return Cx.Populate(new ConstructedType(Cx, this, typeArguments)); } public override void WriteAssemblyPrefix(TextWriter trapFile) { var ct = ContainingType; if (ct is null) - cx.WriteAssemblyPrefix(trapFile); + Cx.WriteAssemblyPrefix(trapFile); else if (IsPrimitiveType) trapFile.Write("builtin:"); else ct.WriteAssemblyPrefix(trapFile); } - IEnumerable MakeTypeParameters() + private IEnumerable MakeTypeParameters() { if (ThisTypeParameters == 0) return Enumerable.Empty(); var newTypeParams = new TypeTypeParameter[ThisTypeParameters]; var genericParams = td.GetGenericParameters(); - int toSkip = genericParams.Count - newTypeParams.Length; + var toSkip = genericParams.Count - newTypeParams.Length; // Two-phase population because type parameters can be mutually dependent - for (int i = 0; i < newTypeParams.Length; ++i) - newTypeParams[i] = cx.Populate(new TypeTypeParameter(this, this, i)); - for (int i = 0; i < newTypeParams.Length; ++i) - newTypeParams[i].PopulateHandle(this, genericParams[i + toSkip]); + for (var i = 0; i < newTypeParams.Length; ++i) + newTypeParams[i] = Cx.Populate(new TypeTypeParameter(this, this, i)); + for (var i = 0; i < newTypeParams.Length; ++i) + newTypeParams[i].PopulateHandle(genericParams[i + toSkip]); return newTypeParams; } - readonly Lazy> typeParams; + private readonly Lazy> typeParams; public override IEnumerable MethodParameters => Enumerable.Empty(); @@ -464,7 +462,7 @@ public override IEnumerable Contents { get { - yield return Tuples.metadata_handle(this, cx.assembly, handle.GetHashCode()); + yield return Tuples.metadata_handle(this, Cx.Assembly, handle.GetHashCode()); foreach (var c in base.Contents) yield return c; @@ -473,7 +471,7 @@ public override IEnumerable Contents foreach (var f in td.GetFields()) { // Populate field if needed - yield return cx.CreateGeneric(this, f); + yield return Cx.CreateGeneric(this, f); } foreach (var prop in td.GetProperties()) @@ -483,16 +481,16 @@ public override IEnumerable Contents foreach (var @event in td.GetEvents()) { - yield return new Event(cx, this, @event); + yield return new Event(Cx, this, @event); } - foreach (var a in Attribute.Populate(cx, this, td.GetCustomAttributes())) + foreach (var a in Attribute.Populate(Cx, this, td.GetCustomAttributes())) yield return a; - foreach (var impl in td.GetMethodImplementations().Select(i => cx.mdReader.GetMethodImplementation(i))) + foreach (var impl in td.GetMethodImplementations().Select(i => Cx.MdReader.GetMethodImplementation(i))) { - var m = (Method)cx.CreateGeneric(this, impl.MethodBody); - var decl = (Method)cx.CreateGeneric(this, impl.MethodDeclaration); + var m = (Method)Cx.CreateGeneric(this, impl.MethodBody); + var decl = (Method)Cx.CreateGeneric(this, impl.MethodDeclaration); yield return m; yield return decl; @@ -520,20 +518,20 @@ public override IEnumerable Contents if (!td.BaseType.IsNil) { - var @base = (Type)cx.CreateGeneric(this, td.BaseType); + var @base = (Type)Cx.CreateGeneric(this, td.BaseType); yield return @base; yield return Tuples.cil_base_class(this, @base); } - foreach (var @interface in td.GetInterfaceImplementations().Select(i => cx.mdReader.GetInterfaceImplementation(i))) + foreach (var @interface in td.GetInterfaceImplementations().Select(i => Cx.MdReader.GetInterfaceImplementation(i))) { - var t = (Type)cx.CreateGeneric(this, @interface.Interface); + var t = (Type)Cx.CreateGeneric(this, @interface.Interface); yield return t; yield return Tuples.cil_base_interface(this, t); } // Only type definitions have locations. - yield return Tuples.cil_type_location(this, cx.assembly); + yield return Tuples.cil_type_location(this, Cx.Assembly); } } @@ -541,11 +539,11 @@ internal override Method LookupMethod(StringHandle name, BlobHandle signature) { foreach (var h in td.GetMethods()) { - var md = cx.mdReader.GetMethodDefinition(h); + var md = Cx.MdReader.GetMethodDefinition(h); if (md.Name == name && md.Signature == signature) { - return (Method)cx.Create(h); + return (Method)Cx.Create(h); } } @@ -558,22 +556,18 @@ internal override Method LookupMethod(StringHandle name, BlobHandle signature) /// public sealed class TypeReferenceType : Type { - readonly TypeReferenceHandle handle; - readonly TypeReference tr; - readonly Lazy typeParams; + private readonly TypeReferenceHandle handle; + private readonly TypeReference tr; + private readonly Lazy typeParams; - public TypeReferenceType(Context cx, TypeReferenceHandle handle) : this(cx, handle, cx.mdReader.GetTypeReference(handle)) - { - typeParams = new Lazy(MakeTypeParameters); - } - - public TypeReferenceType(Context cx, TypeReferenceHandle handle, TypeReference tr) : base(cx) + public TypeReferenceType(Context cx, TypeReferenceHandle handle) : base(cx) { + this.typeParams = new Lazy(MakeTypeParameters); this.handle = handle; - this.tr = tr; + this.tr = cx.MdReader.GetTypeReference(handle); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is TypeReferenceType t && handle.Equals(t.handle); } @@ -583,10 +577,10 @@ public override int GetHashCode() return handle.GetHashCode(); } - TypeTypeParameter[] MakeTypeParameters() + private TypeTypeParameter[] MakeTypeParameters() { var newTypeParams = new TypeTypeParameter[ThisTypeParameters]; - for (int i = 0; i < newTypeParams.Length; ++i) + for (var i = 0; i < newTypeParams.Length; ++i) { newTypeParams[i] = new TypeTypeParameter(this, this, i); } @@ -609,20 +603,20 @@ public override string Name { get { - var name = cx.GetString(tr.Name); + var name = Cx.GetString(tr.Name); var tick = name.IndexOf('`'); return tick == -1 ? name : name.Substring(0, tick); } } - public override Namespace Namespace => cx.CreateNamespace(tr.Namespace); + public override Namespace Namespace => Cx.CreateNamespace(tr.Namespace); public override int ThisTypeParameters { get { // Parse the name - var name = cx.GetString(tr.Name); + var name = Cx.GetString(tr.Name); var tick = name.IndexOf('`'); return tick == -1 ? 0 : int.Parse(name.Substring(tick + 1)); } @@ -637,12 +631,12 @@ public override IEnumerable ThisGenericArguments } } - public override Type ContainingType + public override Type? ContainingType { get { if (tr.ResolutionScope.Kind == HandleKind.TypeReference) - return (Type)cx.Create((TypeReferenceHandle)tr.ResolutionScope); + return (Type)Cx.Create((TypeReferenceHandle)tr.ResolutionScope); return null; } } @@ -654,17 +648,17 @@ public override void WriteAssemblyPrefix(TextWriter trapFile) switch (tr.ResolutionScope.Kind) { case HandleKind.TypeReference: - ContainingType.WriteAssemblyPrefix(trapFile); + ContainingType!.WriteAssemblyPrefix(trapFile); break; case HandleKind.AssemblyReference: - var assemblyDef = cx.mdReader.GetAssemblyReference((AssemblyReferenceHandle)tr.ResolutionScope); - trapFile.Write(cx.GetString(assemblyDef.Name)); + var assemblyDef = Cx.MdReader.GetAssemblyReference((AssemblyReferenceHandle)tr.ResolutionScope); + trapFile.Write(Cx.GetString(assemblyDef.Name)); trapFile.Write('_'); trapFile.Write(assemblyDef.Version.ToString()); trapFile.Write("::"); break; default: - cx.WriteAssemblyPrefix(trapFile); + Cx.WriteAssemblyPrefix(trapFile); break; } } @@ -684,7 +678,7 @@ public override void WriteId(TextWriter trapFile, bool inContext) var ct = ContainingType; if (ct != null) { - ContainingType.GetId(trapFile, inContext); + ct.GetId(trapFile, inContext); } else { @@ -700,7 +694,7 @@ public override void WriteId(TextWriter trapFile, bool inContext) } trapFile.Write('.'); - trapFile.Write(cx.GetString(tr.Name)); + trapFile.Write(Cx.GetString(tr.Name)); } public override Type Construct(IEnumerable typeArguments) @@ -708,7 +702,7 @@ public override Type Construct(IEnumerable typeArguments) if (TotalTypeParametersCheck != typeArguments.Count()) throw new InternalError("Mismatched type arguments"); - return cx.Populate(new ConstructedType(cx, this, typeArguments)); + return Cx.Populate(new ConstructedType(Cx, this, typeArguments)); } } @@ -718,10 +712,12 @@ public override Type Construct(IEnumerable typeArguments) /// public sealed class ConstructedType : Type { - readonly Type unboundGenericType; - readonly Type[] thisTypeArguments; + private readonly Type unboundGenericType; - public override IEnumerable ThisTypeArguments => thisTypeArguments; + // Either null or notEmpty + private readonly Type[]? thisTypeArguments; + + public override IEnumerable ThisTypeArguments => thisTypeArguments.EnumerateNull(); public override IEnumerable ThisGenericArguments => thisTypeArguments.EnumerateNull(); @@ -732,7 +728,7 @@ public override IEnumerable Contents foreach (var c in base.Contents) yield return c; - int i = 0; + var i = 0; foreach (var type in ThisGenericArguments) { yield return type; @@ -751,7 +747,6 @@ public ConstructedType(Context cx, Type unboundType, IEnumerable typeArgum unboundGenericType = unboundType; var thisParams = unboundType.ThisTypeParameters; - var parentParams = suppliedArgs - thisParams; if (typeArguments.Count() == thisParams) { @@ -760,40 +755,45 @@ public ConstructedType(Context cx, Type unboundType, IEnumerable typeArgum } else if (thisParams == 0) { - containingType = unboundType.ContainingType.Construct(typeArguments); + // all type arguments belong to containing type + containingType = unboundType.ContainingType!.Construct(typeArguments); } else { - containingType = unboundType.ContainingType.Construct(typeArguments.Take(parentParams)); + // some type arguments belong to containing type + var parentParams = suppliedArgs - thisParams; + containingType = unboundType.ContainingType!.Construct(typeArguments.Take(parentParams)); thisTypeArguments = typeArguments.Skip(parentParams).ToArray(); } } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - if(obj is ConstructedType t && Equals(unboundGenericType, t.unboundGenericType) && Equals(containingType, t.containingType)) + if (obj is ConstructedType t && Equals(unboundGenericType, t.unboundGenericType) && Equals(containingType, t.containingType)) { - if (thisTypeArguments is null) return t.thisTypeArguments is null; - if (!(t.thisTypeArguments is null)) return thisTypeArguments.SequenceEqual(t.thisTypeArguments); + if (thisTypeArguments is null) + return t.thisTypeArguments is null; + if (!(t.thisTypeArguments is null)) + return thisTypeArguments.SequenceEqual(t.thisTypeArguments); } return false; } public override int GetHashCode() { - int h = unboundGenericType.GetHashCode(); + var h = unboundGenericType.GetHashCode(); h = 13 * h + (containingType is null ? 0 : containingType.GetHashCode()); if (!(thisTypeArguments is null)) h = h * 13 + thisTypeArguments.SequenceHash(); return h; } - readonly Type containingType; - public override Type ContainingType => containingType; + private readonly Type? containingType; + public override Type? ContainingType => containingType; public override string Name => unboundGenericType.Name; - public override Namespace Namespace => unboundGenericType.Namespace; + public override Namespace Namespace => unboundGenericType.Namespace!; public override int ThisTypeParameters => thisTypeArguments == null ? 0 : thisTypeArguments.Length; @@ -826,7 +826,7 @@ public override void WriteId(TextWriter trapFile, bool inContext) if (thisTypeArguments != null && thisTypeArguments.Any()) { trapFile.Write('<'); - int index = 0; + var index = 0; foreach (var t in thisTypeArguments) { trapFile.WriteSeparator(",", ref index); @@ -845,13 +845,13 @@ public override void WriteId(TextWriter trapFile, bool inContext) public sealed class PrimitiveType : Type { - readonly PrimitiveTypeCode typeCode; + private readonly PrimitiveTypeCode typeCode; public PrimitiveType(Context cx, PrimitiveTypeCode tc) : base(cx) { typeCode = tc; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is PrimitiveType pt && typeCode == pt.typeCode; } @@ -869,9 +869,9 @@ public override void WriteId(TextWriter trapFile, bool inContext) public override string Name => typeCode.Id(); - public override Namespace Namespace => cx.SystemNamespace; + public override Namespace Namespace => Cx.SystemNamespace; - public override Type ContainingType => null; + public override Type? ContainingType => null; public override int ThisTypeParameters => 0; @@ -889,24 +889,22 @@ public override void WriteAssemblyPrefix(TextWriter trapFile) { } /// /// An array type. /// - sealed class ArrayType : Type, IArrayType + internal sealed class ArrayType : Type, IArrayType { - readonly Type elementType; - readonly int rank; + private readonly Type elementType; + private readonly int rank; - public ArrayType(Context cx, Type element, ArrayShape shape) : base(cx) + public ArrayType(Context cx, Type elementType, int rank) : base(cx) { - rank = shape.Rank; - elementType = element; + this.rank = rank; + this.elementType = elementType; } - public ArrayType(Context cx, Type element) : base(cx) + public ArrayType(Context cx, Type elementType) : this(cx, elementType, 1) { - rank = 1; - elementType = element; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ArrayType array && elementType.Equals(array.elementType) && rank == array.rank; } @@ -920,24 +918,24 @@ public override void WriteId(TextWriter trapFile, bool inContext) { elementType.GetId(trapFile, inContext); trapFile.Write('['); - for (int i = 1; i < rank; ++i) + for (var i = 1; i < rank; ++i) trapFile.Write(','); trapFile.Write(']'); } public override string Name => elementType.Name + "[]"; - public override Namespace Namespace => cx.SystemNamespace; + public override Namespace Namespace => Cx.SystemNamespace; - public override Type ContainingType => null; + public override Type? ContainingType => null; public override int ThisTypeParameters => elementType.ThisTypeParameters; public override CilTypeKind Kind => CilTypeKind.Array; - public override Type Construct(IEnumerable typeArguments) => cx.Populate(new ArrayType(cx, elementType.Construct(typeArguments))); + public override Type Construct(IEnumerable typeArguments) => Cx.Populate(new ArrayType(Cx, elementType.Construct(typeArguments))); - public override Type SourceDeclaration => cx.Populate(new ArrayType(cx, elementType.SourceDeclaration)); + public override Type SourceDeclaration => Cx.Populate(new ArrayType(Cx, elementType.SourceDeclaration)); public override IEnumerable Contents { @@ -959,22 +957,22 @@ public override IEnumerable Contents public override IEnumerable MethodParameters => throw new NotImplementedException(); } - interface ITypeParameter : IType + internal interface ITypeParameter : IType { } - abstract class TypeParameter : Type, ITypeParameter + internal abstract class TypeParameter : Type, ITypeParameter { protected readonly GenericContext gc; - public TypeParameter(GenericContext gc) : base(gc.cx) + protected TypeParameter(GenericContext gc) : base(gc.Cx) { this.gc = gc; } - public override Namespace Namespace => null; + public override Namespace? Namespace => null; - public override Type ContainingType => null; + public override Type? ContainingType => null; public override int ThisTypeParameters => 0; @@ -984,11 +982,11 @@ public TypeParameter(GenericContext gc) : base(gc.cx) public override Type Construct(IEnumerable typeArguments) => throw new InternalError("Attempt to construct a type parameter"); - public IEnumerable PopulateHandle(GenericContext gc, GenericParameterHandle parameterHandle) + public IEnumerable PopulateHandle(GenericParameterHandle parameterHandle) { if (!parameterHandle.IsNil) { - var tp = cx.mdReader.GetGenericParameter(parameterHandle); + var tp = Cx.MdReader.GetGenericParameter(parameterHandle); if (tp.Attributes.HasFlag(GenericParameterAttributes.Contravariant)) yield return Tuples.cil_typeparam_contravariant(this); @@ -1001,9 +999,9 @@ public IEnumerable PopulateHandle(GenericContext gc, Generic if (tp.Attributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)) yield return Tuples.cil_typeparam_struct(this); - foreach (var constraint in tp.GetConstraints().Select(h => cx.mdReader.GetGenericParameterConstraint(h))) + foreach (var constraint in tp.GetConstraints().Select(h => Cx.MdReader.GetGenericParameterConstraint(h))) { - var t = (Type)cx.CreateGeneric(this.gc, constraint.Type); + var t = (Type)Cx.CreateGeneric(this.gc, constraint.Type); yield return t; yield return Tuples.cil_typeparam_constraint(this, t); } @@ -1011,10 +1009,10 @@ public IEnumerable PopulateHandle(GenericContext gc, Generic } } - sealed class MethodTypeParameter : TypeParameter + internal sealed class MethodTypeParameter : TypeParameter { - readonly Method method; - readonly int index; + private readonly Method method; + private readonly int index; public override void WriteId(TextWriter trapFile, bool inContext) { @@ -1034,7 +1032,7 @@ public MethodTypeParameter(GenericContext gc, Method m, int index) : base(gc) this.index = index; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is MethodTypeParameter tp && method.Equals(tp.method) && index == tp.index; } @@ -1061,10 +1059,10 @@ public override IEnumerable Contents } - sealed class TypeTypeParameter : TypeParameter + internal sealed class TypeTypeParameter : TypeParameter { - readonly Type type; - readonly int index; + private readonly Type type; + private readonly int index; public TypeTypeParameter(GenericContext cx, Type t, int i) : base(cx) { @@ -1072,7 +1070,7 @@ public TypeTypeParameter(GenericContext cx, Type t, int i) : base(cx) type = t; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is TypeTypeParameter tp && type.Equals(tp.type) && index == tp.index; } @@ -1089,7 +1087,7 @@ public override void WriteId(TextWriter trapFile, bool inContext) trapFile.Write(index); } - public override TypeContainer Parent => type ?? gc as TypeContainer; + public override TypeContainer Parent => type; public override string Name => "!" + index; public override IEnumerable TypeParameters => Enumerable.Empty(); @@ -1106,20 +1104,20 @@ public override IEnumerable Contents } } - interface IPointerType : IType + internal interface IPointerType : IType { } - sealed class PointerType : Type, IPointerType + internal sealed class PointerType : Type, IPointerType { - readonly Type pointee; + private readonly Type pointee; public PointerType(Context cx, Type pointee) : base(cx) { this.pointee = pointee; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is PointerType pt && pointee.Equals(pt.pointee); } @@ -1138,9 +1136,9 @@ public override void WriteId(TextWriter trapFile, bool inContext) public override string Name => pointee.Name + "*"; - public override Namespace Namespace => pointee.Namespace; + public override Namespace? Namespace => pointee.Namespace; - public override Type ContainingType => pointee.ContainingType; + public override Type? ContainingType => pointee.ContainingType; public override TypeContainer Parent => pointee.Parent; @@ -1166,7 +1164,7 @@ public override IEnumerable Contents } } - sealed class ErrorType : Type + internal sealed class ErrorType : Type { public ErrorType(Context cx) : base(cx) { @@ -1178,9 +1176,9 @@ public ErrorType(Context cx) : base(cx) public override string Name => "!error"; - public override Namespace Namespace => cx.GlobalNamespace; + public override Namespace Namespace => Cx.GlobalNamespace; - public override Type ContainingType => null; + public override Type? ContainingType => null; public override int ThisTypeParameters => 0; @@ -1193,30 +1191,42 @@ public ErrorType(Context cx) : base(cx) public override Type Construct(IEnumerable typeArguments) => throw new NotImplementedException(); } - interface ITypeSignature + internal interface ITypeSignature { void WriteId(TextWriter trapFile, GenericContext gc); } public class SignatureDecoder : ISignatureTypeProvider { - struct Array : ITypeSignature + private struct Array : ITypeSignature { - public ITypeSignature elementType; - public ArrayShape shape; + private readonly ITypeSignature elementType; + private readonly ArrayShape shape; + + public Array(ITypeSignature elementType, ArrayShape shape) : this() + { + this.elementType = elementType; + this.shape = shape; + } + public void WriteId(TextWriter trapFile, GenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write('['); - for (int i=1; i signature; public void WriteId(TextWriter trapFile, GenericContext gc) { @@ -1236,25 +1245,31 @@ public void WriteId(TextWriter trapFile, GenericContext gc) } ITypeSignature IConstructedTypeProvider.GetArrayType(ITypeSignature elementType, ArrayShape shape) => - new Array { elementType = elementType, shape = shape }; + new Array(elementType, shape); ITypeSignature IConstructedTypeProvider.GetByReferenceType(ITypeSignature elementType) => - new ByRef { elementType = elementType }; + new ByRef(elementType); ITypeSignature ISignatureTypeProvider.GetFunctionPointerType(MethodSignature signature) => - new FnPtr { signature = signature }; + new FnPtr(); - class Instantiation : ITypeSignature + private class Instantiation : ITypeSignature { - public ITypeSignature genericType; - public ImmutableArray typeArguments; + private readonly ITypeSignature genericType; + private readonly ImmutableArray typeArguments; + + public Instantiation(ITypeSignature genericType, ImmutableArray typeArguments) + { + this.genericType = genericType; + this.typeArguments = typeArguments; + } public void WriteId(TextWriter trapFile, GenericContext gc) { genericType.WriteId(trapFile, gc); trapFile.Write('<'); - int index = 0; - foreach(var arg in typeArguments) + var index = 0; + foreach (var arg in typeArguments) { trapFile.WriteSeparator(",", ref index); arg.WriteId(trapFile, gc); @@ -1264,12 +1279,18 @@ public void WriteId(TextWriter trapFile, GenericContext gc) } ITypeSignature IConstructedTypeProvider.GetGenericInstantiation(ITypeSignature genericType, ImmutableArray typeArguments) => - new Instantiation { genericType = genericType, typeArguments = typeArguments }; + new Instantiation(genericType, typeArguments); - class GenericMethodParameter : ITypeSignature + private class GenericMethodParameter : ITypeSignature { - public object innerGc; - public int index; + private readonly object innerGc; + private readonly int index; + + public GenericMethodParameter(object innerGc, int index) + { + this.innerGc = innerGc; + this.index = index; + } public void WriteId(TextWriter trapFile, GenericContext outerGc) { @@ -1282,9 +1303,14 @@ public void WriteId(TextWriter trapFile, GenericContext outerGc) } } - class GenericTypeParameter : ITypeSignature + private class GenericTypeParameter : ITypeSignature { - public int index; + private readonly int index; + + public GenericTypeParameter(int index) + { + this.index = index; + } public void WriteId(TextWriter trapFile, GenericContext gc) { @@ -1294,16 +1320,19 @@ public void WriteId(TextWriter trapFile, GenericContext gc) } ITypeSignature ISignatureTypeProvider.GetGenericMethodParameter(object genericContext, int index) => - new GenericMethodParameter { innerGc = genericContext, index = index }; + new GenericMethodParameter(genericContext, index); ITypeSignature ISignatureTypeProvider.GetGenericTypeParameter(object genericContext, int index) => - new GenericTypeParameter { index = index }; + new GenericTypeParameter(index); - class Modified : ITypeSignature + private class Modified : ITypeSignature { - public ITypeSignature modifier; - public ITypeSignature unmodifiedType; - public bool isRequired; + private readonly ITypeSignature unmodifiedType; + + public Modified(ITypeSignature unmodifiedType) + { + this.unmodifiedType = unmodifiedType; + } public void WriteId(TextWriter trapFile, GenericContext gc) { @@ -1313,12 +1342,17 @@ public void WriteId(TextWriter trapFile, GenericContext gc) ITypeSignature ISignatureTypeProvider.GetModifiedType(ITypeSignature modifier, ITypeSignature unmodifiedType, bool isRequired) { - return new Modified { modifier = modifier, unmodifiedType = unmodifiedType, isRequired = isRequired }; + return new Modified(unmodifiedType); } - class Pinned : ITypeSignature + private class Pinned : ITypeSignature { - public ITypeSignature elementType; + private readonly ITypeSignature elementType; + + public Pinned(ITypeSignature elementType) + { + this.elementType = elementType; + } public void WriteId(TextWriter trapFile, GenericContext gc) { @@ -1329,12 +1363,17 @@ public void WriteId(TextWriter trapFile, GenericContext gc) ITypeSignature ISignatureTypeProvider.GetPinnedType(ITypeSignature elementType) { - return new Pinned { elementType = elementType }; + return new Pinned(elementType); } - class PointerType : ITypeSignature + private class PointerType : ITypeSignature { - public ITypeSignature elementType; + private readonly ITypeSignature elementType; + + public PointerType(ITypeSignature elementType) + { + this.elementType = elementType; + } public void WriteId(TextWriter trapFile, GenericContext gc) { @@ -1345,12 +1384,17 @@ public void WriteId(TextWriter trapFile, GenericContext gc) ITypeSignature IConstructedTypeProvider.GetPointerType(ITypeSignature elementType) { - return new PointerType { elementType = elementType }; + return new PointerType(elementType); } - class Primitive : ITypeSignature + private class Primitive : ITypeSignature { - public PrimitiveTypeCode typeCode; + private readonly PrimitiveTypeCode typeCode; + + public Primitive(PrimitiveTypeCode typeCode) + { + this.typeCode = typeCode; + } public void WriteId(TextWriter trapFile, GenericContext gc) { @@ -1360,12 +1404,18 @@ public void WriteId(TextWriter trapFile, GenericContext gc) ITypeSignature ISimpleTypeProvider.GetPrimitiveType(PrimitiveTypeCode typeCode) { - return new Primitive { typeCode = typeCode }; + return new Primitive(typeCode); } - class SzArrayType : ITypeSignature + private class SzArrayType : ITypeSignature { - public ITypeSignature elementType; + private readonly ITypeSignature elementType; + + public SzArrayType(ITypeSignature elementType) + { + this.elementType = elementType; + } + public void WriteId(TextWriter trapFile, GenericContext gc) { elementType.WriteId(trapFile, gc); @@ -1375,41 +1425,49 @@ public void WriteId(TextWriter trapFile, GenericContext gc) ITypeSignature ISZArrayTypeProvider.GetSZArrayType(ITypeSignature elementType) { - return new SzArrayType { elementType = elementType }; + return new SzArrayType(elementType); } - class TypeDefinition : ITypeSignature + private class TypeDefinition : ITypeSignature { - public TypeDefinitionHandle handle; - public byte rawTypeKind; - Type type; + private readonly TypeDefinitionHandle handle; + + public TypeDefinition(TypeDefinitionHandle handle) + { + this.handle = handle; + } + public void WriteId(TextWriter trapFile, GenericContext gc) { - type = (Type)gc.cx.Create(handle); + var type = (Type)gc.Cx.Create(handle); type.WriteId(trapFile); } } ITypeSignature ISimpleTypeProvider.GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { - return new TypeDefinition { handle = handle, rawTypeKind = rawTypeKind }; + return new TypeDefinition(handle); } - class TypeReference : ITypeSignature + private class TypeReference : ITypeSignature { - public TypeReferenceHandle handle; - public byte rawTypeKind; // struct/class (not used) - Type type; + private readonly TypeReferenceHandle handle; + + public TypeReference(TypeReferenceHandle handle) + { + this.handle = handle; + } + public void WriteId(TextWriter trapFile, GenericContext gc) { - type = (Type)gc.cx.Create(handle); + var type = (Type)gc.Cx.Create(handle); type.WriteId(trapFile); } } ITypeSignature ISimpleTypeProvider.GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { - return new TypeReference { handle = handle, rawTypeKind = rawTypeKind }; + return new TypeReference(handle); } ITypeSignature ISignatureTypeProvider.GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind) @@ -1425,7 +1483,7 @@ ITypeSignature ISignatureTypeProvider.GetTypeFromSpecifi /// public class TypeSignatureDecoder : ISignatureTypeProvider { - readonly Context cx; + private readonly Context cx; public TypeSignatureDecoder(Context cx) { @@ -1433,7 +1491,7 @@ public TypeSignatureDecoder(Context cx) } Type IConstructedTypeProvider.GetArrayType(Type elementType, ArrayShape shape) => - cx.Populate(new ArrayType(cx, elementType, shape)); + cx.Populate(new ArrayType(cx, elementType, shape.Rank)); Type IConstructedTypeProvider.GetByReferenceType(Type elementType) => elementType; // ?? @@ -1451,8 +1509,7 @@ Type ISignatureTypeProvider.GetGenericTypeParameter(Generi genericContext.GetGenericTypeParameter(index); Type ISignatureTypeProvider.GetModifiedType(Type modifier, Type unmodifiedType, bool isRequired) => - // !! Not implemented properly - unmodifiedType; + unmodifiedType; // !! Not implemented properly Type ISignatureTypeProvider.GetPinnedType(Type elementType) => cx.Populate(new PointerType(cx, elementType)); diff --git a/csharp/extractor/Semmle.Extraction.CIL/ExtractionProduct.cs b/csharp/extractor/Semmle.Extraction.CIL/ExtractionProduct.cs index f0e85057903b..52415af93493 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/ExtractionProduct.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/ExtractionProduct.cs @@ -63,12 +63,12 @@ public virtual void Extract(Context cx2) cx2.Extract(this); } - public readonly Context cx; + public Context Cx { get; } protected UnlabelledEntity(Context cx) { - this.cx = cx; - cx.cx.AddFreshLabel(this); + this.Cx = cx; + cx.Cx.AddFreshLabel(this); } TrapStackBehaviour IEntity.TrapStackBehaviour => TrapStackBehaviour.NoLabel; @@ -101,20 +101,18 @@ public void Extract(Context cx2) cx2.Populate(this); } - public readonly Context cx; + public Context Cx { get; } protected LabelledEntity(Context cx) { - this.cx = cx; + this.Cx = cx; } public override string ToString() { - using (var writer = new StringWriter()) - { - WriteQuotedId(writer); - return writer.ToString(); - } + using var writer = new StringWriter(); + WriteQuotedId(writer); + return writer.ToString(); } TrapStackBehaviour IEntity.TrapStackBehaviour => TrapStackBehaviour.NoLabel; @@ -123,9 +121,9 @@ public override string ToString() /// /// A tuple that is an extraction product. /// - class Tuple : IExtractionProduct + internal class Tuple : IExtractionProduct { - readonly Extraction.Tuple tuple; + private readonly Extraction.Tuple tuple; public Tuple(string name, params object[] args) { @@ -134,7 +132,7 @@ public Tuple(string name, params object[] args) public void Extract(Context cx) { - cx.cx.Emit(tuple); + cx.Cx.Emit(tuple); } public override string ToString() => tuple.ToString(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs index f522521a8456..dd09727f3c0d 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs @@ -9,9 +9,9 @@ namespace Semmle.Extraction.CIL /// /// Provides methods for creating and caching various entities. /// - public partial class Context + public sealed partial class Context { - readonly Dictionary ids = new Dictionary(); + private readonly Dictionary ids = new Dictionary(); public T Populate(T e) where T : IExtractedEntity { @@ -27,28 +27,26 @@ public T Populate(T e) where T : IExtractedEntity } else { - e.Label = cx.GetNewLabel(); - cx.DefineLabel(e, cx.TrapWriter.Writer, cx.Extractor); + e.Label = Cx.GetNewLabel(); + Cx.DefineLabel(e, Cx.TrapWriter.Writer, Cx.Extractor); ids.Add(e, e.Label); - cx.PopulateLater(() => + Cx.PopulateLater(() => { foreach (var c in e.Contents) c.Extract(this); }); #if DEBUG_LABELS - using (var writer = new StringWriter()) + using var writer = new StringWriter(); + e.WriteId(writer); + var id = writer.ToString(); + + if (debugLabels.TryGetValue(id, out var previousEntity)) + { + Cx.Extractor.Message(new Message("Duplicate trap ID", id, null, severity: Util.Logging.Severity.Warning)); + } + else { - e.WriteId(writer); - var id = writer.ToString(); - - if (debugLabels.TryGetValue(id, out IExtractedEntity previousEntity)) - { - cx.Extractor.Message(new Message("Duplicate trap ID", id, null, severity: Util.Logging.Severity.Warning)); - } - else - { - debugLabels.Add(id, e); - } + debugLabels.Add(id, e); } #endif } @@ -70,13 +68,15 @@ public IExtractedEntity Create(Handle h) public PrimitiveType Create(PrimitiveTypeCode code) { - PrimitiveType e = primitiveTypes[(int)code]; + var e = primitiveTypes[(int)code]; if (e is null) { - e = new PrimitiveType(this, code); - e.Label = cx.GetNewLabel(); - cx.DefineLabel(e, cx.TrapWriter.Writer, cx.Extractor); + e = new PrimitiveType(this, code) + { + Label = Cx.GetNewLabel() + }; + Cx.DefineLabel(e, Cx.TrapWriter.Writer, Cx.Extractor); primitiveTypes[(int)code] = e; } @@ -97,9 +97,9 @@ public PrimitiveType Create(PrimitiveTypeCode code) /// public IExtractedEntity CreateGeneric(GenericContext genericContext, Handle h) => genericHandleFactory[genericContext, h]; - readonly GenericContext defaultGenericContext; + private readonly GenericContext defaultGenericContext; - IExtractedEntity CreateGenericHandle(GenericContext gc, Handle handle) + private IExtractedEntity CreateGenericHandle(GenericContext gc, Handle handle) { IExtractedEntity entity; switch (handle.Kind) @@ -114,7 +114,7 @@ IExtractedEntity CreateGenericHandle(GenericContext gc, Handle handle) entity = new MethodSpecificationMethod(gc, (MethodSpecificationHandle)handle); break; case HandleKind.FieldDefinition: - entity = new DefinitionField(gc, (FieldDefinitionHandle)handle); + entity = new DefinitionField(gc.Cx, (FieldDefinitionHandle)handle); break; case HandleKind.TypeReference: var tr = new TypeReferenceType(this, (TypeReferenceHandle)handle); @@ -136,9 +136,9 @@ IExtractedEntity CreateGenericHandle(GenericContext gc, Handle handle) return entity; } - IExtractedEntity Create(GenericContext gc, MemberReferenceHandle handle) + private IExtractedEntity Create(GenericContext gc, MemberReferenceHandle handle) { - var mr = mdReader.GetMemberReference(handle); + var mr = MdReader.GetMemberReference(handle); switch (mr.GetKind()) { case MemberReferenceKind.Method: @@ -155,15 +155,15 @@ IExtractedEntity Create(GenericContext gc, MemberReferenceHandle handle) /// /// The string handle. /// The string. - public string GetString(StringHandle h) => mdReader.GetString(h); + public string GetString(StringHandle h) => MdReader.GetString(h); #region Namespaces - readonly CachedFunction namespaceFactory; + private readonly CachedFunction namespaceFactory; public Namespace CreateNamespace(StringHandle fqn) => namespaceFactory[fqn]; - readonly Lazy globalNamespace, systemNamespace; + private readonly Lazy globalNamespace, systemNamespace; /// /// The entity representing the global namespace. @@ -180,9 +180,9 @@ IExtractedEntity Create(GenericContext gc, MemberReferenceHandle handle) /// /// The fully-qualified namespace name. /// The namespace entity. - Namespace CreateNamespace(string fqn) => Populate(new Namespace(this, fqn)); + private Namespace CreateNamespace(string fqn) => Populate(new Namespace(this, fqn)); - readonly CachedFunction namespaceDefinitionFactory; + private readonly CachedFunction namespaceDefinitionFactory; /// /// Creates a namespace from a namespace handle. @@ -191,18 +191,19 @@ IExtractedEntity Create(GenericContext gc, MemberReferenceHandle handle) /// The namespace entity. public Namespace Create(NamespaceDefinitionHandle handle) => namespaceDefinitionFactory[handle]; - Namespace CreateNamespace(NamespaceDefinitionHandle handle) + private Namespace CreateNamespace(NamespaceDefinitionHandle handle) { - if (handle.IsNil) return GlobalNamespace; - NamespaceDefinition nd = mdReader.GetNamespaceDefinition(handle); + if (handle.IsNil) + return GlobalNamespace; + var nd = MdReader.GetNamespaceDefinition(handle); return Populate(new Namespace(this, GetString(nd.Name), Create(nd.Parent))); } #endregion #region Locations - readonly CachedFunction sourceFiles; - readonly CachedFunction folders; - readonly CachedFunction sourceLocations; + private readonly CachedFunction sourceFiles; + private readonly CachedFunction folders; + private readonly CachedFunction sourceLocations; /// /// Creates a source file entity from a PDB source file. @@ -216,7 +217,7 @@ Namespace CreateNamespace(NamespaceDefinitionHandle handle) /// /// The path of the folder. /// A folder entity. - public Folder CreateFolder(string path) => folders[path]; + public Folder CreateFolder(PathTransformer.ITransformedPath path) => folders[path]; /// /// Creates a source location. @@ -227,7 +228,7 @@ Namespace CreateNamespace(NamespaceDefinitionHandle handle) #endregion - readonly CachedFunction genericHandleFactory; + private readonly CachedFunction genericHandleFactory; /// /// Gets the short name of a member, without the preceding interface qualifier. @@ -236,9 +237,11 @@ Namespace CreateNamespace(NamespaceDefinitionHandle handle) /// The short name. public string ShortName(StringHandle handle) { - string str = mdReader.GetString(handle); - if (str.EndsWith(".ctor")) return ".ctor"; - if (str.EndsWith(".cctor")) return ".cctor"; + var str = MdReader.GetString(handle); + if (str.EndsWith(".ctor")) + return ".ctor"; + if (str.EndsWith(".cctor")) + return ".cctor"; var dot = str.LastIndexOf('.'); return dot == -1 ? str : str.Substring(dot + 1); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs b/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs index 33e8460090d8..2384bb3e2e13 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs @@ -13,9 +13,9 @@ namespace Semmle.Extraction.PDB /// /// PDB information can be in a separate PDB file, or embedded in the DLL. /// - class MetadataPdbReader : IPdb + internal sealed class MetadataPdbReader : IPdb { - class SourceFile : ISourceFile + private class SourceFile : ISourceFile { public SourceFile(MetadataReader reader, DocumentHandle handle) { @@ -25,13 +25,13 @@ public SourceFile(MetadataReader reader, DocumentHandle handle) public string Path { get; private set; } - public string Contents => File.Exists(Path) ? File.ReadAllText(Path, System.Text.Encoding.Default) : null; + public string? Contents => File.Exists(Path) ? File.ReadAllText(Path, System.Text.Encoding.Default) : null; } // Turns out to be very important to keep the MetadataReaderProvider live // or the reader will crash. - readonly MetadataReaderProvider provider; - readonly MetadataReader reader; + private readonly MetadataReaderProvider provider; + private readonly MetadataReader reader; public MetadataPdbReader(MetadataReaderProvider provider) { @@ -41,34 +41,40 @@ public MetadataPdbReader(MetadataReaderProvider provider) public IEnumerable SourceFiles => reader.Documents.Select(handle => new SourceFile(reader, handle)); - public IMethod GetMethod(MethodDebugInformationHandle handle) + public IMethod? GetMethod(MethodDebugInformationHandle handle) { var debugInfo = reader.GetMethodDebugInformation(handle); - var sequencePoints = debugInfo.GetSequencePoints(). - Where(p => !p.Document.IsNil && !p.IsHidden). - Select(p => new SequencePoint(p.Offset, new Location(new SourceFile(reader, p.Document), p.StartLine, p.StartColumn, p.EndLine, p.EndColumn))). - Where(p => p.Location.File.Path != null). - ToArray(); + var sequencePoints = debugInfo.GetSequencePoints() + .Where(p => !p.Document.IsNil && !p.IsHidden) + .Select(p => new SequencePoint(p.Offset, new Location( + new SourceFile(reader, p.Document), p.StartLine, p.StartColumn, p.EndLine, p.EndColumn))) + .Where(p => p.Location.File.Path != null) + .ToArray(); - return sequencePoints.Any() ? new Method() { SequencePoints = sequencePoints } : null; + return sequencePoints.Any() ? new Method(sequencePoints) : null; } - public static MetadataPdbReader CreateFromAssembly(string assemblyPath, PEReader peReader) + public static MetadataPdbReader? CreateFromAssembly(string assemblyPath, PEReader peReader) { - foreach (var provider in peReader. - ReadDebugDirectory(). - Where(d => d.Type == DebugDirectoryEntryType.EmbeddedPortablePdb). - Select(dirEntry => peReader.ReadEmbeddedPortablePdbDebugDirectoryData(dirEntry))) + var provider = peReader + .ReadDebugDirectory() + .Where(d => d.Type == DebugDirectoryEntryType.EmbeddedPortablePdb) + .Select(dirEntry => peReader.ReadEmbeddedPortablePdbDebugDirectoryData(dirEntry)) + .FirstOrDefault(); + + if (provider is object) { return new MetadataPdbReader(provider); } try { - MetadataReaderProvider provider; - string pdbPath; - if (peReader.TryOpenAssociatedPortablePdb(assemblyPath, s => new FileStream(s, FileMode.Open, FileAccess.Read, FileShare.Read), out provider, out pdbPath)) + if (peReader.TryOpenAssociatedPortablePdb( + assemblyPath, + s => new FileStream(s, FileMode.Open, FileAccess.Read, FileShare.Read), + out provider, + out _)) { return new MetadataPdbReader(provider); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs b/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs index 0f25b281aab4..a9a504596219 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs @@ -14,34 +14,33 @@ namespace Semmle.Extraction.PDB /// A PDB reader using Microsoft.DiaSymReader.Native. /// This is an unmanaged Windows DLL, which therefore only works on Windows. /// - class NativePdbReader : IPdb + internal sealed class NativePdbReader : IPdb { - sealed class Document : ISourceFile + private sealed class Document : ISourceFile { - readonly ISymUnmanagedDocument document; + private readonly ISymUnmanagedDocument document; public Document(ISymUnmanagedDocument doc) { document = doc; - contents = new Lazy(() => + contents = new Lazy(() => { - bool isEmbedded; - if (document.HasEmbeddedSource(out isEmbedded) == 0 && isEmbedded) + if (document.HasEmbeddedSource(out var isEmbedded) == 0 && isEmbedded) { var rawContents = document.GetEmbeddedSource().ToArray(); return System.Text.Encoding.Default.GetString(rawContents); } - else - { - return File.Exists(Path) ? File.ReadAllText(Path) : null; - } + + return File.Exists(Path) + ? File.ReadAllText(Path) + : null; + }); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - var otherDoc = obj as Document; - return otherDoc != null && Path.Equals(otherDoc.Path); + return obj is Document otherDoc && Path.Equals(otherDoc.Path); } public override int GetHashCode() => Path.GetHashCode(); @@ -50,44 +49,44 @@ public override bool Equals(object obj) public override string ToString() => Path; - readonly Lazy contents; + private readonly Lazy contents; - public string Contents => contents.Value; + public string? Contents => contents.Value; } public IEnumerable SourceFiles => reader.GetDocuments().Select(d => new Document(d)); - public IMethod GetMethod(MethodDebugInformationHandle h) + public IMethod? GetMethod(MethodDebugInformationHandle h) { - int methodToken = MetadataTokens.GetToken(h.ToDefinitionHandle()); + var methodToken = MetadataTokens.GetToken(h.ToDefinitionHandle()); var method = reader.GetMethod(methodToken); if (method != null) { - int count; - if (method.GetSequencePointCount(out count) != 0 || count == 0) + if (method.GetSequencePointCount(out var count) != 0 || count == 0) return null; - var s = method.GetSequencePoints(). - Where(sp => !sp.IsHidden). - Select(sp => new SequencePoint(sp.Offset, new Location(new Document(sp.Document), sp.StartLine, sp.StartColumn, sp.EndLine, sp.EndColumn))). - ToArray(); + var s = method.GetSequencePoints() + .Where(sp => !sp.IsHidden) + .Select(sp => new SequencePoint(sp.Offset, new Location( + new Document(sp.Document), sp.StartLine, sp.StartColumn, sp.EndLine, sp.EndColumn))) + .ToArray(); - return s.Any() ? new Method { SequencePoints = s } : null; + return s.Any() ? new Method(s) : null; } return null; } - NativePdbReader(string path) + private NativePdbReader(string path) { pdbStream = new FileStream(path, FileMode.Open); var metadataProvider = new MdProvider(); reader = SymUnmanagedReaderFactory.CreateReader(pdbStream, metadataProvider); } - readonly ISymUnmanagedReader5 reader; - readonly FileStream pdbStream; + private readonly ISymUnmanagedReader5 reader; + private readonly FileStream pdbStream; - public static NativePdbReader CreateFromAssembly(string assemblyPath, PEReader peReader) + public static NativePdbReader? CreateFromAssembly(PEReader peReader) { // The Native PDB reader uses an unmanaged Windows DLL // so only works on Windows. @@ -96,11 +95,13 @@ public static NativePdbReader CreateFromAssembly(string assemblyPath, PEReader p var debugDirectory = peReader.ReadDebugDirectory(); - foreach (var path in debugDirectory. - Where(d => d.Type == DebugDirectoryEntryType.CodeView). - Select(peReader.ReadCodeViewDebugDirectoryData). - Select(cv => cv.Path). - Where(path => File.Exists(path))) + var path = debugDirectory + .Where(d => d.Type == DebugDirectoryEntryType.CodeView) + .Select(peReader.ReadCodeViewDebugDirectoryData) + .Select(cv => cv.Path) + .FirstOrDefault(File.Exists); + + if (path is object) { return new NativePdbReader(path); } @@ -117,13 +118,13 @@ public void Dispose() /// /// This is not used but is seemingly needed in order to use DiaSymReader. /// - class MdProvider : ISymReaderMetadataProvider + internal class MdProvider : ISymReaderMetadataProvider { public MdProvider() { } - public object GetMetadataImport() => null; + public object? GetMetadataImport() => null; public unsafe bool TryGetStandaloneSignature(int standaloneSignatureToken, out byte* signature, out int length) => throw new NotImplementedException(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/PDB/PdbReader.cs b/csharp/extractor/Semmle.Extraction.CIL/PDB/PdbReader.cs index c2f4f94f59f1..cfdddca8de8e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/PDB/PdbReader.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/PDB/PdbReader.cs @@ -1,5 +1,4 @@ -īģŋusing Microsoft.DiaSymReader; -using System; +īģŋusing System; using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; @@ -16,12 +15,12 @@ public struct SequencePoint /// /// The byte-offset of the instruction. /// - public readonly int Offset; + public int Offset { get; } /// /// The source location of the instruction. /// - public readonly Location Location; + public Location Location { get; } public override string ToString() { @@ -43,23 +42,36 @@ public sealed class Location /// /// The file containing the code. /// - public readonly ISourceFile File; + public ISourceFile File { get; } /// - /// The span of text within the text file. + /// The start line of text within the source file. /// - public readonly int StartLine, StartColumn, EndLine, EndColumn; + public int StartLine { get; } + + /// + /// The start column of text within the source file. + /// + public int StartColumn { get; } + + /// + /// The end line of text within the source file. + /// + public int EndLine { get; } + + /// + /// The end column of text within the source file. + /// + public int EndColumn { get; } public override string ToString() { return string.Format("({0},{1})-({2},{3})", StartLine, StartColumn, EndLine, EndColumn); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - var otherLocation = obj as Location; - - return otherLocation != null && + return obj is Location otherLocation && File.Equals(otherLocation.File) && StartLine == otherLocation.StartLine && StartColumn == otherLocation.StartColumn && @@ -89,9 +101,14 @@ public interface IMethod Location Location { get; } } - class Method : IMethod + internal class Method : IMethod { - public IEnumerable SequencePoints { get; set; } + public IEnumerable SequencePoints { get; } + + public Method(IEnumerable sequencePoints) + { + SequencePoints = sequencePoints; + } public Location Location => SequencePoints.First().Location; } @@ -111,7 +128,7 @@ public interface ISourceFile /// null if the contents are unavailable. /// E.g. if the PDB file exists but the corresponding source files are missing. /// - string Contents { get; } + string? Contents { get; } } /// @@ -131,21 +148,21 @@ public interface IPdb : IDisposable /// /// The handle to query. /// The method information, or null if the method does not have debug information. - IMethod GetMethod(MethodDebugInformationHandle methodHandle); + IMethod? GetMethod(MethodDebugInformationHandle methodHandle); } - class PdbReader + internal class PdbReader { /// /// Returns the PDB information associated with an assembly. /// /// The path to the assembly. - /// The PE reader for the assembky. + /// The PE reader for the assembly. /// A PdbReader, or null if no PDB information is available. - public static IPdb Create(string assemblyPath, PEReader peReader) + public static IPdb? Create(string assemblyPath, PEReader peReader) { - return (IPdb)MetadataPdbReader.CreateFromAssembly(assemblyPath, peReader) ?? - NativePdbReader.CreateFromAssembly(assemblyPath, peReader); + return (IPdb?)MetadataPdbReader.CreateFromAssembly(assemblyPath, peReader) ?? + NativePdbReader.CreateFromAssembly(peReader); } } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj b/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj index 9db880787b0a..eb90b909b81c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj +++ b/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj @@ -1,12 +1,13 @@ īģŋ - netcoreapp3.0 + netcoreapp3.1 Semmle.Extraction.CIL Semmle.Extraction.CIL false true win-x64;linux-x64;osx-x64 + enable diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs index e2892678041f..5806bd375b1d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs @@ -5,7 +5,7 @@ namespace Semmle.Extraction.CSharp /// public class Driver { - static int Main(string[] args) + public static int Main(string[] args) { return (int)Extractor.Run(args); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Semmle.Extraction.CSharp.Driver.csproj b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Semmle.Extraction.CSharp.Driver.csproj index 7475350f9931..3ec25c498fed 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Semmle.Extraction.CSharp.Driver.csproj +++ b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Semmle.Extraction.CSharp.Driver.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 Semmle.Extraction.CSharp.Driver Semmle.Extraction.CSharp.Driver false diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs index f93911b8a383..597ff6f2165d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs @@ -10,7 +10,7 @@ namespace Semmle.BuildAnalyser /// Searches for assembly DLLs, indexes them and provides /// a lookup facility from assembly ID to filename. /// - class AssemblyCache + internal class AssemblyCache { /// /// Locate all reference files and index them. @@ -33,57 +33,57 @@ public AssemblyCache(IEnumerable dirs, IProgressMonitor progress) /// (Indexing is performed at a later stage by IndexReferences()). /// /// The directory to index. - /// The number of DLLs within this directory. - int AddReferenceDirectory(string dir) + private void AddReferenceDirectory(string dir) { - int count = 0; foreach (var dll in new DirectoryInfo(dir).EnumerateFiles("*.dll", SearchOption.AllDirectories)) { - dlls.Add(dll.FullName); - ++count; + pendingDllsToIndex.Enqueue(dll.FullName); } - return count; } /// /// Indexes all DLLs we have located. /// Because this is a potentially time-consuming operation, it is put into a separate stage. /// - void IndexReferences() + private void IndexReferences() { // Read all of the files - foreach (var filename in dlls) + foreach (var filename in pendingDllsToIndex) { - var info = AssemblyInfo.ReadFromFile(filename); - - if (info.Valid) - { - assemblyInfo[filename] = info; - } - else - { - failedDlls.Add(filename); - } + IndexReference(filename); } // Index "assemblyInfo" by version string // The OrderBy is used to ensure that we by default select the highest version number. - foreach (var info in assemblyInfo.Values.OrderBy(info => info.Id)) + foreach (var info in assemblyInfoByFileName.Values.OrderBy(info => info.Id)) { foreach (var index in info.IndexStrings) - references[index] = info; + assemblyInfoById[index] = info; + } + } + + private void IndexReference(string filename) + { + try + { + var info = AssemblyInfo.ReadFromFile(filename); + assemblyInfoByFileName[filename] = info; + } + catch (AssemblyLoadException) + { + failedAssemblyInfoFileNames.Add(filename); } } /// /// The number of DLLs which are assemblies. /// - public int AssemblyCount => assemblyInfo.Count; + public int AssemblyCount => assemblyInfoByFileName.Count; /// /// The number of DLLs which weren't assemblies. (E.g. C++). /// - public int NonAssemblyCount => failedDlls.Count; + public int NonAssemblyCount => failedAssemblyInfoFileNames.Count; /// /// Given an assembly id, determine its full info. @@ -93,70 +93,67 @@ void IndexReferences() public AssemblyInfo ResolveReference(string id) { // Fast path if we've already seen this before. - if (failedReferences.Contains(id)) - return AssemblyInfo.Invalid; + if (failedAssemblyInfoIds.Contains(id)) + throw new AssemblyLoadException(); - var query = AssemblyInfo.MakeFromId(id); - id = query.Id; // Sanitise the id. + string assemblyName; + (id, assemblyName) = AssemblyInfo.ComputeSanitizedAssemblyInfo(id); // Look up the id in our references map. - AssemblyInfo result; - if (references.TryGetValue(id, out result)) + if (assemblyInfoById.TryGetValue(id, out var result)) { // The string is in the references map. return result; } - else - { - // Attempt to load the reference from the GAC. - try - { - var loadedAssembly = System.Reflection.Assembly.ReflectionOnlyLoad(id); - - if (loadedAssembly != null) - { - // The assembly was somewhere we haven't indexed before. - // Add this assembly to our index so that subsequent lookups are faster. - - result = AssemblyInfo.MakeFromAssembly(loadedAssembly); - references[id] = result; - assemblyInfo[loadedAssembly.Location] = result; - return result; - } - } - catch (FileNotFoundException) - { - // A suitable assembly could not be found - } - catch (FileLoadException) - { - // The assembly cannot be loaded for some reason - // e.g. The name is malformed. - } - catch (PlatformNotSupportedException) - { - // .NET Core does not have a GAC. - } - // Fallback position - locate the assembly by its lower-case name only. - var asmName = query.Name.ToLowerInvariant(); + // Attempt to load the reference from the GAC. + try + { + var loadedAssembly = System.Reflection.Assembly.ReflectionOnlyLoad(id); - if (references.TryGetValue(asmName, out result)) + if (loadedAssembly != null) { - references[asmName] = result; // Speed up the next time the same string is resolved + // The assembly was somewhere we haven't indexed before. + // Add this assembly to our index so that subsequent lookups are faster. + + result = AssemblyInfo.MakeFromAssembly(loadedAssembly); + assemblyInfoById[id] = result; + assemblyInfoByFileName[loadedAssembly.Location] = result; return result; } + } + catch (FileNotFoundException) + { + // A suitable assembly could not be found + } + catch (FileLoadException) + { + // The assembly cannot be loaded for some reason + // e.g. The name is malformed. + } + catch (PlatformNotSupportedException) + { + // .NET Core does not have a GAC. + } - failedReferences.Add(id); // Fail early next time + // Fallback position - locate the assembly by its lower-case name only. + var asmName = assemblyName.ToLowerInvariant(); - return AssemblyInfo.Invalid; + if (assemblyInfoById.TryGetValue(asmName, out result)) + { + assemblyInfoById[asmName] = result; // Speed up the next time the same string is resolved + return result; } + + failedAssemblyInfoIds.Add(id); // Fail early next time + + throw new AssemblyLoadException(); } /// /// All the assemblies we have indexed. /// - public IEnumerable AllAssemblies => assemblyInfo.Select(a => a.Value); + public IEnumerable AllAssemblies => assemblyInfoByFileName.Select(a => a.Value); /// /// Retrieve the assembly info of a pre-cached assembly. @@ -165,32 +162,32 @@ public AssemblyInfo ResolveReference(string id) /// The assembly info. public AssemblyInfo GetAssemblyInfo(string filepath) { - if(assemblyInfo.TryGetValue(filepath, out var info)) + if (assemblyInfoByFileName.TryGetValue(filepath, out var info)) { return info; } - else + + IndexReference(filepath); + + if (assemblyInfoByFileName.TryGetValue(filepath, out info)) { - info = AssemblyInfo.ReadFromFile(filepath); - assemblyInfo.Add(filepath, info); return info; } + + throw new AssemblyLoadException(); } - // List of pending DLLs to index. - readonly List dlls = new List(); + private readonly Queue pendingDllsToIndex = new Queue(); - // Map from filename to assembly info. - readonly Dictionary assemblyInfo = new Dictionary(); + private readonly Dictionary assemblyInfoByFileName = new Dictionary(); // List of DLLs which are not assemblies. // We probably don't need to keep this - readonly List failedDlls = new List(); + private readonly List failedAssemblyInfoFileNames = new List(); // Map from assembly id (in various formats) to the full info. - readonly Dictionary references = new Dictionary(); + private readonly Dictionary assemblyInfoById = new Dictionary(); - // Set of failed assembly ids. - readonly HashSet failedReferences = new HashSet(); + private readonly HashSet failedAssemblyInfoIds = new HashSet(); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyInfo.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyInfo.cs index 29115d4a4499..15d29ed7b100 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyInfo.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyInfo.cs @@ -11,40 +11,35 @@ namespace Semmle.BuildAnalyser /// /// Stores information about an assembly file (DLL). /// - sealed class AssemblyInfo + internal sealed class AssemblyInfo { /// /// The file containing the assembly. /// - public string Filename { get; private set; } - - /// - /// Was the information correctly determined? - /// - public bool Valid { get; private set; } + public string Filename { get; } /// /// The short name of this assembly. /// - public string Name { get; private set; } + public string Name { get; } /// /// The version number of this assembly. /// - public System.Version Version { get; private set; } + public System.Version? Version { get; } /// /// The public key token of the assembly. /// - public string PublicKeyToken { get; private set; } + public string? PublicKeyToken { get; } /// /// The culture. /// - public string Culture { get; private set; } + public string? Culture { get; } /// - /// Get/parse a canonical ID of this assembly. + /// Gets the canonical ID of this assembly. /// public string Id { @@ -59,25 +54,6 @@ public string Id result = string.Format("{0}, PublicKeyToken={1}", result, PublicKeyToken); return result; } - - private set - { - var sections = value.Split(new string[] { ", " }, StringSplitOptions.None); - - Name = sections.First(); - - foreach (var section in sections.Skip(1)) - { - if (section.StartsWith("Version=")) - Version = new Version(section.Substring(8)); - else if (section.StartsWith("Culture=")) - Culture = section.Substring(8); - else if (section.StartsWith("PublicKeyToken=")) - PublicKeyToken = section.Substring(15); - // else: Some other field like processorArchitecture - ignore. - } - - } } public override string ToString() => Id; @@ -92,7 +68,8 @@ public IEnumerable IndexStrings yield return Id; if (Version != null) { - if (Culture != null) yield return string.Format("{0}, Version={1}, Culture={2}", Name, Version, Culture); + if (Culture != null) + yield return string.Format("{0}, Version={1}, Culture={2}", Name, Version, Culture); yield return string.Format("{0}, Version={1}", Name, Version); } yield return Name; @@ -100,27 +77,58 @@ public IEnumerable IndexStrings } } - /// - /// Get an invalid assembly info (Valid==false). - /// - public static AssemblyInfo Invalid { get; } = new AssemblyInfo(); + private AssemblyInfo(string id, string filename) + { + var sections = id.Split(new string[] { ", " }, StringSplitOptions.None); - private AssemblyInfo() { } + Name = sections.First(); + + foreach (var section in sections.Skip(1)) + { + if (section.StartsWith("Version=")) + Version = new Version(section.Substring(8)); + else if (section.StartsWith("Culture=")) + Culture = section.Substring(8); + else if (section.StartsWith("PublicKeyToken=")) + PublicKeyToken = section.Substring(15); + // else: Some other field like processorArchitecture - ignore. + } + + Filename = filename; + } + + private AssemblyInfo(string filename, string name, Version version, string culture, string publicKeyToken) + { + Filename = filename; + Name = name; + Version = version; + Culture = culture; + PublicKeyToken = publicKeyToken; + } /// /// Get AssemblyInfo from a loaded Assembly. /// /// The assembly. /// Info about the assembly. - public static AssemblyInfo MakeFromAssembly(Assembly assembly) => new AssemblyInfo() { Valid = true, Filename = assembly.Location, Id = assembly.FullName }; + public static AssemblyInfo MakeFromAssembly(Assembly assembly) + { + if (assembly.FullName is null) + { + throw new InvalidOperationException("Assembly with empty full name is not expected."); + } + + return new AssemblyInfo(assembly.FullName, assembly.Location); + } /// - /// Parse an assembly name/Id into an AssemblyInfo, - /// populating the available fields and leaving the others null. + /// Returns the id and name of the assembly that would be created from the received id. /// - /// The assembly name/Id. - /// The deconstructed assembly info. - public static AssemblyInfo MakeFromId(string id) => new AssemblyInfo() { Valid = true, Id = id }; + public static (string id, string name) ComputeSanitizedAssemblyInfo(string id) + { + var assembly = new AssemblyInfo(id, string.Empty); + return (assembly.Id, assembly.Name); + } /// /// Reads the assembly info from a file. @@ -131,48 +139,42 @@ private AssemblyInfo() { } /// The information about the assembly. public static AssemblyInfo ReadFromFile(string filename) { - var result = new AssemblyInfo() { Filename = filename }; try { /* This method is significantly faster and more lightweight than using * System.Reflection.Assembly.ReflectionOnlyLoadFrom. It also allows * loading the same assembly from different locations. */ - using (var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))) - using (var sha1 = new SHA1CryptoServiceProvider()) + using var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)); + using var sha1 = new SHA1CryptoServiceProvider(); + var metadata = pereader.GetMetadata(); + unsafe { - var metadata = pereader.GetMetadata(); - unsafe - { - var reader = new System.Reflection.Metadata.MetadataReader(metadata.Pointer, metadata.Length); - var def = reader.GetAssemblyDefinition(); - - // This is how you compute the public key token from the full public key. - // The last 8 bytes of the SHA1 of the public key. - var publicKey = reader.GetBlobBytes(def.PublicKey); - var publicKeyToken = sha1.ComputeHash(publicKey); - var publicKeyString = new StringBuilder(); - foreach (var b in publicKeyToken.Skip(12).Reverse()) - publicKeyString.AppendFormat("{0:x2}", b); - - result.Name = reader.GetString(def.Name); - result.Version = def.Version; - result.Culture = def.Culture.IsNil ? "neutral" : reader.GetString(def.Culture); - result.PublicKeyToken = publicKeyString.ToString(); - result.Valid = true; - } + var reader = new System.Reflection.Metadata.MetadataReader(metadata.Pointer, metadata.Length); + var def = reader.GetAssemblyDefinition(); + + // This is how you compute the public key token from the full public key. + // The last 8 bytes of the SHA1 of the public key. + var publicKey = reader.GetBlobBytes(def.PublicKey); + var publicKeyToken = sha1.ComputeHash(publicKey); + var publicKeyString = new StringBuilder(); + foreach (var b in publicKeyToken.Skip(12).Reverse()) + publicKeyString.AppendFormat("{0:x2}", b); + + var culture = def.Culture.IsNil ? "neutral" : reader.GetString(def.Culture); + return new AssemblyInfo(filename, reader.GetString(def.Name), def.Version, culture, publicKeyString.ToString()); } } catch (BadImageFormatException) { - // The DLL wasn't an assembly -> result.Valid = false. + // The DLL wasn't an assembly } catch (InvalidOperationException) { - // Some other failure -> result.Valid = false. + // Some other failure } - return result; + throw new AssemblyLoadException(); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyLoadException.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyLoadException.cs new file mode 100644 index 000000000000..cdb1380e291c --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyLoadException.cs @@ -0,0 +1,6 @@ +using System; + +namespace Semmle.BuildAnalyser +{ + public class AssemblyLoadException : Exception { } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs index 2894222ca891..bb779a313739 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs @@ -14,7 +14,7 @@ namespace Semmle.BuildAnalyser /// /// The output of a build analysis. /// - interface IBuildAnalysis + internal interface IBuildAnalysis { /// /// Full filepaths of external references. @@ -46,15 +46,13 @@ interface IBuildAnalysis /// /// Main implementation of the build analysis. /// - class BuildAnalysis : IBuildAnalysis, IDisposable + internal sealed class BuildAnalysis : IBuildAnalysis, IDisposable { private readonly AssemblyCache assemblyCache; - private readonly NugetPackages nuget; private readonly IProgressMonitor progressMonitor; private readonly IDictionary usedReferences = new ConcurrentDictionary(); private readonly IDictionary sources = new ConcurrentDictionary(); private readonly IDictionary unresolvedReferences = new ConcurrentDictionary(); - private readonly DirectoryInfo sourceDir; private int failedProjects, succeededProjects; private readonly string[] allSources; private int conflictedReferences = 0; @@ -69,26 +67,26 @@ public BuildAnalysis(Options options, IProgressMonitor progress) var startTime = DateTime.Now; progressMonitor = progress; - sourceDir = new DirectoryInfo(options.SrcDir); + var sourceDir = new DirectoryInfo(options.SrcDir); progressMonitor.FindingFiles(options.SrcDir); - allSources = sourceDir.GetFiles("*.cs", SearchOption.AllDirectories). - Select(d => d.FullName). - Where(d => !options.ExcludesFile(d)). - ToArray(); + allSources = sourceDir.GetFiles("*.cs", SearchOption.AllDirectories) + .Select(d => d.FullName) + .Where(d => !options.ExcludesFile(d)) + .ToArray(); var dllDirNames = options.DllDirs.Select(Path.GetFullPath).ToList(); - PackageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName)); + packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName)); if (options.UseNuGet) { try { - nuget = new NugetPackages(sourceDir.FullName, PackageDirectory); - ReadNugetFiles(); + var nuget = new NugetPackages(sourceDir.FullName, packageDirectory); + nuget.InstallPackages(progressMonitor); } - catch(FileNotFoundException) + catch (FileNotFoundException) { progressMonitor.MissingNuGet(); } @@ -97,7 +95,9 @@ public BuildAnalysis(Options options, IProgressMonitor progress) // Find DLLs in the .Net Framework if (options.ScanNetFrameworkDlls) { - dllDirNames.Add(Runtime.Runtimes.First()); + var runtimeLocation = Runtime.GetRuntime(options.UseSelfContainedDotnet); + progressMonitor.Log(Util.Logging.Severity.Debug, $"Runtime location selected: {runtimeLocation}"); + dllDirNames.Add(runtimeLocation); } // These files can sometimes prevent `dotnet restore` from working correctly. @@ -109,7 +109,7 @@ public BuildAnalysis(Options options, IProgressMonitor progress) sourceDir.GetFiles("*.sln", SearchOption.AllDirectories).Select(d => d.FullName); RestoreSolutions(solutions); - dllDirNames.Add(PackageDirectory.DirInfo.FullName); + dllDirNames.Add(packageDirectory.DirInfo.FullName); assemblyCache = new BuildAnalyser.AssemblyCache(dllDirNames, progress); AnalyseSolutions(solutions); @@ -171,14 +171,25 @@ private static string ComputeTempDirectory(string srcDir) /// If the same assembly name is duplicated with different versions, /// resolve to the higher version number. /// - void ResolveConflicts() + private void ResolveConflicts() { - var sortedReferences = usedReferences. - Select(r => assemblyCache.GetAssemblyInfo(r.Key)). - OrderBy(r => r.Version). - ToArray(); + var sortedReferences = new List(); + foreach (var usedReference in usedReferences) + { + try + { + var assemblyInfo = assemblyCache.GetAssemblyInfo(usedReference.Key); + sortedReferences.Add(assemblyInfo); + } + catch (AssemblyLoadException) + { + progressMonitor.Log(Util.Logging.Severity.Warning, $"Could not load assembly information from {usedReference.Key}"); + } + } - Dictionary finalAssemblyList = new Dictionary(); + sortedReferences = sortedReferences.OrderBy(r => r.Version).ToList(); + + var finalAssemblyList = new Dictionary(); // Pick the highest version for each assembly name foreach (var r in sortedReferences) @@ -201,20 +212,11 @@ void ResolveConflicts() } } - /// - /// Find and restore NuGet packages. - /// - void ReadNugetFiles() - { - nuget.FindPackages(); - nuget.InstallPackages(progressMonitor); - } - /// /// Store that a particular reference file is used. /// /// The filename of the reference. - void UseReference(string reference) + private void UseReference(string reference) { usedReferences[reference] = true; } @@ -223,7 +225,7 @@ void UseReference(string reference) /// Store that a particular source file is used (by a project file). /// /// The source file. - void UseSource(FileInfo sourceFile) + private void UseSource(FileInfo sourceFile) { sources[sourceFile.FullName] = sourceFile.Exists; } @@ -260,26 +262,26 @@ void UseSource(FileInfo sourceFile) /// /// The assembly ID. /// The project file making the reference. - void UnresolvedReference(string id, string projectFile) + private void UnresolvedReference(string id, string projectFile) { unresolvedReferences[id] = projectFile; } - readonly TemporaryDirectory PackageDirectory; + private readonly TemporaryDirectory packageDirectory; /// /// Reads all the source files and references from the given list of projects. /// /// The list of projects to analyse. - void AnalyseProjectFiles(IEnumerable projectFiles) + private void AnalyseProjectFiles(IEnumerable projectFiles) { foreach (var proj in projectFiles) AnalyseProject(proj); } - void AnalyseProject(FileInfo project) + private void AnalyseProject(FileInfo project) { - if(!project.Exists) + if (!project.Exists) { progressMonitor.MissingProject(project.FullName); return; @@ -291,14 +293,14 @@ void AnalyseProject(FileInfo project) foreach (var @ref in csProj.References) { - AssemblyInfo resolved = assemblyCache.ResolveReference(@ref); - if (!resolved.Valid) + try { - UnresolvedReference(@ref, project.FullName); + var resolved = assemblyCache.ResolveReference(@ref); + UseReference(resolved.Filename); } - else + catch (AssemblyLoadException) { - UseReference(resolved.Filename); + UnresolvedReference(@ref, project.FullName); } } @@ -320,10 +322,10 @@ void AnalyseProject(FileInfo project) } - void Restore(string projectOrSolution) + private void Restore(string projectOrSolution) { - int exit = DotNet.RestoreToDirectory(projectOrSolution, PackageDirectory.DirInfo.FullName); - switch(exit) + var exit = DotNet.RestoreToDirectory(projectOrSolution, packageDirectory.DirInfo.FullName); + switch (exit) { case 0: case 1: @@ -342,7 +344,7 @@ public void RestoreSolutions(IEnumerable solutions) public void AnalyseSolutions(IEnumerable solutions) { - Parallel.ForEach(solutions, new ParallelOptions { MaxDegreeOfParallelism = 4 } , solutionFile => + Parallel.ForEach(solutions, new ParallelOptions { MaxDegreeOfParallelism = 4 }, solutionFile => { try { @@ -359,7 +361,7 @@ public void AnalyseSolutions(IEnumerable solutions) public void Dispose() { - PackageDirectory?.Dispose(); + packageDirectory?.Dispose(); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/CsProjFile.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/CsProjFile.cs index 1083c9b6257c..dfbe8df2f06b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/CsProjFile.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/CsProjFile.cs @@ -1,4 +1,5 @@ -īģŋusing System.Collections.Generic; +īģŋusing System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; @@ -8,11 +9,11 @@ namespace Semmle.BuildAnalyser /// /// Represents a .csproj file and reads information from it. /// - class CsProjFile + internal class CsProjFile { private string Filename { get; } - private string Directory => Path.GetDirectoryName(Filename); + private string Directory { get; } /// /// Reads the .csproj file. @@ -22,20 +23,29 @@ public CsProjFile(FileInfo filename) { Filename = filename.FullName; + var directoryName = Path.GetDirectoryName(Filename); + + if (directoryName is null) + { + throw new Extraction.InternalError($"Directory of file '{Filename}' is null"); + } + + Directory = directoryName; + try { // This can fail if the .csproj is invalid or has // unrecognised content or is the wrong version. // This currently always fails on Linux because // Microsoft.Build is not cross platform. - ReadMsBuildProject(filename); + (csFiles, references) = ReadMsBuildProject(filename); } catch // lgtm[cs/catch-of-all-exceptions] { // There was some reason why the project couldn't be loaded. // Fall back to reading the Xml document directly. // This method however doesn't handle variable expansion. - ReadProjectFileAsXml(filename); + (csFiles, references) = ReadProjectFileAsXml(filename, Directory); } } @@ -45,21 +55,22 @@ public CsProjFile(FileInfo filename) /// and there seems to be no way to make it succeed. Fails on Linux. /// /// The file to read. - private void ReadMsBuildProject(FileInfo filename) + private static (string[] csFiles, string[] references) ReadMsBuildProject(FileInfo filename) { var msbuildProject = new Microsoft.Build.Execution.ProjectInstance(filename.FullName); - references = msbuildProject. - Items. - Where(item => item.ItemType == "Reference"). - Select(item => item.EvaluatedInclude). - ToArray(); + var references = msbuildProject.Items + .Where(item => item.ItemType == "Reference") + .Select(item => item.EvaluatedInclude) + .ToArray(); - csFiles = msbuildProject.Items + var csFiles = msbuildProject.Items .Where(item => item.ItemType == "Compile") .Select(item => item.GetMetadataValue("FullPath")) .Where(fn => fn.EndsWith(".cs")) .ToArray(); + + return (csFiles, references); } /// @@ -67,62 +78,56 @@ private void ReadMsBuildProject(FileInfo filename) /// This doesn't handle variables etc, and should only used as a /// fallback if ReadMsBuildProject() fails. /// - /// The .csproj file. - private void ReadProjectFileAsXml(FileInfo filename) + /// The .csproj file. + private static (string[] csFiles, string[] references) ReadProjectFileAsXml(FileInfo fileName, string directoryName) { var projFile = new XmlDocument(); var mgr = new XmlNamespaceManager(projFile.NameTable); mgr.AddNamespace("msbuild", "http://schemas.microsoft.com/developer/msbuild/2003"); - projFile.Load(filename.FullName); - var projDir = filename.Directory; + projFile.Load(fileName.FullName); + var projDir = fileName.Directory; var root = projFile.DocumentElement; // Figure out if it's dotnet core - bool netCoreProjectFile = root.GetAttribute("Sdk") == "Microsoft.NET.Sdk"; + var netCoreProjectFile = root.GetAttribute("Sdk") == "Microsoft.NET.Sdk"; if (netCoreProjectFile) { - var relativeCsIncludes = - root.SelectNodes("/Project/ItemGroup/Compile/@Include", mgr). - NodeList(). - Select(node => node.Value). - ToArray(); + var explicitCsFiles = root + .SelectNodes("/Project/ItemGroup/Compile/@Include", mgr) + .NodeList() + .Select(node => node.Value) + .Select(cs => Path.DirectorySeparatorChar == '/' ? cs.Replace("\\", "/") : cs) + .Select(f => Path.GetFullPath(Path.Combine(projDir.FullName, f))); - var explicitCsFiles = relativeCsIncludes. - Select(cs => Path.DirectorySeparatorChar == '/' ? cs.Replace("\\", "/") : cs). - Select(f => Path.GetFullPath(Path.Combine(projDir.FullName, f))); + var additionalCsFiles = System.IO.Directory.GetFiles(directoryName, "*.cs", SearchOption.AllDirectories); - var additionalCsFiles = System.IO.Directory.GetFiles(Directory, "*.cs", SearchOption.AllDirectories); + return (explicitCsFiles.Concat(additionalCsFiles).ToArray(), Array.Empty()); + } - csFiles = explicitCsFiles.Concat(additionalCsFiles).ToArray(); + var references = root + .SelectNodes("/msbuild:Project/msbuild:ItemGroup/msbuild:Reference/@Include", mgr) + .NodeList() + .Select(node => node.Value) + .ToArray(); - references = new string[0]; - } - else - { + var relativeCsIncludes = root + .SelectNodes("/msbuild:Project/msbuild:ItemGroup/msbuild:Compile/@Include", mgr) + .NodeList() + .Select(node => node.Value) + .ToArray(); - references = - root.SelectNodes("/msbuild:Project/msbuild:ItemGroup/msbuild:Reference/@Include", mgr). - NodeList(). - Select(node => node.Value). - ToArray(); - - var relativeCsIncludes = - root.SelectNodes("/msbuild:Project/msbuild:ItemGroup/msbuild:Compile/@Include", mgr). - NodeList(). - Select(node => node.Value). - ToArray(); - - csFiles = relativeCsIncludes. - Select(cs => Path.DirectorySeparatorChar == '/' ? cs.Replace("\\", "/") : cs). - Select(f => Path.GetFullPath(Path.Combine(projDir.FullName, f))). - ToArray(); - } + var csFiles = relativeCsIncludes + .Select(cs => Path.DirectorySeparatorChar == '/' ? cs.Replace("\\", "/") : cs) + .Select(f => Path.GetFullPath(Path.Combine(projDir.FullName, f))) + .ToArray(); + + return (csFiles, references); } - string[] references; - string[] csFiles; + private readonly string[] references; + private readonly string[] csFiles; /// /// The list of references as a list of assembly IDs. @@ -135,7 +140,7 @@ private void ReadProjectFileAsXml(FileInfo filename) public IEnumerable Sources => csFiles; } - static class XmlNodeHelper + internal static class XmlNodeHelper { /// /// Helper to convert an XmlNodeList into an IEnumerable. @@ -145,8 +150,7 @@ static class XmlNodeHelper /// A more useful data type. public static IEnumerable NodeList(this XmlNodeList list) { - foreach (var i in list) - yield return i as XmlNode; + return list.OfType(); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/DotNet.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/DotNet.cs index 6edd217af8dc..4045519d3e0d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/DotNet.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/DotNet.cs @@ -1,15 +1,11 @@ -īģŋusing System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; +īģŋusing System.Diagnostics; namespace Semmle.BuildAnalyser { /// /// Utilities to run the "dotnet" command. /// - static class DotNet + internal static class DotNet { public static int RestoreToDirectory(string projectOrSolutionFile, string packageDirectory) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs index 2ea3afb6c691..e29b1b4ac1ec 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs @@ -12,7 +12,7 @@ namespace Semmle.BuildAnalyser /// Locates packages in a source tree and downloads all of the /// referenced assemblies to a temp folder. /// - class NugetPackages + internal class NugetPackages { /// /// Create the package manager for a specified source tree. @@ -25,24 +25,22 @@ public NugetPackages(string sourceDir, TemporaryDirectory packageDirectory) // Expect nuget.exe to be in a `nuget` directory under the directory containing this exe. var currentAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location; - nugetExe = Path.Combine(Path.GetDirectoryName(currentAssembly), "nuget", "nuget.exe"); + var directory = Path.GetDirectoryName(currentAssembly); + if (directory is null) + throw new FileNotFoundException($"Directory path '{currentAssembly}' of current assembly is null"); + + nugetExe = Path.Combine(directory, "nuget", "nuget.exe"); if (!File.Exists(nugetExe)) throw new FileNotFoundException(string.Format("NuGet could not be found at {0}", nugetExe)); - } - /// - /// Locate all NuGet packages but don't download them yet. - /// - public void FindPackages() - { packages = new DirectoryInfo(SourceDirectory). EnumerateFiles("packages.config", SearchOption.AllDirectories). ToArray(); } // List of package files to download. - FileInfo[] packages; + private readonly FileInfo[] packages; /// /// The list of package files. @@ -82,7 +80,7 @@ public string SourceDirectory /// /// The package file. /// Where to log progress/errors. - void RestoreNugetPackage(string package, IProgressMonitor pm) + private void RestoreNugetPackage(string package, IProgressMonitor pm) { pm.NugetInstall(package); @@ -115,8 +113,8 @@ void RestoreNugetPackage(string package, IProgressMonitor pm) { using var p = Process.Start(pi); - string output = p.StandardOutput.ReadToEnd(); - string error = p.StandardError.ReadToEnd(); + var output = p.StandardOutput.ReadToEnd(); + var error = p.StandardError.ReadToEnd(); p.WaitForExit(); if (p.ExitCode != 0) @@ -131,6 +129,6 @@ void RestoreNugetPackage(string package, IProgressMonitor pm) } } - readonly string nugetExe; + private readonly string nugetExe; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Options.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Options.cs index 89cc18007f0e..fedcf909c0c8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Options.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Options.cs @@ -11,9 +11,9 @@ namespace Semmle.Extraction.CSharp.Standalone /// public sealed class Options : CommonOptions { - public override bool handleFlag(string key, bool value) + public override bool HandleFlag(string key, bool value) { - switch(key) + switch (key) { case "silent": Verbosity = value ? Verbosity.Off : Verbosity.Info; @@ -36,14 +36,17 @@ public override bool handleFlag(string key, bool value) case "skip-dotnet": ScanNetFrameworkDlls = !value; return true; + case "self-contained-dotnet": + UseSelfContainedDotnet = value; + return true; default: - return base.handleFlag(key, value); + return base.HandleFlag(key, value); } } - public override bool handleOption(string key, string value) + public override bool HandleOption(string key, string value) { - switch(key) + switch (key) { case "exclude": Excludes.Add(value); @@ -52,11 +55,11 @@ public override bool handleOption(string key, string value) DllDirs.Add(value); return true; default: - return base.handleOption(key, value); + return base.HandleOption(key, value); } } - public override bool handleArgument(string arg) + public override bool HandleArgument(string arg) { SolutionFile = arg; var fi = new FileInfo(SolutionFile); @@ -68,7 +71,7 @@ public override bool handleArgument(string arg) return true; } - public override void invalidArgument(string argument) + public override void InvalidArgument(string argument) { System.Console.WriteLine($"Error: Invalid argument {argument}"); Errors = true; @@ -77,62 +80,63 @@ public override void invalidArgument(string argument) /// /// Files/patterns to exclude. /// - public IList Excludes = new List(); + public IList Excludes { get; } = new List(); - /// - /// The number of concurrent threads to use. - /// - public int NumberOfThreads = Semmle.Extraction.Extractor.DefaultNumberOfThreads; /// /// The directory containing the source code; /// - public readonly string SrcDir = System.IO.Directory.GetCurrentDirectory(); + public string SrcDir { get; } = System.IO.Directory.GetCurrentDirectory(); /// /// Whether to analyse NuGet packages. /// - public bool UseNuGet = true; + public bool UseNuGet { get; private set; } = true; /// /// Directories to search DLLs in. /// - public IList DllDirs = new List(); + public IList DllDirs { get; } = new List(); /// /// Whether to search the .Net framework directory. /// - public bool ScanNetFrameworkDlls = true; + public bool ScanNetFrameworkDlls { get; private set; } = true; /// /// Whether to use mscorlib as a reference. /// - public bool UseMscorlib = true; + public bool UseMscorlib { get; private set; } = true; /// /// Whether to search .csproj files. /// - public bool AnalyseCsProjFiles = true; + public bool AnalyseCsProjFiles { get; private set; } = true; /// /// The solution file to analyse, or null if not specified. /// - public string SolutionFile; + public string? SolutionFile { get; private set; } /// /// Whether the extraction phase should be skipped (dry-run). /// - public bool SkipExtraction = false; + public bool SkipExtraction { get; private set; } = false; /// /// Whether errors were encountered parsing the arguments. /// - public bool Errors = false; + public bool Errors { get; private set; } = false; /// /// Whether to show help. /// - public bool Help = false; + public bool Help { get; private set; } = false; + + /// + /// Whether to use the packaged dotnet runtime. + /// + public bool UseSelfContainedDotnet { get; private set; } = false; /// /// Determine whether the given path should be excluded. @@ -147,7 +151,7 @@ public bool ExcludesFile(string path) /// /// Outputs the command line options to the console. /// - public void ShowHelp(System.IO.TextWriter output) + public static void ShowHelp(System.IO.TextWriter output) { output.WriteLine("C# standalone extractor\n\nExtracts a C# project in the current directory without performing a build.\n"); output.WriteLine("Additional options:\n"); @@ -162,6 +166,7 @@ public void ShowHelp(System.IO.TextWriter output) output.WriteLine(" --threads:nnn Specify number of threads (default=CPU cores)"); output.WriteLine(" --verbose Produce more output"); output.WriteLine(" --pdb Cross-reference information from PDBs where available"); + output.WriteLine(" --self-contained-dotnet Use the .Net Framework packaged with the extractor"); } private Options() diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs index 106771faef2f..dc67a7679e8a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Semmle.BuildAnalyser; using Semmle.Util.Logging; @@ -9,67 +8,39 @@ namespace Semmle.Extraction.CSharp.Standalone /// /// One independent run of the extractor. /// - class Extraction + internal class Extraction { public Extraction(string directory) { - this.directory = directory; + Directory = directory; } - public readonly string directory; - public readonly List Sources = new List(); + public string Directory { get; } + public List Sources { get; } = new List(); }; /// /// Searches for source/references and creates separate extractions. /// - class Analysis : IDisposable + internal sealed class Analysis : IDisposable { - readonly ILogger logger; - - public Analysis(ILogger logger) + public Analysis(ILogger logger, Options options) { - this.logger = logger; + var progressMonitor = new ProgressMonitor(logger); + buildAnalysis = new BuildAnalysis(options, progressMonitor); + References = buildAnalysis.ReferenceFiles; + Extraction = new Extraction(options.SrcDir); + Extraction.Sources.AddRange(options.SolutionFile == null ? buildAnalysis.AllSourceFiles : buildAnalysis.ProjectSourceFiles); } - // The extraction configuration for the entire project. - Extraction projectExtraction; - - public IEnumerable References - { - get; private set; - } + public IEnumerable References { get; } /// /// The extraction configuration. /// - public Extraction Extraction => projectExtraction; + public Extraction Extraction { get; } - /// - /// Creates an extraction for the current directory - /// and adds it to the list of all extractions. - /// - /// The directory of the extraction. - /// The extraction. - void CreateExtraction(string dir) - { - projectExtraction = new Extraction(dir); - } - - BuildAnalysis buildAnalysis; - - /// - /// Analyse projects/solution and resolves references. - /// - /// The build analysis options. - public void AnalyseProjects(Options options) - { - CreateExtraction(options.SrcDir); - var progressMonitor = new ProgressMonitor(logger); - buildAnalysis = new BuildAnalysis(options, progressMonitor); - References = buildAnalysis.ReferenceFiles; - projectExtraction.Sources.AddRange(options.SolutionFile == null ? buildAnalysis.AllSourceFiles : buildAnalysis.ProjectSourceFiles); - } + private readonly BuildAnalysis buildAnalysis; public void Dispose() { @@ -79,16 +50,15 @@ public void Dispose() public class Program { - static int Main(string[] args) + public static int Main(string[] args) { var options = Options.Create(args); // options.CIL = true; // To do: Enable this - var output = new ConsoleLogger(options.Verbosity); - using var a = new Analysis(output); + using var output = new ConsoleLogger(options.Verbosity); if (options.Help) { - options.ShowHelp(System.Console.Out); + Options.ShowHelp(System.Console.Out); return 0; } @@ -98,10 +68,10 @@ static int Main(string[] args) var start = DateTime.Now; output.Log(Severity.Info, "Running C# standalone extractor"); - a.AnalyseProjects(options); - int sourceFiles = a.Extraction.Sources.Count(); + using var a = new Analysis(output, options); + var sourceFileCount = a.Extraction.Sources.Count; - if (sourceFiles == 0) + if (sourceFileCount == 0) { output.Log(Severity.Error, "No source files found"); return 1; @@ -109,33 +79,39 @@ static int Main(string[] args) if (!options.SkipExtraction) { + using var fileLogger = new FileLogger(options.Verbosity, Extractor.GetCSharpLogPath()); + output.Log(Severity.Info, ""); output.Log(Severity.Info, "Extracting..."); Extractor.ExtractStandalone( a.Extraction.Sources, a.References, new ExtractionProgress(output), - new FileLogger(options.Verbosity, Extractor.GetCSharpLogPath()), + fileLogger, options); - output.Log(Severity.Info, $"Extraction completed in {DateTime.Now-start}"); + output.Log(Severity.Info, $"Extraction completed in {DateTime.Now - start}"); } return 0; } - class ExtractionProgress : IProgressMonitor + private class ExtractionProgress : IProgressMonitor { public ExtractionProgress(ILogger output) { logger = output; } - readonly ILogger logger; + private readonly ILogger logger; public void Analysed(int item, int total, string source, string output, TimeSpan time, AnalysisAction action) { logger.Log(Severity.Info, "[{0}/{1}] {2} ({3})", item, total, source, - action == AnalysisAction.Extracted ? time.ToString() : action == AnalysisAction.Excluded ? "excluded" : "up to date"); + action == AnalysisAction.Extracted + ? time.ToString() + : action == AnalysisAction.Excluded + ? "excluded" + : "up to date"); } public void MissingType(string type) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/ProgressMonitor.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/ProgressMonitor.cs index 5bbe1e3a5b28..5b1da9292512 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/ProgressMonitor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/ProgressMonitor.cs @@ -6,7 +6,7 @@ namespace Semmle.BuildAnalyser /// /// Callback for various events that may happen during the build analysis. /// - interface IProgressMonitor + internal interface IProgressMonitor { void FindingFiles(string dir); void UnresolvedReference(string id, string project); @@ -16,16 +16,16 @@ interface IProgressMonitor void NugetInstall(string package); void ResolvedReference(string filename); void Summary(int existingSources, int usedSources, int missingSources, int references, int unresolvedReferences, int resolvedConflicts, int totalProjects, int failedProjects, TimeSpan analysisTime); - void Warning(string message); + void Log(Severity severity, string message); void ResolvedConflict(string asm1, string asm2); void MissingProject(string projectFile); void CommandFailed(string exe, string arguments, int exitCode); void MissingNuGet(); } - class ProgressMonitor : IProgressMonitor + internal class ProgressMonitor : IProgressMonitor { - readonly ILogger logger; + private readonly ILogger logger; public ProgressMonitor(ILogger logger) { @@ -93,9 +93,9 @@ public void Summary(int existingSources, int usedSources, int missingSources, logger.Log(Severity.Info, "Build analysis completed in {0}", analysisTime); } - public void Warning(string message) + public void Log(Severity severity, string message) { - logger.Log(Severity.Warning, message); + logger.Log(severity, message); } public void ResolvedConflict(string asm1, string asm2) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs index 489d7f95140b..a922a3bf07a3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs @@ -10,14 +10,14 @@ namespace Semmle.Extraction.CSharp.Standalone /// /// Locates .NET Runtimes. /// - static class Runtime + internal static class Runtime { - static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory(); + private static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory(); /// /// Locates .NET Core Runtimes. /// - public static IEnumerable CoreRuntimes + private static IEnumerable CoreRuntimes { get { @@ -27,8 +27,12 @@ public static IEnumerable CoreRuntimes : new[] { "/usr/share/dotnet", @"C:\Program Files\dotnet" }; var coreDirs = dotnetDirs.Select(d => Path.Combine(d, "shared", "Microsoft.NETCore.App")); - foreach (var dir in coreDirs.Where(Directory.Exists)) + var dir = coreDirs.FirstOrDefault(Directory.Exists); + if (dir is object) + { return Directory.EnumerateDirectories(dir).OrderByDescending(Path.GetFileName); + } + return Enumerable.Empty(); } } @@ -37,7 +41,7 @@ public static IEnumerable CoreRuntimes /// Locates .NET Desktop Runtimes. /// This includes Mono and Microsoft.NET. /// - public static IEnumerable DesktopRuntimes + private static IEnumerable DesktopRuntimes { get { @@ -48,22 +52,29 @@ public static IEnumerable DesktopRuntimes if (Directory.Exists(@"C:\Windows\Microsoft.NET\Framework64")) { - return Directory.EnumerateDirectories(@"C:\Windows\Microsoft.NET\Framework64", "v*"). - OrderByDescending(Path.GetFileName); + return Directory.EnumerateDirectories(@"C:\Windows\Microsoft.NET\Framework64", "v*") + .OrderByDescending(Path.GetFileName); } - foreach (var dir in monoDirs.Where(Directory.Exists)) + var dir = monoDirs.FirstOrDefault(Directory.Exists); + + if (dir is object) { - return Directory.EnumerateDirectories(dir). - Where(d => Char.IsDigit(Path.GetFileName(d)[0])). - OrderByDescending(Path.GetFileName); + return Directory.EnumerateDirectories(dir) + .Where(d => Char.IsDigit(Path.GetFileName(d)[0])) + .OrderByDescending(Path.GetFileName); } return Enumerable.Empty(); } } - public static IEnumerable Runtimes + /// + /// Gets the .NET runtime location to use for extraction + /// + public static string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : Runtimes.First(); + + private static IEnumerable Runtimes { get { diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj index f9efd0d9ebbb..277310cc3413 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 Semmle.Extraction.CSharp.Standalone Semmle.Extraction.CSharp.Standalone false @@ -10,6 +10,7 @@ false win-x64;linux-x64;osx-x64 + enable diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/SolutionFile.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/SolutionFile.cs index b4551dd80243..8fbe706aa452 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/SolutionFile.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/SolutionFile.cs @@ -8,9 +8,9 @@ namespace Semmle.BuildAnalyser /// /// Access data in a .sln file. /// - class SolutionFile + internal class SolutionFile { - readonly Microsoft.Build.Construction.SolutionFile solutionFile; + private readonly Microsoft.Build.Construction.SolutionFile solutionFile; private string FullPath { get; } @@ -32,10 +32,10 @@ public IEnumerable MsBuildProjects { get { - return solutionFile.ProjectsInOrder. - Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat). - Select(p => p.AbsolutePath). - Select(p => Path.DirectorySeparatorChar == '/' ? p.Replace("\\", "/") : p); + return solutionFile.ProjectsInOrder + .Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat) + .Select(p => p.AbsolutePath) + .Select(p => Path.DirectorySeparatorChar == '/' ? p.Replace("\\", "/") : p); } } @@ -46,21 +46,26 @@ public IEnumerable NestedProjects { get { - return solutionFile.ProjectsInOrder. - Where(p => p.ProjectType == SolutionProjectType.SolutionFolder). - Where(p => Directory.Exists(p.AbsolutePath)). - SelectMany(p => new DirectoryInfo(p.AbsolutePath).EnumerateFiles("*.csproj", SearchOption.AllDirectories)). - Select(f => f.FullName); + return solutionFile.ProjectsInOrder + .Where(p => p.ProjectType == SolutionProjectType.SolutionFolder) + .Where(p => Directory.Exists(p.AbsolutePath)) + .SelectMany(p => new DirectoryInfo(p.AbsolutePath).EnumerateFiles("*.csproj", SearchOption.AllDirectories)) + .Select(f => f.FullName); } } /// /// List of projects which were mentioned but don't exist on disk. /// - public IEnumerable MissingProjects => + public IEnumerable MissingProjects + { + get + { // Only projects in the solution file can be missing. // (NestedProjects are located on disk so always exist.) - MsBuildProjects.Where(p => !File.Exists(p)); + return MsBuildProjects.Where(p => !File.Exists(p)); + } + } /// /// The list of project files. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs index 544a1819117a..88087c1580c3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs @@ -15,34 +15,44 @@ namespace Semmle.Extraction.CSharp /// /// Encapsulates a C# analysis task. /// - public class Analyser : IDisposable + public sealed class Analyser : IDisposable { - IExtractor extractor; + private IExtractor extractor; + private CSharpCompilation compilation; + private Layout layout; + private bool init; + private readonly object progressMutex = new object(); + private int taskCount = 0; + private CommonOptions options; + private Entities.Compilation compilationEntity; + private IDisposable compilationTrapFile; + + private readonly Stopwatch stopWatch = new Stopwatch(); + + private readonly IProgressMonitor progressMonitor; - readonly Stopwatch stopWatch = new Stopwatch(); + public ILogger Logger { get; } - readonly IProgressMonitor progressMonitor; + public bool AddAssemblyTrapPrefix { get; } - public readonly ILogger Logger; + public PathTransformer PathTransformer { get; } - public Analyser(IProgressMonitor pm, ILogger logger) + public Analyser(IProgressMonitor pm, ILogger logger, bool addAssemblyTrapPrefix, PathTransformer pathTransformer) { Logger = logger; + AddAssemblyTrapPrefix = addAssemblyTrapPrefix; Logger.Log(Severity.Info, "EXTRACTION STARTING at {0}", DateTime.Now); stopWatch.Start(); progressMonitor = pm; + PathTransformer = pathTransformer; } - CSharpCompilation compilation; - Layout layout; - - private bool init; /// /// Start initialization of the analyser. /// /// The arguments passed to Roslyn. /// A Boolean indicating whether to proceed with extraction. - public bool BeginInitialize(string[] roslynArgs) + public bool BeginInitialize(IEnumerable roslynArgs) { return init = LogRoslynArgs(roslynArgs, Extraction.Extractor.Version); } @@ -64,7 +74,7 @@ public void EndInitialize( layout = new Layout(); this.options = options; this.compilation = compilation; - extractor = new Extraction.Extractor(false, GetOutputName(compilation, commandLineArguments), Logger); + extractor = new Extraction.Extractor(false, GetOutputName(compilation, commandLineArguments), Logger, PathTransformer); LogDiagnostics(); SetReferencePaths(); @@ -78,7 +88,7 @@ public void EndInitialize( /// Roslyn doesn't record the relationship between a filename and its assembly /// information, so we need to retrieve this information manually. /// - void SetReferencePaths() + private void SetReferencePaths() { foreach (var reference in compilation.References.OfType()) { @@ -90,18 +100,18 @@ void SetReferencePaths() * System.Reflection.Assembly.ReflectionOnlyLoadFrom. It is also allows * loading the same assembly from different locations. */ - using (var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(refPath, FileMode.Open, FileAccess.Read, FileShare.Read))) + using var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(refPath, FileMode.Open, FileAccess.Read, FileShare.Read)); + + var metadata = pereader.GetMetadata(); + string assemblyIdentity; + unsafe { - var metadata = pereader.GetMetadata(); - string assemblyIdentity; - unsafe - { - var reader = new System.Reflection.Metadata.MetadataReader(metadata.Pointer, metadata.Length); - var def = reader.GetAssemblyDefinition(); - assemblyIdentity = reader.GetString(def.Name) + " " + def.Version; - } - extractor.SetAssemblyFile(assemblyIdentity, refPath); + var reader = new System.Reflection.Metadata.MetadataReader(metadata.Pointer, metadata.Length); + var def = reader.GetAssemblyDefinition(); + assemblyIdentity = reader.GetString(def.Name) + " " + def.Version; } + extractor.SetAssemblyFile(assemblyIdentity, refPath); + } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { @@ -114,20 +124,20 @@ public void InitializeStandalone(CSharpCompilation compilationIn, CommonOptions { compilation = compilationIn; layout = new Layout(); - extractor = new Extraction.Extractor(true, null, Logger); + extractor = new Extraction.Extractor(true, null, Logger, PathTransformer); this.options = options; LogExtractorInfo(Extraction.Extractor.Version); SetReferencePaths(); } - readonly HashSet errorsToIgnore = new HashSet + private readonly HashSet errorsToIgnore = new HashSet { "CS7027", // Code signing failure "CS1589", // XML referencing not supported "CS1569" // Error writing XML documentation }; - IEnumerable FilteredDiagnostics + private IEnumerable FilteredDiagnostics { get { @@ -148,7 +158,7 @@ IEnumerable FilteredDiagnostics /// Information about the compilation. /// Cancellation token required. /// The filename. - static string GetOutputName(CSharpCompilation compilation, + private static string GetOutputName(CSharpCompilation compilation, CSharpCommandLineArguments commandLineArguments) { // There's no apparent way to access the output filename from the compilation, @@ -161,22 +171,18 @@ static string GetOutputName(CSharpCompilation compilation, if (entry == null) { if (compilation.SyntaxTrees.Length == 0) - throw new ArgumentNullException("No source files seen"); + throw new InvalidOperationException("No source files seen"); // Probably invalid, but have a go anyway. var entryPointFile = compilation.SyntaxTrees.First().FilePath; return Path.ChangeExtension(entryPointFile, ".exe"); } - else - { - var entryPointFilename = entry.Locations.First().SourceTree.FilePath; - return Path.ChangeExtension(entryPointFilename, ".exe"); - } - } - else - { - return Path.Combine(commandLineArguments.OutputDirectory, commandLineArguments.OutputFileName); + + var entryPointFilename = entry.Locations.First().SourceTree.FilePath; + return Path.ChangeExtension(entryPointFilename, ".exe"); } + + return Path.Combine(commandLineArguments.OutputDirectory, commandLineArguments.OutputFileName); } /// @@ -192,7 +198,7 @@ public void AnalyseTree(SyntaxTree tree) /// Perform an analysis on an assembly. /// /// Assembly to analyse. - void AnalyseAssembly(PortableExecutableReference assembly) + private void AnalyseAssembly(PortableExecutableReference assembly) { // CIL first - it takes longer. if (options.CIL) @@ -200,12 +206,7 @@ void AnalyseAssembly(PortableExecutableReference assembly) extractionTasks.Add(() => DoAnalyseAssembly(assembly)); } - readonly object progressMutex = new object(); - int taskCount = 0; - - CommonOptions options; - - static bool FileIsUpToDate(string src, string dest) + private static bool FileIsUpToDate(string src, string dest) { return File.Exists(dest) && File.GetLastWriteTime(dest) >= File.GetLastWriteTime(src); @@ -219,19 +220,19 @@ public void AnalyseCompilation(string cwd, string[] args) extractionTasks.Add(() => DoAnalyseCompilation(cwd, args)); } - Entities.Compilation compilationEntity; - IDisposable compilationTrapFile; - void DoAnalyseCompilation(string cwd, string[] args) + + private void DoAnalyseCompilation(string cwd, string[] args) { try { var assemblyPath = extractor.OutputPath; + var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); var assembly = compilation.Assembly; - var projectLayout = layout.LookupProjectOrDefault(assemblyPath); - var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true, options.TrapCompression); + var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); + var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, true, options.TrapCompression); compilationTrapFile = trapWriter; // Dispose later - var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true)); + var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true), AddAssemblyTrapPrefix); compilationEntity = new Entities.Compilation(cx, cwd, args); } @@ -249,7 +250,7 @@ void DoAnalyseCompilation(string cwd, string[] args) /// extraction within the snapshot. /// /// The assembly to extract. - void DoAnalyseAssembly(PortableExecutableReference r) + private void DoAnalyseAssembly(PortableExecutableReference r) { try { @@ -257,48 +258,47 @@ void DoAnalyseAssembly(PortableExecutableReference r) stopwatch.Start(); var assemblyPath = r.FilePath; - var projectLayout = layout.LookupProjectOrDefault(assemblyPath); - using (var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true, options.TrapCompression)) + var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); + var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); + using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, true, options.TrapCompression); + + var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile); + + if (!skipExtraction) { - var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile); + /* Note on parallel builds: + * + * The trap writer and source archiver both perform atomic moves + * of the file to the final destination. + * + * If the same source file or trap file are generated concurrently + * (by different parallel invocations of the extractor), then + * last one wins. + * + * Specifically, if two assemblies are analysed concurrently in a build, + * then there is a small amount of duplicated work but the output should + * still be correct. + */ + + // compilation.Clone() reduces memory footprint by allowing the symbols + // in c to be garbage collected. + Compilation c = compilation.Clone(); - if (!skipExtraction) - { - /* Note on parallel builds: - * - * The trap writer and source archiver both perform atomic moves - * of the file to the final destination. - * - * If the same source file or trap file are generated concurrently - * (by different parallel invocations of the extractor), then - * last one wins. - * - * Specifically, if two assemblies are analysed concurrently in a build, - * then there is a small amount of duplicated work but the output should - * still be correct. - */ - - // compilation.Clone() reduces memory footprint by allowing the symbols - // in c to be garbage collected. - Compilation c = compilation.Clone(); - - var assembly = c.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol; - - if (assembly != null) - { - var cx = extractor.CreateContext(c, trapWriter, new AssemblyScope(assembly, assemblyPath, false)); - foreach (var module in assembly.Modules) - { - AnalyseNamespace(cx, module.GlobalNamespace); - } + if (c.GetAssemblyOrModuleSymbol(r) is IAssemblySymbol assembly) + { + var cx = extractor.CreateContext(c, trapWriter, new AssemblyScope(assembly, assemblyPath, false), AddAssemblyTrapPrefix); - cx.PopulateAll(); + foreach (var module in assembly.Modules) + { + AnalyseNamespace(cx, module.GlobalNamespace); } - } - ReportProgress(assemblyPath, trapWriter.TrapFile, stopwatch.Elapsed, skipExtraction ? AnalysisAction.UpToDate : AnalysisAction.Extracted); + cx.PopulateAll(); + } } + + ReportProgress(assemblyPath, trapWriter.TrapFile, stopwatch.Elapsed, skipExtraction ? AnalysisAction.UpToDate : AnalysisAction.Extracted); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { @@ -306,18 +306,16 @@ void DoAnalyseAssembly(PortableExecutableReference r) } } - void DoExtractCIL(PortableExecutableReference r) + private void DoExtractCIL(PortableExecutableReference r) { var stopwatch = new Stopwatch(); stopwatch.Start(); - string trapFile; - bool extracted; - CIL.Entities.Assembly.ExtractCIL(layout, r.FilePath, Logger, !options.Cache, options.PDB, options.TrapCompression, out trapFile, out extracted); + CIL.Entities.Assembly.ExtractCIL(layout, r.FilePath, Logger, !options.Cache, options.PDB, options.TrapCompression, out var trapFile, out var extracted); stopwatch.Stop(); ReportProgress(r.FilePath, trapFile, stopwatch.Elapsed, extracted ? AnalysisAction.Extracted : AnalysisAction.UpToDate); } - void AnalyseNamespace(Context cx, INamespaceSymbol ns) + private void AnalyseNamespace(Context cx, INamespaceSymbol ns) { foreach (var memberNamespace in ns.GetNamespaceMembers()) { @@ -342,46 +340,50 @@ public void AnalyseReferences() } // The bulk of the extraction work, potentially executed in parallel. - readonly List extractionTasks = new List(); + private readonly List extractionTasks = new List(); - void ReportProgress(string src, string output, TimeSpan time, AnalysisAction action) + private void ReportProgress(string src, string output, TimeSpan time, AnalysisAction action) { lock (progressMutex) progressMonitor.Analysed(++taskCount, extractionTasks.Count, src, output, time, action); } - void DoExtractTree(SyntaxTree tree) + private void DoExtractTree(SyntaxTree tree) { try { var stopwatch = new Stopwatch(); stopwatch.Start(); var sourcePath = tree.FilePath; + var transformedSourcePath = PathTransformer.Transform(sourcePath); - var projectLayout = layout.LookupProjectOrNull(sourcePath); - bool excluded = projectLayout == null; - string trapPath = excluded ? "" : projectLayout.GetTrapPath(Logger, sourcePath, options.TrapCompression); - bool upToDate = false; + var projectLayout = layout.LookupProjectOrNull(transformedSourcePath); + var excluded = projectLayout == null; + var trapPath = excluded ? "" : projectLayout.GetTrapPath(Logger, transformedSourcePath, options.TrapCompression); + var upToDate = false; if (!excluded) { // compilation.Clone() is used to allow symbols to be garbage collected. - using (var trapWriter = projectLayout.CreateTrapWriter(Logger, sourcePath, false, options.TrapCompression)) - { - upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile); + using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedSourcePath, false, options.TrapCompression); - if (!upToDate) - { - Context cx = extractor.CreateContext(compilation.Clone(), trapWriter, new SourceScope(tree)); - Populators.CompilationUnit.Extract(cx, tree.GetRoot()); - cx.PopulateAll(); - cx.ExtractComments(cx.CommentGenerator); - cx.PopulateAll(); - } + upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile); + + if (!upToDate) + { + var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new SourceScope(tree), AddAssemblyTrapPrefix); + Populators.CompilationUnit.Extract(cx, tree.GetRoot()); + cx.PopulateAll(); + cx.ExtractComments(cx.CommentGenerator); + cx.PopulateAll(); } } - ReportProgress(sourcePath, trapPath, stopwatch.Elapsed, excluded ? AnalysisAction.Excluded : upToDate ? AnalysisAction.UpToDate : AnalysisAction.Extracted); + ReportProgress(sourcePath, trapPath, stopwatch.Elapsed, excluded + ? AnalysisAction.Excluded + : upToDate + ? AnalysisAction.UpToDate + : AnalysisAction.Extracted); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { @@ -445,7 +447,7 @@ public void LogExtractorInfo(string extractorVersion) /// /// The arguments passed to Roslyn. /// A Boolean indicating whether the same arguments have been logged previously. - public bool LogRoslynArgs(string[] roslynArgs, string extractorVersion) + private bool LogRoslynArgs(IEnumerable roslynArgs, string extractorVersion) { LogExtractorInfo(extractorVersion); Logger.Log(Severity.Info, $" Arguments to Roslyn: {string.Join(' ', roslynArgs)}"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CompilerVersion.cs b/csharp/extractor/Semmle.Extraction.CSharp/CompilerVersion.cs index c39052726559..f8da28affc44 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CompilerVersion.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CompilerVersion.cs @@ -13,8 +13,8 @@ namespace Semmle.Extraction.CSharp /// public class CompilerVersion { - const string csc_rsp = "csc.rsp"; - readonly string specifiedFramework = null; + private const string csc_rsp = "csc.rsp"; + private readonly string specifiedFramework = null; /// /// The value specified by --compiler, or null. @@ -85,7 +85,7 @@ public CompilerVersion(Options options) ArgsWithResponse = AddDefaultResponse(CscRsp, options.CompilerArguments).ToArray(); } - void SkipExtractionBecause(string reason) + private void SkipExtractionBecause(string reason) { SkipExtraction = true; SkipReason = reason; @@ -99,7 +99,7 @@ void SkipExtractionBecause(string reason) /// /// The file csc.rsp. /// - string CscRsp => Path.Combine(FrameworkPath, csc_rsp); + private string CscRsp => Path.Combine(FrameworkPath, csc_rsp); /// /// Should we skip extraction? @@ -122,18 +122,18 @@ public bool SkipExtraction /// The full pathname of csc.rsp. /// The other command line arguments. /// Modified list of arguments. - static IEnumerable AddDefaultResponse(string responseFile, IEnumerable args) + private static IEnumerable AddDefaultResponse(string responseFile, IEnumerable args) { return SuppressDefaultResponseFile(args) || !File.Exists(responseFile) ? args : new[] { "@" + responseFile }.Concat(args); } - static bool SuppressDefaultResponseFile(IEnumerable args) + private static bool SuppressDefaultResponseFile(IEnumerable args) { return args.Any(arg => new[] { "/noconfig", "-noconfig" }.Contains(arg.ToLowerInvariant())); } - public readonly string[] ArgsWithResponse; + public IEnumerable ArgsWithResponse { get; } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs index 98c021c8a6d6..c53a2c402e01 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs @@ -4,31 +4,33 @@ namespace Semmle.Extraction.CSharp.Entities { - class Accessor : Method + internal class Accessor : Method { protected Accessor(Context cx, IMethodSymbol init) : base(cx, init) { } /// - /// Gets the property symbol associated with this accessor. + /// Gets the property symbol associated accessor `symbol`, or `null` + /// if there is no associated symbol. /// - IPropertySymbol PropertySymbol + public static IPropertySymbol GetPropertySymbol(IMethodSymbol symbol) { - get - { - // Usually, the property/indexer can be fetched from the associated symbol - var prop = symbol.AssociatedSymbol as IPropertySymbol; - if (prop != null) - return prop; + // Usually, the property/indexer can be fetched from the associated symbol + if (symbol.AssociatedSymbol is IPropertySymbol prop) + return prop; - // But for properties/indexers that implement explicit interfaces, Roslyn - // does not properly populate `AssociatedSymbol` - var props = symbol.ContainingType.GetMembers().OfType(); - props = props.Where(p => SymbolEqualityComparer.Default.Equals(symbol, p.GetMethod) || SymbolEqualityComparer.Default.Equals(symbol, p.SetMethod)); - return props.SingleOrDefault(); - } + // But for properties/indexers that implement explicit interfaces, Roslyn + // does not properly populate `AssociatedSymbol` + var props = symbol.ContainingType.GetMembers().OfType(); + props = props.Where(p => SymbolEqualityComparer.Default.Equals(symbol, p.GetMethod) || SymbolEqualityComparer.Default.Equals(symbol, p.SetMethod)); + return props.SingleOrDefault(); } + /// + /// Gets the property symbol associated with this accessor. + /// + private IPropertySymbol PropertySymbol => GetPropertySymbol(symbol); + public new Accessor OriginalDefinition => Create(Context, symbol.OriginalDefinition); public override void Populate(TextWriter trapFile) @@ -76,12 +78,12 @@ public override void Populate(TextWriter trapFile) } } - public new static Accessor Create(Context cx, IMethodSymbol symbol) => - AccessorFactory.Instance.CreateEntity(cx, symbol); + public static new Accessor Create(Context cx, IMethodSymbol symbol) => + AccessorFactory.Instance.CreateEntityFromSymbol(cx, symbol); - class AccessorFactory : ICachedEntityFactory + private class AccessorFactory : ICachedEntityFactory { - public static readonly AccessorFactory Instance = new AccessorFactory(); + public static AccessorFactory Instance { get; } = new AccessorFactory(); public Accessor Create(Context cx, IMethodSymbol init) => new Accessor(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs index 93b9c6fad5e9..ea2d798e6052 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs @@ -1,38 +1,35 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Entities; -using System.Collections.Generic; using System.IO; -using System.Linq; namespace Semmle.Extraction.CSharp.Entities { - class Attribute : FreshEntity, IExpressionParentEntity + internal class Attribute : FreshEntity, IExpressionParentEntity { bool IExpressionParentEntity.IsTopLevelParent => true; - private readonly AttributeData AttributeData; - private readonly IEntity Entity; + private readonly AttributeData attribute; + private readonly IEntity entity; public Attribute(Context cx, AttributeData attribute, IEntity entity) : base(cx) { - AttributeData = attribute; - Entity = entity; + this.attribute = attribute; + this.entity = entity; TryPopulate(); } protected override void Populate(TextWriter trapFile) { - if (AttributeData.ApplicationSyntaxReference != null) + if (attribute.ApplicationSyntaxReference != null) { // !! Extract attributes from assemblies. // This is harder because the "expression" entities presume the // existence of a syntax tree. This is not the case for compiled // attributes. - var syntax = AttributeData.ApplicationSyntaxReference.GetSyntax() as AttributeSyntax; - ExtractAttribute(cx.TrapWriter.Writer, syntax, AttributeData.AttributeClass, Entity); + var syntax = attribute.ApplicationSyntaxReference.GetSyntax() as AttributeSyntax; + ExtractAttribute(cx.TrapWriter.Writer, syntax, attribute.AttributeClass, entity); } } @@ -43,7 +40,7 @@ public Attribute(Context cx, AttributeSyntax attribute, IEntity entity) ExtractAttribute(cx.TrapWriter.Writer, attribute, info.Symbol.ContainingType, entity); } - void ExtractAttribute(System.IO.TextWriter trapFile, AttributeSyntax syntax, ITypeSymbol attributeClass, IEntity entity) + private void ExtractAttribute(System.IO.TextWriter trapFile, AttributeSyntax syntax, ITypeSymbol attributeClass, IEntity entity) { var type = Type.Create(cx, attributeClass); trapFile.attributes(this, type.TypeRef, entity); @@ -59,7 +56,7 @@ void ExtractAttribute(System.IO.TextWriter trapFile, AttributeSyntax syntax, ITy { cx.PopulateLater(() => { - int child = 0; + var child = 0; foreach (var arg in syntax.ArgumentList.Arguments) { var expr = Expression.Create(cx, arg.Expression, this, child++); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs index 0e851df1e6b2..ab6802cf4570 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs @@ -4,15 +4,15 @@ namespace Semmle.Extraction.CSharp.Entities { - class CommentBlock : CachedEntity + internal class CommentBlock : CachedEntity { - CommentBlock(Context cx, ICommentBlock init) + private CommentBlock(Context cx, ICommentBlock init) : base(cx, init) { } public override void Populate(TextWriter trapFile) { trapFile.commentblock(this); - int child = 0; + var child = 0; trapFile.commentblock_location(this, Context.Create(symbol.Location)); foreach (var l in symbol.CommentLines) { @@ -35,11 +35,11 @@ public void BindTo(Label entity, CommentBinding binding) Context.TrapWriter.Writer.commentblock_binding(this, entity, binding); } - public static CommentBlock Create(Context cx, ICommentBlock block) => CommentBlockFactory.Instance.CreateEntity(cx, block); + public static CommentBlock Create(Context cx, ICommentBlock block) => CommentBlockFactory.Instance.CreateEntity(cx, block, block); - class CommentBlockFactory : ICachedEntityFactory + private class CommentBlockFactory : ICachedEntityFactory { - public static readonly CommentBlockFactory Instance = new CommentBlockFactory(); + public static CommentBlockFactory Instance { get; } = new CommentBlockFactory(); public CommentBlock Create(Context cx, ICommentBlock init) => new CommentBlock(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs index a3ba9896ef1c..ba1770f20e52 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs @@ -6,9 +6,9 @@ namespace Semmle.Extraction.CSharp.Entities { - class CommentLine : CachedEntity<(Microsoft.CodeAnalysis.Location, string)>, ICommentLine + internal class CommentLine : CachedEntity<(Microsoft.CodeAnalysis.Location, string)>, ICommentLine { - CommentLine(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw) + private CommentLine(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw) : base(cx, (loc, text)) { Type = type; @@ -36,14 +36,14 @@ So split it up. var split = text.Split('\n'); var currentLocation = trivia.GetLocation().SourceSpan.Start - 3; - for (int line = 0; line < split.Length - 1; ++line) + for (var line = 0; line < split.Length - 1; ++line) { - string fullLine = split[line]; + var fullLine = split[line]; var nextLineLocation = currentLocation + fullLine.Length + 1; fullLine = fullLine.TrimEnd('\r'); - string trimmedLine = fullLine; + var trimmedLine = fullLine; - int leadingSpaces = trimmedLine.IndexOf('/'); + var leadingSpaces = trimmedLine.IndexOf('/'); if (leadingSpaces != -1) { fullLine = fullLine.Substring(leadingSpaces); @@ -66,7 +66,7 @@ So split it up. case SyntaxKind.SingleLineCommentTrivia: { - string contents = trivia.ToString().Substring(2); + var contents = trivia.ToString().Substring(2); var commentType = CommentLineType.Singleline; if (contents.Length > 0 && contents[0] == '/') { @@ -86,14 +86,16 @@ So we split it into separate lines split = text.Split('\n'); currentLocation = trivia.GetLocation().SourceSpan.Start; - for (int line = 0; line < split.Length; ++line) + for (var line = 0; line < split.Length; ++line) { - string fullLine = split[line]; + var fullLine = split[line]; var nextLineLocation = currentLocation + fullLine.Length + 1; fullLine = fullLine.TrimEnd('\r'); - string trimmedLine = fullLine; - if (line == 0) trimmedLine = trimmedLine.Substring(2); - if (line == split.Length - 1) trimmedLine = trimmedLine.Substring(0, trimmedLine.Length - 2); + var trimmedLine = fullLine; + if (line == 0) + trimmedLine = trimmedLine.Substring(2); + if (line == split.Length - 1) + trimmedLine = trimmedLine.Substring(0, trimmedLine.Length - 2); trimmedLine = trimmedLine.Trim(); var span = Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(currentLocation, currentLocation + fullLine.Length); @@ -110,7 +112,7 @@ So we split it into separate lines } } - Extraction.Entities.Location location; + private Extraction.Entities.Location location; public override void Populate(TextWriter trapFile) { @@ -129,11 +131,15 @@ public override void WriteId(TextWriter trapFile) trapFile.Write(";commentline"); } - static CommentLine Create(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw) => CommentLineFactory.Instance.CreateEntity(cx, loc, type, text, raw); + private static CommentLine Create(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw) + { + var init = (loc, type, text, raw); + return CommentLineFactory.Instance.CreateEntity(cx, init, init); + } - class CommentLineFactory : ICachedEntityFactory<(Microsoft.CodeAnalysis.Location, CommentLineType, string, string), CommentLine> + private class CommentLineFactory : ICachedEntityFactory<(Microsoft.CodeAnalysis.Location, CommentLineType, string, string), CommentLine> { - public static readonly CommentLineFactory Instance = new CommentLineFactory(); + public static CommentLineFactory Instance { get; } = new CommentLineFactory(); public CommentLine Create(Context cx, (Microsoft.CodeAnalysis.Location, CommentLineType, string, string) init) => new CommentLine(cx, init.Item1, init.Item2, init.Item3, init.Item4); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs index b6ff91f5988e..67a4325ce35e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs @@ -3,10 +3,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Semmle.Util; namespace Semmle.Extraction.CSharp.Entities { - class Compilation : FreshEntity + internal class Compilation : FreshEntity { private readonly string cwd; private readonly string[] args; @@ -22,32 +23,32 @@ protected override void Populate(TextWriter trapFile) { Extraction.Entities.Assembly.CreateOutputAssembly(cx); - trapFile.compilations(this, Extraction.Entities.File.PathAsDatabaseString(cwd)); + trapFile.compilations(this, FileUtils.ConvertToUnix(cwd)); // Arguments - int index = 0; - foreach(var arg in args) + var index = 0; + foreach (var arg in args) { trapFile.compilation_args(this, index++, arg); } // Files index = 0; - foreach(var file in cx.Compilation.SyntaxTrees.Select(tree => Extraction.Entities.File.Create(cx, tree.FilePath))) + foreach (var file in cx.Compilation.SyntaxTrees.Select(tree => Extraction.Entities.File.Create(cx, tree.FilePath))) { trapFile.compilation_compiling_files(this, index++, file); } // References index = 0; - foreach(var file in cx.Compilation.References.OfType().Select(r => Extraction.Entities.File.Create(cx, r.FilePath))) + foreach (var file in cx.Compilation.References.OfType().Select(r => Extraction.Entities.File.Create(cx, r.FilePath))) { trapFile.compilation_referencing_files(this, index++, file); } // Diagnostics index = 0; - foreach(var diag in cx.Compilation.GetDiagnostics().Select(d => new Diagnostic(cx, d))) + foreach (var diag in cx.Compilation.GetDiagnostics().Select(d => new Diagnostic(cx, d))) { trapFile.diagnostic_for(diag, this, 0, index++); } @@ -56,8 +57,8 @@ protected override void Populate(TextWriter trapFile) public void PopulatePerformance(PerformanceMetrics p) { var trapFile = cx.TrapWriter.Writer; - int index = 0; - foreach(float metric in p.Metrics) + var index = 0; + foreach (var metric in p.Metrics) { trapFile.compilation_time(this, -1, index++, metric); } @@ -67,11 +68,11 @@ public void PopulatePerformance(PerformanceMetrics p) public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; } - class Diagnostic : FreshEntity + internal class Diagnostic : FreshEntity { public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; - readonly Microsoft.CodeAnalysis.Diagnostic diagnostic; + private readonly Microsoft.CodeAnalysis.Diagnostic diagnostic; public Diagnostic(Context cx, Microsoft.CodeAnalysis.Diagnostic diag) : base(cx) { @@ -88,7 +89,9 @@ protected override void Populate(TextWriter trapFile) public struct Timings { - public TimeSpan Elapsed, Cpu, User; + public TimeSpan Elapsed { get; set; } + public TimeSpan Cpu { get; set; } + public TimeSpan User { get; set; } } /// @@ -96,8 +99,10 @@ public struct Timings /// public struct PerformanceMetrics { - public Timings Frontend, Extractor, Total; - public long PeakWorkingSet; + public Timings Frontend { get; set; } + public Timings Extractor { get; set; } + public Timings Total { get; set; } + public long PeakWorkingSet { get; set; } /// /// These are in database order (0 indexed) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index 298f838cfb93..35b3f2eb8086 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -1,6 +1,5 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using Semmle.Util; using System.Linq; using Microsoft.CodeAnalysis.CSharp; @@ -11,7 +10,7 @@ namespace Semmle.Extraction.CSharp.Entities { public class Constructor : Method { - Constructor(Context cx, IMethodSymbol init) + private Constructor(Context cx, IMethodSymbol init) : base(cx, init) { } public override void Populate(TextWriter trapFile) @@ -34,12 +33,14 @@ public override void Populate(TextWriter trapFile) protected override void ExtractInitializers(TextWriter trapFile) { // Do not extract initializers for constructed types. - if (!IsSourceDeclaration) return; + if (!IsSourceDeclaration) + return; var syntax = Syntax; - var initializer = syntax == null ? null : syntax.Initializer; + var initializer = syntax?.Initializer; - if (initializer == null) return; + if (initializer == null) + return; Type initializerType; var symbolInfo = Context.GetSymbolInfo(initializer); @@ -78,33 +79,34 @@ protected override void ExtractInitializers(TextWriter trapFile) trapFile.expr_call(init, target); - int child = 0; + var child = 0; foreach (var arg in initializer.ArgumentList.Arguments) { Expression.Create(Context, arg.Expression, init, child++); } } - ConstructorDeclarationSyntax Syntax + private ConstructorDeclarationSyntax Syntax { get { - return symbol.DeclaringSyntaxReferences. - Select(r => r.GetSyntax()). - OfType(). - FirstOrDefault(); + return symbol.DeclaringSyntaxReferences + .Select(r => r.GetSyntax()) + .OfType() + .FirstOrDefault(); } } - public new static Constructor Create(Context cx, IMethodSymbol constructor) + public static new Constructor Create(Context cx, IMethodSymbol constructor) { - if (constructor == null) return null; + if (constructor == null) + return null; switch (constructor.MethodKind) { case MethodKind.StaticConstructor: case MethodKind.Constructor: - return ConstructorFactory.Instance.CreateEntity(cx, constructor); + return ConstructorFactory.Instance.CreateEntityFromSymbol(cx, constructor); default: throw new InternalError(constructor, "Attempt to create a Constructor from a symbol that isn't a constructor"); } @@ -112,13 +114,14 @@ ConstructorDeclarationSyntax Syntax public override void WriteId(TextWriter trapFile) { - if (symbol.IsStatic) trapFile.Write("static"); + if (symbol.IsStatic) + trapFile.Write("static"); trapFile.WriteSubId(ContainingType); AddParametersToId(Context, trapFile, symbol); trapFile.Write(";constructor"); } - ConstructorDeclarationSyntax GetSyntax() => + private ConstructorDeclarationSyntax GetSyntax() => symbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax()).OfType().FirstOrDefault(); public override Microsoft.CodeAnalysis.Location FullLocation => ReportingLocation; @@ -132,20 +135,19 @@ public override Microsoft.CodeAnalysis.Location ReportingLocation { return syn.Identifier.GetLocation(); } - else if (symbol.IsImplicitlyDeclared) + + if (symbol.IsImplicitlyDeclared) { return ContainingType.ReportingLocation; } - else - { - return symbol.ContainingType.Locations.FirstOrDefault(); - } + + return symbol.ContainingType.Locations.FirstOrDefault(); } } - class ConstructorFactory : ICachedEntityFactory + private class ConstructorFactory : ICachedEntityFactory { - public static readonly ConstructorFactory Instance = new ConstructorFactory(); + public static ConstructorFactory Instance { get; } = new ConstructorFactory(); public Constructor Create(Context cx, IMethodSymbol init) => new Constructor(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs index e365ca53ae0c..aa125cae0a17 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs @@ -5,31 +5,30 @@ namespace Semmle.Extraction.CSharp.Entities { - class Conversion : UserOperator + internal class Conversion : UserOperator { - Conversion(Context cx, IMethodSymbol init) + private Conversion(Context cx, IMethodSymbol init) : base(cx, init) { } - public new static Conversion Create(Context cx, IMethodSymbol symbol) => - ConversionFactory.Instance.CreateEntity(cx, symbol); + public static new Conversion Create(Context cx, IMethodSymbol symbol) => + ConversionFactory.Instance.CreateEntityFromSymbol(cx, symbol); public override Microsoft.CodeAnalysis.Location ReportingLocation { get { - return symbol. - DeclaringSyntaxReferences. - Select(r => r.GetSyntax()). - OfType(). - Select(s => s.FixedLocation()). - Concat(symbol.Locations). - FirstOrDefault(); + return symbol.DeclaringSyntaxReferences + .Select(r => r.GetSyntax()) + .OfType() + .Select(s => s.FixedLocation()) + .Concat(symbol.Locations) + .FirstOrDefault(); } } - class ConversionFactory : ICachedEntityFactory + private class ConversionFactory : ICachedEntityFactory { - public static readonly ConversionFactory Instance = new ConversionFactory(); + public static ConversionFactory Instance { get; } = new ConversionFactory(); public Conversion Create(Context cx, IMethodSymbol init) => new Conversion(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs index 33f3a330f944..a5c4acc3c03b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs @@ -3,9 +3,9 @@ namespace Semmle.Extraction.CSharp.Entities { - class Destructor : Method + internal class Destructor : Method { - Destructor(Context cx, IMethodSymbol init) + private Destructor(Context cx, IMethodSymbol init) : base(cx, init) { } public override void Populate(TextWriter trapFile) @@ -18,17 +18,17 @@ public override void Populate(TextWriter trapFile) trapFile.destructor_location(this, Location); } - static new Destructor OriginalDefinition(Context cx, Destructor original, IMethodSymbol symbol) + private static new Destructor OriginalDefinition(Context cx, Destructor original, IMethodSymbol symbol) { return symbol.OriginalDefinition == null || SymbolEqualityComparer.Default.Equals(symbol.OriginalDefinition, symbol) ? original : Create(cx, symbol.OriginalDefinition); } - public new static Destructor Create(Context cx, IMethodSymbol symbol) => - DestructorFactory.Instance.CreateEntity(cx, symbol); + public static new Destructor Create(Context cx, IMethodSymbol symbol) => + DestructorFactory.Instance.CreateEntityFromSymbol(cx, symbol); - class DestructorFactory : ICachedEntityFactory + private class DestructorFactory : ICachedEntityFactory { - public static readonly DestructorFactory Instance = new DestructorFactory(); + public static DestructorFactory Instance { get; } = new DestructorFactory(); public Destructor Create(Context cx, IMethodSymbol init) => new Destructor(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index 224c81b9524b..5c24cf97655c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities { - class Event : CachedSymbol + internal class Event : CachedSymbol { - Event(Context cx, IEventSymbol init) + private Event(Context cx, IEventSymbol init) : base(cx, init) { } public override void WriteId(TextWriter trapFile) @@ -53,18 +53,21 @@ public override void Populate(TextWriter trapFile) foreach (var l in Locations) trapFile.event_location(this, l); - foreach (var syntaxType in declSyntaxReferences.OfType(). - Select(d => d.Parent). - OfType(). - Select(syntax => syntax.Type)) + foreach (var syntaxType in declSyntaxReferences + .OfType() + .Select(d => d.Parent) + .OfType() + .Select(syntax => syntax.Type)) + { TypeMention.Create(Context, syntaxType, this, type); + } } - public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntity(cx, symbol); + public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntityFromSymbol(cx, symbol); - class EventFactory : ICachedEntityFactory + private class EventFactory : ICachedEntityFactory { - public static readonly EventFactory Instance = new EventFactory(); + public static EventFactory Instance { get; } = new EventFactory(); public Event Create(Context cx, IEventSymbol init) => new Event(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs index 7dc0a4dfcffa..c75b6766dc7b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs @@ -3,15 +3,15 @@ namespace Semmle.Extraction.CSharp.Entities { - class EventAccessor : Accessor + internal class EventAccessor : Accessor { - EventAccessor(Context cx, IMethodSymbol init) + private EventAccessor(Context cx, IMethodSymbol init) : base(cx, init) { } /// /// Gets the event symbol associated with this accessor. /// - IEventSymbol EventSymbol => symbol.AssociatedSymbol as IEventSymbol; + private IEventSymbol EventSymbol => symbol.AssociatedSymbol as IEventSymbol; public override void Populate(TextWriter trapFile) { @@ -52,12 +52,12 @@ public override void Populate(TextWriter trapFile) Overrides(trapFile); } - public new static EventAccessor Create(Context cx, IMethodSymbol symbol) => - EventAccessorFactory.Instance.CreateEntity(cx, symbol); + public static new EventAccessor Create(Context cx, IMethodSymbol symbol) => + EventAccessorFactory.Instance.CreateEntityFromSymbol(cx, symbol); - class EventAccessorFactory : ICachedEntityFactory + private class EventAccessorFactory : ICachedEntityFactory { - public static readonly EventAccessorFactory Instance = new EventAccessorFactory(); + public static EventAccessorFactory Instance { get; } = new EventAccessorFactory(); public EventAccessor Create(Context cx, IMethodSymbol init) => new EventAccessor(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index 7e57ca9cddc9..5a7a4962f3b3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -17,17 +17,17 @@ public interface IExpressionParentEntity : IEntity bool IsTopLevelParent { get; } } - class Expression : FreshEntity, IExpressionParentEntity + internal class Expression : FreshEntity, IExpressionParentEntity { - private readonly IExpressionInfo Info; - public readonly AnnotatedType Type; - public readonly Extraction.Entities.Location Location; - public readonly ExprKind Kind; + private readonly IExpressionInfo info; + public AnnotatedType Type { get; } + public Extraction.Entities.Location Location { get; } + public ExprKind Kind { get; } internal Expression(IExpressionInfo info) : base(info.Context) { - Info = info; + this.info = info; Location = info.Location; Kind = info.Kind; Type = info.Type; @@ -40,10 +40,10 @@ internal Expression(IExpressionInfo info) protected sealed override void Populate(TextWriter trapFile) { trapFile.expressions(this, Kind, Type.Type.TypeRef); - if (Info.Parent.IsTopLevelParent) - trapFile.expr_parent_top_level(this, Info.Child, Info.Parent); + if (info.Parent.IsTopLevelParent) + trapFile.expr_parent_top_level(this, info.Child, info.Parent); else - trapFile.expr_parent(this, Info.Child, Info.Parent); + trapFile.expr_parent(this, info.Child, info.Parent); trapFile.expr_location(this, Location); var annotatedType = Type.Symbol; @@ -53,15 +53,15 @@ protected sealed override void Populate(TextWriter trapFile) trapFile.type_nullability(this, n); } - if(Info.FlowState != NullableFlowState.None) + if (info.FlowState != NullableFlowState.None) { - trapFile.expr_flowstate(this, (int)Info.FlowState); + trapFile.expr_flowstate(this, (int)info.FlowState); } - if (Info.IsCompilerGenerated) + if (info.IsCompilerGenerated) trapFile.expr_compiler_generated(this); - if (Info.ExprValue is string value) + if (info.ExprValue is string value) trapFile.expr_value(this, value); Type.Type.PopulateGenerics(); @@ -78,7 +78,13 @@ protected sealed override void Populate(TextWriter trapFile) /// The string representation. public static string ValueAsString(object value) { - return value == null ? "null" : value is bool ? ((bool)value ? "true" : "false") : value.ToString(); + return value == null + ? "null" + : value is bool b + ? b + ? "true" + : "false" + : value.ToString(); } /// @@ -116,7 +122,7 @@ public static void CreateDeferred(Context cx, ExpressionSyntax node, IExpression cx.PopulateLater(() => Create(cx, node, parent, child)); } - static bool ContainsPattern(SyntaxNode node) => + private static bool ContainsPattern(SyntaxNode node) => node is PatternSyntax || node is VariableDesignationSyntax || node.ChildNodes().Any(ContainsPattern); /// @@ -144,7 +150,7 @@ public void OperatorCall(TextWriter trapFile, ExpressionSyntax node) var callType = GetCallType(cx, node); if (callType == CallType.Dynamic) { - UserOperator.OperatorSymbol(method.Name, out string operatorName); + UserOperator.OperatorSymbol(method.Name, out var operatorName); trapFile.dynamic_member_name(this, operatorName); return; } @@ -212,10 +218,11 @@ protected static ExpressionSyntax FindConditionalQualifier(ExpressionSyntax node { for (SyntaxNode n = node; n != null; n = n.Parent) { - var conditionalAccess = n.Parent as ConditionalAccessExpressionSyntax; - - if (conditionalAccess != null && conditionalAccess.WhenNotNull == n) + if (n.Parent is ConditionalAccessExpressionSyntax conditionalAccess && + conditionalAccess.WhenNotNull == n) + { return conditionalAccess.Expression; + } } throw new InternalError(node, "Unable to locate a ConditionalAccessExpression"); @@ -266,7 +273,7 @@ private void PopulateArgument(TextWriter trapFile, ArgumentSyntax arg, int child public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; } - static class CallTypeExtensions + internal static class CallTypeExtensions { /// /// Adjust the expression kind to match this call type. @@ -284,15 +291,15 @@ public static ExprKind AdjustKind(this Expression.CallType ct, ExprKind k) } } - abstract class Expression : Expression - where SyntaxNode : ExpressionSyntax + internal abstract class Expression : Expression + where TExpressionSyntax : ExpressionSyntax { - public readonly SyntaxNode Syntax; + public TExpressionSyntax Syntax { get; } protected Expression(ExpressionNodeInfo info) : base(info) { - Syntax = (SyntaxNode)info.Node; + Syntax = (TExpressionSyntax)info.Node; } /// @@ -307,7 +314,7 @@ protected Expression(ExpressionNodeInfo info) protected new Expression TryPopulate() { - cx.Try(Syntax, null, ()=>PopulateExpression(cx.TrapWriter.Writer)); + cx.Try(Syntax, null, () => PopulateExpression(cx.TrapWriter.Writer)); return this; } } @@ -315,7 +322,7 @@ protected Expression(ExpressionNodeInfo info) /// /// Holds all information required to create an Expression entity. /// - interface IExpressionInfo + internal interface IExpressionInfo { Context Context { get; } @@ -363,7 +370,7 @@ interface IExpressionInfo /// /// Explicitly constructed expression information. /// - class ExpressionInfo : IExpressionInfo + internal class ExpressionInfo : IExpressionInfo { public Context Context { get; } public AnnotatedType Type { get; } @@ -394,7 +401,7 @@ public ExpressionInfo(Context cx, AnnotatedType type, Extraction.Entities.Locati /// /// Expression information constructed from a syntax node. /// - class ExpressionNodeInfo : IExpressionInfo + internal class ExpressionNodeInfo : IExpressionInfo { public ExpressionNodeInfo(Context cx, ExpressionSyntax node, IExpressionParentEntity parent, int child) : this(cx, node, parent, child, cx.GetTypeInfo(node)) @@ -434,8 +441,7 @@ public AnnotatedTypeSymbol ExpressionType // Clearly a bug. if (type.Symbol?.TypeKind == Microsoft.CodeAnalysis.TypeKind.Error) { - var arrayCreation = Node as ArrayCreationExpressionSyntax; - if (arrayCreation != null) + if (Node is ArrayCreationExpressionSyntax arrayCreation) { var elementType = Context.GetType(arrayCreation.Type.ElementType); @@ -451,7 +457,7 @@ public AnnotatedTypeSymbol ExpressionType } } - Microsoft.CodeAnalysis.Location location; + private Microsoft.CodeAnalysis.Location location; public Microsoft.CodeAnalysis.Location CodeAnalysisLocation { @@ -478,7 +484,7 @@ public string ExprValue } } - AnnotatedType cachedType; + private AnnotatedType cachedType; public AnnotatedType Type { @@ -494,7 +500,7 @@ public AnnotatedType Type } } - Extraction.Entities.Location cachedLocation; + private Extraction.Entities.Location cachedLocation; public Extraction.Entities.Location Location { @@ -540,7 +546,7 @@ public ExpressionNodeInfo SetNode(ExpressionSyntax node) return this; } - SymbolInfo cachedSymbolInfo; + private SymbolInfo cachedSymbolInfo; public SymbolInfo SymbolInfo { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Access.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Access.cs index dca2a2b96bbe..e91b81a3d77d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Access.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Access.cs @@ -1,12 +1,11 @@ īģŋusing Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Access : Expression + internal class Access : Expression { - static ExprKind AccessKind(Context cx, ISymbol symbol) + private static ExprKind AccessKind(Context cx, ISymbol symbol) { switch (symbol.Kind) { @@ -43,7 +42,7 @@ static ExprKind AccessKind(Context cx, ISymbol symbol) } } - Access(ExpressionNodeInfo info, ISymbol symbol, bool implicitThis, IEntity target) + private Access(ExpressionNodeInfo info, ISymbol symbol, bool implicitThis, IEntity target) : base(info.SetKind(AccessKind(info.Context, symbol))) { if (!(target is null)) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArgList.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArgList.cs index cb45b7593894..bd96850bbe13 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArgList.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArgList.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class ArgList : Expression + internal class ArgList : Expression { - ArgList(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.UNKNOWN)) { } + private ArgList(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.UNKNOWN)) { } protected override void PopulateExpression(TextWriter trapFile) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs index a4b1a4beb093..adb7644e9195 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Kinds; using System.IO; @@ -6,22 +7,24 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - abstract class ArrayCreation : Expression where SyntaxNode : ExpressionSyntax + internal abstract class ArrayCreation : Expression + where TSyntaxNode : ExpressionSyntax { protected ArrayCreation(ExpressionNodeInfo info) : base(info) { } } - abstract class ExplicitArrayCreation : ArrayCreation where SyntaxNode : ExpressionSyntax + internal abstract class ExplicitArrayCreation : ArrayCreation + where TSyntaxNode : ExpressionSyntax { protected ExplicitArrayCreation(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.ARRAY_CREATION)) { } protected abstract ArrayTypeSyntax TypeSyntax { get; } - public abstract InitializerExpressionSyntax Initializer { get; } + public abstract InitializerExpressionSyntax Initializer { get; } protected override void PopulateExpression(TextWriter trapFile) { - var child = 0; + var explicitlySized = false; if (TypeSyntax is null) @@ -29,38 +32,21 @@ protected override void PopulateExpression(TextWriter trapFile) cx.ModelError(Syntax, "Array has unexpected type syntax"); } - foreach (var rank in TypeSyntax.RankSpecifiers.SelectMany(rs => rs.Sizes)) + var firstLevelSizes = TypeSyntax.RankSpecifiers.First()?.Sizes ?? SyntaxFactory.SeparatedList(); + + if (firstLevelSizes.OfType().Any(s => s is OmittedArraySizeExpressionSyntax)) { - if (rank is OmittedArraySizeExpressionSyntax) - { - // Create an expression which simulates the explicit size of the array - - if (!(Initializer is null)) - { - // An implicitly-sized array must have an initializer. - // Guard it just in case. - var size = Initializer.Expressions.Count; - - var info = new ExpressionInfo( - cx, - new AnnotatedType(Entities.Type.Create(cx, cx.Compilation.GetSpecialType(Microsoft.CodeAnalysis.SpecialType.System_Int32)), NullableAnnotation.None), - Location, - ExprKind.INT_LITERAL, - this, - child, - false, - size.ToString()); - - new Expression(info); - } - } - else + SetArraySizes(Initializer, firstLevelSizes.Count); + } + else + { + for (var sizeIndex = 0; sizeIndex < firstLevelSizes.Count; sizeIndex++) { - Create(cx, rank, this, child); - explicitlySized = true; + Create(cx, firstLevelSizes[sizeIndex], this, sizeIndex); } - child++; + explicitlySized = true; } + if (!(Initializer is null)) { ArrayInitializer.Create(new ExpressionNodeInfo(cx, Initializer, this, -1)); @@ -69,9 +55,34 @@ protected override void PopulateExpression(TextWriter trapFile) if (explicitlySized) trapFile.explicitly_sized_array_creation(this); } + + private void SetArraySizes(InitializerExpressionSyntax initializer, int rank) + { + for (var level = 0; level < rank; level++) + { + if (initializer is null) + { + return; + } + + var info = new ExpressionInfo( + cx, + new AnnotatedType(Entities.Type.Create(cx, cx.Compilation.GetSpecialType(Microsoft.CodeAnalysis.SpecialType.System_Int32)), NullableAnnotation.None), + Location, + ExprKind.INT_LITERAL, + this, + level, + true, + initializer.Expressions.Count.ToString()); + + new Expression(info); + + initializer = initializer.Expressions.FirstOrDefault() as InitializerExpressionSyntax; + } + } } - class NormalArrayCreation : ExplicitArrayCreation + internal class NormalArrayCreation : ExplicitArrayCreation { private NormalArrayCreation(ExpressionNodeInfo info) : base(info) { } @@ -82,9 +93,9 @@ private NormalArrayCreation(ExpressionNodeInfo info) : base(info) { } public static Expression Create(ExpressionNodeInfo info) => new NormalArrayCreation(info).TryPopulate(); } - class StackAllocArrayCreation : ExplicitArrayCreation + internal class StackAllocArrayCreation : ExplicitArrayCreation { - StackAllocArrayCreation(ExpressionNodeInfo info) : base(info) { } + private StackAllocArrayCreation(ExpressionNodeInfo info) : base(info) { } protected override ArrayTypeSyntax TypeSyntax => Syntax.Type as ArrayTypeSyntax; @@ -99,9 +110,9 @@ protected override void PopulateExpression(TextWriter trapFile) public static Expression Create(ExpressionNodeInfo info) => new StackAllocArrayCreation(info).TryPopulate(); } - class ImplicitStackAllocArrayCreation : ArrayCreation + internal class ImplicitStackAllocArrayCreation : ArrayCreation { - ImplicitStackAllocArrayCreation(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.ARRAY_CREATION)) { } + private ImplicitStackAllocArrayCreation(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.ARRAY_CREATION)) { } public static Expression Create(ExpressionNodeInfo info) => new ImplicitStackAllocArrayCreation(info).TryPopulate(); @@ -113,9 +124,9 @@ protected override void PopulateExpression(TextWriter trapFile) } } - class ImplicitArrayCreation : ArrayCreation + internal class ImplicitArrayCreation : ArrayCreation { - ImplicitArrayCreation(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.ARRAY_CREATION)) { } + private ImplicitArrayCreation(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.ARRAY_CREATION)) { } public static Expression Create(ExpressionNodeInfo info) => new ImplicitArrayCreation(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs index d82fc1160eac..f785ce65da32 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs @@ -1,15 +1,14 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Kinds; using Microsoft.CodeAnalysis; using System.IO; namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Assignment : Expression + internal class Assignment : Expression { - Assignment(ExpressionNodeInfo info) + private Assignment(ExpressionNodeInfo info) : base(info.SetKind(GetKind(info.Context, (AssignmentExpressionSyntax)info.Node))) { } @@ -46,7 +45,7 @@ protected override void PopulateExpression(TextWriter trapFile) } } - static ExprKind GetAssignmentOperation(Context cx, AssignmentExpressionSyntax syntax) + private static ExprKind GetAssignmentOperation(Context cx, AssignmentExpressionSyntax syntax) { switch (syntax.OperatorToken.Kind()) { @@ -80,10 +79,8 @@ static ExprKind GetAssignmentOperation(Context cx, AssignmentExpressionSyntax sy } } - static ExprKind GetKind(Context cx, AssignmentExpressionSyntax syntax) + private static ExprKind GetKind(Context cx, AssignmentExpressionSyntax syntax) { - var leftSymbol = cx.GetSymbolInfo(syntax.Left); - bool assignEvent = leftSymbol.Symbol != null && leftSymbol.Symbol is IEventSymbol; var kind = GetAssignmentOperation(cx, syntax); var leftType = cx.GetType(syntax.Left); @@ -94,6 +91,9 @@ static ExprKind GetKind(Context cx, AssignmentExpressionSyntax syntax) return kind; } + var leftSymbol = cx.GetSymbolInfo(syntax.Left); + var assignEvent = leftSymbol.Symbol is IEventSymbol; + if (kind == ExprKind.ASSIGN_ADD && assignEvent) { return ExprKind.ADD_EVENT; @@ -112,7 +112,7 @@ static ExprKind GetKind(Context cx, AssignmentExpressionSyntax syntax) /// assignment is not an assignment operator). For example, the operator /// kind of `*=` is `*`. /// - ExprKind? OperatorKind + private ExprKind? OperatorKind { get { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Await.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Await.cs index 4a0d8eac0dd6..2f8726c80706 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Await.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Await.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Await : Expression + internal class Await : Expression { - Await(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.AWAIT)) { } + private Await(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.AWAIT)) { } public static Expression Create(ExpressionNodeInfo info) => new Await(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Base.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Base.cs index e67c18245f65..cfee9aa2e7cf 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Base.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Base.cs @@ -1,11 +1,10 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Base : Expression + internal class Base : Expression { - Base(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.BASE_ACCESS)) { } + private Base(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.BASE_ACCESS)) { } public static Base Create(ExpressionNodeInfo info) => new Base(info); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs index 938a21d7642e..32ee3be5f735 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Binary : Expression + internal class Binary : Expression { - Binary(ExpressionNodeInfo info) + private Binary(ExpressionNodeInfo info) : base(info.SetKind(GetKind(info.Context, (BinaryExpressionSyntax)info.Node))) { } @@ -21,13 +21,13 @@ protected override void PopulateExpression(TextWriter trapFile) CreateDeferred(cx, Syntax.Right, this, 1); } - static ExprKind GetKind(Context cx, BinaryExpressionSyntax node) + private static ExprKind GetKind(Context cx, BinaryExpressionSyntax node) { var k = GetBinaryTokenKind(cx, node.OperatorToken.Kind()); return GetCallType(cx, node).AdjustKind(k); } - static ExprKind GetBinaryTokenKind(Context cx, SyntaxKind kind) + private static ExprKind GetBinaryTokenKind(Context cx, SyntaxKind kind) { switch (kind) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs index 9af08f696e35..52e2529b54ff 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Cast : Expression + internal class Cast : Expression { - Cast(ExpressionNodeInfo info) : base(info.SetKind(UnaryOperatorKind(info.Context, ExprKind.CAST, info.Node))) { } + private Cast(ExpressionNodeInfo info) : base(info.SetKind(UnaryOperatorKind(info.Context, ExprKind.CAST, info.Node))) { } public static Expression Create(ExpressionNodeInfo info) => new Cast(info).TryPopulate(); @@ -15,8 +15,9 @@ protected override void PopulateExpression(TextWriter trapFile) Create(cx, Syntax.Expression, this, 0); if (Kind == ExprKind.CAST) - // Type cast + { // Type cast TypeAccess.Create(new ExpressionNodeInfo(cx, Syntax.Type, this, 1)); + } else { // Type conversion diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Checked.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Checked.cs index 4988a3c469d3..e6958c4511cc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Checked.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Checked.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Checked : Expression + internal class Checked : Expression { - Checked(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.CHECKED)) { } + private Checked(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.CHECKED)) { } public static Expression Create(ExpressionNodeInfo info) => new Checked(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Conditional.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Conditional.cs index 48b5031cdd25..bfa4a836feb2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Conditional.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Conditional.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Conditional : Expression + internal class Conditional : Expression { - Conditional(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.CONDITIONAL)) { } + private Conditional(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.CONDITIONAL)) { } public static Expression Create(ExpressionNodeInfo info) => new Conditional(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs index 460dd65d2d6a..326516bba1b1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Default : Expression + internal class Default : Expression { - Default(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.DEFAULT)) { } + private Default(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.DEFAULT)) { } public static Expression Create(ExpressionNodeInfo info) => new Default(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Discard.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Discard.cs index d812c19496f2..194261c46141 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Discard.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Discard.cs @@ -1,18 +1,17 @@ -īģŋusing Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; +īģŋusing Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Entities; using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Discard : Expression + internal class Discard : Expression { public Discard(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.DISCARD)) { } - Discard(Context cx, CSharpSyntaxNode syntax, IExpressionParentEntity parent, int child) : + private Discard(Context cx, CSharpSyntaxNode syntax, IExpressionParentEntity parent, int child) : base(new ExpressionInfo(cx, Entities.Type.Create(cx, cx.GetType(syntax)), cx.Create(syntax.GetLocation()), ExprKind.DISCARD, parent, child, false, null)) { } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs index b0252f9f8de5..afc4cd2cd93d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs @@ -1,48 +1,45 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Kinds; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis; -using Semmle.Extraction.Entities; using System.IO; namespace Semmle.Extraction.CSharp.Entities.Expressions { - abstract class ElementAccess : Expression + internal abstract class ElementAccess : Expression { protected ElementAccess(ExpressionNodeInfo info, ExpressionSyntax qualifier, BracketedArgumentListSyntax argumentList) : base(info.SetKind(GetKind(info.Context, qualifier))) { - Qualifier = qualifier; - ArgumentList = argumentList; + this.qualifier = qualifier; + this.argumentList = argumentList; } - readonly ExpressionSyntax Qualifier; - readonly BracketedArgumentListSyntax ArgumentList; + private readonly ExpressionSyntax qualifier; + private readonly BracketedArgumentListSyntax argumentList; protected override void PopulateExpression(TextWriter trapFile) { if (Kind == ExprKind.POINTER_INDIRECTION) { - var qualifierInfo = new ExpressionNodeInfo(cx, Qualifier, this, 0); + var qualifierInfo = new ExpressionNodeInfo(cx, qualifier, this, 0); var add = new Expression(new ExpressionInfo(cx, qualifierInfo.Type, Location, ExprKind.ADD, this, 0, false, null)); qualifierInfo.SetParent(add, 0); CreateFromNode(qualifierInfo); - PopulateArguments(trapFile, ArgumentList, 1); + PopulateArguments(trapFile, argumentList, 1); } else { var child = -1; - Create(cx, Qualifier, this, child++); - foreach (var a in ArgumentList.Arguments) + Create(cx, qualifier, this, child++); + foreach (var a in argumentList.Arguments) { cx.Extract(a, this, child++); } var symbolInfo = cx.GetSymbolInfo(base.Syntax); - var indexer = symbolInfo.Symbol as IPropertySymbol; - if (indexer != null) + if (symbolInfo.Symbol is IPropertySymbol indexer) { trapFile.expr_access(this, Indexer.Create(cx, indexer)); } @@ -51,12 +48,13 @@ protected override void PopulateExpression(TextWriter trapFile) public sealed override Microsoft.CodeAnalysis.Location ReportingLocation => base.ReportingLocation; - static ExprKind GetKind(Context cx, ExpressionSyntax qualifier) + private static ExprKind GetKind(Context cx, ExpressionSyntax qualifier) { var qualifierType = cx.GetType(qualifier); // This is a compilation error, so make a guess and continue. - if (qualifierType.Symbol == null) return ExprKind.ARRAY_ACCESS; + if (qualifierType.Symbol == null) + return ExprKind.ARRAY_ACCESS; if (qualifierType.Symbol.TypeKind == Microsoft.CodeAnalysis.TypeKind.Pointer) { @@ -64,25 +62,25 @@ static ExprKind GetKind(Context cx, ExpressionSyntax qualifier) return ExprKind.POINTER_INDIRECTION; } - return IsDynamic(cx, qualifier) ? - ExprKind.DYNAMIC_ELEMENT_ACCESS : - qualifierType.Symbol.TypeKind == Microsoft.CodeAnalysis.TypeKind.Array ? - ExprKind.ARRAY_ACCESS : - ExprKind.INDEXER_ACCESS; + return IsDynamic(cx, qualifier) + ? ExprKind.DYNAMIC_ELEMENT_ACCESS + : qualifierType.Symbol.TypeKind == Microsoft.CodeAnalysis.TypeKind.Array + ? ExprKind.ARRAY_ACCESS + : ExprKind.INDEXER_ACCESS; } } - class NormalElementAccess : ElementAccess + internal class NormalElementAccess : ElementAccess { - NormalElementAccess(ExpressionNodeInfo info) + private NormalElementAccess(ExpressionNodeInfo info) : base(info, ((ElementAccessExpressionSyntax)info.Node).Expression, ((ElementAccessExpressionSyntax)info.Node).ArgumentList) { } public static Expression Create(ExpressionNodeInfo info) => new NormalElementAccess(info).TryPopulate(); } - class BindingElementAccess : ElementAccess + internal class BindingElementAccess : ElementAccess { - BindingElementAccess(ExpressionNodeInfo info) + private BindingElementAccess(ExpressionNodeInfo info) : base(info, FindConditionalQualifier(info.Node), ((ElementBindingExpressionSyntax)info.Node).ArgumentList) { } public static Expression Create(ExpressionNodeInfo info) => new BindingElementAccess(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs index eb0cd847295b..889dbfd378bc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs @@ -1,7 +1,5 @@ -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs index 503136fa22fb..a9884a5df5d8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs @@ -1,13 +1,9 @@ using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Kinds; -using System.IO; namespace Semmle.Extraction.CSharp.Entities.Expressions { - class ImplicitCast : Expression + internal class ImplicitCast : Expression { public Expression Expr { @@ -22,7 +18,7 @@ public ImplicitCast(ExpressionNodeInfo info) } public ImplicitCast(ExpressionNodeInfo info, IMethodSymbol method) - : base(new ExpressionInfo(info.Context, Entities.Type.Create(info.Context, info.ConvertedType), info.Location, ExprKind.OPERATOR_INVOCATION, info.Parent, info.Child, true, info.ExprValue) ) + : base(new ExpressionInfo(info.Context, Entities.Type.Create(info.Context, info.ConvertedType), info.Location, ExprKind.OPERATOR_INVOCATION, info.Parent, info.Child, true, info.ExprValue)) { Expr = Factory.Create(info.SetParent(this, 0)); @@ -50,12 +46,13 @@ public static Expression Create(ExpressionNodeInfo info) if (conversion.MethodSymbol != null) { - bool convertedToDelegate = Entities.Type.IsDelegate(convertedType.Symbol); + var convertedToDelegate = Entities.Type.IsDelegate(convertedType.Symbol); if (convertedToDelegate) { - var objectCreation = info.Parent as ExplicitObjectCreation; - bool isExplicitConversion = objectCreation != null && objectCreation.Kind == ExprKind.EXPLICIT_DELEGATE_CREATION; + var isExplicitConversion = + info.Parent is ExplicitObjectCreation objectCreation && + objectCreation.Kind == ExprKind.EXPLICIT_DELEGATE_CREATION; if (!isExplicitConversion) { @@ -72,7 +69,7 @@ public static Expression Create(ExpressionNodeInfo info) return new ImplicitCast(info, conversion.MethodSymbol); } - bool implicitUpcast = conversion.IsImplicit && + var implicitUpcast = conversion.IsImplicit && convertedType.Symbol != null && !conversion.IsBoxing && ( @@ -86,6 +83,15 @@ public static Expression Create(ExpressionNodeInfo info) return new ImplicitCast(info); } + if (conversion.IsIdentity && conversion.IsImplicit && + convertedType.Symbol is IPointerTypeSymbol && + !(resolvedType.Symbol is IPointerTypeSymbol)) + { + // int[] -> int* + // string -> char* + return new ImplicitCast(info); + } + // Default: Just create the expression without a conversion. return Factory.Create(info); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs index 6af92a0d8cb4..cec690ea79d1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs @@ -1,21 +1,20 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Entities; using Semmle.Extraction.Kinds; using System.IO; namespace Semmle.Extraction.CSharp.Entities.Expressions { - abstract class Initializer : Expression + internal abstract class Initializer : Expression { protected Initializer(ExpressionNodeInfo info) : base(info) { } } - class ArrayInitializer : Expression + internal class ArrayInitializer : Expression { - ArrayInitializer(ExpressionNodeInfo info) : base(info.SetType(NullType.Create(info.Context)).SetKind(ExprKind.ARRAY_INIT)) { } + private ArrayInitializer(ExpressionNodeInfo info) : base(info.SetType(NullType.Create(info.Context)).SetKind(ExprKind.ARRAY_INIT)) { } public static Expression Create(ExpressionNodeInfo info) => new ArrayInitializer(info).TryPopulate(); @@ -39,9 +38,9 @@ protected override void PopulateExpression(TextWriter trapFile) } // Array initializer { ..., ... }. - class ImplicitArrayInitializer : Initializer + internal class ImplicitArrayInitializer : Initializer { - ImplicitArrayInitializer(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.ARRAY_CREATION)) { } + private ImplicitArrayInitializer(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.ARRAY_CREATION)) { } public static Expression Create(ExpressionNodeInfo info) => new ImplicitArrayInitializer(info).TryPopulate(); @@ -52,9 +51,9 @@ protected override void PopulateExpression(TextWriter trapFile) } } - class ObjectInitializer : Initializer + internal class ObjectInitializer : Initializer { - ObjectInitializer(ExpressionNodeInfo info) + private ObjectInitializer(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.OBJECT_INIT)) { } public static Expression Create(ExpressionNodeInfo info) => new ObjectInitializer(info).TryPopulate(); @@ -65,16 +64,14 @@ protected override void PopulateExpression(TextWriter trapFile) foreach (var init in Syntax.Expressions) { - var assignment = init as AssignmentExpressionSyntax; - - if (assignment != null) + if (init is AssignmentExpressionSyntax assignment) { var assignmentInfo = new ExpressionNodeInfo(cx, init, this, child++).SetKind(ExprKind.SIMPLE_ASSIGN); var assignmentEntity = new Expression(assignmentInfo); var typeInfoRight = cx.GetTypeInfo(assignment.Right); if (typeInfoRight.Type is null) // The type may be null for nested initializers such as - // ``` + // ```csharp // new ClassWithArrayField() { As = { [0] = a } } // ``` // In this case we take the type from the assignment @@ -86,7 +83,7 @@ protected override void PopulateExpression(TextWriter trapFile) // If the target is null, then assume that this is an array initializer (of the form `[...] = ...`) - Expression access = target.Symbol is null ? + var access = target.Symbol is null ? new Expression(new ExpressionNodeInfo(cx, assignment.Left, assignmentEntity, 1).SetKind(ExprKind.ARRAY_ACCESS)) : Access.Create(new ExpressionNodeInfo(cx, assignment.Left, assignmentEntity, 1), target.Symbol, false, cx.CreateEntity(target.Symbol)); @@ -94,7 +91,7 @@ protected override void PopulateExpression(TextWriter trapFile) { // An array/indexer initializer of the form `[...] = ...` - int indexChild = 0; + var indexChild = 0; foreach (var arg in iea.ArgumentList.Arguments) { Expression.Create(cx, arg.Expression, access, indexChild++); @@ -110,9 +107,9 @@ protected override void PopulateExpression(TextWriter trapFile) } } - class CollectionInitializer : Initializer + internal class CollectionInitializer : Initializer { - CollectionInitializer(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.COLLECTION_INIT)) { } + private CollectionInitializer(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.COLLECTION_INIT)) { } public static Expression Create(ExpressionNodeInfo info) => new CollectionInitializer(info).TryPopulate(); @@ -139,7 +136,7 @@ protected override void PopulateExpression(TextWriter trapFile) var init = (InitializerExpressionSyntax)i; - int addChild = 0; + var addChild = 0; foreach (var arg in init.Expressions) { Create(cx, arg, invocation, addChild++); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs index b333e8d00484..152ff145897c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs @@ -7,9 +7,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class InterpolatedString : Expression + internal class InterpolatedString : Expression { - InterpolatedString(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.INTERPOLATED_STRING)) { } + private InterpolatedString(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.INTERPOLATED_STRING)) { } public static Expression Create(ExpressionNodeInfo info) => new InterpolatedString(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs index 50aede6e52cb..7a0e2fcc6836 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs @@ -7,15 +7,15 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Invocation : Expression + internal class Invocation : Expression { - Invocation(ExpressionNodeInfo info) + private Invocation(ExpressionNodeInfo info) : base(info.SetKind(GetKind(info))) { this.info = info; } - readonly ExpressionNodeInfo info; + private readonly ExpressionNodeInfo info; public static Expression Create(ExpressionNodeInfo info) => new Invocation(info).TryPopulate(); @@ -53,12 +53,11 @@ protected override void PopulateExpression(TextWriter trapFile) if (target != null && !target.IsStatic) { // Implicit `this` qualifier; add explicitly - var callingMethod = cx.GetModel(Syntax).GetEnclosingSymbol(Location.symbol.SourceSpan.Start) as IMethodSymbol; - if (callingMethod == null) - cx.ModelError(Syntax, "Couldn't determine implicit this type"); - else + if (cx.GetModel(Syntax).GetEnclosingSymbol(Location.symbol.SourceSpan.Start) is IMethodSymbol callingMethod) This.CreateImplicit(cx, Entities.Type.Create(cx, callingMethod.ContainingType), Location, this, child++); + else + cx.ModelError(Syntax, "Couldn't determine implicit this type"); } else { @@ -94,7 +93,7 @@ protected override void PopulateExpression(TextWriter trapFile) trapFile.expr_call(this, targetKey); } - static bool IsDynamicCall(ExpressionNodeInfo info) + private static bool IsDynamicCall(ExpressionNodeInfo info) { // Either the qualifier (Expression) is dynamic, // or one of the arguments is dynamic. @@ -111,7 +110,8 @@ public IMethodSymbol TargetSymbol { var si = SymbolInfo; - if (si.Symbol != null) return si.Symbol as IMethodSymbol; + if (si.Symbol != null) + return si.Symbol as IMethodSymbol; if (si.CandidateReason == CandidateReason.OverloadResolutionFailure) { @@ -119,10 +119,10 @@ public IMethodSymbol TargetSymbol // For some reason, typeof(X).InvokeMember(...) fails to resolve the correct // InvokeMember() method, even though the number of parameters clearly identifies the correct method - var candidates = si.CandidateSymbols. - OfType(). - Where(method => method.Parameters.Length >= Syntax.ArgumentList.Arguments.Count). - Where(method => method.Parameters.Count(p => !p.HasExplicitDefaultValue) <= Syntax.ArgumentList.Arguments.Count); + var candidates = si.CandidateSymbols + .OfType() + .Where(method => method.Parameters.Length >= Syntax.ArgumentList.Arguments.Count) + .Where(method => method.Parameters.Count(p => !p.HasExplicitDefaultValue) <= Syntax.ArgumentList.Arguments.Count); return cx.Extractor.Standalone ? candidates.FirstOrDefault() : @@ -133,13 +133,15 @@ public IMethodSymbol TargetSymbol } } - static bool IsDelegateCall(ExpressionNodeInfo info) + private static bool IsDelegateCall(ExpressionNodeInfo info) { var si = info.SymbolInfo; if (si.CandidateReason == CandidateReason.OverloadResolutionFailure && si.CandidateSymbols.OfType().All(s => s.MethodKind == MethodKind.DelegateInvoke)) + { return true; + } // Delegate variable is a dynamic var node = (InvocationExpressionSyntax)info.Node; @@ -147,36 +149,37 @@ static bool IsDelegateCall(ExpressionNodeInfo info) node.Expression is IdentifierNameSyntax && IsDynamic(info.Context, node.Expression) && si.Symbol == null) + { return true; + } return si.Symbol != null && si.Symbol.Kind == SymbolKind.Method && ((IMethodSymbol)si.Symbol).MethodKind == MethodKind.DelegateInvoke; } - static bool IsLocalFunctionInvocation(ExpressionNodeInfo info) + private static bool IsLocalFunctionInvocation(ExpressionNodeInfo info) { - var target = info.SymbolInfo.Symbol as IMethodSymbol; - return target != null && target.MethodKind == MethodKind.LocalFunction; + return info.SymbolInfo.Symbol is IMethodSymbol target && + target.MethodKind == MethodKind.LocalFunction; } - static ExprKind GetKind(ExpressionNodeInfo info) + private static ExprKind GetKind(ExpressionNodeInfo info) { - return IsNameof((InvocationExpressionSyntax)info.Node) ? - ExprKind.NAMEOF : - IsDelegateCall(info) ? - ExprKind.DELEGATE_INVOCATION : - IsLocalFunctionInvocation(info) ? - ExprKind.LOCAL_FUNCTION_INVOCATION : - ExprKind.METHOD_INVOCATION; + return IsNameof((InvocationExpressionSyntax)info.Node) + ? ExprKind.NAMEOF + : IsDelegateCall(info) + ? ExprKind.DELEGATE_INVOCATION + : IsLocalFunctionInvocation(info) + ? ExprKind.LOCAL_FUNCTION_INVOCATION + : ExprKind.METHOD_INVOCATION; } - static bool IsNameof(InvocationExpressionSyntax syntax) + private static bool IsNameof(InvocationExpressionSyntax syntax) { // Odd that this is not a separate expression type. // Maybe it will be in the future. - var id = syntax.Expression as IdentifierNameSyntax; - return id != null && id.Identifier.Text == "nameof"; + return syntax.Expression is IdentifierNameSyntax id && id.Identifier.Text == "nameof"; } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/IsPattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/IsPattern.cs index 637e0dee84d1..5bdb02f5ac35 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/IsPattern.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/IsPattern.cs @@ -7,7 +7,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - static class PatternExtensions + internal static class PatternExtensions { public static Expression CreatePattern(this Context cx, PatternSyntax syntax, IExpressionParentEntity parent, int child) { @@ -50,10 +50,8 @@ public static Expression CreatePattern(this Context cx, PatternSyntax syntax, IE return VariableDeclaration.Create(cx, symbol, type, null, cx.Create(syntax.GetLocation()), true, parent, child); } - else - { - throw new InternalError(varPattern, "Unable to get the declared symbol of the var pattern designation."); - } + + throw new InternalError(varPattern, "Unable to get the declared symbol of the var pattern designation."); default: throw new InternalError("var pattern designation is unhandled"); } @@ -67,7 +65,7 @@ public static Expression CreatePattern(this Context cx, PatternSyntax syntax, IE } } - class PropertyPattern : Expression + internal class PropertyPattern : Expression { internal PropertyPattern(Context cx, PropertyPatternClauseSyntax pp, IExpressionParentEntity parent, int child) : base(new ExpressionInfo(cx, Entities.NullType.Create(cx), cx.Create(pp.GetLocation()), ExprKind.PROPERTY_PATTERN, parent, child, false, null)) @@ -82,7 +80,7 @@ internal PropertyPattern(Context cx, PropertyPatternClauseSyntax pp, IExpression } } - class PositionalPattern : Expression + internal class PositionalPattern : Expression { internal PositionalPattern(Context cx, PositionalPatternClauseSyntax posPc, IExpressionParentEntity parent, int child) : base(new ExpressionInfo(cx, Entities.NullType.Create(cx), cx.Create(posPc.GetLocation()), ExprKind.POSITIONAL_PATTERN, parent, child, false, null)) @@ -95,7 +93,7 @@ internal PositionalPattern(Context cx, PositionalPatternClauseSyntax posPc, IExp } } - class RecursivePattern : Expression + internal class RecursivePattern : Expression { /// /// Creates and populates a recursive pattern. @@ -132,7 +130,7 @@ public RecursivePattern(Context cx, RecursivePatternSyntax syntax, IExpressionPa } } - class IsPattern : Expression + internal class IsPattern : Expression { private IsPattern(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.IS)) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Lambda.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Lambda.cs index 27fb113f9acd..28e2b3b7601d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Lambda.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Lambda.cs @@ -9,19 +9,19 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Lambda : Expression, IStatementParentEntity + internal class Lambda : Expression, IStatementParentEntity { bool IStatementParentEntity.IsTopLevelParent => false; protected override void PopulateExpression(TextWriter trapFile) { } - void VisitParameter(ParameterSyntax p) + private void VisitParameter(ParameterSyntax p) { var symbol = cx.GetModel(p).GetDeclaredSymbol(p); Parameter.Create(cx, symbol, this); } - Lambda(ExpressionNodeInfo info, CSharpSyntaxNode body, IEnumerable @params) + private Lambda(ExpressionNodeInfo info, CSharpSyntaxNode body, IEnumerable @params) : base(info) { // No need to use `Populate` as the population happens later @@ -30,27 +30,28 @@ void VisitParameter(ParameterSyntax p) foreach (var param in @params) VisitParameter(param); - if (body is ExpressionSyntax) - Create(cx, (ExpressionSyntax)body, this, 0); - else if (body is BlockSyntax) - Statements.Block.Create(cx, (BlockSyntax)body, this, 0); + if (body is ExpressionSyntax exprBody) + Create(cx, exprBody, this, 0); + else if (body is BlockSyntax blockBody) + Statements.Block.Create(cx, blockBody, this, 0); else cx.ModelError(body, "Unhandled lambda body"); }); } - Lambda(ExpressionNodeInfo info, ParenthesizedLambdaExpressionSyntax node) + private Lambda(ExpressionNodeInfo info, ParenthesizedLambdaExpressionSyntax node) : this(info.SetKind(ExprKind.LAMBDA), node.Body, node.ParameterList.Parameters) { } public static Lambda Create(ExpressionNodeInfo info, ParenthesizedLambdaExpressionSyntax node) => new Lambda(info, node); - Lambda(ExpressionNodeInfo info, SimpleLambdaExpressionSyntax node) + private Lambda(ExpressionNodeInfo info, SimpleLambdaExpressionSyntax node) : this(info.SetKind(ExprKind.LAMBDA), node.Body, Enumerators.Singleton(node.Parameter)) { } public static Lambda Create(ExpressionNodeInfo info, SimpleLambdaExpressionSyntax node) => new Lambda(info, node); - Lambda(ExpressionNodeInfo info, AnonymousMethodExpressionSyntax node) : - this(info.SetKind(ExprKind.ANONYMOUS_METHOD), node.Body, node.ParameterList == null ? Enumerable.Empty() : node.ParameterList.Parameters) { } + private Lambda(ExpressionNodeInfo info, AnonymousMethodExpressionSyntax node) : + this(info.SetKind(ExprKind.ANONYMOUS_METHOD), node.Body, node.ParameterList == null ? Enumerable.Empty() : node.ParameterList.Parameters) + { } public static Lambda Create(ExpressionNodeInfo info, AnonymousMethodExpressionSyntax node) => new Lambda(info, node); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs index 528c6a8a9f52..7afd4ee55ae9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs @@ -1,23 +1,22 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Kinds; using Microsoft.CodeAnalysis; -using Semmle.Extraction.CSharp.Populators; using Microsoft.CodeAnalysis.CSharp; using System.IO; namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Literal : Expression + internal class Literal : Expression { - Literal(ExpressionNodeInfo info) : base(info.SetKind(GetKind(info)) ) { } + private Literal(ExpressionNodeInfo info) : base(info.SetKind(GetKind(info))) { } public static Expression Create(ExpressionNodeInfo info) => new Literal(info).TryPopulate(); protected override void PopulateExpression(TextWriter trapFile) { } - static ExprKind GetKind(ExpressionNodeInfo info) + private static ExprKind GetKind(ExpressionNodeInfo info) { - switch(info.Node.Kind()) + switch (info.Node.Kind()) { case SyntaxKind.DefaultLiteralExpression: return ExprKind.DEFAULT; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MakeRef.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MakeRef.cs index bc8630609f00..751d6b6e92c3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MakeRef.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MakeRef.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class MakeRef : Expression + internal class MakeRef : Expression { - MakeRef(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.REF)) { } + private MakeRef(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.REF)) { } public static Expression Create(ExpressionNodeInfo info) => new MakeRef(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs index 0bc84ca9c0c3..7d2777540f0f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs @@ -1,15 +1,11 @@ using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { - class MemberAccess : Expression + internal class MemberAccess : Expression { - readonly IEntity Target; - private MemberAccess(ExpressionNodeInfo info, ExpressionSyntax qualifier, ISymbol target) : base(info) { var trapFile = info.Context.TrapWriter.Writer; @@ -22,15 +18,17 @@ private MemberAccess(ExpressionNodeInfo info, ExpressionSyntax qualifier, ISymbo } else { - Target = cx.CreateEntity(target); - trapFile.expr_access(this, Target); + var t = cx.CreateEntity(target); + trapFile.expr_access(this, t); } } - public static Expression Create(ExpressionNodeInfo info, ConditionalAccessExpressionSyntax node) => + public static Expression Create(ExpressionNodeInfo info, ConditionalAccessExpressionSyntax node) + { // The qualifier is located by walking the syntax tree. // `node.WhenNotNull` will contain a MemberBindingExpressionSyntax, calling the method below. - CreateFromNode(new ExpressionNodeInfo(info.Context, node.WhenNotNull, info.Parent, info.Child, info.TypeInfo)); + return CreateFromNode(new ExpressionNodeInfo(info.Context, node.WhenNotNull, info.Parent, info.Child, info.TypeInfo)); + } public static Expression Create(ExpressionNodeInfo info, MemberBindingExpressionSyntax node) { @@ -42,7 +40,7 @@ public static Expression Create(ExpressionNodeInfo info, MemberBindingExpression public static Expression Create(ExpressionNodeInfo info, MemberAccessExpressionSyntax node) => Create(info, node.Expression, node.Name); - static Expression Create(ExpressionNodeInfo info, ExpressionSyntax expression, SimpleNameSyntax name) + private static Expression Create(ExpressionNodeInfo info, ExpressionSyntax expression, SimpleNameSyntax name) { if (IsDynamic(info.Context, expression)) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Name.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Name.cs index 931b3a740fda..d44c85d6ddc0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Name.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Name.cs @@ -1,12 +1,10 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using System.Linq; namespace Semmle.Extraction.CSharp.Entities.Expressions { - static class Name + internal static class Name { public static Expression Create(ExpressionNodeInfo info) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation.cs index 98723d6111c2..ae3d0f105068 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation.cs @@ -1,7 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Entities; using Semmle.Extraction.Kinds; using System.IO; @@ -9,27 +8,28 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - abstract class ObjectCreation : Expression where SyntaxNode : ExpressionSyntax + internal abstract class ObjectCreation : Expression + where TExpressionSyntax : ExpressionSyntax { protected ObjectCreation(ExpressionNodeInfo info) : base(info) { } } // new Foo(...) { ... }. - class ExplicitObjectCreation : ObjectCreation + internal class ExplicitObjectCreation : ObjectCreation { - static bool IsDynamicObjectCreation(Context cx, ObjectCreationExpressionSyntax node) + private static bool IsDynamicObjectCreation(Context cx, ObjectCreationExpressionSyntax node) { return node.ArgumentList != null && node.ArgumentList.Arguments.Any(arg => IsDynamic(cx, arg.Expression)); } - static ExprKind GetKind(Context cx, ObjectCreationExpressionSyntax node) + private static ExprKind GetKind(Context cx, ObjectCreationExpressionSyntax node) { var si = cx.GetModel(node).GetSymbolInfo(node.Type); return Entities.Type.IsDelegate(si.Symbol as INamedTypeSymbol) ? ExprKind.EXPLICIT_DELEGATE_CREATION : ExprKind.OBJECT_CREATION; } - ExplicitObjectCreation(ExpressionNodeInfo info) + private ExplicitObjectCreation(ExpressionNodeInfo info) : base(info.SetKind(GetKind(info.Context, (ObjectCreationExpressionSyntax)info.Node))) { } public static Expression Create(ExpressionNodeInfo info) => new ExplicitObjectCreation(info).TryPopulate(); @@ -77,7 +77,7 @@ protected override void PopulateExpression(TextWriter trapFile) TypeMention.Create(cx, Syntax.Type, this, Type); } - static SyntaxToken? GetDynamicName(CSharpSyntaxNode name) + private static SyntaxToken? GetDynamicName(CSharpSyntaxNode name) { switch (name.Kind()) { @@ -94,7 +94,7 @@ protected override void PopulateExpression(TextWriter trapFile) } } - class ImplicitObjectCreation : ObjectCreation + internal class ImplicitObjectCreation : ObjectCreation { public ImplicitObjectCreation(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.OBJECT_CREATION)) { } @@ -113,7 +113,7 @@ protected override void PopulateExpression(TextWriter trapFile) } var child = 0; - Expression objectInitializer = Syntax.Initializers.Any() ? + var objectInitializer = Syntax.Initializers.Any() ? new Expression(new ExpressionInfo(cx, Type, Location, ExprKind.OBJECT_INIT, this, -1, false, null)) : null; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PointerMemberAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PointerMemberAccess.cs index 61ee4a8ef9af..01df338d1b33 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PointerMemberAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PointerMemberAccess.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class PointerMemberAccess : Expression + internal class PointerMemberAccess : Expression { - PointerMemberAccess(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.POINTER_INDIRECTION)) { } + private PointerMemberAccess(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.POINTER_INDIRECTION)) { } public static Expression Create(ExpressionNodeInfo info) => new PointerMemberAccess(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PostfixUnary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PostfixUnary.cs index 71f0d8e0378e..30c6058d95ff 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PostfixUnary.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/PostfixUnary.cs @@ -4,26 +4,26 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class PostfixUnary : Expression + internal class PostfixUnary : Expression { - PostfixUnary(ExpressionNodeInfo info, ExprKind kind, ExpressionSyntax operand) + private PostfixUnary(ExpressionNodeInfo info, ExprKind kind, ExpressionSyntax operand) : base(info.SetKind(UnaryOperatorKind(info.Context, kind, info.Node))) { - Operand = operand; - OperatorKind = kind; + this.operand = operand; + operatorKind = kind; } - readonly ExpressionSyntax Operand; - readonly ExprKind OperatorKind; + private readonly ExpressionSyntax operand; + private readonly ExprKind operatorKind; public static Expression Create(ExpressionNodeInfo info, ExpressionSyntax operand) => new PostfixUnary(info, info.Kind, operand).TryPopulate(); protected override void PopulateExpression(TextWriter trapFile) { - Create(cx, Operand, this, 0); + Create(cx, operand, this, 0); OperatorCall(trapFile, Syntax); - if ((OperatorKind == ExprKind.POST_INCR || OperatorKind == ExprKind.POST_DECR) && + if ((operatorKind == ExprKind.POST_INCR || operatorKind == ExprKind.POST_DECR) && Kind == ExprKind.OPERATOR_INVOCATION) { trapFile.mutator_invocation_mode(this, 2); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs index 03cd8545a76a..83c4a25d3a4e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs @@ -4,12 +4,11 @@ using System.Linq; using Semmle.Extraction.Kinds; using System.Collections.Generic; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Entities; namespace Semmle.Extraction.CSharp.Entities.Expressions { - static class Query + internal static class Query { /// /// An expression representing a call in a LINQ query. @@ -35,7 +34,7 @@ public QueryCall(Context cx, IMethodSymbol method, SyntaxNode clause, IExpressio /// /// Represents a chain of method calls (the operand being recursive). /// - abstract class Clause + private abstract class Clause { protected readonly IMethodSymbol method; protected readonly List arguments = new List(); @@ -82,7 +81,9 @@ protected Expression DeclareRangeVariable(Context cx, IExpressionParentEntity pa } } else + { declType = type; + } var decl = VariableDeclaration.Create(cx, variableSymbol, @@ -114,10 +115,10 @@ protected void PopulateArguments(Context cx, QueryCall callExpr, int child) public abstract Expression Populate(Context cx, IExpressionParentEntity parent, int child); } - class RangeClause : Clause + private class RangeClause : Clause { - readonly ISymbol declaration; - readonly SyntaxToken name; + private readonly ISymbol declaration; + private readonly SyntaxToken name; public RangeClause(IMethodSymbol method, SyntaxNode node, ISymbol declaration, SyntaxToken name) : base(method, node) { @@ -129,12 +130,12 @@ public override Expression Populate(Context cx, IExpressionParentEntity parent, DeclareRangeVariable(cx, parent, child, true, declaration, name); } - class LetClause : Clause + private class LetClause : Clause { - readonly Clause operand; - readonly ISymbol declaration; - readonly SyntaxToken name; - ISymbol intoDeclaration; + private readonly Clause operand; + private readonly ISymbol declaration; + private readonly SyntaxToken name; + private ISymbol intoDeclaration; public LetClause(Clause operand, IMethodSymbol method, SyntaxNode node, ISymbol declaration, SyntaxToken name) : base(method, node) { @@ -149,7 +150,7 @@ public Clause WithInto(ISymbol into) return this; } - void DeclareIntoVariable(Context cx, IExpressionParentEntity parent, int intoChild, bool getElement) + private void DeclareIntoVariable(Context cx, IExpressionParentEntity parent, int intoChild, bool getElement) { if (intoDeclaration != null) DeclareRangeVariable(cx, parent, intoChild, getElement, intoDeclaration, name); @@ -169,9 +170,9 @@ public override Expression Populate(Context cx, IExpressionParentEntity parent, } } - class CallClause : Clause + private class CallClause : Clause { - readonly Clause operand; + private readonly Clause operand; public CallClause(Clause operand, IMethodSymbol method, SyntaxNode node) : base(method, node) { @@ -194,12 +195,12 @@ public override Expression Populate(Context cx, IExpressionParentEntity parent, /// The extraction context. /// The query expression. /// A "syntax tree" of the query. - static Clause ConstructQueryExpression(Context cx, QueryExpressionSyntax node) + private static Clause ConstructQueryExpression(Context cx, QueryExpressionSyntax node) { var info = cx.GetModel(node).GetQueryClauseInfo(node.FromClause); var method = info.OperationInfo.Symbol as IMethodSymbol; - Clause clauseExpr = new RangeClause(method, node.FromClause, cx.GetModel(node).GetDeclaredSymbol(node.FromClause), node.FromClause.Identifier).AddArgument(node.FromClause.Expression); + var clauseExpr = new RangeClause(method, node.FromClause, cx.GetModel(node).GetDeclaredSymbol(node.FromClause), node.FromClause.Identifier).AddArgument(node.FromClause.Expression); foreach (var qc in node.Body.Clauses) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RangeExpression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RangeExpression.cs index c9c00b2a76c3..db5cd1381553 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RangeExpression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RangeExpression.cs @@ -4,7 +4,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class RangeExpression : Expression + internal class RangeExpression : Expression { private RangeExpression(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.RANGE)) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Ref.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Ref.cs index e916259e772f..5b38b9b8ee61 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Ref.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Ref.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Ref : Expression + internal class Ref : Expression { - Ref(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.REF)) { } + private Ref(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.REF)) { } public static Expression Create(ExpressionNodeInfo info) => new Ref(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RefType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RefType.cs index ed2049c27f99..37f7e2a3f760 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RefType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RefType.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class RefType : Expression + internal class RefType : Expression { - RefType(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.UNKNOWN)) { } + private RefType(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.UNKNOWN)) { } public static Expression Create(ExpressionNodeInfo info) => new RefType(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RefValue.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RefValue.cs index 93c2542c3135..2372cc4bb09e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RefValue.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/RefValue.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class RefValue : Expression + internal class RefValue : Expression { - RefValue(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.REF)) { } + private RefValue(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.REF)) { } public static Expression Create(ExpressionNodeInfo info) => new RefValue(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Sizeof.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Sizeof.cs index 1e06cfa72e4b..a4b89928e001 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Sizeof.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Sizeof.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class SizeOf : Expression + internal class SizeOf : Expression { - SizeOf(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.SIZEOF)) { } + private SizeOf(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.SIZEOF)) { } public static Expression Create(ExpressionNodeInfo info) => new SizeOf(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs index 5316998a38f6..b5bfebea92ec 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs @@ -5,7 +5,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Switch : Expression + internal class Switch : Expression { private Switch(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.SWITCH)) { @@ -18,18 +18,19 @@ private Switch(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.SWITCH)) protected override void PopulateExpression(TextWriter trapFile) { SwitchedExpr = Expression.Create(cx, Syntax.GoverningExpression, this, -1); - int child = 0; - foreach (var arm in Syntax.Arms) + for (var i = 0; i < Syntax.Arms.Count; i++) { - new SwitchCase(cx, arm, this, child++); + new SwitchCase(cx, Syntax.Arms[i], this, i); } } } - class SwitchCase : Expression + internal class SwitchCase : Expression { internal SwitchCase(Context cx, SwitchExpressionArmSyntax arm, Switch parent, int child) : - base(new ExpressionInfo(cx, parent.SwitchedExpr.Type, cx.Create(arm.GetLocation()), ExprKind.SWITCH_CASE, parent, child, false, null)) + base(new ExpressionInfo( + cx, Entities.Type.Create(cx, cx.GetType(arm.Expression)), cx.Create(arm.GetLocation()), + ExprKind.SWITCH_CASE, parent, child, false, null)) { cx.CreatePattern(arm.Pattern, this, 0); if (arm.WhenClause is WhenClauseSyntax when) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/This.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/This.cs index 490bd8b56e4c..838e494ff00f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/This.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/This.cs @@ -3,9 +3,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class This : Expression + internal class This : Expression { - This(IExpressionInfo info) : base(info) { } + private This(IExpressionInfo info) : base(info) { } public static This CreateImplicit(Context cx, Type @class, Extraction.Entities.Location loc, IExpressionParentEntity parent, int child) => new This(new ExpressionInfo(cx, new AnnotatedType(@class, NullableAnnotation.None), loc, Kinds.ExprKind.THIS_ACCESS, parent, child, true, null)); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Throw.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Throw.cs index d64113cdad29..5ef8feaebb6f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Throw.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Throw.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Throw : Expression + internal class Throw : Expression { - Throw(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.THROW)) { } + private Throw(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.THROW)) { } public static Expression Create(ExpressionNodeInfo info) => new Throw(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Tuple.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Tuple.cs index d38685d2cd6a..f2911bdcf754 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Tuple.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Tuple.cs @@ -5,17 +5,17 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Tuple : Expression + internal class Tuple : Expression { public static Expression Create(ExpressionNodeInfo info) => new Tuple(info).TryPopulate(); - Tuple(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.TUPLE)) + private Tuple(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.TUPLE)) { } protected override void PopulateExpression(TextWriter trapFile) { - int child = 0; + var child = 0; foreach (var argument in Syntax.Arguments.Select(a => a.Expression)) { Expression.Create(cx, argument, this, child++); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeAccess.cs index ab5915aa274b..e87c63047f11 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeAccess.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class TypeAccess : Expression + internal class TypeAccess : Expression { - TypeAccess(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.TYPE_ACCESS)) { } + private TypeAccess(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.TYPE_ACCESS)) { } protected override void PopulateExpression(TextWriter trapFile) { @@ -36,4 +36,3 @@ protected override void PopulateExpression(TextWriter trapFile) public static Expression Create(ExpressionNodeInfo info) => new TypeAccess(info).TryPopulate(); } } - diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeOf.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeOf.cs index d4f5e91fbea0..fa46ef8c6642 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeOf.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeOf.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class TypeOf : Expression + internal class TypeOf : Expression { - TypeOf(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.TYPEOF)) { } + private TypeOf(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.TYPEOF)) { } public static Expression Create(ExpressionNodeInfo info) => new TypeOf(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unary.cs index 98023028d75f..f3c2c73b074e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unary.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unary.cs @@ -4,15 +4,15 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Unary : Expression + internal class Unary : Expression { - Unary(ExpressionNodeInfo info, ExprKind kind) + private Unary(ExpressionNodeInfo info, ExprKind kind) : base(info.SetKind(UnaryOperatorKind(info.Context, info.Kind, info.Node))) { - OperatorKind = kind; + operatorKind = kind; } - readonly ExprKind OperatorKind; + private readonly ExprKind operatorKind; public static Unary Create(ExpressionNodeInfo info) { @@ -26,7 +26,7 @@ protected override void PopulateExpression(TextWriter trapFile) Create(cx, Syntax.Operand, this, 0); OperatorCall(trapFile, Syntax); - if ((OperatorKind == ExprKind.PRE_INCR || OperatorKind == ExprKind.PRE_DECR) && + if ((operatorKind == ExprKind.PRE_INCR || operatorKind == ExprKind.PRE_DECR) && Kind == ExprKind.OPERATOR_INVOCATION) { trapFile.mutator_invocation_mode(this, 1); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unchecked.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unchecked.cs index c4a46a7d74d3..5ffcf414132b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unchecked.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unchecked.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Unchecked : Expression + internal class Unchecked : Expression { - Unchecked(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.UNCHECKED)) { } + private Unchecked(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.UNCHECKED)) { } public static Expression Create(ExpressionNodeInfo info) => new Unchecked(info).TryPopulate(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unknown.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unknown.cs index c547626e6393..6df1e6ad040b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unknown.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Unknown.cs @@ -1,9 +1,8 @@ -īģŋusing Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.Kinds; +īģŋusing Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { - class Unknown : Expression + internal class Unknown : Expression { public Unknown(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.UNKNOWN)) { } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs index 21780a6ca756..c0b561dd8430 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs @@ -7,9 +7,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { - class VariableDeclaration : Expression + internal class VariableDeclaration : Expression { - VariableDeclaration(IExpressionInfo info) : base(info) { } + private VariableDeclaration(IExpressionInfo info) : base(info) { } public static VariableDeclaration Create(Context cx, ISymbol symbol, AnnotatedType type, TypeSyntax optionalSyntax, Extraction.Entities.Location exprLocation, bool isVar, IExpressionParentEntity parent, int child) { @@ -24,7 +24,7 @@ public static VariableDeclaration Create(Context cx, ISymbol symbol, AnnotatedTy return ret; } - static VariableDeclaration CreateSingle(Context cx, DeclarationExpressionSyntax node, SingleVariableDesignationSyntax designation, IExpressionParentEntity parent, int child) + private static VariableDeclaration CreateSingle(Context cx, DeclarationExpressionSyntax node, SingleVariableDesignationSyntax designation, IExpressionParentEntity parent, int child) { var variableSymbol = cx.GetModel(designation).GetDeclaredSymbol(designation) as ILocalSymbol; if (variableSymbol == null) @@ -71,6 +71,7 @@ public static Expression CreateParenthesized(Context cx, VarPatternSyntax varPat { var child0 = 0; foreach (var variable in designation.Variables) + { switch (variable) { case ParenthesizedVariableDesignationSyntax paren: @@ -94,13 +95,14 @@ public static Expression CreateParenthesized(Context cx, VarPatternSyntax varPat default: throw new InternalError(variable, "Unhandled designation type"); } + } }); return tuple; } - static Expression Create(Context cx, DeclarationExpressionSyntax node, VariableDesignationSyntax designation, IExpressionParentEntity parent, int child) + private static Expression Create(Context cx, DeclarationExpressionSyntax node, VariableDesignationSyntax designation, IExpressionParentEntity parent, int child) { switch (designation) { @@ -157,15 +159,14 @@ public static VariableDeclaration CreateDeclarator(Context cx, VariableDeclarato cx.TrapWriter.Writer.expr_access(access, localVar); } - var decl = d.Parent as VariableDeclarationSyntax; - if (decl != null) + if (d.Parent is VariableDeclarationSyntax decl) TypeMention.Create(cx, decl.Type, ret, type); }); return ret; } } - static class VariableDeclarations + internal static class VariableDeclarations { public static void Populate(Context cx, VariableDeclarationSyntax decl, IExpressionParentEntity parent, int child, int childIncrement = 1) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 7ae4572cd7d0..2990332648d8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -1,5 +1,4 @@ using Microsoft.CodeAnalysis; -using Semmle.Extraction.CSharp.Populators; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; @@ -10,15 +9,15 @@ namespace Semmle.Extraction.CSharp.Entities { - class Field : CachedSymbol, IExpressionParentEntity + internal class Field : CachedSymbol, IExpressionParentEntity { - Field(Context cx, IFieldSymbol init) + private Field(Context cx, IFieldSymbol init) : base(cx, init) { type = new Lazy(() => Entities.Type.Create(cx, symbol.GetAnnotatedType())); } - public static Field Create(Context cx, IFieldSymbol field) => FieldFactory.Instance.CreateEntity(cx, field); + public static Field Create(Context cx, IFieldSymbol field) => FieldFactory.Instance.CreateEntityFromSymbol(cx, field); // Do not populate backing fields. // Populate Tuple fields. @@ -32,7 +31,7 @@ public override void Populate(TextWriter trapFile) ContainingType.PopulateGenerics(); PopulateNullability(trapFile, symbol.GetAnnotatedType()); - Field unboundFieldKey = Field.Create(Context, symbol.OriginalDefinition); + var unboundFieldKey = Field.Create(Context, symbol.OriginalDefinition); trapFile.fields(this, (symbol.IsConst ? 2 : 1), symbol.Name, ContainingType, Type.Type.TypeRef, unboundFieldKey); PopulateModifiers(trapFile); @@ -58,49 +57,70 @@ public override void Populate(TextWriter trapFile) Context.BindComments(this, Location.symbol); - int child = 0; - foreach (var initializer in - symbol.DeclaringSyntaxReferences. - Select(n => n.GetSyntax()). - OfType(). - Where(n => n.Initializer != null)) + var child = 0; + foreach (var initializer in symbol.DeclaringSyntaxReferences + .Select(n => n.GetSyntax()) + .OfType() + .Where(n => n.Initializer != null)) { Context.PopulateLater(() => { var loc = Context.Create(initializer.GetLocation()); - var simpleAssignExpr = new Expression(new ExpressionInfo(Context, Type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, false, null)); - Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Initializer.Value, simpleAssignExpr, 0)); - var access = new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 1, false, null)); - trapFile.expr_access(access, this); + + var fieldAccess = AddInitializerAssignment(trapFile, initializer.Initializer.Value, loc, null, ref child); + if (!symbol.IsStatic) { - This.CreateImplicit(Context, Entities.Type.Create(Context, symbol.ContainingType), Location, access, -1); + This.CreateImplicit(Context, Entities.Type.Create(Context, symbol.ContainingType), Location, fieldAccess, -1); } }); } - foreach (var initializer in symbol.DeclaringSyntaxReferences. - Select(n => n.GetSyntax()). - OfType(). - Where(n => n.EqualsValue != null)) + foreach (var initializer in symbol.DeclaringSyntaxReferences + .Select(n => n.GetSyntax()) + .OfType() + .Where(n => n.EqualsValue != null)) { // Mark fields that have explicit initializers. - var expr = new Expression(new ExpressionInfo(Context, Type, Context.Create(initializer.EqualsValue.Value.FixedLocation()), Kinds.ExprKind.FIELD_ACCESS, this, child++, false, null)); - trapFile.expr_access(expr, this); + var constValue = symbol.HasConstantValue + ? Expression.ValueAsString(symbol.ConstantValue) + : null; + + var loc = Context.Create(initializer.GetLocation()); + + AddInitializerAssignment(trapFile, initializer.EqualsValue.Value, loc, constValue, ref child); } if (IsSourceDeclaration) - foreach (var syntax in symbol.DeclaringSyntaxReferences. - Select(d => d.GetSyntax()).OfType(). - Select(d => d.Parent).OfType()) + { + foreach (var syntax in symbol.DeclaringSyntaxReferences + .Select(d => d.GetSyntax()) + .OfType() + .Select(d => d.Parent) + .OfType()) + { TypeMention.Create(Context, syntax.Type, this, Type); + } + } + } + + private Expression AddInitializerAssignment(TextWriter trapFile, ExpressionSyntax initializer, Extraction.Entities.Location loc, + string constValue, ref int child) + { + var simpleAssignExpr = new Expression(new ExpressionInfo(Context, Type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, false, constValue)); + Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 0)); + var access = new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 1, false, constValue)); + trapFile.expr_access(access, this); + return access; } - readonly Lazy type; + private readonly Lazy type; public AnnotatedType Type => type.Value; public override void WriteId(TextWriter trapFile) { + trapFile.WriteSubId(Type.Type); + trapFile.Write(" "); trapFile.WriteSubId(ContainingType); trapFile.Write('.'); trapFile.Write(symbol.Name); @@ -109,9 +129,9 @@ public override void WriteId(TextWriter trapFile) bool IExpressionParentEntity.IsTopLevelParent => true; - class FieldFactory : ICachedEntityFactory + private class FieldFactory : ICachedEntityFactory { - public static readonly FieldFactory Instance = new FieldFactory(); + public static FieldFactory Instance { get; } = new FieldFactory(); public Field Create(Context cx, IFieldSymbol init) => new Field(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs index b9ece3d8c8e1..5873d3e19f48 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs @@ -5,12 +5,12 @@ namespace Semmle.Extraction.CSharp.Entities { - class Indexer : Property, IExpressionParentEntity + internal class Indexer : Property, IExpressionParentEntity { protected Indexer(Context cx, IPropertySymbol init) : base(cx, init) { } - Indexer OriginalDefinition => IsSourceDeclaration ? this : Create(Context, symbol.OriginalDefinition); + private Indexer OriginalDefinition => IsSourceDeclaration ? this : Create(Context, symbol.OriginalDefinition); public override void Populate(TextWriter trapFile) { @@ -71,7 +71,7 @@ public override void Populate(TextWriter trapFile) TypeMention.Create(Context, syntax.Type, this, type); } - public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntity(cx, prop); + public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntityFromSymbol(cx, prop); public override void WriteId(TextWriter trapFile) { @@ -87,22 +87,20 @@ public override Microsoft.CodeAnalysis.Location FullLocation { get { - return - symbol. - DeclaringSyntaxReferences. - Select(r => r.GetSyntax()). - OfType(). - Select(s => s.GetLocation()). - Concat(symbol.Locations). - First(); + return symbol.DeclaringSyntaxReferences + .Select(r => r.GetSyntax()) + .OfType() + .Select(s => s.GetLocation()) + .Concat(symbol.Locations) + .First(); } } bool IExpressionParentEntity.IsTopLevelParent => true; - class IndexerFactory : ICachedEntityFactory + private class IndexerFactory : ICachedEntityFactory { - public static readonly IndexerFactory Instance = new IndexerFactory(); + public static IndexerFactory Instance { get; } = new IndexerFactory(); public Indexer Create(Context cx, IPropertySymbol init) => new Indexer(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs index e570802ab3d8..8b072a04bd9d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs @@ -6,9 +6,9 @@ namespace Semmle.Extraction.CSharp.Entities { - class LocalFunction : Method + internal class LocalFunction : Method { - LocalFunction(Context cx, IMethodSymbol init) : base(cx, init) + private LocalFunction(Context cx, IMethodSymbol init) : base(cx, init) { } @@ -22,11 +22,11 @@ public override void WriteQuotedId(TextWriter trapFile) trapFile.Write('*'); } - public static new LocalFunction Create(Context cx, IMethodSymbol field) => LocalFunctionFactory.Instance.CreateEntity(cx, field); + public static new LocalFunction Create(Context cx, IMethodSymbol field) => LocalFunctionFactory.Instance.CreateEntityFromSymbol(cx, field); - class LocalFunctionFactory : ICachedEntityFactory + private class LocalFunctionFactory : ICachedEntityFactory { - public static readonly LocalFunctionFactory Instance = new LocalFunctionFactory(); + public static LocalFunctionFactory Instance { get; } = new LocalFunctionFactory(); public LocalFunction Create(Context cx, IMethodSymbol init) => new LocalFunction(cx, init); } @@ -51,5 +51,7 @@ public override void Populate(TextWriter trapFile) trapFile.local_functions(this, symbol.Name, returnType, originalDefinition); ExtractRefReturn(trapFile); } + + public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs index 6d0f14a014eb..de44b16d69f0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities { - class LocalVariable : CachedSymbol + internal class LocalVariable : CachedSymbol { - LocalVariable(Context cx, ISymbol init) : base(cx, init) { } + private LocalVariable(Context cx, ISymbol init) : base(cx, init) { } public override void WriteId(TextWriter trapFile) { @@ -23,10 +23,9 @@ public override void Populate(TextWriter trapFile) { } public void PopulateManual(Expression parent, bool isVar) { var trapFile = Context.TrapWriter.Writer; - var (kind, type) = - symbol is ILocalSymbol l ? - (l.IsRef ? 3 : l.IsConst ? 2 : 1, Type.Create(Context, l.GetAnnotatedType())) : - (1, parent.Type); + var (kind, type) = symbol is ILocalSymbol l + ? (l.IsRef ? 3 : l.IsConst ? 2 : 1, Type.Create(Context, l.GetAnnotatedType())) + : (1, parent.Type); trapFile.localvars(this, kind, symbol.Name, isVar ? 1 : 0, type.Type.TypeRef, parent); if (symbol is ILocalSymbol local) @@ -43,21 +42,20 @@ symbol is ILocalSymbol l ? public static LocalVariable Create(Context cx, ISymbol local) { - return LocalVariableFactory.Instance.CreateEntity(cx, local); + return LocalVariableFactory.Instance.CreateEntityFromSymbol(cx, local); } - void DefineConstantValue(TextWriter trapFile) + private void DefineConstantValue(TextWriter trapFile) { - var local = symbol as ILocalSymbol; - if (local != null && local.HasConstantValue) + if (symbol is ILocalSymbol local && local.HasConstantValue) { trapFile.constant_value(this, Expression.ValueAsString(local.ConstantValue)); } } - class LocalVariableFactory : ICachedEntityFactory + private class LocalVariableFactory : ICachedEntityFactory { - public static readonly LocalVariableFactory Instance = new LocalVariableFactory(); + public static LocalVariableFactory Instance { get; } = new LocalVariableFactory(); public LocalVariable Create(Context cx, ISymbol init) => new LocalVariable(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 982d21b25698..94e8c838e7be 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -9,10 +9,10 @@ namespace Semmle.Extraction.CSharp.Entities { public abstract class Method : CachedSymbol, IExpressionParentEntity, IStatementParentEntity { - public Method(Context cx, IMethodSymbol init) + protected Method(Context cx, IMethodSymbol init) : base(cx, init) { } - protected void PopulateParameters(TextWriter trapFile) + protected void PopulateParameters() { var originalMethod = OriginalDefinition; IEnumerable parameters = symbol.Parameters; @@ -62,7 +62,7 @@ protected virtual void ExtractInitializers(TextWriter trapFile) // so there's nothing to extract. } - void PopulateMethodBody(TextWriter trapFile) + private void PopulateMethodBody(TextWriter trapFile) { if (!IsSourceDeclaration) return; @@ -71,30 +71,34 @@ void PopulateMethodBody(TextWriter trapFile) var expr = ExpressionBody; if (block != null || expr != null) + { Context.PopulateLater( - () => - { - ExtractInitializers(trapFile); - if (block != null) - Statements.Block.Create(Context, block, this, 0); - else - Expression.Create(Context, expr, this, 0); - - Context.NumberOfLines(trapFile, symbol, this); - }); + () => + { + ExtractInitializers(trapFile); + if (block != null) + Statements.Block.Create(Context, block, this, 0); + else + Expression.Create(Context, expr, this, 0); + + Context.NumberOfLines(trapFile, BodyDeclaringSymbol, this); + }); + } } public void Overrides(TextWriter trapFile) { - foreach (var explicitInterface in symbol.ExplicitInterfaceImplementations. - Where(sym => sym.MethodKind == MethodKind.Ordinary). - Select(impl => Type.Create(Context, impl.ContainingType))) + foreach (var explicitInterface in symbol.ExplicitInterfaceImplementations + .Where(sym => sym.MethodKind == MethodKind.Ordinary) + .Select(impl => Type.Create(Context, impl.ContainingType))) { trapFile.explicitly_implements(this, explicitInterface.TypeRef); if (IsSourceDeclaration) + { foreach (var syntax in symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).OfType()) TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier.Name, this, explicitInterface); + } } if (symbol.OverriddenMethod != null) @@ -108,6 +112,9 @@ public void Overrides(TextWriter trapFile) /// protected static void BuildMethodId(Method m, TextWriter trapFile) { + m.symbol.ReturnType.BuildOrWriteId(m.Context, trapFile, m.symbol); + trapFile.Write(" "); + trapFile.WriteSubId(m.ContainingType); AddExplicitInterfaceQualifierToId(m.Context, trapFile, m.symbol.ExplicitInterfaceImplementations); @@ -126,10 +133,10 @@ protected static void BuildMethodId(Method m, TextWriter trapFile) { trapFile.Write('<'); // Encode the nullability of the type arguments in the label. - // Type arguments with different nullability can result in + // Type arguments with different nullability can result in // a constructed method with different nullability of its parameters and return type, // so we need to create a distinct database entity for it. - trapFile.BuildList(",", m.symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { AddSignatureTypeToId(m.Context, tb0, m.symbol, ta.Symbol); trapFile.Write((int)ta.Nullability); }); + trapFile.BuildList(",", m.symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { ta.Symbol.BuildOrWriteId(m.Context, tb0, m.symbol); trapFile.Write((int)ta.Nullability); }); trapFile.Write('>'); } } @@ -163,65 +170,21 @@ public override void WriteId(TextWriter trapFile) BuildMethodId(this, trapFile); } - /// - /// Adds an appropriate label ID to the trap builder - /// for the type belonging to the signature of method - /// . - /// - /// For methods without type parameters this will always add the key of the - /// corresponding type. - /// - /// For methods with type parameters, this will add the key of the - /// corresponding type if the type does *not* contain one of the method - /// type parameters, otherwise it will add a textual representation of - /// the type. This distinction is required because type parameter IDs - /// refer to their declaring methods. - /// - /// Example: - /// - /// - /// int Count<T>(IEnumerable items) - /// - /// - /// The label definitions for Count (#4) and T - /// (#5) will look like: - /// - /// - /// #1=<label for System.Int32> - /// #2=<label for type containing Count> - /// #3=<label for IEnumerable`1> - /// #4=@"{#1} {#2}.Count`2(#3);method" - /// #5=@"{#4}T;typeparameter" - /// - /// - /// Note how int is referenced in the label definition #3 for - /// Count, while T[] is represented textually in order - /// to make the reference to #3 in the label definition #4 for - /// T valid. - /// - protected static void AddSignatureTypeToId(Context cx, TextWriter trapFile, IMethodSymbol method, ITypeSymbol type) - { - if (type.ContainsTypeParameters(cx, method)) - type.BuildTypeId(cx, trapFile, (cx0, tb0, type0) => AddSignatureTypeToId(cx, tb0, method, type0)); - else - trapFile.WriteSubId(Type.Create(cx, type)); - } - protected static void AddParametersToId(Context cx, TextWriter trapFile, IMethodSymbol method) { trapFile.Write('('); - int index = 0; + var index = 0; - if (method.MethodKind == MethodKind.ReducedExtension) - { - trapFile.WriteSeparator(",", ref index); - AddSignatureTypeToId(cx, trapFile, method, method.ReceiverType); - } + var @params = method.MethodKind == MethodKind.ReducedExtension + ? method.ReducedFrom.Parameters + : method.Parameters; - foreach (var param in method.Parameters) + foreach (var param in @params) { trapFile.WriteSeparator(",", ref index); - AddSignatureTypeToId(cx, trapFile, method, param.Type); + param.Type.BuildOrWriteId(cx, trapFile, method); + trapFile.Write(" "); + trapFile.Write(param.Name); switch (param.RefKind) { case RefKind.Out: @@ -245,9 +208,7 @@ protected static void AddParametersToId(Context cx, TextWriter trapFile, IMethod public static void AddExplicitInterfaceQualifierToId(Context cx, System.IO.TextWriter trapFile, IEnumerable explicitInterfaceImplementations) { if (explicitInterfaceImplementations.Any()) - { trapFile.AppendList(",", explicitInterfaceImplementations.Select(impl => cx.CreateEntity(impl.ContainingType))); - } } public virtual string Name => symbol.Name; @@ -260,7 +221,8 @@ public static void AddExplicitInterfaceQualifierToId(Context cx, System.IO.TextW /// public static Method Create(Context cx, IMethodSymbol methodDecl) { - if (methodDecl == null) return null; + if (methodDecl == null) + return null; var methodKind = methodDecl.MethodKind; @@ -283,7 +245,7 @@ public static Method Create(Context cx, IMethodSymbol methodDecl) return Destructor.Create(cx, methodDecl); case MethodKind.PropertyGet: case MethodKind.PropertySet: - return methodDecl.AssociatedSymbol is null ? OrdinaryMethod.Create(cx, methodDecl) : (Method)Accessor.Create(cx, methodDecl); + return Accessor.GetPropertySymbol(methodDecl) is null ? OrdinaryMethod.Create(cx, methodDecl) : (Method)Accessor.Create(cx, methodDecl); case MethodKind.EventAdd: case MethodKind.EventRemove: return EventAccessor.Create(cx, methodDecl); @@ -322,7 +284,7 @@ public static Method Create(Context cx, IMethodSymbol methodDecl) public bool IsBoundGeneric => IsGeneric && !IsUnboundGeneric; - bool IsReducedExtension => symbol.MethodKind == MethodKind.ReducedExtension; + private bool IsReducedExtension => symbol.MethodKind == MethodKind.ReducedExtension; protected IMethodSymbol ConstructedFromSymbol => symbol.ConstructedFrom.ReducedFrom ?? symbol.ConstructedFrom; @@ -336,7 +298,7 @@ protected void PopulateGenerics(TextWriter trapFile) if (IsGeneric) { - int child = 0; + var child = 0; if (isFullyConstructed) { @@ -375,7 +337,7 @@ protected void PopulateMethod(TextWriter trapFile) // Common population code for all callables BindComments(); PopulateAttributes(); - PopulateParameters(trapFile); + PopulateParameters(); PopulateMethodBody(trapFile); PopulateGenerics(trapFile); PopulateMetadataHandle(trapFile); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs index 4de6318786ce..4b2725b89f30 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs @@ -1,41 +1,18 @@ using Microsoft.CodeAnalysis; -using System; using System.IO; -using System.Reflection; namespace Semmle.Extraction.CSharp.Entities { - /// - /// Provide a "Key" object to allow modifiers to exist as entities in the extractor - /// hash map. (Raw strings would work as keys but might clash with other types). - /// - class ModifierKey : Object + internal class Modifier : Extraction.CachedEntity { - public readonly string name; - - public ModifierKey(string m) - { - name = m; - } - - public override bool Equals(Object obj) - { - return obj.GetType() == GetType() && name == ((ModifierKey)obj).name; - } - - public override int GetHashCode() => 13 * name.GetHashCode(); - } - - class Modifier : Extraction.CachedEntity - { - Modifier(Context cx, ModifierKey init) + private Modifier(Context cx, string init) : base(cx, init) { } public override Microsoft.CodeAnalysis.Location ReportingLocation => null; public override void WriteId(TextWriter trapFile) { - trapFile.Write(symbol.name); + trapFile.Write(symbol); trapFile.Write(";modifier"); } @@ -43,7 +20,7 @@ public override void WriteId(TextWriter trapFile) public override void Populate(TextWriter trapFile) { - trapFile.modifiers(Label, symbol.name); + trapFile.modifiers(Label, symbol); } public static string AccessbilityModifier(Accessibility access) @@ -95,7 +72,7 @@ public static void HasModifier(Context cx, TextWriter trapFile, IEntity target, public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol) { - bool interfaceDefinition = symbol.ContainingType != null + var interfaceDefinition = symbol.ContainingType != null && symbol.ContainingType.Kind == SymbolKind.NamedType && symbol.ContainingType.TypeKind == TypeKind.Interface; @@ -109,7 +86,7 @@ public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key if (symbol.IsSealed) HasModifier(cx, trapFile, key, "sealed"); - bool fromSource = symbol.DeclaringSyntaxReferences.Length > 0; + var fromSource = symbol.DeclaringSyntaxReferences.Length > 0; if (symbol.IsStatic && !(symbol.Kind == SymbolKind.Field && ((IFieldSymbol)symbol).IsConst && !fromSource)) HasModifier(cx, trapFile, key, "static"); @@ -138,7 +115,7 @@ public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key if (symbol.Kind == SymbolKind.NamedType) { - INamedTypeSymbol nt = symbol as INamedTypeSymbol; + var nt = symbol as INamedTypeSymbol; if (nt is null) throw new InternalError(symbol, "Symbol kind is inconsistent with its type"); @@ -152,17 +129,22 @@ public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key } } - public static Modifier Create(Context cx, string modifier) => - ModifierFactory.Instance.CreateEntity(cx, new ModifierKey(modifier)); + public static Modifier Create(Context cx, string modifier) + { + return ModifierFactory.Instance.CreateEntity(cx, (typeof(Modifier), modifier), modifier); + } - public static Modifier Create(Context cx, Accessibility access) => - ModifierFactory.Instance.CreateEntity(cx, new ModifierKey(AccessbilityModifier(access))); + public static Modifier Create(Context cx, Accessibility access) + { + var modifier = AccessbilityModifier(access); + return ModifierFactory.Instance.CreateEntity(cx, (typeof(Modifier), modifier), modifier); + } - class ModifierFactory : ICachedEntityFactory + private class ModifierFactory : ICachedEntityFactory { - public static readonly ModifierFactory Instance = new ModifierFactory(); + public static ModifierFactory Instance { get; } = new ModifierFactory(); - public Modifier Create(Context cx, ModifierKey init) => new Modifier(cx, init); + public Modifier Create(Context cx, string init) => new Modifier(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs index 1d663b1b5616..6ae31c82b649 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs @@ -3,9 +3,9 @@ namespace Semmle.Extraction.CSharp.Entities { - sealed class Namespace : CachedEntity + internal sealed class Namespace : CachedEntity { - Namespace(Context cx, INamespaceSymbol init) + private Namespace(Context cx, INamespaceSymbol init) : base(cx, init) { } public override Microsoft.CodeAnalysis.Location ReportingLocation => null; @@ -16,7 +16,7 @@ public override void Populate(TextWriter trapFile) if (symbol.ContainingNamespace != null) { - Namespace parent = Create(Context, symbol.ContainingNamespace); + var parent = Create(Context, symbol.ContainingNamespace); trapFile.parent_namespace(this, parent); } } @@ -34,11 +34,11 @@ public override void WriteId(TextWriter trapFile) trapFile.Write(";namespace"); } - public static Namespace Create(Context cx, INamespaceSymbol ns) => NamespaceFactory.Instance.CreateEntity2(cx, ns); + public static Namespace Create(Context cx, INamespaceSymbol ns) => NamespaceFactory.Instance.CreateEntityFromSymbol(cx, ns); - class NamespaceFactory : ICachedEntityFactory + private class NamespaceFactory : ICachedEntityFactory { - public static readonly NamespaceFactory Instance = new NamespaceFactory(); + public static NamespaceFactory Instance { get; } = new NamespaceFactory(); public Namespace Create(Context cx, INamespaceSymbol init) => new Namespace(cx, init); } @@ -47,7 +47,7 @@ class NamespaceFactory : ICachedEntityFactory public override int GetHashCode() => QualifiedName.GetHashCode(); - string QualifiedName => symbol.ToDisplayString(); + private string QualifiedName => symbol.ToDisplayString(); public override bool Equals(object obj) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs index 7a14bb719fc1..de07d82ac822 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs @@ -8,36 +8,36 @@ namespace Semmle.Extraction.CSharp.Entities { - class NamespaceDeclaration : FreshEntity + internal class NamespaceDeclaration : FreshEntity { - private readonly NamespaceDeclaration Parent; - private readonly NamespaceDeclarationSyntax Node; + private readonly NamespaceDeclaration parent; + private readonly NamespaceDeclarationSyntax node; public NamespaceDeclaration(Context cx, NamespaceDeclarationSyntax node, NamespaceDeclaration parent) : base(cx) { - Node = node; - Parent = parent; + this.node = node; + this.parent = parent; TryPopulate(); } protected override void Populate(TextWriter trapFile) { - var @namespace = (INamespaceSymbol) cx.GetModel(Node).GetSymbolInfo(Node.Name).Symbol; + var @namespace = (INamespaceSymbol)cx.GetModel(node).GetSymbolInfo(node.Name).Symbol; var ns = Namespace.Create(cx, @namespace); trapFile.namespace_declarations(this, ns); - trapFile.namespace_declaration_location(this, cx.Create(Node.Name.GetLocation())); + trapFile.namespace_declaration_location(this, cx.Create(node.Name.GetLocation())); var visitor = new Populators.TypeOrNamespaceVisitor(cx, trapFile, this); - foreach (var member in Node.Members.Cast().Concat(Node.Usings)) + foreach (var member in node.Members.Cast().Concat(node.Usings)) { member.Accept(visitor); } - if (Parent != null) + if (parent != null) { - trapFile.parent_namespace_declaration(this, Parent); + trapFile.parent_namespace_declaration(this, parent); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs index cb12918e72e7..dbeab838b25c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs @@ -6,13 +6,15 @@ namespace Semmle.Extraction.CSharp.Entities { - class OrdinaryMethod : Method + internal class OrdinaryMethod : Method { - OrdinaryMethod(Context cx, IMethodSymbol init) + private OrdinaryMethod(Context cx, IMethodSymbol init) : base(cx, init) { } public override string Name => symbol.GetName(); + protected override IMethodSymbol BodyDeclaringSymbol => symbol.PartialImplementationPart ?? symbol; + public IMethodSymbol SourceDeclaration { get @@ -34,11 +36,13 @@ public override void Populate(TextWriter trapFile) trapFile.methods(this, Name, ContainingType, returnType.TypeRef, OriginalDefinition); if (IsSourceDeclaration) + { foreach (var declaration in symbol.DeclaringSyntaxReferences.Select(s => s.GetSyntax()).OfType()) { Context.BindComments(this, declaration.Identifier.GetLocation()); TypeMention.Create(Context, declaration.ReturnType, this, returnType); } + } foreach (var l in Locations) trapFile.method_location(this, l); @@ -49,11 +53,11 @@ public override void Populate(TextWriter trapFile) ExtractCompilerGenerated(trapFile); } - public new static OrdinaryMethod Create(Context cx, IMethodSymbol method) => OrdinaryMethodFactory.Instance.CreateEntity(cx, method); + public static new OrdinaryMethod Create(Context cx, IMethodSymbol method) => OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method); - class OrdinaryMethodFactory : ICachedEntityFactory + private class OrdinaryMethodFactory : ICachedEntityFactory { - public static readonly OrdinaryMethodFactory Instance = new OrdinaryMethodFactory(); + public static OrdinaryMethodFactory Instance { get; } = new OrdinaryMethodFactory(); public OrdinaryMethod Create(Context cx, IMethodSymbol init) => new OrdinaryMethod(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index 9a64115f24b2..c42a3edd2366 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -9,8 +9,8 @@ namespace Semmle.Extraction.CSharp.Entities { public class Parameter : CachedSymbol, IExpressionParentEntity { - protected IEntity Parent; - protected readonly Parameter Original; + protected IEntity Parent { get; set; } + protected Parameter Original { get; } protected Parameter(Context cx, IParameterSymbol init, IEntity parent, Parameter original) : base(cx, init) @@ -34,13 +34,14 @@ protected virtual int Ordinal // omit the "this" parameter, so the parameters are // actually numbered from 1. // This is to be consistent from the original (unreduced) extension method. - var method = symbol.ContainingSymbol as IMethodSymbol; - bool isReducedExtension = method != null && method.MethodKind == MethodKind.ReducedExtension; + var isReducedExtension = + symbol.ContainingSymbol is IMethodSymbol method && + method.MethodKind == MethodKind.ReducedExtension; return symbol.Ordinal + (isReducedExtension ? 1 : 0); } } - Kind ParamKind + private Kind ParamKind { get { @@ -53,12 +54,13 @@ Kind ParamKind case RefKind.In: return Kind.In; default: - if (symbol.IsParams) return Kind.Params; + if (symbol.IsParams) + return Kind.Params; if (Ordinal == 0) { - var method = symbol.ContainingSymbol as IMethodSymbol; - if (method != null && method.IsExtensionMethod) return Kind.This; + if (symbol.ContainingSymbol is IMethodSymbol method && method.IsExtensionMethod) + return Kind.This; } return Kind.None; } @@ -66,10 +68,10 @@ Kind ParamKind } public static Parameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter original = null) => - ParameterFactory.Instance.CreateEntity(cx, param, parent, original); + ParameterFactory.Instance.CreateEntity(cx, param, (param, parent, original)); public static Parameter Create(Context cx, IParameterSymbol param) => - ParameterFactory.Instance.CreateEntity(cx, param, null, null); + ParameterFactory.Instance.CreateEntity(cx, param, (param, null, null)); public override void WriteId(TextWriter trapFile) { @@ -83,14 +85,14 @@ public override void WriteId(TextWriter trapFile) public override bool NeedsPopulation => true; - string Name + private string Name { get { // Very rarely, two parameters have the same name according to the data model. // This breaks our database constraints. // Generate an impossible name to ensure that it doesn't conflict. - int conflictingCount = symbol.ContainingSymbol.GetParameters().Count(p => p.Ordinal < symbol.Ordinal && p.Name == symbol.Name); + var conflictingCount = symbol.ContainingSymbol.GetParameters().Count(p => p.Ordinal < symbol.Ordinal && p.Name == symbol.Name); return conflictingCount > 0 ? symbol.Name + "`" + conflictingCount : symbol.Name; } } @@ -116,11 +118,15 @@ public override void Populate(TextWriter trapFile) BindComments(); if (IsSourceDeclaration) - foreach (var syntax in symbol.DeclaringSyntaxReferences. - Select(d => d.GetSyntax()). - OfType(). - Where(s => s.Type != null)) + { + foreach (var syntax in symbol.DeclaringSyntaxReferences + .Select(d => d.GetSyntax()) + .OfType() + .Where(s => s.Type != null)) + { TypeMention.Create(Context, syntax.Type, this, type); + } + } if (symbol.HasExplicitDefaultValue && Context.Defines(symbol)) { @@ -157,15 +163,15 @@ public override void Populate(TextWriter trapFile) bool IExpressionParentEntity.IsTopLevelParent => true; - static EqualsValueClauseSyntax GetParameterDefaultValue(IParameterSymbol parameter) + private static EqualsValueClauseSyntax GetParameterDefaultValue(IParameterSymbol parameter) { var syntax = parameter.DeclaringSyntaxReferences.Select(@ref => @ref.GetSyntax()).OfType().FirstOrDefault(); - return syntax != null ? syntax.Default : null; + return syntax?.Default; } - class ParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), Parameter> + private class ParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), Parameter> { - public static readonly ParameterFactory Instance = new ParameterFactory(); + public static ParameterFactory Instance { get; } = new ParameterFactory(); public Parameter Create(Context cx, (IParameterSymbol, IEntity, Parameter) init) => new Parameter(cx, init.Item1, init.Item2, init.Item3); } @@ -173,9 +179,9 @@ class ParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parame public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; } - class VarargsType : Type + internal class VarargsType : Type { - VarargsType(Context cx) + private VarargsType(Context cx) : base(cx, null) { } public override void Populate(TextWriter trapFile) @@ -202,19 +208,19 @@ public override bool Equals(object obj) return obj != null && obj.GetType() == typeof(VarargsType); } - public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateNullableEntity(cx, null); + public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateEntity(cx, typeof(VarargsType), null); - class VarargsTypeFactory : ICachedEntityFactory + private class VarargsTypeFactory : ICachedEntityFactory { - public static readonly VarargsTypeFactory Instance = new VarargsTypeFactory(); + public static VarargsTypeFactory Instance { get; } = new VarargsTypeFactory(); public VarargsType Create(Context cx, string init) => new VarargsType(cx); } } - class VarargsParam : Parameter + internal class VarargsParam : Parameter { - VarargsParam(Context cx, Method methodKey) + private VarargsParam(Context cx, Method methodKey) : base(cx, null, methodKey, null) { } public override void Populate(TextWriter trapFile) @@ -237,50 +243,39 @@ public override bool Equals(object obj) return obj != null && obj.GetType() == typeof(VarargsParam); } - public static VarargsParam Create(Context cx, Method method) => VarargsParamFactory.Instance.CreateEntity(cx, method); + public static VarargsParam Create(Context cx, Method method) => VarargsParamFactory.Instance.CreateEntity(cx, typeof(VarargsParam), method); - class VarargsParamFactory : ICachedEntityFactory + private class VarargsParamFactory : ICachedEntityFactory { - public static readonly VarargsParamFactory Instance = new VarargsParamFactory(); + public static VarargsParamFactory Instance { get; } = new VarargsParamFactory(); public VarargsParam Create(Context cx, Method init) => new VarargsParam(cx, init); } } - class ConstructedExtensionParameter : Parameter + internal class ConstructedExtensionParameter : Parameter { - readonly ITypeSymbol ConstructedType; + private readonly ITypeSymbol constructedType; - ConstructedExtensionParameter(Context cx, Method method, Parameter original) + private ConstructedExtensionParameter(Context cx, Method method, Parameter original) : base(cx, original.symbol, method, original) { - ConstructedType = method.symbol.ReceiverType; + constructedType = method.symbol.ReceiverType; } public override void Populate(TextWriter trapFile) { - var typeKey = Type.Create(Context, ConstructedType); + var typeKey = Type.Create(Context, constructedType); trapFile.@params(this, Original.symbol.Name, typeKey.TypeRef, 0, Kind.This, Parent, Original); trapFile.param_location(this, Original.Location); } - public override int GetHashCode() => symbol.GetHashCode() + 31 * ConstructedType.GetHashCode(); - - public override bool Equals(object obj) - { - var other = obj as ConstructedExtensionParameter; - if (other == null || other.GetType() != typeof(ConstructedExtensionParameter)) - return false; - - return SymbolEqualityComparer.Default.Equals(symbol, other.symbol) && SymbolEqualityComparer.Default.Equals(ConstructedType, other.ConstructedType); - } - public static ConstructedExtensionParameter Create(Context cx, Method method, Parameter parameter) => - ExtensionParamFactory.Instance.CreateEntity(cx, (method, parameter)); + ExtensionParamFactory.Instance.CreateEntity(cx, (new SymbolEqualityWrapper(parameter.symbol), new SymbolEqualityWrapper(method.symbol.ReceiverType)), (method, parameter)); - class ExtensionParamFactory : ICachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter> + private class ExtensionParamFactory : ICachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter> { - public static readonly ExtensionParamFactory Instance = new ExtensionParamFactory(); + public static ExtensionParamFactory Instance { get; } = new ExtensionParamFactory(); public ConstructedExtensionParameter Create(Context cx, (Method, Parameter) init) => new ConstructedExtensionParameter(cx, init.Item1, init.Item2); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index e5e2cc8ab261..739708138adc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.CSharp.Entities.Expressions; @@ -8,13 +9,22 @@ namespace Semmle.Extraction.CSharp.Entities { - class Property : CachedSymbol, IExpressionParentEntity + internal class Property : CachedSymbol, IExpressionParentEntity { protected Property(Context cx, IPropertySymbol init) - : base(cx, init) { } + : base(cx, init) + { + type = new Lazy(() => Type.Create(Context, symbol.Type)); + } + + private readonly Lazy type; + + private Type Type => type.Value; public override void WriteId(TextWriter trapFile) { + trapFile.WriteSubId(Type); + trapFile.Write(" "); trapFile.WriteSubId(ContainingType); trapFile.Write('.'); Method.AddExplicitInterfaceQualifierToId(Context, trapFile, symbol.ExplicitInterfaceImplementations); @@ -31,7 +41,7 @@ public override void Populate(TextWriter trapFile) PopulateNullability(trapFile, symbol.GetAnnotatedType()); PopulateRefKind(trapFile, symbol.RefKind); - var type = Type.Create(Context, symbol.Type); + var type = Type; trapFile.properties(this, symbol.GetName(), ContainingType, type.TypeRef, Create(Context, symbol.OriginalDefinition)); var getter = symbol.GetMethod; @@ -67,10 +77,10 @@ public override void Populate(TextWriter trapFile) Context.PopulateLater(() => Expression.Create(Context, expressionBody, this, 0)); } - int child = 1; - foreach (var initializer in declSyntaxReferences. - Select(n => n.Initializer). - Where(i => i != null)) + var child = 1; + foreach (var initializer in declSyntaxReferences + .Select(n => n.Initializer) + .Where(i => i != null)) { Context.PopulateLater(() => { @@ -96,14 +106,12 @@ public override Microsoft.CodeAnalysis.Location FullLocation { get { - return - symbol. - DeclaringSyntaxReferences. - Select(r => r.GetSyntax()). - OfType(). - Select(s => s.GetLocation()). - Concat(symbol.Locations). - First(); + return symbol.DeclaringSyntaxReferences + .Select(r => r.GetSyntax()) + .OfType() + .Select(s => s.GetLocation()) + .Concat(symbol.Locations) + .First(); } } @@ -111,18 +119,14 @@ public override Microsoft.CodeAnalysis.Location FullLocation public static Property Create(Context cx, IPropertySymbol prop) { - bool isIndexer = prop.IsIndexer || prop.Parameters.Any(); + var isIndexer = prop.IsIndexer || prop.Parameters.Any(); - return isIndexer ? Indexer.Create(cx, prop) : PropertyFactory.Instance.CreateEntity(cx, prop); - } - - public void VisitDeclaration(Context cx, PropertyDeclarationSyntax p) - { + return isIndexer ? Indexer.Create(cx, prop) : PropertyFactory.Instance.CreateEntityFromSymbol(cx, prop); } - class PropertyFactory : ICachedEntityFactory + private class PropertyFactory : ICachedEntityFactory { - public static readonly PropertyFactory Instance = new PropertyFactory(); + public static PropertyFactory Instance { get; } = new PropertyFactory(); public Property Create(Context cx, IPropertySymbol init) => new Property(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statement.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statement.cs index 790ff96d70fc..44bd7f80583f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statement.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statement.cs @@ -14,7 +14,7 @@ public interface IStatementParentEntity : IEntity bool IsTopLevelParent { get; } } - abstract class Statement : FreshEntity, IExpressionParentEntity, IStatementParentEntity + internal abstract class Statement : FreshEntity, IExpressionParentEntity, IStatementParentEntity { protected Statement(Context cx) : base(cx) { } @@ -38,13 +38,13 @@ public static Statement Create(Context cx, StatementSyntax node, Statement paren public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel; } - abstract class Statement : Statement where TSyntax : CSharpSyntaxNode + internal abstract class Statement : Statement where TSyntax : CSharpSyntaxNode { protected readonly TSyntax Stmt; - private readonly int Child; - private readonly Kinds.StmtKind Kind; - private readonly IStatementParentEntity Parent; - private readonly Location Location; + private readonly int child; + private readonly Kinds.StmtKind kind; + private readonly IStatementParentEntity parent; + private readonly Location location; protected override CSharpSyntaxNode GetStatementSyntax() => Stmt; @@ -52,21 +52,21 @@ protected Statement(Context cx, TSyntax stmt, Kinds.StmtKind kind, IStatementPar : base(cx) { Stmt = stmt; - Parent = parent; - Child = child; - Location = location; - Kind = kind; + this.parent = parent; + this.child = child; + this.location = location; + this.kind = kind; cx.BindComments(this, location.symbol); } protected sealed override void Populate(TextWriter trapFile) { - trapFile.statements(this, Kind); - if (Parent.IsTopLevelParent) - trapFile.stmt_parent_top_level(this, Child, Parent); + trapFile.statements(this, kind); + if (parent.IsTopLevelParent) + trapFile.stmt_parent_top_level(this, child, parent); else - trapFile.stmt_parent(this, Child, Parent); - trapFile.stmt_location(this, Location); + trapFile.stmt_parent(this, child, parent); + trapFile.stmt_location(this, location); PopulateStatement(trapFile); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Block.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Block.cs index f9358216848f..c3e2e15baf3f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Block.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Block.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Block : Statement + internal class Block : Statement { - Block(Context cx, BlockSyntax block, IStatementParentEntity parent, int child) + private Block(Context cx, BlockSyntax block, IStatementParentEntity parent, int child) : base(cx, block, StmtKind.BLOCK, parent, child) { } public static Block Create(Context cx, BlockSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Break.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Break.cs index 8ce111634b5a..22428be6f04c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Break.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Break.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Break : Statement + internal class Break : Statement { - Break(Context cx, BreakStatementSyntax node, IStatementParentEntity parent, int child) + private Break(Context cx, BreakStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.BREAK, parent, child) { } public static Break Create(Context cx, BreakStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Case.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Case.cs index da7d86dae815..fe91851193b4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Case.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Case.cs @@ -7,7 +7,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - abstract class Case : Statement where TSyntax : SwitchLabelSyntax + internal abstract class Case : Statement where TSyntax : SwitchLabelSyntax { protected Case(Context cx, TSyntax node, Switch parent, int child) : base(cx, node, StmtKind.CASE, parent, child, cx.Create(node.GetLocation())) { } @@ -28,9 +28,9 @@ public static Statement Create(Context cx, SwitchLabelSyntax node, Switch parent } } - class CaseLabel : Case + internal class CaseLabel : Case { - CaseLabel(Context cx, CaseSwitchLabelSyntax node, Switch parent, int child) + private CaseLabel(Context cx, CaseSwitchLabelSyntax node, Switch parent, int child) : base(cx, node, parent, child) { } protected override void PopulateStatement(TextWriter trapFile) @@ -48,9 +48,9 @@ public static CaseLabel Create(Context cx, CaseSwitchLabelSyntax node, Switch pa } } - class CaseDefault : Case + internal class CaseDefault : Case { - CaseDefault(Context cx, DefaultSwitchLabelSyntax node, Switch parent, int child) + private CaseDefault(Context cx, DefaultSwitchLabelSyntax node, Switch parent, int child) : base(cx, node, parent, child) { } protected override void PopulateStatement(TextWriter trapFile) { } @@ -63,7 +63,7 @@ public static CaseDefault Create(Context cx, DefaultSwitchLabelSyntax node, Swit } } - class CasePattern : Case + internal class CasePattern : Case { private CasePattern(Context cx, CasePatternSwitchLabelSyntax node, Switch parent, int child) : base(cx, node, parent, child) { } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Catch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Catch.cs index 85e9e85f5b3d..13cc39f7fea9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Catch.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Catch.cs @@ -1,22 +1,21 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Kinds; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Entities; using System.IO; namespace Semmle.Extraction.CSharp.Entities.Statements { - class Catch : Statement + internal class Catch : Statement { - static readonly string SystemExceptionName = typeof(System.Exception).ToString(); + private static readonly string systemExceptionName = typeof(System.Exception).ToString(); - Catch(Context cx, CatchClauseSyntax node, Try parent, int child) + private Catch(Context cx, CatchClauseSyntax node, Try parent, int child) : base(cx, node, StmtKind.CATCH, parent, child, cx.Create(node.GetLocation())) { } protected override void PopulateStatement(TextWriter trapFile) { - bool isSpecificCatchClause = Stmt.Declaration != null; - bool hasVariableDeclaration = isSpecificCatchClause && Stmt.Declaration.Identifier.RawKind != 0; + var isSpecificCatchClause = Stmt.Declaration != null; + var hasVariableDeclaration = isSpecificCatchClause && Stmt.Declaration.Identifier.RawKind != 0; if (hasVariableDeclaration) // A catch clause of the form 'catch(Ex ex) { ... }' { @@ -29,7 +28,7 @@ protected override void PopulateStatement(TextWriter trapFile) } else // A catch clause of the form 'catch { ... }' { - var exception = Type.Create(cx, cx.Compilation.GetTypeByMetadataName(SystemExceptionName)); + var exception = Type.Create(cx, cx.Compilation.GetTypeByMetadataName(systemExceptionName)); trapFile.catch_type(this, exception, false); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Checked.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Checked.cs index b2523eeb5a13..4ce000bed3fd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Checked.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Checked.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Checked : Statement + internal class Checked : Statement { - Checked(Context cx, CheckedStatementSyntax stmt, IStatementParentEntity parent, int child) + private Checked(Context cx, CheckedStatementSyntax stmt, IStatementParentEntity parent, int child) : base(cx, stmt, StmtKind.CHECKED, parent, child) { } public static Checked Create(Context cx, CheckedStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Continue.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Continue.cs index c7cf7261acae..3e819eeff0cc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Continue.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Continue.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Continue : Statement + internal class Continue : Statement { - Continue(Context cx, ContinueStatementSyntax stmt, IStatementParentEntity parent, int child) + private Continue(Context cx, ContinueStatementSyntax stmt, IStatementParentEntity parent, int child) : base(cx, stmt, StmtKind.CONTINUE, parent, child) { } public static Continue Create(Context cx, ContinueStatementSyntax node, IStatementParentEntity parent, int child) @@ -19,4 +19,3 @@ public static Continue Create(Context cx, ContinueStatementSyntax node, IStateme protected override void PopulateStatement(TextWriter trapFile) { } } } - diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Do.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Do.cs index 26a46730967d..a0cd9da7372d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Do.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Do.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Do : Statement + internal class Do : Statement { - Do(Context cx, DoStatementSyntax node, IStatementParentEntity parent, int child) + private Do(Context cx, DoStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.DO, parent, child, cx.Create(node.GetLocation())) { } public static Do Create(Context cx, DoStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Empty.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Empty.cs index 8d9a6acedae4..8611727ddecc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Empty.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Empty.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Empty : Statement + internal class Empty : Statement { - Empty(Context cx, EmptyStatementSyntax block, IStatementParentEntity parent, int child) + private Empty(Context cx, EmptyStatementSyntax block, IStatementParentEntity parent, int child) : base(cx, block, StmtKind.EMPTY, parent, child) { } public static Empty Create(Context cx, EmptyStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ExpressionStatement.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ExpressionStatement.cs index cdbc15e9699e..252e867efa98 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ExpressionStatement.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ExpressionStatement.cs @@ -3,9 +3,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class ExpressionStatement : Statement + internal class ExpressionStatement : Statement { - ExpressionStatement(Context cx, ExpressionStatementSyntax node, IStatementParentEntity parent, int child) + private ExpressionStatement(Context cx, ExpressionStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, Kinds.StmtKind.EXPR, parent, child) { } public static ExpressionStatement Create(Context cx, ExpressionStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Factory.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Factory.cs index 0b5057f7d855..69ebdca1c168 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Factory.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Factory.cs @@ -3,7 +3,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - static class Factory + internal static class Factory { internal static Statement Create(Context cx, StatementSyntax node, Statement parent, int child) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Fixed.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Fixed.cs index 6cabf07591df..7cd8350c7d10 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Fixed.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Fixed.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Fixed : Statement + internal class Fixed : Statement { - Fixed(Context cx, FixedStatementSyntax @fixed, IStatementParentEntity parent, int child) + private Fixed(Context cx, FixedStatementSyntax @fixed, IStatementParentEntity parent, int child) : base(cx, @fixed, StmtKind.FIXED, parent, child) { } public static Fixed Create(Context cx, FixedStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/For.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/For.cs index 7e21cd6b5c2c..15c7151a8a62 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/For.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/For.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class For : Statement + internal class For : Statement { - For(Context cx, ForStatementSyntax node, IStatementParentEntity parent, int child) + private For(Context cx, ForStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.FOR, parent, child) { } public static For Create(Context cx, ForStatementSyntax node, IStatementParentEntity parent, int child) @@ -19,7 +19,7 @@ public static For Create(Context cx, ForStatementSyntax node, IStatementParentEn protected override void PopulateStatement(TextWriter trapFile) { - int child = -1; + var child = -1; if (Stmt.Declaration != null) VariableDeclarations.Populate(cx, Stmt.Declaration, this, child, childIncrement: -1); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ForEach.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ForEach.cs index 9758fee7743a..653ed384e7b8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ForEach.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ForEach.cs @@ -6,9 +6,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class ForEach : Statement + internal class ForEach : Statement { - ForEach(Context cx, ForEachStatementSyntax stmt, IStatementParentEntity parent, int child) + private ForEach(Context cx, ForEachStatementSyntax stmt, IStatementParentEntity parent, int child) : base(cx, stmt, StmtKind.FOREACH, parent, child) { } public static ForEach Create(Context cx, ForEachStatementSyntax node, IStatementParentEntity parent, int child) @@ -33,9 +33,9 @@ protected override void PopulateStatement(TextWriter _) } } - class ForEachVariable : Statement + internal class ForEachVariable : Statement { - ForEachVariable(Context cx, ForEachVariableStatementSyntax stmt, IStatementParentEntity parent, int child) + private ForEachVariable(Context cx, ForEachVariableStatementSyntax stmt, IStatementParentEntity parent, int child) : base(cx, stmt, StmtKind.FOREACH, parent, child) { } public static ForEachVariable Create(Context cx, ForEachVariableStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Goto.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Goto.cs index 76d00e81f05d..772d69cff7c4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Goto.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Goto.cs @@ -8,9 +8,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements /// /// A goto, goto case or goto default. /// - class Goto : Statement + internal class Goto : Statement { - static StmtKind GetKind(GotoStatementSyntax node) + private static StmtKind GetKind(GotoStatementSyntax node) { switch (node.CaseOrDefaultKeyword.Kind()) { @@ -21,7 +21,7 @@ static StmtKind GetKind(GotoStatementSyntax node) } } - Goto(Context cx, GotoStatementSyntax node, IStatementParentEntity parent, int child) + private Goto(Context cx, GotoStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, GetKind(node), parent, child) { } public static Goto Create(Context cx, GotoStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/If.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/If.cs index a038f60c9987..b132c6ebe547 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/If.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/If.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class If : Statement + internal class If : Statement { - If(Context cx, IfStatementSyntax node, IStatementParentEntity parent, int child) + private If(Context cx, IfStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.IF, parent, child) { } public static If Create(Context cx, IfStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Labeled.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Labeled.cs index 2ef376c5e6f7..1fce622b3477 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Labeled.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Labeled.cs @@ -4,16 +4,16 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Labeled : Statement + internal class Labeled : Statement { - readonly Statement Parent; - readonly int Child; + private readonly Statement parent; + private readonly int child; - Labeled(Context cx, LabeledStatementSyntax stmt, Statement parent, int child) + private Labeled(Context cx, LabeledStatementSyntax stmt, Statement parent, int child) : base(cx, stmt, StmtKind.LABEL, parent, child) { - Parent = parent; - Child = child; + this.parent = parent; + this.child = child; } public static Labeled Create(Context cx, LabeledStatementSyntax node, Statement parent, int child) @@ -29,10 +29,10 @@ protected override void PopulateStatement(TextWriter trapFile) // For compatilibty with the Mono extractor, make insert the labelled statement into the same block // as this one. The parent MUST be a block statement. - labelledStmt = Statement.Create(cx, Stmt.Statement, Parent, Child + 1); + labelledStmt = Statement.Create(cx, Stmt.Statement, parent, child + 1); } - Statement labelledStmt; + private Statement labelledStmt; public override int NumberOfStatements => 1 + labelledStmt.NumberOfStatements; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalDeclaration.cs index 3eeb962ac7a0..87559681fd8a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalDeclaration.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class LocalDeclaration : Statement + internal class LocalDeclaration : Statement { - static StmtKind GetKind(LocalDeclarationStatementSyntax declStmt) + private static StmtKind GetKind(LocalDeclarationStatementSyntax declStmt) { if (declStmt.UsingKeyword.RawKind != 0) return StmtKind.USING_DECL; @@ -18,7 +18,7 @@ static StmtKind GetKind(LocalDeclarationStatementSyntax declStmt) return StmtKind.VAR_DECL; } - LocalDeclaration(Context cx, LocalDeclarationStatementSyntax declStmt, IStatementParentEntity parent, int child) + private LocalDeclaration(Context cx, LocalDeclarationStatementSyntax declStmt, IStatementParentEntity parent, int child) : base(cx, declStmt, GetKind(declStmt), parent, child) { } public static LocalDeclaration Create(Context cx, LocalDeclarationStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalFunction.cs index 8cda84c68f26..66f4e5ac41f3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalFunction.cs @@ -7,9 +7,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class LocalFunction : Statement + internal class LocalFunction : Statement { - LocalFunction(Context cx, LocalFunctionStatementSyntax node, IStatementParentEntity parent, int child) + private LocalFunction(Context cx, LocalFunctionStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.LOCAL_FUNCTION, parent, child, cx.Create(node.GetLocation())) { } public static LocalFunction Create(Context cx, LocalFunctionStatementSyntax node, IStatementParentEntity parent, int child) @@ -22,7 +22,7 @@ public static LocalFunction Create(Context cx, LocalFunctionStatementSyntax node /// /// Gets the IMethodSymbol for this local function statement. /// - IMethodSymbol Symbol + private IMethodSymbol Symbol { get { @@ -39,7 +39,7 @@ IMethodSymbol Symbol /// /// Gets the function defined by this local statement. /// - Entities.LocalFunction Function => Entities.LocalFunction.Create(cx, Symbol); + private Entities.LocalFunction Function => Entities.LocalFunction.Create(cx, Symbol); protected override void PopulateStatement(TextWriter trapFile) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Lock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Lock.cs index 314b675866e0..fbcb346b367f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Lock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Lock.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Lock : Statement + internal class Lock : Statement { - Lock(Context cx, LockStatementSyntax @lock, IStatementParentEntity parent, int child) + private Lock(Context cx, LockStatementSyntax @lock, IStatementParentEntity parent, int child) : base(cx, @lock, StmtKind.LOCK, parent, child) { } public static Lock Create(Context cx, LockStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Return.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Return.cs index 69688a8e6f12..2ff04b82b87a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Return.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Return.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Return : Statement + internal class Return : Statement { - Return(Context cx, ReturnStatementSyntax node, IStatementParentEntity parent, int child) + private Return(Context cx, ReturnStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.RETURN, parent, child) { } public static Return Create(Context cx, ReturnStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs index 95c88ac7d280..f64067be9c4a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs @@ -5,20 +5,20 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Switch : Statement + internal class Switch : Statement { - static readonly object NullLabel = new object(); - public static readonly object DefaultLabel = new object(); + private static readonly object nullLabel = new object(); + public static object DefaultLabel { get; } = new object(); // Sometimes, the literal "null" is used as a label. // This is inconveniently represented by the "null" object. // This cannot be stored in a Dictionary<>, so substitute an object which can be. public static object LabelForValue(object label) { - return label ?? NullLabel; + return label ?? nullLabel; } - Switch(Context cx, SwitchStatementSyntax node, IStatementParentEntity parent, int child) + private Switch(Context cx, SwitchStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.SWITCH, parent, child) { } public static Switch Create(Context cx, SwitchStatementSyntax node, IStatementParentEntity parent, int child) @@ -31,7 +31,7 @@ public static Switch Create(Context cx, SwitchStatementSyntax node, IStatementPa protected override void PopulateStatement(TextWriter trapFile) { Expression.Create(cx, Stmt.Expression, this, 0); - int childIndex = 0; + var childIndex = 0; foreach (var section in Stmt.Sections) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Throw.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Throw.cs index cafc769cc883..c550b1f1e39a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Throw.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Throw.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Throw : Statement + internal class Throw : Statement { - Throw(Context cx, ThrowStatementSyntax node, IStatementParentEntity parent, int child) + private Throw(Context cx, ThrowStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.THROW, parent, child) { } public static Throw Create(Context cx, ThrowStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Try.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Try.cs index 2262b404011c..5a6937790c26 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Try.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Try.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Try : Statement + internal class Try : Statement { - Try(Context cx, TryStatementSyntax node, IStatementParentEntity parent, int child) + private Try(Context cx, TryStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.TRY, parent, child) { } public static Try Create(Context cx, TryStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Unchecked.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Unchecked.cs index 6151cae5d9ea..2d69bdc8d4cb 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Unchecked.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Unchecked.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Unchecked : Statement + internal class Unchecked : Statement { - Unchecked(Context cx, CheckedStatementSyntax stmt, IStatementParentEntity parent, int child) + private Unchecked(Context cx, CheckedStatementSyntax stmt, IStatementParentEntity parent, int child) : base(cx, stmt, StmtKind.UNCHECKED, parent, child) { } public static Unchecked Create(Context cx, CheckedStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Unsafe.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Unsafe.cs index 1bd9ce48411e..4e371604333c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Unsafe.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Unsafe.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Unsafe : Statement + internal class Unsafe : Statement { - Unsafe(Context cx, UnsafeStatementSyntax node, IStatementParentEntity parent, int child) + private Unsafe(Context cx, UnsafeStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.UNSAFE, parent, child) { } public static Unsafe Create(Context cx, UnsafeStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Using.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Using.cs index b1aa2de2e764..93d0f6a88ff6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Using.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Using.cs @@ -5,9 +5,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Using : Statement + internal class Using : Statement { - Using(Context cx, UsingStatementSyntax node, IStatementParentEntity parent, int child) + private Using(Context cx, UsingStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.USING, parent, child) { } public static Using Create(Context cx, UsingStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/While.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/While.cs index 6fa739a45a2c..8def91a84182 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/While.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/While.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class While : Statement + internal class While : Statement { - While(Context cx, WhileStatementSyntax node, IStatementParentEntity parent, int child) + private While(Context cx, WhileStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.WHILE, parent, child) { } public static While Create(Context cx, WhileStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Yield.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Yield.cs index 0e65c69ee34e..9aace8e0b834 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Yield.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Yield.cs @@ -4,9 +4,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { - class Yield : Statement + internal class Yield : Statement { - Yield(Context cx, YieldStatementSyntax node, IStatementParentEntity parent, int child) + private Yield(Context cx, YieldStatementSyntax node, IStatementParentEntity parent, int child) : base(cx, node, StmtKind.YIELD, parent, child) { } public static Yield Create(Context cx, YieldStatementSyntax node, IStatementParentEntity parent, int child) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs index 4f239083d24f..5e0850ba8c8d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CSharp.Entities { public abstract class CachedSymbol : CachedEntity where T : ISymbol { - public CachedSymbol(Context cx, T init) + protected CachedSymbol(Context cx, T init) : base(cx, init) { } public virtual Type ContainingType => symbol.ContainingType != null ? Type.Create(Context, symbol.ContainingType) : null; @@ -95,16 +95,16 @@ protected void BindComments() Context.BindComments(this, FullLocation); } + protected virtual T BodyDeclaringSymbol => symbol; + public BlockSyntax Block { get { - return symbol. - DeclaringSyntaxReferences. - Select(r => r.GetSyntax()). - SelectMany(n => n.ChildNodes()). - OfType(). - FirstOrDefault(); + return BodyDeclaringSymbol.DeclaringSyntaxReferences + .SelectMany(r => r.GetSyntax().ChildNodes()) + .OfType() + .FirstOrDefault(); } } @@ -112,12 +112,11 @@ public ExpressionSyntax ExpressionBody { get { - return symbol. - DeclaringSyntaxReferences. - SelectMany(r => r.GetSyntax().ChildNodes()). - OfType(). - Select(arrow => arrow.Expression). - FirstOrDefault(); + return BodyDeclaringSymbol.DeclaringSyntaxReferences + .SelectMany(r => r.GetSyntax().ChildNodes()) + .OfType() + .Select(arrow => arrow.Expression) + .FirstOrDefault(); } } @@ -135,16 +134,34 @@ protected void PopulateMetadataHandle(TextWriter trapFile) trapFile.metadata_handle(this, Location, MetadataTokens.GetToken(handle.Value)); } + private static System.Reflection.PropertyInfo GetPropertyInfo(object o, string name) + { + return o.GetType().GetProperty(name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetProperty); + } + public Handle? MetadataHandle { get { - var propertyInfo = symbol.GetType().GetProperty("Handle", - System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetProperty); + var handleProp = GetPropertyInfo(symbol, "Handle"); + object handleObj = symbol; + + if (handleProp is null) + { + var underlyingSymbolProp = GetPropertyInfo(symbol, "UnderlyingSymbol"); + if (underlyingSymbolProp is object) + { + if (underlyingSymbolProp.GetValue(symbol) is object underlying) + { + handleProp = GetPropertyInfo(underlying, "Handle"); + handleObj = underlying; + } + } + } - if (propertyInfo != null) + if (handleProp is object) { - switch (propertyInfo.GetValue(symbol)) + switch (handleProp.GetValue(handleObj)) { case MethodDefinitionHandle md: return md; case TypeDefinitionHandle td: return td; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs index 69836af5ca26..b2f0419f8008 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs @@ -7,23 +7,23 @@ namespace Semmle.Extraction.CSharp.Entities { - class TypeMention : FreshEntity + internal class TypeMention : FreshEntity { - readonly TypeSyntax Syntax; - readonly IEntity Parent; - readonly Type Type; - readonly Microsoft.CodeAnalysis.Location Loc; + private readonly TypeSyntax syntax; + private readonly IEntity parent; + private readonly Type type; + private readonly Microsoft.CodeAnalysis.Location loc; - TypeMention(Context cx, TypeSyntax syntax, IEntity parent, Type type, Microsoft.CodeAnalysis.Location loc = null) + private TypeMention(Context cx, TypeSyntax syntax, IEntity parent, Type type, Microsoft.CodeAnalysis.Location loc = null) : base(cx) { - Syntax = syntax; - Parent = parent; - Type = type; - Loc = loc; + this.syntax = syntax; + this.parent = parent; + this.type = type; + this.loc = loc; } - static TypeSyntax GetElementType(TypeSyntax type) + private static TypeSyntax GetElementType(TypeSyntax type) { switch (type) { @@ -36,7 +36,7 @@ static TypeSyntax GetElementType(TypeSyntax type) } } - static Type GetElementType(Type type) + private static Type GetElementType(Type type) { switch (type) { @@ -51,62 +51,62 @@ static Type GetElementType(Type type) protected override void Populate(TextWriter trapFile) { - switch (Syntax.Kind()) + switch (syntax.Kind()) { case SyntaxKind.ArrayType: - Emit(trapFile, Loc ?? Syntax.GetLocation(), Parent, Type); - Create(cx, GetElementType(Syntax), this, GetElementType(Type)); + Emit(trapFile, loc ?? syntax.GetLocation(), parent, type); + Create(cx, GetElementType(syntax), this, GetElementType(type)); return; case SyntaxKind.NullableType: - var nts = (NullableTypeSyntax)Syntax; - if (Type is NamedType nt) + var nts = (NullableTypeSyntax)syntax; + if (type is NamedType nt) { - Emit(trapFile, Loc ?? Syntax.GetLocation(), Parent, Type); + Emit(trapFile, loc ?? syntax.GetLocation(), parent, type); Create(cx, nts.ElementType, this, nt.symbol.IsReferenceType ? nt : nt.TypeArguments[0]); } - else if(Type is ArrayType array) + else if (type is ArrayType array) { - Create(cx, nts.ElementType, Parent, array); + Create(cx, nts.ElementType, parent, array); } return; case SyntaxKind.TupleType: - var tts = (TupleTypeSyntax)Syntax; - var tt = (TupleType)Type; - Emit(trapFile, Loc ?? Syntax.GetLocation(), Parent, Type); + var tts = (TupleTypeSyntax)syntax; + var tt = (TupleType)type; + Emit(trapFile, loc ?? syntax.GetLocation(), parent, type); tts.Elements.Zip(tt.TupleElements, (s, t) => Create(cx, s.Type, this, t.Type)).Enumerate(); return; case SyntaxKind.PointerType: - var pts = (PointerTypeSyntax)Syntax; - var pt = (PointerType)Type; - Emit(trapFile, Loc ?? Syntax.GetLocation(), Parent, Type); + var pts = (PointerTypeSyntax)syntax; + var pt = (PointerType)type; + Emit(trapFile, loc ?? syntax.GetLocation(), parent, type); Create(cx, pts.ElementType, this, pt.PointedAtType); return; case SyntaxKind.GenericName: - var gns = (GenericNameSyntax)Syntax; - Emit(trapFile, Loc ?? gns.Identifier.GetLocation(), Parent, Type); - cx.PopulateLater(() => gns.TypeArgumentList.Arguments.Zip(Type.TypeMentions, (s, t) => Create(cx, s, this, t)).Enumerate()); + var gns = (GenericNameSyntax)syntax; + Emit(trapFile, loc ?? gns.Identifier.GetLocation(), parent, type); + cx.PopulateLater(() => gns.TypeArgumentList.Arguments.Zip(type.TypeMentions, (s, t) => Create(cx, s, this, t)).Enumerate()); return; case SyntaxKind.QualifiedName: - if (Type.ContainingType == null) + if (type.ContainingType == null) { // namespace qualifier - Emit(trapFile, Loc ?? Syntax.GetLocation(), Parent, Type); + Emit(trapFile, loc ?? syntax.GetLocation(), parent, type); } else { // Type qualifier - var qns = (QualifiedNameSyntax)Syntax; - var right = Create(cx, qns.Right, Parent, Type); - Create(cx, qns.Left, right, Type.ContainingType); + var qns = (QualifiedNameSyntax)syntax; + var right = Create(cx, qns.Right, parent, type); + Create(cx, qns.Left, right, type.ContainingType); } return; default: - Emit(trapFile, Loc ?? Syntax.GetLocation(), Parent, Type); + Emit(trapFile, loc ?? syntax.GetLocation(), parent, type); return; } } - void Emit(TextWriter trapFile, Microsoft.CodeAnalysis.Location loc, IEntity parent, Type type) + private void Emit(TextWriter trapFile, Microsoft.CodeAnalysis.Location loc, IEntity parent, Type type) { trapFile.type_mention(this, type.TypeRef, parent); trapFile.type_mention_location(this, cx.Create(loc)); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs index c4a79a7764b0..2c617158219d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs @@ -3,15 +3,15 @@ namespace Semmle.Extraction.CSharp.Entities { - class ArrayType : Type + internal class ArrayType : Type { - ArrayType(Context cx, IArrayTypeSymbol init) + private ArrayType(Context cx, IArrayTypeSymbol init) : base(cx, init) { element = Create(cx, symbol.GetAnnotatedElementType()); } - readonly AnnotatedType element; + private readonly AnnotatedType element; public int Rank => symbol.Rank; @@ -36,11 +36,11 @@ public override void WriteId(TextWriter trapFile) trapFile.Write(";type"); } - public static ArrayType Create(Context cx, IArrayTypeSymbol symbol) => ArrayTypeFactory.Instance.CreateEntity(cx, symbol); + public static ArrayType Create(Context cx, IArrayTypeSymbol symbol) => ArrayTypeFactory.Instance.CreateEntityFromSymbol(cx, symbol); - class ArrayTypeFactory : ICachedEntityFactory + private class ArrayTypeFactory : ICachedEntityFactory { - public static readonly ArrayTypeFactory Instance = new ArrayTypeFactory(); + public static ArrayTypeFactory Instance { get; } = new ArrayTypeFactory(); public ArrayType Create(Context cx, IArrayTypeSymbol init) => new ArrayType(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs index f5b6bf36c440..2516afe037bc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs @@ -4,12 +4,12 @@ namespace Semmle.Extraction.CSharp.Entities { - class DynamicType : Type + internal class DynamicType : Type { - DynamicType(Context cx, IDynamicTypeSymbol init) + private DynamicType(Context cx, IDynamicTypeSymbol init) : base(cx, init) { } - public static DynamicType Create(Context cx, IDynamicTypeSymbol type) => DynamicTypeFactory.Instance.CreateEntity(cx, type); + public static DynamicType Create(Context cx, IDynamicTypeSymbol type) => DynamicTypeFactory.Instance.CreateEntityFromSymbol(cx, type); public override Microsoft.CodeAnalysis.Location ReportingLocation => Context.Compilation.ObjectType.Locations.FirstOrDefault(); @@ -27,9 +27,9 @@ public override void WriteId(TextWriter trapFile) trapFile.Write("dynamic;type"); } - class DynamicTypeFactory : ICachedEntityFactory + private class DynamicTypeFactory : ICachedEntityFactory { - public static readonly DynamicTypeFactory Instance = new DynamicTypeFactory(); + public static DynamicTypeFactory Instance { get; } = new DynamicTypeFactory(); public DynamicType Create(Context cx, IDynamicTypeSymbol init) => new DynamicType(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index 93c13726750e..5812c4a93032 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -9,15 +9,26 @@ namespace Semmle.Extraction.CSharp.Entities { - class NamedType : Type + internal class NamedType : Type { - NamedType(Context cx, INamedTypeSymbol init) + private NamedType(Context cx, INamedTypeSymbol init, bool constructUnderlyingTupleType) : base(cx, init) { typeArgumentsLazy = new Lazy(() => symbol.TypeArguments.Select(t => Create(cx, t)).ToArray()); + this.constructUnderlyingTupleType = constructUnderlyingTupleType; } - public static NamedType Create(Context cx, INamedTypeSymbol type) => NamedTypeFactory.Instance.CreateEntity(cx, type); + public static NamedType Create(Context cx, INamedTypeSymbol type) => + NamedTypeFactory.Instance.CreateEntityFromSymbol(cx, type); + + /// + /// Creates a named type entity from a tuple type. Unlike `Create`, this + /// will create an entity for the underlying `System.ValueTuple` struct. + /// For example, `(int, string)` will result in an entity for + /// `System.ValueTuple`. + /// + public static NamedType CreateNamedTypeFromTupleType(Context cx, INamedTypeSymbol type) => + UnderlyingTupleTypeFactory.Instance.CreateEntity(cx, (new SymbolEqualityWrapper(type), typeof(TupleType)), type); public override bool NeedsPopulation => base.NeedsPopulation || symbol.TypeKind == TypeKind.Error; @@ -29,7 +40,8 @@ public override void Populate(TextWriter trapFile) return; } - trapFile.typeref_type((NamedTypeRef)TypeRef, this); + if (UsesTypeRef) + trapFile.typeref_type((NamedTypeRef)TypeRef, this); if (symbol.IsGenericType) { @@ -40,7 +52,7 @@ public override void Populate(TextWriter trapFile) } else if (symbol.IsReallyUnbound()) { - for (int i = 0; i < symbol.TypeParameters.Length; ++i) + for (var i = 0; i < symbol.TypeParameters.Length; ++i) { TypeParameter.Create(Context, symbol.TypeParameters[i]); var param = symbol.TypeParameters[i]; @@ -50,16 +62,19 @@ public override void Populate(TextWriter trapFile) } else { - trapFile.constructed_generic(this, Type.Create(Context, symbol.ConstructedFrom).TypeRef); + var unbound = constructUnderlyingTupleType + ? CreateNamedTypeFromTupleType(Context, symbol.ConstructedFrom) + : Type.Create(Context, symbol.ConstructedFrom); + trapFile.constructed_generic(this, unbound.TypeRef); - for (int i = 0; i < symbol.TypeArguments.Length; ++i) + for (var i = 0; i < symbol.TypeArguments.Length; ++i) { trapFile.type_arguments(TypeArguments[i].TypeRef, i, this); } } } - PopulateType(trapFile); + PopulateType(trapFile, constructUnderlyingTupleType); if (symbol.EnumUnderlyingType != null) { @@ -74,7 +89,9 @@ public override void Populate(TextWriter trapFile) } } - readonly Lazy typeArgumentsLazy; + private readonly Lazy typeArgumentsLazy; + private readonly bool constructUnderlyingTupleType; + public Type[] TypeArguments => typeArgumentsLazy.Value; public override IEnumerable TypeMentions => TypeArguments; @@ -91,25 +108,40 @@ public override IEnumerable Locations } } - static IEnumerable GetLocations(INamedTypeSymbol type) + private static IEnumerable GetLocations(INamedTypeSymbol type) { - return type.Locations. - Where(l => l.IsInMetadata). - Concat( - type. - DeclaringSyntaxReferences. - Select(loc => loc.GetSyntax()). - OfType(). - Select(l => l.FixedLocation()) + return type.Locations + .Where(l => l.IsInMetadata) + .Concat(type.DeclaringSyntaxReferences + .Select(loc => loc.GetSyntax()) + .OfType() + .Select(l => l.FixedLocation()) ); } public override Microsoft.CodeAnalysis.Location ReportingLocation => GetLocations(symbol).FirstOrDefault(); + private bool IsAnonymousType() => symbol.IsAnonymousType || symbol.Name.Contains("__AnonymousType"); + public override void WriteId(TextWriter trapFile) { - symbol.BuildTypeId(Context, trapFile, (cx0, tb0, sub) => tb0.WriteSubId(Create(cx0, sub))); - trapFile.Write(";type"); + if (IsAnonymousType()) + { + trapFile.Write('*'); + } + else + { + symbol.BuildTypeId(Context, trapFile, symbol, constructUnderlyingTupleType); + trapFile.Write(";type"); + } + } + + public override void WriteQuotedId(TextWriter trapFile) + { + if (IsAnonymousType()) + trapFile.Write('*'); + else + base.WriteQuotedId(trapFile); } /// @@ -118,42 +150,56 @@ public override void WriteId(TextWriter trapFile) /// Extraction context. /// The enumerable type. /// The element type, or null. - static AnnotatedTypeSymbol GetElementType(Context cx, INamedTypeSymbol type) + private static AnnotatedTypeSymbol GetElementType(Context cx, INamedTypeSymbol type) { var et = GetEnumerableType(cx, type); - if (et.Symbol != null) return et; - - return type.AllInterfaces. - Where(i => i.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T). - Concat(type.AllInterfaces.Where(i => i.SpecialType == SpecialType.System_Collections_IEnumerable)). - Select(i => GetEnumerableType(cx, i)). - FirstOrDefault(); + if (et.Symbol != null) + return et; + + return type.AllInterfaces + .Where(i => i.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T) + .Concat(type.AllInterfaces.Where(i => i.SpecialType == SpecialType.System_Collections_IEnumerable)) + .Select(i => GetEnumerableType(cx, i)) + .FirstOrDefault(); } - static AnnotatedTypeSymbol GetEnumerableType(Context cx, INamedTypeSymbol type) + private static AnnotatedTypeSymbol GetEnumerableType(Context cx, INamedTypeSymbol type) { - return type.SpecialType == SpecialType.System_Collections_IEnumerable ? - cx.Compilation.ObjectType.WithAnnotation(NullableAnnotation.NotAnnotated) : - type.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T ? - type.GetAnnotatedTypeArguments().First() : - default(AnnotatedTypeSymbol); + return type.SpecialType == SpecialType.System_Collections_IEnumerable + ? cx.Compilation.ObjectType.WithAnnotation(NullableAnnotation.NotAnnotated) + : type.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T + ? type.GetAnnotatedTypeArguments().First() + : default(AnnotatedTypeSymbol); } public override AnnotatedType ElementType => Type.Create(Context, GetElementType(Context, symbol)); - class NamedTypeFactory : ICachedEntityFactory + private class NamedTypeFactory : ICachedEntityFactory { - public static readonly NamedTypeFactory Instance = new NamedTypeFactory(); + public static NamedTypeFactory Instance { get; } = new NamedTypeFactory(); - public NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init); + public NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init, false); } - public override Type TypeRef => NamedTypeRef.Create(Context, symbol); + private class UnderlyingTupleTypeFactory : ICachedEntityFactory + { + public static UnderlyingTupleTypeFactory Instance { get; } = new UnderlyingTupleTypeFactory(); + + public NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init, true); + } + + // Do not create typerefs of constructed generics as they are always in the current trap file. + // Create typerefs for constructed error types in case they are fully defined elsewhere. + // We cannot use `!this.NeedsPopulation` because this would not be stable as it would depend on + // the assembly that was being extracted at the time. + private bool UsesTypeRef => symbol.TypeKind == TypeKind.Error || SymbolEqualityComparer.Default.Equals(symbol.OriginalDefinition, symbol); + + public override Type TypeRef => UsesTypeRef ? (Type)NamedTypeRef.Create(Context, symbol) : this; } - class NamedTypeRef : Type + internal class NamedTypeRef : Type { - readonly Type referencedType; + private readonly Type referencedType; public NamedTypeRef(Context cx, INamedTypeSymbol symbol) : base(cx, symbol) { @@ -161,11 +207,13 @@ public NamedTypeRef(Context cx, INamedTypeSymbol symbol) : base(cx, symbol) } public static NamedTypeRef Create(Context cx, INamedTypeSymbol type) => - NamedTypeRefFactory.Instance.CreateEntity2(cx, type); + // We need to use a different cache key than `type` to avoid mixing up + // `NamedType`s and `NamedTypeRef`s + NamedTypeRefFactory.Instance.CreateEntity(cx, (typeof(NamedTypeRef), new SymbolEqualityWrapper(type)), type); - class NamedTypeRefFactory : ICachedEntityFactory + private class NamedTypeRefFactory : ICachedEntityFactory { - public static readonly NamedTypeRefFactory Instance = new NamedTypeRefFactory(); + public static NamedTypeRefFactory Instance { get; } = new NamedTypeRefFactory(); public NamedTypeRef Create(Context cx, INamedTypeSymbol init) => new NamedTypeRef(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs index 7ef079b932e4..2822e346384f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs @@ -3,9 +3,9 @@ namespace Semmle.Extraction.CSharp.Entities { - class NullType : Type + internal class NullType : Type { - NullType(Context cx) + private NullType(Context cx) : base(cx, null) { } public override void Populate(TextWriter trapFile) @@ -27,11 +27,11 @@ public override bool Equals(object obj) return obj != null && obj.GetType() == typeof(NullType); } - public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateNullableEntity(cx, null), NullableAnnotation.None); + public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateEntity(cx, typeof(NullType), null), NullableAnnotation.None); - class NullTypeFactory : ICachedEntityFactory + private class NullTypeFactory : ICachedEntityFactory { - public static readonly NullTypeFactory Instance = new NullTypeFactory(); + public static NullTypeFactory Instance { get; } = new NullTypeFactory(); public NullType Create(Context cx, ITypeSymbol init) => new NullType(cx); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs index 05a4300133a4..106efdd0cde4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs @@ -9,8 +9,8 @@ public sealed class Nullability { public int Annotation { get; } - static readonly Nullability[] EmptyArray = new Nullability[0]; - public readonly Nullability[] NullableParameters; + private static readonly Nullability[] emptyArray = System.Array.Empty(); + public IEnumerable NullableParameters { get; } public static Nullability Create(AnnotatedTypeSymbol ts) { @@ -30,11 +30,11 @@ public static Nullability Create(AnnotatedTypeSymbol ts) return new Nullability(ts); } - public bool IsOblivious => Annotation == 0 && NullableParameters.Length == 0; + public bool IsOblivious => Annotation == 0 && !NullableParameters.Any(); - static readonly Nullability oblivious = new Nullability(NullableAnnotation.None); - static readonly Nullability annotated = new Nullability(NullableAnnotation.Annotated); - static readonly Nullability notannotated = new Nullability(NullableAnnotation.NotAnnotated); + private static readonly Nullability oblivious = new Nullability(NullableAnnotation.None); + private static readonly Nullability annotated = new Nullability(NullableAnnotation.Annotated); + private static readonly Nullability notannotated = new Nullability(NullableAnnotation.NotAnnotated); private Nullability(NullableAnnotation n) { @@ -50,12 +50,12 @@ private Nullability(NullableAnnotation n) Annotation = 0; break; } - NullableParameters = EmptyArray; + NullableParameters = emptyArray; } private Nullability(AnnotatedTypeSymbol ts) : this(ts.Nullability) { - NullableParameters = ts.HasConsistentNullability() ? EmptyArray : ts.GetAnnotatedTypeArguments().Select(Create).ToArray(); + NullableParameters = ts.HasConsistentNullability() ? emptyArray : ts.GetAnnotatedTypeArguments().Select(Create).ToArray(); } public Nullability(IMethodSymbol method) @@ -71,7 +71,7 @@ public override bool Equals(object other) public override int GetHashCode() { - int h = Annotation; + var h = Annotation; foreach (var t in NullableParameters) h = h * 5 + t.GetHashCode(); @@ -90,11 +90,9 @@ public void WriteId(TextWriter trapFile) public override string ToString() { - using (var w = new StringWriter()) - { - WriteId(w); - return w.ToString(); - } + using var w = new StringWriter(); + WriteId(w); + return w.ToString(); } } @@ -114,7 +112,7 @@ public override void Populate(TextWriter trapFile) { trapFile.nullability(this, symbol.Annotation); - int i = 0; + var i = 0; foreach (var s in symbol.NullableParameters) { trapFile.nullability_parent(Create(Context, s), i, this); @@ -127,11 +125,11 @@ public override void WriteId(TextWriter trapFile) symbol.WriteId(trapFile); } - public static NullabilityEntity Create(Context cx, Nullability init) => NullabilityFactory.Instance.CreateEntity(cx, init); + public static NullabilityEntity Create(Context cx, Nullability init) => NullabilityFactory.Instance.CreateEntity(cx, init, init); - class NullabilityFactory : ICachedEntityFactory + private class NullabilityFactory : ICachedEntityFactory { - public static readonly NullabilityFactory Instance = new NullabilityFactory(); + public static NullabilityFactory Instance { get; } = new NullabilityFactory(); public NullabilityEntity Create(Context cx, Nullability init) => new NullabilityEntity(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs index db8b5ff8c23b..e2fbb8677cc8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs @@ -3,9 +3,9 @@ namespace Semmle.Extraction.CSharp.Entities { - class PointerType : Type + internal class PointerType : Type { - PointerType(Context cx, IPointerTypeSymbol init) + private PointerType(Context cx, IPointerTypeSymbol init) : base(cx, init) { PointedAtType = Create(cx, symbol.PointedAtType); @@ -29,11 +29,11 @@ public override void Populate(TextWriter trapFile) public Type PointedAtType { get; private set; } - public static PointerType Create(Context cx, IPointerTypeSymbol symbol) => PointerTypeFactory.Instance.CreateEntity(cx, symbol); + public static PointerType Create(Context cx, IPointerTypeSymbol symbol) => PointerTypeFactory.Instance.CreateEntityFromSymbol(cx, symbol); - class PointerTypeFactory : ICachedEntityFactory + private class PointerTypeFactory : ICachedEntityFactory { - public static readonly PointerTypeFactory Instance = new PointerTypeFactory(); + public static PointerTypeFactory Instance { get; } = new PointerTypeFactory(); public PointerType Create(Context cx, IPointerTypeSymbol init) => new PointerType(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index de6ea2170849..af9c4bf4fa64 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -11,18 +11,18 @@ namespace Semmle.Extraction.CSharp.Entities /// A tuple type, which is a C# type but not a .Net type. /// Tuples have the underlying type System.ValueTuple. /// - class TupleType : Type + internal class TupleType : Type { - public static TupleType Create(Context cx, INamedTypeSymbol type) => TupleTypeFactory.Instance.CreateEntity(cx, type); + public static TupleType Create(Context cx, INamedTypeSymbol type) => TupleTypeFactory.Instance.CreateEntityFromSymbol(cx, type); - class TupleTypeFactory : ICachedEntityFactory + private class TupleTypeFactory : ICachedEntityFactory { - public static readonly TupleTypeFactory Instance = new TupleTypeFactory(); + public static TupleTypeFactory Instance { get; } = new TupleTypeFactory(); public TupleType Create(Context cx, INamedTypeSymbol init) => new TupleType(cx, init); } - TupleType(Context cx, INamedTypeSymbol init) : base(cx, init) + private TupleType(Context cx, INamedTypeSymbol init) : base(cx, init) { tupleElementsLazy = new Lazy(() => symbol.TupleElements.Select(t => Field.Create(cx, t)).ToArray()); } @@ -32,7 +32,7 @@ class TupleTypeFactory : ICachedEntityFactory public override void WriteId(TextWriter trapFile) { - symbol.BuildTypeId(Context, trapFile, (cx0, tb0, sub) => tb0.WriteSubId(Create(cx0, sub))); + symbol.BuildTypeId(Context, trapFile, symbol); trapFile.Write(";tuple"); } @@ -41,10 +41,11 @@ public override void Populate(TextWriter trapFile) PopulateType(trapFile); PopulateGenerics(); - var underlyingType = NamedType.Create(Context, symbol.TupleUnderlyingType); + var underlyingType = NamedType.CreateNamedTypeFromTupleType(Context, symbol.TupleUnderlyingType ?? symbol); + trapFile.tuple_underlying_type(this, underlyingType); - int index = 0; + var index = 0; foreach (var element in TupleElements) trapFile.tuple_element(this, index++, element); @@ -55,7 +56,7 @@ public override void Populate(TextWriter trapFile) trapFile.type_location(this, Context.Create(l)); } - readonly Lazy tupleElementsLazy; + private readonly Lazy tupleElementsLazy; public Field[] TupleElements => tupleElementsLazy.Value; public override IEnumerable TypeMentions => TupleElements.Select(e => e.Type.Type); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index 7d9f5e3651e8..1b1d205b3d72 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -21,9 +21,9 @@ public AnnotatedType(Type t, NullableAnnotation n) /// /// The underlying type. /// - public Type Type; + public Type Type { get; private set; } - readonly private NullableAnnotation annotation; + private readonly NullableAnnotation annotation; /// /// Gets the annotated type symbol of this annotated type. @@ -33,7 +33,7 @@ public AnnotatedType(Type t, NullableAnnotation n) public abstract class Type : CachedSymbol { - public Type(Context cx, ITypeSymbol init) + protected Type(Context cx, ITypeSymbol init) : base(cx, init) { } public virtual AnnotatedType ElementType => default(AnnotatedType); @@ -47,7 +47,7 @@ public static bool ConstructedOrParentIsConstructed(INamedTypeSymbol symbol) symbol.ContainingType != null && ConstructedOrParentIsConstructed(symbol.ContainingType); } - static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t) + private static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool constructUnderlyingTupleType) { switch (t.SpecialType) { @@ -67,17 +67,22 @@ static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t) case SpecialType.System_Single: return Kinds.TypeKind.FLOAT; case SpecialType.System_IntPtr: return Kinds.TypeKind.INT_PTR; default: - if (t.IsBoundNullable()) return Kinds.TypeKind.NULLABLE; + if (t.IsBoundNullable()) + return Kinds.TypeKind.NULLABLE; + switch (t.TypeKind) { case TypeKind.Class: return Kinds.TypeKind.CLASS; case TypeKind.Struct: - return ((INamedTypeSymbol)t).IsTupleType ? Kinds.TypeKind.TUPLE : Kinds.TypeKind.STRUCT; + return ((INamedTypeSymbol)t).IsTupleType && !constructUnderlyingTupleType + ? Kinds.TypeKind.TUPLE + : Kinds.TypeKind.STRUCT; case TypeKind.Interface: return Kinds.TypeKind.INTERFACE; case TypeKind.Array: return Kinds.TypeKind.ARRAY; case TypeKind.Enum: return Kinds.TypeKind.ENUM; case TypeKind.Delegate: return Kinds.TypeKind.DELEGATE; case TypeKind.Pointer: return Kinds.TypeKind.POINTER; + case TypeKind.Error: return Kinds.TypeKind.UNKNOWN; default: cx.ModelError(t, $"Unhandled type kind '{t.TypeKind}'"); return Kinds.TypeKind.UNKNOWN; @@ -85,7 +90,7 @@ static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t) } } - protected void PopulateType(TextWriter trapFile) + protected void PopulateType(TextWriter trapFile, bool constructUnderlyingTupleType = false) { PopulateMetadataHandle(trapFile); PopulateAttributes(); @@ -93,26 +98,21 @@ protected void PopulateType(TextWriter trapFile) trapFile.Write("types("); trapFile.WriteColumn(this); trapFile.Write(','); - trapFile.WriteColumn((int)GetClassType(Context, symbol)); + trapFile.WriteColumn((int)GetClassType(Context, symbol, constructUnderlyingTupleType)); trapFile.Write(",\""); - symbol.BuildDisplayName(Context, trapFile); + symbol.BuildDisplayName(Context, trapFile, constructUnderlyingTupleType); trapFile.WriteLine("\")"); // Visit base types var baseTypes = new List(); - if (symbol.BaseType != null) + if (symbol.GetNonObjectBaseType(Context) is INamedTypeSymbol @base) { - Type baseKey = Create(Context, symbol.BaseType); + var baseKey = Create(Context, @base); trapFile.extend(this, baseKey.TypeRef); if (symbol.TypeKind != TypeKind.Struct) baseTypes.Add(baseKey); } - if (symbol.TypeKind == TypeKind.Interface) - { - trapFile.extend(this, Create(Context, Context.Compilation.ObjectType)); - } - if (!(base.symbol is IArrayTypeSymbol)) { foreach (var t in base.symbol.Interfaces.Select(i => Create(Context, i))) @@ -125,7 +125,7 @@ protected void PopulateType(TextWriter trapFile) var containingType = ContainingType; if (containingType != null && symbol.Kind != SymbolKind.TypeParameter) { - Type originalDefinition = symbol.TypeKind == TypeKind.Error ? this : Create(Context, symbol.OriginalDefinition); + var originalDefinition = symbol.TypeKind == TypeKind.Error ? this : Create(Context, symbol.OriginalDefinition); trapFile.nested_types(this, containingType, originalDefinition); } else if (symbol.ContainingNamespace != null) @@ -133,19 +133,19 @@ protected void PopulateType(TextWriter trapFile) trapFile.parent_namespace(this, Namespace.Create(Context, symbol.ContainingNamespace)); } - if (symbol is IArrayTypeSymbol) + if (symbol is IArrayTypeSymbol array) { // They are in the namespace of the original object - ITypeSymbol elementType = ((IArrayTypeSymbol)symbol).ElementType; - INamespaceSymbol ns = elementType.TypeKind == TypeKind.TypeParameter ? Context.Compilation.GlobalNamespace : elementType.ContainingNamespace; + var elementType = array.ElementType; + var ns = elementType.TypeKind == TypeKind.TypeParameter ? Context.Compilation.GlobalNamespace : elementType.ContainingNamespace; if (ns != null) trapFile.parent_namespace(this, Namespace.Create(Context, ns)); } - if (symbol is IPointerTypeSymbol) + if (symbol is IPointerTypeSymbol pointer) { - ITypeSymbol elementType = ((IPointerTypeSymbol)symbol).PointedAtType; - INamespaceSymbol ns = elementType.TypeKind == TypeKind.TypeParameter ? Context.Compilation.GlobalNamespace : elementType.ContainingNamespace; + var elementType = pointer.PointedAtType; + var ns = elementType.TypeKind == TypeKind.TypeParameter ? Context.Compilation.GlobalNamespace : elementType.ContainingNamespace; if (ns != null) trapFile.parent_namespace(this, Namespace.Create(Context, ns)); @@ -185,12 +185,13 @@ protected void PopulateType(TextWriter trapFile) baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); - baseLists. - Where(bl => bl != null). - SelectMany(bl => bl.Types). - Zip(baseTypes.Where(bt => bt.symbol.SpecialType != SpecialType.System_Object), - (s, t) => TypeMention.Create(Context, s.Type, this, t)). - Enumerate(); + baseLists + .Where(bl => bl != null) + .SelectMany(bl => bl.Types) + .Zip( + baseTypes.Where(bt => bt.symbol.SpecialType != SpecialType.System_Object), + (s, t) => TypeMention.Create(Context, s.Type, this, t)) + .Enumerate(); } } @@ -275,9 +276,9 @@ public void ExtractRecursive(TextWriter trapFile, IEntity parent) public static Type Create(Context cx, ITypeSymbol type) { type = type.DisambiguateType(); - const bool errorTypeIsNull = false; - return type == null || (errorTypeIsNull && type.TypeKind == TypeKind.Error) ? - NullType.Create(cx).Type : (Type)cx.CreateEntity(type); + return type == null + ? NullType.Create(cx).Type + : (Type)cx.CreateEntity(type); } public static AnnotatedType Create(Context cx, AnnotatedTypeSymbol type) => @@ -292,17 +293,19 @@ public static bool IsDelegate(ITypeSymbol symbol) => /// A copy of a delegate "Invoke" method parameter used for the delgate /// type. /// - class DelegateTypeParameter : Parameter + private class DelegateTypeParameter : Parameter { - DelegateTypeParameter(Context cx, IParameterSymbol init, IEntity parent, Parameter original) + private DelegateTypeParameter(Context cx, IParameterSymbol init, IEntity parent, Parameter original) : base(cx, init, parent, original) { } - new public static DelegateTypeParameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter original = null) => - DelegateTypeParameterFactory.Instance.CreateEntity(cx, (param, parent, original)); + public static new DelegateTypeParameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter original = null) => + // We need to use a different cache key than `param` to avoid mixing up + // `DelegateTypeParameter`s and `Parameter`s + DelegateTypeParameterFactory.Instance.CreateEntity(cx, (typeof(DelegateTypeParameter), new SymbolEqualityWrapper(param)), (param, parent, original)); - class DelegateTypeParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), DelegateTypeParameter> + private class DelegateTypeParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), DelegateTypeParameter> { - public static readonly DelegateTypeParameterFactory Instance = new DelegateTypeParameterFactory(); + public static DelegateTypeParameterFactory Instance { get; } = new DelegateTypeParameterFactory(); public DelegateTypeParameter Create(Context cx, (IParameterSymbol, IEntity, Parameter) init) => new DelegateTypeParameter(cx, init.Item1, init.Item2, init.Item3); @@ -324,11 +327,19 @@ public virtual IEnumerable TypeMentions } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; + + public override bool Equals(object obj) + { + var other = obj as Type; + return other?.GetType() == GetType() && SymbolEqualityComparer.IncludeNullability.Equals(other.symbol, symbol); + } + + public override int GetHashCode() => SymbolEqualityComparer.IncludeNullability.GetHashCode(symbol); } - abstract class Type : Type where T : ITypeSymbol + internal abstract class Type : Type where T : ITypeSymbol { - public Type(Context cx, T init) + protected Type(Context cx, T init) : base(cx, init) { } public new T symbol => (T)base.symbol; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs index 2fdb8cd3a834..b4d35f7b5067 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs @@ -7,20 +7,18 @@ namespace Semmle.Extraction.CSharp.Entities { - enum Variance + internal enum Variance { None = 0, Out = 1, In = 2 } - class TypeParameter : Type + internal class TypeParameter : Type { - TypeParameter(Context cx, ITypeParameterSymbol init) + private TypeParameter(Context cx, ITypeParameterSymbol init) : base(cx, init) { } - static readonly string valueTypeName = typeof(System.ValueType).ToString(); - public override void Populate(TextWriter trapFile) { var constraints = new TypeParameterConstraints(Context); @@ -35,20 +33,14 @@ public override void Populate(TextWriter trapFile) if (symbol.HasConstructorConstraint) trapFile.general_type_parameter_constraints(constraints, 3); - if(symbol.HasUnmanagedTypeConstraint) + if (symbol.HasUnmanagedTypeConstraint) trapFile.general_type_parameter_constraints(constraints, 4); - ITypeSymbol baseType = symbol.HasValueTypeConstraint ? - Context.Compilation.GetTypeByMetadataName(valueTypeName) : - Context.Compilation.ObjectType; - - if(symbol.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated) + if (symbol.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated) trapFile.general_type_parameter_constraints(constraints, 5); foreach (var abase in symbol.GetAnnotatedTypeConstraints()) { - if (abase.Symbol.TypeKind != TypeKind.Interface) - baseType = abase.Symbol; var t = Create(Context, abase.Symbol); trapFile.specific_type_parameter_constraints(constraints, t.TypeRef); if (!abase.HasObliviousNullability()) @@ -56,9 +48,8 @@ public override void Populate(TextWriter trapFile) } trapFile.types(this, Kinds.TypeKind.TYPE_PARAMETER, symbol.Name); - trapFile.extend(this, Create(Context, baseType).TypeRef); - Namespace parentNs = Namespace.Create(Context, symbol.TypeParameterKind == TypeParameterKind.Method ? Context.Compilation.GlobalNamespace : symbol.ContainingNamespace); + var parentNs = Namespace.Create(Context, symbol.TypeParameterKind == TypeParameterKind.Method ? Context.Compilation.GlobalNamespace : symbol.ContainingNamespace); trapFile.parent_namespace(this, parentNs); foreach (var l in symbol.Locations) @@ -68,8 +59,12 @@ public override void Populate(TextWriter trapFile) if (IsSourceDeclaration) { - var declSyntaxReferences = symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()). - Select(s => s.Parent).Where(p => p != null).Select(p => p.Parent).ToArray(); + var declSyntaxReferences = symbol.DeclaringSyntaxReferences + .Select(d => d.GetSyntax()) + .Select(s => s.Parent) + .Where(p => p != null) + .Select(p => p.Parent) + .ToArray(); var clauses = declSyntaxReferences.OfType().SelectMany(m => m.ConstraintClauses); clauses = clauses.Concat(declSyntaxReferences.OfType().SelectMany(c => c.ConstraintClauses)); clauses = clauses.Concat(declSyntaxReferences.OfType().SelectMany(c => c.ConstraintClauses)); @@ -87,8 +82,8 @@ public override void Populate(TextWriter trapFile) } } - static public TypeParameter Create(Context cx, ITypeParameterSymbol p) => - TypeParameterFactory.Instance.CreateEntity(cx, p); + public static TypeParameter Create(Context cx, ITypeParameterSymbol p) => + TypeParameterFactory.Instance.CreateEntityFromSymbol(cx, p); /// /// The variance of this type parameter. @@ -132,9 +127,9 @@ public override void WriteId(TextWriter trapFile) trapFile.Write(kind); } - class TypeParameterFactory : ICachedEntityFactory + private class TypeParameterFactory : ICachedEntityFactory { - public static readonly TypeParameterFactory Instance = new TypeParameterFactory(); + public static TypeParameterFactory Instance { get; } = new TypeParameterFactory(); public TypeParameter Create(Context cx, ITypeParameterSymbol init) => new TypeParameter(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs index 8d70741a70a3..04ab78a0f547 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs @@ -2,7 +2,7 @@ namespace Semmle.Extraction.CSharp.Entities { - class TypeParameterConstraints : FreshEntity + internal class TypeParameterConstraints : FreshEntity { public TypeParameterConstraints(Context cx) : base(cx) { } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs index 2a64a29fa0d2..d4c6fa457c1f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs @@ -5,7 +5,7 @@ namespace Semmle.Extraction.CSharp.Entities { - class UserOperator : Method + internal class UserOperator : Method { protected UserOperator(Context cx, IMethodSymbol init) : base(cx, init) { } @@ -49,26 +49,20 @@ public override Type ContainingType } } - public override void WriteId(TextWriter trapFile) - { - AddSignatureTypeToId(Context, trapFile, symbol, symbol.ReturnType); // Needed for op_explicit(), which differs only by return type. - trapFile.Write(' '); - BuildMethodId(this, trapFile); - } - /// /// For some reason, some operators are missing from the Roslyn database of mscorlib. /// This method returns true for such operators. /// /// The type containing this operator. /// - bool IsImplicitOperator(out ITypeSymbol containingType) + private bool IsImplicitOperator(out ITypeSymbol containingType) { containingType = symbol.ContainingType; if (containingType != null) { var containingNamedType = containingType as INamedTypeSymbol; - return containingNamedType == null || !containingNamedType.MemberNames.Contains(symbol.Name); + return containingNamedType == null || + !containingNamedType.GetMembers(symbol.Name).Contains(symbol); } var pointerType = symbol.Parameters.Select(p => p.Type).OfType().FirstOrDefault(); @@ -184,17 +178,16 @@ public static bool OperatorSymbol(string methodName, out string operatorName) /// The converted name. public static string OperatorSymbol(Context cx, string methodName) { - string result; - if (!OperatorSymbol(methodName, out result)) + if (!OperatorSymbol(methodName, out var result)) cx.ModelError($"Unhandled operator name in OperatorSymbol(): '{methodName}'"); return result; } - public new static UserOperator Create(Context cx, IMethodSymbol symbol) => UserOperatorFactory.Instance.CreateEntity(cx, symbol); + public static new UserOperator Create(Context cx, IMethodSymbol symbol) => UserOperatorFactory.Instance.CreateEntityFromSymbol(cx, symbol); - class UserOperatorFactory : ICachedEntityFactory + private class UserOperatorFactory : ICachedEntityFactory { - public static readonly UserOperatorFactory Instance = new UserOperatorFactory(); + public static UserOperatorFactory Instance { get; } = new UserOperatorFactory(); public UserOperator Create(Context cx, IMethodSymbol init) => new UserOperator(cx, init); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UsingDirective.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UsingDirective.cs index 02b67efc1642..caa12ab0082f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UsingDirective.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UsingDirective.cs @@ -1,68 +1,59 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Entities; -using System.Collections.Generic; using System.IO; namespace Semmle.Extraction.CSharp.Entities { - class UsingDirective : FreshEntity + internal class UsingDirective : FreshEntity { - readonly UsingDirectiveSyntax Node; - readonly NamespaceDeclaration Parent; + private readonly UsingDirectiveSyntax node; + private readonly NamespaceDeclaration parent; public UsingDirective(Context cx, UsingDirectiveSyntax usingDirective, NamespaceDeclaration parent) : base(cx) { - Node = usingDirective; - Parent = parent; + node = usingDirective; + this.parent = parent; TryPopulate(); } protected override void Populate(TextWriter trapFile) { - var info = cx.GetModel(Node).GetSymbolInfo(Node.Name); + var info = cx.GetModel(node).GetSymbolInfo(node.Name); - if (Node.StaticKeyword.Kind() == SyntaxKind.None) + if (node.StaticKeyword.Kind() == SyntaxKind.None) { // A normal using - var namespaceSymbol = info.Symbol as INamespaceSymbol; - - if (namespaceSymbol == null) - { - cx.Extractor.MissingNamespace(Node.Name.ToFullString(), cx.FromSource); - cx.ModelError(Node, "Namespace not found"); - return; - } - else + if (info.Symbol is INamespaceSymbol namespaceSymbol) { var ns = Namespace.Create(cx, namespaceSymbol); trapFile.using_namespace_directives(this, ns); trapFile.using_directive_location(this, cx.Create(ReportingLocation)); } + else + { + cx.Extractor.MissingNamespace(node.Name.ToFullString(), cx.FromSource); + cx.ModelError(node, "Namespace not found"); + return; + } } else { // A "using static" - Type m = Type.Create(cx, (ITypeSymbol)info.Symbol); + var m = Type.Create(cx, (ITypeSymbol)info.Symbol); trapFile.using_static_directives(this, m.TypeRef); trapFile.using_directive_location(this, cx.Create(ReportingLocation)); } - if (Parent != null) + if (parent != null) { - trapFile.parent_namespace_declaration(this, Parent); + trapFile.parent_namespace_declaration(this, parent); } } - public sealed override Microsoft.CodeAnalysis.Location ReportingLocation => Node.GetLocation(); - - public IEnumerable GetTuples() - { - yield break; - } + public sealed override Microsoft.CodeAnalysis.Location ReportingLocation => node.GetLocation(); public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs index 7ca12e00f26b..cf991e0930a4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs @@ -23,21 +23,25 @@ public enum ExitCode Failed // Trap could not be generated } - class LogProgressMonitor : IProgressMonitor + private class LogProgressMonitor : IProgressMonitor { - readonly ILogger Logger; + private readonly ILogger logger; public LogProgressMonitor(ILogger logger) { - Logger = logger; + this.logger = logger; } public void Analysed(int item, int total, string source, string output, TimeSpan time, AnalysisAction action) { if (action != AnalysisAction.UpToDate) { - Logger.Log(Severity.Info, " {0} ({1})", source, - action == AnalysisAction.Extracted ? time.ToString() : action == AnalysisAction.Excluded ? "excluded" : "up to date"); + logger.Log(Severity.Info, " {0} ({1})", source, + action == AnalysisAction.Extracted + ? time.ToString() + : action == AnalysisAction.Excluded + ? "excluded" + : "up to date"); } } @@ -66,7 +70,7 @@ public static ExitCode Run(string[] args) stopwatch.Start(); var commandLineArguments = Options.CreateWithEnvironment(args); var fileLogger = new FileLogger(commandLineArguments.Verbosity, GetCSharpLogPath()); - var logger = commandLineArguments.Console + using var logger = commandLineArguments.Console ? new CombinedLogger(new ConsoleLogger(commandLineArguments.Verbosity), fileLogger) : (ILogger)fileLogger; @@ -76,124 +80,123 @@ public static ExitCode Run(string[] args) return ExitCode.Ok; } - using (var analyser = new Analyser(new LogProgressMonitor(logger), logger)) - using (var references = new BlockingCollection()) - { - try - { - var compilerVersion = new CompilerVersion(commandLineArguments); + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); + var pathTransformer = new PathTransformer(canonicalPathCache); - bool preserveSymlinks = Environment.GetEnvironmentVariable("SEMMLE_PRESERVE_SYMLINKS") == "true"; - var canonicalPathCache = CanonicalPathCache.Create(logger, 1000, preserveSymlinks ? CanonicalPathCache.Symlinks.Preserve : CanonicalPathCache.Symlinks.Follow); + using var analyser = new Analyser(new LogProgressMonitor(logger), logger, commandLineArguments.AssemblySensitiveTrap, pathTransformer); + using var references = new BlockingCollection(); + try + { + var compilerVersion = new CompilerVersion(commandLineArguments); - if (compilerVersion.SkipExtraction) - { - logger.Log(Severity.Warning, " Unrecognized compiler '{0}' because {1}", compilerVersion.SpecifiedCompiler, compilerVersion.SkipReason); - return ExitCode.Ok; - } + if (compilerVersion.SkipExtraction) + { + logger.Log(Severity.Warning, " Unrecognized compiler '{0}' because {1}", compilerVersion.SpecifiedCompiler, compilerVersion.SkipReason); + return ExitCode.Ok; + } - var cwd = Directory.GetCurrentDirectory(); - var compilerArguments = CSharpCommandLineParser.Default.Parse( - compilerVersion.ArgsWithResponse, - cwd, - compilerVersion.FrameworkPath, - compilerVersion.AdditionalReferenceDirectories - ); + var cwd = Directory.GetCurrentDirectory(); + var compilerArguments = CSharpCommandLineParser.Default.Parse( + compilerVersion.ArgsWithResponse, + cwd, + compilerVersion.FrameworkPath, + compilerVersion.AdditionalReferenceDirectories + ); - if (compilerArguments == null) - { - var sb = new StringBuilder(); - sb.Append(" Failed to parse command line: ").AppendList(" ", args); - logger.Log(Severity.Error, sb.ToString()); - ++analyser.CompilationErrors; - return ExitCode.Failed; - } + if (compilerArguments == null) + { + var sb = new StringBuilder(); + sb.Append(" Failed to parse command line: ").AppendList(" ", args); + logger.Log(Severity.Error, sb.ToString()); + ++analyser.CompilationErrors; + return ExitCode.Failed; + } - if (!analyser.BeginInitialize(compilerVersion.ArgsWithResponse)) - { - logger.Log(Severity.Info, "Skipping extraction since files have already been extracted"); - return ExitCode.Ok; - } + if (!analyser.BeginInitialize(compilerVersion.ArgsWithResponse)) + { + logger.Log(Severity.Info, "Skipping extraction since files have already been extracted"); + return ExitCode.Ok; + } - var referenceTasks = ResolveReferences(compilerArguments, analyser, canonicalPathCache, references); + var referenceTasks = ResolveReferences(compilerArguments, analyser, canonicalPathCache, references); - var syntaxTrees = new List(); - var syntaxTreeTasks = ReadSyntaxTrees( - compilerArguments.SourceFiles. - Select(src => canonicalPathCache.GetCanonicalPath(src.Path)), - analyser, - compilerArguments.ParseOptions, - compilerArguments.Encoding, - syntaxTrees); + var syntaxTrees = new List(); + var syntaxTreeTasks = ReadSyntaxTrees( + compilerArguments.SourceFiles. + Select(src => canonicalPathCache.GetCanonicalPath(src.Path)), + analyser, + compilerArguments.ParseOptions, + compilerArguments.Encoding, + syntaxTrees); - var sw1 = new Stopwatch(); - sw1.Start(); + var sw1 = new Stopwatch(); + sw1.Start(); - Parallel.Invoke( - new ParallelOptions { MaxDegreeOfParallelism = commandLineArguments.Threads }, - referenceTasks.Interleave(syntaxTreeTasks).ToArray()); + Parallel.Invoke( + new ParallelOptions { MaxDegreeOfParallelism = commandLineArguments.Threads }, + referenceTasks.Interleave(syntaxTreeTasks).ToArray()); - if (syntaxTrees.Count == 0) - { - logger.Log(Severity.Error, " No source files"); - ++analyser.CompilationErrors; - return ExitCode.Failed; - } + if (syntaxTrees.Count == 0) + { + logger.Log(Severity.Error, " No source files"); + ++analyser.CompilationErrors; + return ExitCode.Failed; + } - var compilation = CSharpCompilation.Create( - compilerArguments.CompilationName, - syntaxTrees, - references, - compilerArguments.CompilationOptions. - WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default). - WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths)) - // csc.exe (CSharpCompiler.cs) also provides WithMetadataReferenceResolver, - // WithXmlReferenceResolver and - // WithSourceReferenceResolver. - // These would be needed if we hadn't explicitly provided the source/references - // already. - ); - - analyser.EndInitialize(compilerArguments, commandLineArguments, compilation); - analyser.AnalyseCompilation(cwd, args); - analyser.AnalyseReferences(); - - foreach (var tree in compilation.SyntaxTrees) - { - analyser.AnalyseTree(tree); - } + // csc.exe (CSharpCompiler.cs) also provides CompilationOptions + // .WithMetadataReferenceResolver(), + // .WithXmlReferenceResolver() and + // .WithSourceReferenceResolver(). + // These would be needed if we hadn't explicitly provided the source/references + // already. + var compilation = CSharpCompilation.Create( + compilerArguments.CompilationName, + syntaxTrees, + references, + compilerArguments.CompilationOptions. + WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default). + WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths)) + ); + + analyser.EndInitialize(compilerArguments, commandLineArguments, compilation); + analyser.AnalyseCompilation(cwd, args); + analyser.AnalyseReferences(); + + foreach (var tree in compilation.SyntaxTrees) + { + analyser.AnalyseTree(tree); + } - var currentProcess = Process.GetCurrentProcess(); - var cpuTime1 = currentProcess.TotalProcessorTime; - var userTime1 = currentProcess.UserProcessorTime; - sw1.Stop(); - logger.Log(Severity.Info, " Models constructed in {0}", sw1.Elapsed); + var currentProcess = Process.GetCurrentProcess(); + var cpuTime1 = currentProcess.TotalProcessorTime; + var userTime1 = currentProcess.UserProcessorTime; + sw1.Stop(); + logger.Log(Severity.Info, " Models constructed in {0}", sw1.Elapsed); - var sw2 = new Stopwatch(); - sw2.Start(); - analyser.PerformExtraction(commandLineArguments.Threads); - sw2.Stop(); - var cpuTime2 = currentProcess.TotalProcessorTime; - var userTime2 = currentProcess.UserProcessorTime; + var sw2 = new Stopwatch(); + sw2.Start(); + analyser.PerformExtraction(commandLineArguments.Threads); + sw2.Stop(); + var cpuTime2 = currentProcess.TotalProcessorTime; + var userTime2 = currentProcess.UserProcessorTime; - var performance = new Entities.PerformanceMetrics() - { - Frontend = new Entities.Timings() { Elapsed = sw1.Elapsed, Cpu = cpuTime1, User = userTime1 }, - Extractor = new Entities.Timings() { Elapsed = sw2.Elapsed, Cpu = cpuTime2 - cpuTime1, User = userTime2 - userTime1 }, - Total = new Entities.Timings() { Elapsed = stopwatch.Elapsed, Cpu = cpuTime2, User = userTime2 }, - PeakWorkingSet = currentProcess.PeakWorkingSet64 - }; + var performance = new Entities.PerformanceMetrics() + { + Frontend = new Entities.Timings() { Elapsed = sw1.Elapsed, Cpu = cpuTime1, User = userTime1 }, + Extractor = new Entities.Timings() { Elapsed = sw2.Elapsed, Cpu = cpuTime2 - cpuTime1, User = userTime2 - userTime1 }, + Total = new Entities.Timings() { Elapsed = stopwatch.Elapsed, Cpu = cpuTime2, User = userTime2 }, + PeakWorkingSet = currentProcess.PeakWorkingSet64 + }; - analyser.LogPerformance(performance); - logger.Log(Severity.Info, " Extraction took {0}", sw2.Elapsed); + analyser.LogPerformance(performance); + logger.Log(Severity.Info, " Extraction took {0}", sw2.Elapsed); - return analyser.TotalErrors == 0 ? ExitCode.Ok : ExitCode.Errors; - } - catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] - { - logger.Log(Severity.Error, " Unhandled exception: {0}", ex); - return ExitCode.Errors; - } + return analyser.TotalErrors == 0 ? ExitCode.Ok : ExitCode.Errors; + } + catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] + { + logger.Log(Severity.Error, " Unhandled exception: {0}", ex); + return ExitCode.Errors; } } @@ -202,7 +205,7 @@ public static ExitCode Run(string[] args) /// /// Command line arguments. /// List of directories. - static IEnumerable FixedReferencePaths(Microsoft.CodeAnalysis.CommandLineArguments args) + private static IEnumerable FixedReferencePaths(Microsoft.CodeAnalysis.CommandLineArguments args) { // See https://msdn.microsoft.com/en-us/library/s5bac5fx.aspx // on how csc resolves references. Basically, @@ -221,7 +224,7 @@ static IEnumerable FixedReferencePaths(Microsoft.CodeAnalysis.CommandLin yield return lib; } - static MetadataReference MakeReference(CommandLineReference reference, string path) + private static MetadataReference MakeReference(CommandLineReference reference, string path) { return MetadataReference.CreateFromFile(path).WithProperties(reference.Properties); } @@ -232,7 +235,7 @@ static MetadataReference MakeReference(CommandLineReference reference, string pa /// The resolved references will be added (thread-safely) to the supplied /// list . /// - static IEnumerable ResolveReferences(Microsoft.CodeAnalysis.CommandLineArguments args, Analyser analyser, CanonicalPathCache canonicalPathCache, BlockingCollection ret) + private static IEnumerable ResolveReferences(Microsoft.CodeAnalysis.CommandLineArguments args, Analyser analyser, CanonicalPathCache canonicalPathCache, BlockingCollection ret) { var referencePaths = new Lazy(() => FixedReferencePaths(args).ToArray()); return args.MetadataReferences.Select(clref => () => @@ -255,25 +258,23 @@ static IEnumerable ResolveReferences(Microsoft.CodeAnalysis.CommandLineA } else { - bool referenceFound = false; + var composed = referencePaths.Value + .Select(path => Path.Combine(path, clref.Reference)) + .Where(path => File.Exists(path)) + .Select(path => canonicalPathCache.GetCanonicalPath(path)) + .FirstOrDefault(); + + if (composed is object) { - foreach (var composed in referencePaths.Value. - Select(path => Path.Combine(path, clref.Reference)). - Where(path => File.Exists(path)). - Select(path => canonicalPathCache.GetCanonicalPath(path))) - { - referenceFound = true; - var reference = MakeReference(clref, composed); - ret.Add(reference); - break; - } - if (!referenceFound) + var reference = MakeReference(clref, composed); + ret.Add(reference); + } + else + { + lock (analyser) { - lock (analyser) - { - analyser.Logger.Log(Severity.Error, " Unable to resolve reference '{0}'", clref.Reference); - ++analyser.CompilationErrors; - } + analyser.Logger.Log(Severity.Error, " Unable to resolve reference '{0}'", clref.Reference); + ++analyser.CompilationErrors; } } } @@ -286,18 +287,16 @@ static IEnumerable ResolveReferences(Microsoft.CodeAnalysis.CommandLineA /// The constructed syntax trees will be added (thread-safely) to the supplied /// list . /// - static IEnumerable ReadSyntaxTrees(IEnumerable sources, Analyser analyser, CSharpParseOptions parseOptions, Encoding encoding, IList ret) + private static IEnumerable ReadSyntaxTrees(IEnumerable sources, Analyser analyser, CSharpParseOptions parseOptions, Encoding encoding, IList ret) { return sources.Select(path => () => { try { - using (var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - var st = CSharpSyntaxTree.ParseText(SourceText.From(file, encoding), parseOptions, path); - lock (ret) - ret.Add(st); - } + using var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + var st = CSharpSyntaxTree.ParseText(SourceText.From(file, encoding), parseOptions, path); + lock (ret) + ret.Add(st); } catch (IOException ex) { @@ -317,71 +316,72 @@ public static void ExtractStandalone( ILogger logger, CommonOptions options) { - using (var analyser = new Analyser(pm, logger)) - using (var references = new BlockingCollection()) + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); + var pathTransformer = new PathTransformer(canonicalPathCache); + + using var analyser = new Analyser(pm, logger, false, pathTransformer); + using var references = new BlockingCollection(); + try { - try + var referenceTasks = referencePaths.Select(path => () => { - var referenceTasks = referencePaths.Select(path => () => - { - var reference = MetadataReference.CreateFromFile(path); - references.Add(reference); - }); + var reference = MetadataReference.CreateFromFile(path); + references.Add(reference); + }); - var syntaxTrees = new List(); - var syntaxTreeTasks = ReadSyntaxTrees(sources, analyser, null, null, syntaxTrees); + var syntaxTrees = new List(); + var syntaxTreeTasks = ReadSyntaxTrees(sources, analyser, null, null, syntaxTrees); - var sw = new Stopwatch(); - sw.Start(); + var sw = new Stopwatch(); + sw.Start(); - Parallel.Invoke( - new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, - referenceTasks.Interleave(syntaxTreeTasks).ToArray()); - - if (syntaxTrees.Count == 0) - { - analyser.Logger.Log(Severity.Error, " No source files"); - ++analyser.CompilationErrors; - } + Parallel.Invoke( + new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, + referenceTasks.Interleave(syntaxTreeTasks).ToArray()); - var compilation = CSharpCompilation.Create( - "csharp.dll", - syntaxTrees, - references - ); - - analyser.InitializeStandalone(compilation, options); - analyser.AnalyseReferences(); + if (syntaxTrees.Count == 0) + { + analyser.Logger.Log(Severity.Error, " No source files"); + ++analyser.CompilationErrors; + } - foreach (var tree in compilation.SyntaxTrees) - { - analyser.AnalyseTree(tree); - } + var compilation = CSharpCompilation.Create( + "csharp.dll", + syntaxTrees, + references + ); - sw.Stop(); - analyser.Logger.Log(Severity.Info, " Models constructed in {0}", sw.Elapsed); + analyser.InitializeStandalone(compilation, options); + analyser.AnalyseReferences(); - sw.Restart(); - analyser.PerformExtraction(options.Threads); - sw.Stop(); - analyser.Logger.Log(Severity.Info, " Extraction took {0}", sw.Elapsed); + foreach (var tree in compilation.SyntaxTrees) + { + analyser.AnalyseTree(tree); + } - foreach (var type in analyser.MissingNamespaces) - { - pm.MissingNamespace(type); - } + sw.Stop(); + analyser.Logger.Log(Severity.Info, " Models constructed in {0}", sw.Elapsed); - foreach (var type in analyser.MissingTypes) - { - pm.MissingType(type); - } + sw.Restart(); + analyser.PerformExtraction(options.Threads); + sw.Stop(); + analyser.Logger.Log(Severity.Info, " Extraction took {0}", sw.Elapsed); - pm.MissingSummary(analyser.MissingTypes.Count(), analyser.MissingNamespaces.Count()); + foreach (var type in analyser.MissingNamespaces) + { + pm.MissingNamespace(type); } - catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] + + foreach (var type in analyser.MissingTypes) { - analyser.Logger.Log(Severity.Error, " Unhandled exception: {0}", ex); + pm.MissingType(type); } + + pm.MissingSummary(analyser.MissingTypes.Count(), analyser.MissingNamespaces.Count()); + } + catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] + { + analyser.Logger.Log(Severity.Error, " Unhandled exception: {0}", ex); } } @@ -403,7 +403,7 @@ public static string GetCSharpArgsLogPath(string hash) => public static IEnumerable GetCSharpArgsLogs() => Directory.EnumerateFiles(GetCSharpLogDirectory(), "csharp.*.txt"); - static string GetCSharpLogDirectory() + private static string GetCSharpLogDirectory() { var codeQlLogDir = Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_LOG_DIR"); if (!string.IsNullOrEmpty(codeQlLogDir)) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Options.cs b/csharp/extractor/Semmle.Extraction.CSharp/Options.cs index 8fa7be08b675..e20d4e846d44 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Options.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Options.cs @@ -9,22 +9,27 @@ public sealed class Options : CommonOptions /// /// The compiler exe, or null if unspecified. /// - public string CompilerName; + public string CompilerName { get; set; } /// /// Specified .Net Framework dir, or null if unspecified. /// - public string Framework; + public string Framework { get; set; } /// /// All other arguments passed to the compilation. /// - public IList CompilerArguments = new List(); + public IList CompilerArguments { get; } = new List(); /// /// Holds if the extractor was launched from the CLR tracer. /// - public bool ClrTracer = false; + public bool ClrTracer { get; private set; } = false; + + /// + /// Holds if assembly information should be prefixed to TRAP labels. + /// + public bool AssemblySensitiveTrap { get; private set; } = false; public static Options CreateWithEnvironment(string[] arguments) { @@ -41,19 +46,19 @@ public static Options CreateWithEnvironment(string[] arguments) return options; } - public override bool handleArgument(string argument) + public override bool HandleArgument(string argument) { CompilerArguments.Add(argument); return true; } - public override void invalidArgument(string argument) + public override void InvalidArgument(string argument) { // Unrecognised arguments are passed to the compiler. CompilerArguments.Add(argument); } - public override bool handleOption(string key, string value) + public override bool HandleOption(string key, string value) { switch (key) { @@ -64,19 +69,22 @@ public override bool handleOption(string key, string value) Framework = value; return true; default: - return base.handleOption(key, value); + return base.HandleOption(key, value); } } - public override bool handleFlag(string flag, bool value) + public override bool HandleFlag(string flag, bool value) { switch (flag) { case "clrtracer": ClrTracer = value; return true; + case "assemblysensitivetrap": + AssemblySensitiveTrap = value; + return true; default: - return base.handleFlag(flag, value); + return base.HandleFlag(flag, value); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Ast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Ast.cs index 469034024fec..866d436bda13 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Ast.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Ast.cs @@ -5,11 +5,11 @@ namespace Semmle.Extraction.CSharp.Populators { - class Ast : CSharpSyntaxVisitor + internal class Ast : CSharpSyntaxVisitor { - readonly Context cx; - readonly IExpressionParentEntity parent; - readonly int child; + private readonly Context cx; + private readonly IExpressionParentEntity parent; + private readonly int child; public Ast(Context cx, IExpressionParentEntity parent, int child) { @@ -23,15 +23,9 @@ public override void DefaultVisit(SyntaxNode node) cx.ModelError(node, $"Unhandled syntax node {node.Kind()}"); } - public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) - { - ((Property)parent).VisitDeclaration(cx, node); - } - - public override void VisitArgumentList(ArgumentListSyntax node) { - int c = 0; + var c = 0; foreach (var m in node.Arguments) { cx.Extract(m, parent, c++); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnit.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnit.cs index 7b1cd245fd5c..333ebabc2dc9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnit.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnit.cs @@ -10,9 +10,9 @@ namespace Semmle.Extraction.CSharp.Populators { public class TypeContainerVisitor : CSharpSyntaxVisitor { - protected readonly Context cx; - protected readonly IEntity parent; - protected readonly TextWriter trapFile; + protected Context cx { get; } + protected IEntity parent { get; } + protected TextWriter trapFile { get; } public TypeContainerVisitor(Context cx, TextWriter trapFile, IEntity parent) { @@ -53,7 +53,8 @@ public override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) public override void VisitAttributeList(AttributeListSyntax node) { - if (cx.Extractor.Standalone) return; + if (cx.Extractor.Standalone) + return; var outputAssembly = Assembly.CreateOutputAssembly(cx); foreach (var attribute in node.Attributes) @@ -64,7 +65,7 @@ public override void VisitAttributeList(AttributeListSyntax node) } } - class TypeOrNamespaceVisitor : TypeContainerVisitor + internal class TypeOrNamespaceVisitor : TypeContainerVisitor { public TypeOrNamespaceVisitor(Context cx, TextWriter trapFile, IEntity parent) : base(cx, trapFile, parent) { } @@ -82,7 +83,7 @@ public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) } } - class CompilationUnitVisitor : TypeOrNamespaceVisitor + internal class CompilationUnitVisitor : TypeOrNamespaceVisitor { public CompilationUnitVisitor(Context cx) : base(cx, cx.TrapWriter.Writer, null) { } @@ -101,7 +102,7 @@ public override void VisitCompilationUnit(CompilationUnitSyntax compilationUnit) } // Gather comments: - foreach (SyntaxTrivia trivia in compilationUnit.DescendantTrivia(compilationUnit.Span)) + foreach (var trivia in compilationUnit.DescendantTrivia(compilationUnit.Span)) { CommentLine.Extract(cx, trivia); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Locations.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Locations.cs index a9a4863a2e44..22f6c8ec6a96 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Locations.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Locations.cs @@ -19,13 +19,11 @@ public static Location ExtendLocation(this Location l1, SyntaxNode n2) { return l1; } - else - { - var l2 = n2.FixedLocation(); - int start = System.Math.Min(l1.SourceSpan.Start, l2.SourceSpan.Start); - int end = System.Math.Max(l1.SourceSpan.End, l2.SourceSpan.End); - return Location.Create(n2.SyntaxTree, new Microsoft.CodeAnalysis.Text.TextSpan(start, end - start)); - } + + var l2 = n2.FixedLocation(); + var start = System.Math.Min(l1.SourceSpan.Start, l2.SourceSpan.Start); + var end = System.Math.Max(l1.SourceSpan.End, l2.SourceSpan.End); + return Location.Create(n2.SyntaxTree, new Microsoft.CodeAnalysis.Text.TextSpan(start, end - start)); } /// diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Methods.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Methods.cs index b889ef0af2b4..148160b8815e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Methods.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Methods.cs @@ -8,11 +8,11 @@ namespace Semmle.Extraction.CSharp.Populators { public static class MethodExtensions { - class AstLineCounter : CSharpSyntaxVisitor + private class AstLineCounter : CSharpSyntaxVisitor { public override LineCounts DefaultVisit(SyntaxNode node) { - string text = node.SyntaxTree.GetText().GetSubText(node.GetLocation().SourceSpan).ToString(); + var text = node.SyntaxTree.GetText().GetSubText(node.GetLocation().SourceSpan).ToString(); return Semmle.Util.LineCounter.ComputeLineCounts(text); } @@ -21,14 +21,14 @@ public override LineCounts VisitMethodDeclaration(MethodDeclarationSyntax method return Visit(method.Identifier, method.Body ?? (SyntaxNode)method.ExpressionBody); } - public LineCounts Visit(SyntaxToken identifier, SyntaxNode body) + public static LineCounts Visit(SyntaxToken identifier, SyntaxNode body) { - int start = identifier.GetLocation().SourceSpan.Start; - int end = body.GetLocation().SourceSpan.End - 1; + var start = identifier.GetLocation().SourceSpan.Start; + var end = body.GetLocation().SourceSpan.End - 1; var textSpan = new Microsoft.CodeAnalysis.Text.TextSpan(start, end - start); - string text = body.SyntaxTree.GetText().GetSubText(textSpan) + "\r\n"; + var text = body.SyntaxTree.GetText().GetSubText(textSpan) + "\r\n"; return Semmle.Util.LineCounter.ComputeLineCounts(text); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Symbols.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Symbols.cs index 4e3d495776c3..ddf64257cbe4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Symbols.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Symbols.cs @@ -1,13 +1,11 @@ -using System; -using System.Linq; using Microsoft.CodeAnalysis; using Semmle.Extraction.CSharp.Entities; namespace Semmle.Extraction.CSharp.Populators { - class Symbols : SymbolVisitor + internal class Symbols : SymbolVisitor { - readonly Context cx; + private readonly Context cx; public Symbols(Context cx) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj b/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj index 87234841c5ff..905af7f616a8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj +++ b/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 Semmle.Extraction.CSharp Semmle.Extraction.CSharp false @@ -20,7 +20,7 @@ - + diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index 6050ad910a52..5732fe60b26e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -15,8 +15,8 @@ namespace Semmle.Extraction.CSharp /// public struct AnnotatedTypeSymbol { - public ITypeSymbol Symbol; - public NullableAnnotation Nullability; + public ITypeSymbol Symbol { get; set; } + public NullableAnnotation Nullability { get; } public AnnotatedTypeSymbol(ITypeSymbol symbol, NullableAnnotation nullability) { @@ -25,7 +25,7 @@ public AnnotatedTypeSymbol(ITypeSymbol symbol, NullableAnnotation nullability) } } - static class SymbolExtensions + internal static class SymbolExtensions { /// /// Tries to recover from an ErrorType. @@ -46,11 +46,10 @@ public static ITypeSymbol DisambiguateType(this ITypeSymbol type) * The conservative option would be to resolve all error types as null. */ - var errorType = type as IErrorTypeSymbol; - return errorType != null && errorType.CandidateSymbols.Any() ? - errorType.CandidateSymbols.First() as ITypeSymbol : - type; + return type is IErrorTypeSymbol errorType && errorType.CandidateSymbols.Any() + ? errorType.CandidateSymbols.First() as ITypeSymbol + : type; } /// @@ -71,55 +70,74 @@ public static string GetName(this ISymbol symbol, bool useMetadataName = false) /// public static IEnumerable GetSourceLevelModifiers(this ISymbol symbol) { - var methodModifiers = - symbol.DeclaringSyntaxReferences. - Select(r => r.GetSyntax()). - OfType(). - SelectMany(md => md.Modifiers); - var typeModifers = - symbol.DeclaringSyntaxReferences. - Select(r => r.GetSyntax()). - OfType(). - SelectMany(cd => cd.Modifiers); + var methodModifiers = symbol.DeclaringSyntaxReferences + .Select(r => r.GetSyntax()) + .OfType() + .SelectMany(md => md.Modifiers); + var typeModifers = symbol.DeclaringSyntaxReferences + .Select(r => r.GetSyntax()) + .OfType() + .SelectMany(cd => cd.Modifiers); return methodModifiers.Concat(typeModifers).Select(m => m.Text); } /// - /// Holds if this type symbol contains a type parameter from the - /// declaring generic . + /// Holds if the ID generated for `dependant` will contain a reference to + /// the ID for `symbol`. If this is the case, then the ID for `symbol` must + /// not contain a reference back to `dependant`. /// - public static bool ContainsTypeParameters(this ITypeSymbol type, Context cx, ISymbol declaringGeneric) + public static bool IdDependsOn(this ITypeSymbol dependant, Context cx, ISymbol symbol) { - using (cx.StackGuard) + var seen = new HashSet(SymbolEqualityComparer.Default); + + bool IdDependsOnImpl(ITypeSymbol type) { - switch (type.TypeKind) + if (SymbolEqualityComparer.Default.Equals(type, symbol)) + return true; + + if (type is null || seen.Contains(type)) + return false; + + seen.Add(type); + + using (cx.StackGuard) { - case TypeKind.Array: - var array = (IArrayTypeSymbol)type; - return array.ElementType.ContainsTypeParameters(cx, declaringGeneric); - case TypeKind.Class: - case TypeKind.Interface: - case TypeKind.Struct: - case TypeKind.Enum: - case TypeKind.Delegate: - case TypeKind.Error: - var named = (INamedTypeSymbol)type; - if (named.IsTupleType) - named = named.TupleUnderlyingType; - if (named.ContainingType != null && named.ContainingType.ContainsTypeParameters(cx, declaringGeneric)) - return true; - return named.TypeArguments.Any(arg => arg.ContainsTypeParameters(cx, declaringGeneric)); - case TypeKind.Pointer: - var ptr = (IPointerTypeSymbol)type; - return ptr.PointedAtType.ContainsTypeParameters(cx, declaringGeneric); - case TypeKind.TypeParameter: - var tp = (ITypeParameterSymbol)type; - var declaringGen = tp.TypeParameterKind == TypeParameterKind.Method ? tp.DeclaringMethod : (ISymbol)tp.DeclaringType; - return SymbolEqualityComparer.Default.Equals(declaringGen, declaringGeneric); - default: - return false; + switch (type.TypeKind) + { + case TypeKind.Array: + var array = (IArrayTypeSymbol)type; + return IdDependsOnImpl(array.ElementType); + case TypeKind.Class: + case TypeKind.Interface: + case TypeKind.Struct: + case TypeKind.Enum: + case TypeKind.Delegate: + case TypeKind.Error: + var named = (INamedTypeSymbol)type; + if (named.IsTupleType && named.TupleUnderlyingType is object) + named = named.TupleUnderlyingType; + if (IdDependsOnImpl(named.ContainingType)) + return true; + if (IdDependsOnImpl(named.GetNonObjectBaseType(cx))) + return true; + if (IdDependsOnImpl(named.ConstructedFrom)) + return true; + return named.TypeArguments.Any(IdDependsOnImpl); + case TypeKind.Pointer: + var ptr = (IPointerTypeSymbol)type; + return IdDependsOnImpl(ptr.PointedAtType); + case TypeKind.TypeParameter: + var tp = (ITypeParameterSymbol)type; + return tp.ContainingSymbol is ITypeSymbol cont + ? IdDependsOnImpl(cont) + : SymbolEqualityComparer.Default.Equals(tp.ContainingSymbol, symbol); + default: + return false; + } } } + + return IdDependsOnImpl(dependant); } /// @@ -130,27 +148,20 @@ public static bool ContainsTypeParameters(this ITypeSymbol type, Context cx, ISy /// /// The extraction context. /// The trap builder used to store the result. - /// The action to apply to syntactic sub terms of this type. - public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, Action subTermAction) - { - if (type.SpecialType != SpecialType.None) - { - /* - * Use the keyword ("int" etc) for the built-in types. - * This makes the IDs shorter and means that all built-in types map to - * the same entities (even when using multiple versions of mscorlib). - */ - trapFile.Write(type.ToDisplayString()); - return; - } + /// The outer symbol being defined (to avoid recursive ids). + /// Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types. + public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) => + type.BuildTypeId(cx, trapFile, symbolBeingDefined, true, constructUnderlyingTupleType); + private static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + { using (cx.StackGuard) { switch (type.TypeKind) { case TypeKind.Array: var array = (IArrayTypeSymbol)type; - subTermAction(cx, trapFile, array.ElementType); + array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); array.BuildArraySuffix(trapFile); return; case TypeKind.Class: @@ -160,15 +171,17 @@ public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter tra case TypeKind.Delegate: case TypeKind.Error: var named = (INamedTypeSymbol)type; - named.BuildNamedTypeId(cx, trapFile, subTermAction); + named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; - subTermAction(cx, trapFile, ptr.PointedAtType); + ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); trapFile.Write('*'); return; case TypeKind.TypeParameter: var tp = (ITypeParameterSymbol)type; + tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + trapFile.Write('_'); trapFile.Write(tp.Name); return; case TypeKind.Dynamic: @@ -180,6 +193,44 @@ public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter tra } } + private static void BuildOrWriteId(this ISymbol symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false) + { + // We need to keep track of the symbol being defined in order to avoid cyclic labels. + // For example, in + // + // ```csharp + // class C : IEnumerable { } + // ``` + // + // when we generate the label for ``C`1``, the base class `IEnumerable` has `T` as a type + // argument, which will be qualified with `__self__` instead of the label we are defining. + // In effect, the label will (simplified) look like + // + // ``` + // #123 = @"C`1 : IEnumerable<__self___T>" + // ``` + if (SymbolEqualityComparer.Default.Equals(symbol, symbolBeingDefined)) + trapFile.Write("__self__"); + else if (symbol is ITypeSymbol type && type.IdDependsOn(cx, symbolBeingDefined)) + type.BuildTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + else if (symbol is INamedTypeSymbol namedType && namedType.IsTupleType && constructUnderlyingTupleType) + trapFile.WriteSubId(NamedType.CreateNamedTypeFromTupleType(cx, namedType)); + else + trapFile.WriteSubId(CreateEntity(cx, symbol)); + } + + /// + /// Adds an appropriate ID to the trap builder + /// for the symbol belonging to + /// . + /// + /// This will either write a reference to the ID of the entity belonging to + /// (`{#label}`), or if that will lead to cyclic IDs, + /// it will generate an appropriate ID that encodes the signature of + /// . + /// + public static void BuildOrWriteId(this ISymbol symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined) => + symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, true); /// /// Constructs an array suffix string for this array type symbol. @@ -188,7 +239,7 @@ public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter tra public static void BuildArraySuffix(this IArrayTypeSymbol array, TextWriter trapFile) { trapFile.Write('['); - for (int i = 0; i < array.Rank - 1; i++) + for (var i = 0; i < array.Rank - 1; i++) trapFile.Write(','); trapFile.Write(']'); } @@ -211,12 +262,9 @@ private static void BuildAssembly(IAssemblySymbol asm, TextWriter trapFile, bool trapFile.Write("::"); } - static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter trapFile, Action subTermAction) + private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) { - bool prefixAssembly = true; - if (named.ContainingAssembly is null) prefixAssembly = false; - - if (named.IsTupleType) + if (!constructUnderlyingTupleType && named.IsTupleType) { trapFile.Write('('); trapFile.BuildList(",", named.TupleElements, @@ -224,72 +272,87 @@ static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter { trapFile.Write(f.Name); trapFile.Write(":"); - subTermAction(cx, tb0, f.Type); + f.Type.BuildOrWriteId(cx, tb0, symbolBeingDefined, addBaseClass); } ); trapFile.Write(")"); return; } - if (named.ContainingType != null) + void AddContaining() { - subTermAction(cx, trapFile, named.ContainingType); - trapFile.Write('.'); - } - else if (named.ContainingNamespace != null) - { - if (prefixAssembly) - BuildAssembly(named.ContainingAssembly, trapFile); - named.ContainingNamespace.BuildNamespace(cx, trapFile); + if (named.ContainingType != null) + { + named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + trapFile.Write('.'); + } + else if (named.ContainingNamespace != null) + { + if (cx.ShouldAddAssemblyTrapPrefix && named.ContainingAssembly is object) + BuildAssembly(named.ContainingAssembly, trapFile); + named.ContainingNamespace.BuildNamespace(cx, trapFile); + } } - if (named.IsAnonymousType) - named.BuildAnonymousName(cx, trapFile, subTermAction, true); - else if (named.TypeParameters.IsEmpty) + if (named.TypeParameters.IsEmpty) + { + AddContaining(); trapFile.Write(named.Name); - else if (IsReallyUnbound(named)) + } + else if (named.IsReallyUnbound()) { + AddContaining(); trapFile.Write(named.Name); trapFile.Write("`"); trapFile.Write(named.TypeParameters.Length); } else { - subTermAction(cx, trapFile, named.ConstructedFrom); + named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); trapFile.Write('<'); // Encode the nullability of the type arguments in the label. - // Type arguments with different nullability can result in + // Type arguments with different nullability can result in // a constructed type with different nullability of its members and methods, // so we need to create a distinct database entity for it. trapFile.BuildList(",", named.GetAnnotatedTypeArguments(), - (ta, tb0) => subTermAction(cx, tb0, ta.Symbol) + (ta, tb0) => ta.Symbol.BuildOrWriteId(cx, tb0, symbolBeingDefined, addBaseClass) ); trapFile.Write('>'); } + + if (addBaseClass && named.GetNonObjectBaseType(cx) is INamedTypeSymbol @base) + { + // We need to limit unfolding of base classes. For example, in + // + // ```csharp + // class C1 { } + // class C2 : C1> { } + // class C3 : C1> { } + // class C4 : C3 { } + // ``` + // + // when we generate the label for `C4`, the base class `C3` has itself `C1>` as + // a base class, which in turn has `C1>` as a base class. The latter has the original + // base class `C3` as a type argument, which would lead to infinite unfolding. + trapFile.Write(" : "); + @base.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass: false); + } } - static void BuildNamespace(this INamespaceSymbol ns, Context cx, TextWriter trapFile) + private static void BuildNamespace(this INamespaceSymbol ns, Context cx, TextWriter trapFile) { trapFile.WriteSubId(Namespace.Create(cx, ns)); trapFile.Write('.'); } - static void BuildAnonymousName(this ITypeSymbol type, Context cx, TextWriter trapFile, Action subTermAction, bool includeParamName) + private static void BuildAnonymousName(this INamedTypeSymbol type, Context cx, TextWriter trapFile) { - var buildParam = includeParamName - ? (prop, tb0) => - { - tb0.Write(prop.Name); - tb0.Write(' '); - subTermAction(cx, tb0, prop.Type); - } - : (Action)((prop, tb0) => subTermAction(cx, tb0, prop.Type)); - int memberCount = type.GetMembers().OfType().Count(); - int hackTypeNumber = memberCount == 1 ? 1 : 0; + var memberCount = type.GetMembers().OfType().Count(); + var hackTypeNumber = memberCount == 1 ? 1 : 0; trapFile.Write("<>__AnonType"); trapFile.Write(hackTypeNumber); trapFile.Write('<'); - trapFile.BuildList(",", type.GetMembers().OfType(), buildParam); + trapFile.BuildList(",", type.GetMembers().OfType(), (prop, tb0) => BuildDisplayName(prop.Type, cx, tb0)); trapFile.Write('>'); } @@ -297,7 +360,7 @@ static void BuildAnonymousName(this ITypeSymbol type, Context cx, TextWriter tra /// Constructs a display name string for this type symbol. /// /// The trap builder used to store the result. - public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWriter trapFile) + public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType = false) { using (cx.StackGuard) { @@ -306,7 +369,7 @@ public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWrite case TypeKind.Array: var array = (IArrayTypeSymbol)type; var elementType = array.ElementType; - if (elementType.MetadataName.IndexOf("`") >= 0) + if (elementType.MetadataName.Contains("`")) { trapFile.Write(elementType.Name); return; @@ -321,7 +384,7 @@ public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWrite case TypeKind.Delegate: case TypeKind.Error: var named = (INamedTypeSymbol)type; - named.BuildNamedTypeDisplayName(cx, trapFile); + named.BuildNamedTypeDisplayName(cx, trapFile, constructUnderlyingTupleType); return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; @@ -340,9 +403,9 @@ public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWrite } } - public static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile) + public static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType) { - if (namedType.IsTupleType) + if (!constructUnderlyingTupleType && namedType.IsTupleType) { trapFile.Write('('); trapFile.BuildList(",", namedType.TupleElements.Select(f => f.Type), @@ -354,11 +417,9 @@ public static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Co } if (namedType.IsAnonymousType) - { - namedType.BuildAnonymousName(cx, trapFile, (cx0, tb0, sub) => sub.BuildDisplayName(cx0, tb0), false); - } - - trapFile.Write(namedType.Name); + namedType.BuildAnonymousName(cx, trapFile); + else + trapFile.Write(namedType.Name); if (namedType.IsGenericType && namedType.TypeKind != TypeKind.Error && namedType.TypeArguments.Any()) { trapFile.Write('<'); @@ -395,11 +456,11 @@ public static bool IsUnboundNullable(this ITypeSymbol type) => /// The list of parameters, or an empty list. public static IEnumerable GetParameters(this ISymbol parameterizable) { - if (parameterizable is IMethodSymbol) - return ((IMethodSymbol)parameterizable).Parameters; + if (parameterizable is IMethodSymbol meth) + return meth.Parameters; - if (parameterizable is IPropertySymbol) - return ((IPropertySymbol)parameterizable).Parameters; + if (parameterizable is IPropertySymbol prop) + return prop.Parameters; return Enumerable.Empty(); } @@ -425,18 +486,24 @@ public static bool IsSourceDeclaration(this IMethodSymbol method) => /// public static bool IsSourceDeclaration(this IParameterSymbol parameter) { - var method = parameter.ContainingSymbol as IMethodSymbol; - if (method != null) + if (parameter.ContainingSymbol is IMethodSymbol method) return method.IsSourceDeclaration(); - var property = parameter.ContainingSymbol as IPropertySymbol; - if (property != null && property.IsIndexer) + if (parameter.ContainingSymbol is IPropertySymbol property && property.IsIndexer) return property.IsSourceDeclaration(); return true; } + /// + /// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base + /// types of type parameters as well as `object` base types. + /// + public static INamedTypeSymbol GetNonObjectBaseType(this ITypeSymbol symbol, Context cx) => + symbol is ITypeParameterSymbol || SymbolEqualityComparer.Default.Equals(symbol.BaseType, cx.Compilation.ObjectType) ? null : symbol.BaseType; + public static IEntity CreateEntity(this Context cx, ISymbol symbol) { - if (symbol == null) return null; + if (symbol == null) + return null; using (cx.StackGuard) { diff --git a/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs b/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs new file mode 100644 index 000000000000..a4b2214b5e86 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs @@ -0,0 +1,48 @@ +īģŋusing Xunit; + +namespace Semmle.Extraction.Tests +{ + public class FilePatternTests + { + [Fact] + public void TestRegexCompilation() + { + var fp = new FilePattern("/hadoop*"); + Assert.Equal("^hadoop[^/]*.*", fp.RegexPattern); + fp = new FilePattern("**/org/apache/hadoop"); + Assert.Equal("^.*/org/apache/hadoop.*", fp.RegexPattern); + fp = new FilePattern("hadoop-common/**/test// "); + Assert.Equal("^hadoop-common/.*/test(?/).*", fp.RegexPattern); + fp = new FilePattern(@"-C:\agent\root\asdf//"); + Assert.Equal("^C:/agent/root/asdf(?/).*", fp.RegexPattern); + fp = new FilePattern(@"-C:\agent+\[root]\asdf//"); + Assert.Equal(@"^C:/agent\+/\[root]/asdf(?/).*", fp.RegexPattern); + } + + [Fact] + public void TestMatching() + { + var fp1 = new FilePattern(@"C:\agent\root\abc//"); + var fp2 = new FilePattern(@"C:\agent\root\def//ghi"); + var patterns = new[] { fp1, fp2 }; + + var success = FilePattern.Matches(patterns, @"C:\agent\root\abc\file.cs", out var s); + Assert.True(success); + Assert.Equal("/file.cs", s); + + success = FilePattern.Matches(patterns, @"C:\agent\root\def\ghi\file.cs", out s); + Assert.True(success); + Assert.Equal("/ghi/file.cs", s); + + success = FilePattern.Matches(patterns, @"C:\agent\root\def\file.cs", out _); + Assert.False(success); + } + + [Fact] + public void TestInvalidPatterns() + { + Assert.Throws(() => new FilePattern("/abc//def//ghi")); + Assert.Throws(() => new FilePattern("/abc**def")); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.Tests/Layout.cs b/csharp/extractor/Semmle.Extraction.Tests/Layout.cs index 303ea5bf71e6..ceb83019ba9c 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Layout.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/Layout.cs @@ -1,40 +1,66 @@ -īģŋusing System.IO; +using System.IO; using Xunit; using Semmle.Util.Logging; using System.Runtime.InteropServices; namespace Semmle.Extraction.Tests { + internal struct TransformedPathStub : PathTransformer.ITransformedPath + { + private readonly string value; + public TransformedPathStub(string value) => this.value = value; + public string Value => value; + + public string Extension => throw new System.NotImplementedException(); + + public string NameWithoutExtension => throw new System.NotImplementedException(); + + public PathTransformer.ITransformedPath ParentDirectory => throw new System.NotImplementedException(); + + public string DatabaseId => throw new System.NotImplementedException(); + + public PathTransformer.ITransformedPath WithSuffix(string suffix) + { + throw new System.NotImplementedException(); + } + } + public class Layout { - readonly ILogger Logger = new LoggerMock(); + private readonly ILogger logger = new LoggerMock(); [Fact] public void TestDefaultLayout() { var layout = new Semmle.Extraction.Layout(null, null, null); - var project = layout.LookupProjectOrNull("foo.cs"); + var project = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs")); + + Assert.NotNull(project); // All files are mapped when there's no layout file. - Assert.True(layout.FileInLayout("foo.cs")); + Assert.True(layout.FileInLayout(new TransformedPathStub("foo.cs"))); // Test trap filename var tmpDir = Path.GetTempPath(); Directory.SetCurrentDirectory(tmpDir); if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - // `Directory.SetCurrentDirectory()` doesn't seem to work on macOS, - // so disable this test on macOS, for now + // `Directory.SetCurrentDirectory()` seems to slightly change the path on macOS, + // so adjusting it: Assert.NotEqual(Directory.GetCurrentDirectory(), tmpDir); - return; + tmpDir = "/private" + tmpDir; + // Remove trailing slash: + Assert.Equal('/', tmpDir[tmpDir.Length - 1]); + tmpDir = tmpDir.Substring(0, tmpDir.Length - 1); + Assert.Equal(Directory.GetCurrentDirectory(), tmpDir); } - var f1 = project.GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip); - var g1 = TrapWriter.NestPaths(Logger, tmpDir, "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + var f1 = project!.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip); + var g1 = TrapWriter.NestPaths(logger, tmpDir, "foo.cs.trap.gz"); Assert.Equal(f1, g1); // Test trap file generation - var trapwriterFilename = project.GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip); - using (var trapwriter = project.CreateTrapWriter(Logger, "foo.cs", false, TrapWriter.CompressionMode.Gzip)) + var trapwriterFilename = project.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip); + using (var trapwriter = project.CreateTrapWriter(logger, new TransformedPathStub("foo.cs"), false, TrapWriter.CompressionMode.Gzip)) { trapwriter.Emit("1=*"); Assert.False(File.Exists(trapwriterFilename)); @@ -63,23 +89,24 @@ public void TestLayoutFile() var layout = new Semmle.Extraction.Layout(null, null, "layout.txt"); // Test general pattern matching - Assert.True(layout.FileInLayout("bar.cs")); - Assert.False(layout.FileInLayout("foo.cs")); - Assert.False(layout.FileInLayout("goo.cs")); - Assert.False(layout.FileInLayout("excluded/bar.cs")); - Assert.True(layout.FileInLayout("excluded/foo.cs")); - Assert.True(layout.FileInLayout("included/foo.cs")); + Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs"))); + Assert.False(layout.FileInLayout(new TransformedPathStub("foo.cs"))); + Assert.False(layout.FileInLayout(new TransformedPathStub("goo.cs"))); + Assert.False(layout.FileInLayout(new TransformedPathStub("excluded/bar.cs"))); + Assert.True(layout.FileInLayout(new TransformedPathStub("excluded/foo.cs"))); + Assert.True(layout.FileInLayout(new TransformedPathStub("included/foo.cs"))); // Test the trap file - var project = layout.LookupProjectOrNull("bar.cs"); - var trapwriterFilename = project.GetTrapPath(Logger, "bar.cs", TrapWriter.CompressionMode.Gzip); - Assert.Equal(TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "bar.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE), + var project = layout.LookupProjectOrNull(new TransformedPathStub("bar.cs")); + Assert.NotNull(project); + var trapwriterFilename = project!.GetTrapPath(logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip); + Assert.Equal(TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap"), "bar.cs.trap.gz"), trapwriterFilename); // Test the source archive - var trapWriter = project.CreateTrapWriter(Logger, "bar.cs", false, TrapWriter.CompressionMode.Gzip); - trapWriter.Archive("layout.txt", System.Text.Encoding.ASCII); - var writtenFile = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\archive"), "layout.txt", TrapWriter.InnerPathComputation.ABSOLUTE); + var trapWriter = project.CreateTrapWriter(logger, new TransformedPathStub("bar.cs"), false, TrapWriter.CompressionMode.Gzip); + trapWriter.Archive("layout.txt", new TransformedPathStub("layout.txt"), System.Text.Encoding.ASCII); + var writtenFile = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\archive"), "layout.txt"); Assert.True(File.Exists(writtenFile)); File.Delete("layout.txt"); } @@ -89,9 +116,11 @@ public void TestTrapOverridesLayout() { // When you specify both a trap file and a layout, use the trap file. var layout = new Semmle.Extraction.Layout(Path.GetFullPath("snapshot\\trap"), null, "something.txt"); - Assert.True(layout.FileInLayout("bar.cs")); - var f1 = layout.LookupProjectOrNull("foo.cs").GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip); - var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs"))); + var subProject = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs")); + Assert.NotNull(subProject); + var f1 = subProject!.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip); + var g1 = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap"), "foo.cs.trap.gz"); Assert.Equal(f1, g1); } @@ -117,26 +146,30 @@ public void TestMultipleSections() var layout = new Semmle.Extraction.Layout(null, null, "layout.txt"); // Use Section 2 - Assert.True(layout.FileInLayout("bar.cs")); - var f1 = layout.LookupProjectOrNull("bar.cs").GetTrapPath(Logger, "bar.cs", TrapWriter.CompressionMode.Gzip); - var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap2"), "bar.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs"))); + var subProject = layout.LookupProjectOrNull(new TransformedPathStub("bar.cs")); + Assert.NotNull(subProject); + var f1 = subProject!.GetTrapPath(logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip); + var g1 = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap2"), "bar.cs.trap.gz"); Assert.Equal(f1, g1); // Use Section 1 - Assert.True(layout.FileInLayout("foo.cs")); - var f2 = layout.LookupProjectOrNull("foo.cs").GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip); - var g2 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + Assert.True(layout.FileInLayout(new TransformedPathStub("foo.cs"))); + subProject = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs")); + Assert.NotNull(subProject); + var f2 = subProject!.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip); + var g2 = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap1"), "foo.cs.trap.gz"); Assert.Equal(f2, g2); // boo.dll is not in the layout, so use layout from first section. - Assert.False(layout.FileInLayout("boo.dll")); - var f3 = layout.LookupProjectOrDefault("boo.dll").GetTrapPath(Logger, "boo.dll", TrapWriter.CompressionMode.Gzip); - var g3 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "boo.dll.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + Assert.False(layout.FileInLayout(new TransformedPathStub("boo.dll"))); + var f3 = layout.LookupProjectOrDefault(new TransformedPathStub("boo.dll")).GetTrapPath(logger, new TransformedPathStub("boo.dll"), TrapWriter.CompressionMode.Gzip); + var g3 = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap1"), "boo.dll.trap.gz"); Assert.Equal(f3, g3); // boo.cs is not in the layout, so return null - Assert.False(layout.FileInLayout("boo.cs")); - Assert.Null(layout.LookupProjectOrNull("boo.cs")); + Assert.False(layout.FileInLayout(new TransformedPathStub("boo.cs"))); + Assert.Null(layout.LookupProjectOrNull(new TransformedPathStub("boo.cs"))); } [Fact] @@ -166,34 +199,32 @@ public void InvalidLayout() new Semmle.Extraction.Layout(null, null, "layout.txt")); } - class LoggerMock : ILogger + private sealed class LoggerMock : ILogger { public void Dispose() { } public void Log(Severity s, string text) { } - - public void Log(Severity s, string text, params object[] args) { } } } - static class TrapWriterTestExtensions + internal static class TrapWriterTestExtensions { public static void Emit(this TrapWriter trapFile, string s) { trapFile.Emit(new StringTrapEmitter(s)); } - class StringTrapEmitter : ITrapEmitter + private class StringTrapEmitter : ITrapEmitter { - string Content; + private readonly string content; public StringTrapEmitter(string content) { - Content = content; + this.content = content; } public void EmitTrap(TextWriter trapFile) { - trapFile.Write(Content); + trapFile.Write(content); } } } diff --git a/csharp/extractor/Semmle.Extraction.Tests/Options.cs b/csharp/extractor/Semmle.Extraction.Tests/Options.cs index ce240ce146d9..f9a1c34e563d 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Options.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/Options.cs @@ -9,8 +9,8 @@ namespace Semmle.Extraction.Tests { public class OptionsTests { - CSharp.Options options; - CSharp.Standalone.Options standaloneOptions; + private CSharp.Options? options; + private CSharp.Standalone.Options? standaloneOptions; public OptionsTests() { @@ -21,7 +21,7 @@ public OptionsTests() [Fact] public void DefaultOptions() { - options = CSharp.Options.CreateWithEnvironment(new string[] { }); + options = CSharp.Options.CreateWithEnvironment(Array.Empty()); Assert.True(options.Cache); Assert.False(options.CIL); Assert.Null(options.Framework); @@ -141,7 +141,7 @@ public void EnvironmentVariables() [Fact] public void StandaloneDefaults() { - standaloneOptions = CSharp.Standalone.Options.Create(new string[] { }); + standaloneOptions = CSharp.Standalone.Options.Create(Array.Empty()); Assert.Equal(0, standaloneOptions.DllDirs.Count); Assert.True(standaloneOptions.UseNuGet); Assert.True(standaloneOptions.UseMscorlib); @@ -184,14 +184,14 @@ public void ShowingHelp() public void Fast() { Environment.SetEnvironmentVariable("LGTM_INDEX_EXTRACTOR", "--fast"); - options = CSharp.Options.CreateWithEnvironment(new string[] { }); + options = CSharp.Options.CreateWithEnvironment(Array.Empty()); Assert.True(options.Fast); } [Fact] public void ArchiveArguments() { - var sw = new StringWriter(); + using var sw = new StringWriter(); var file = Path.GetTempFileName(); try diff --git a/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs new file mode 100644 index 000000000000..990644eb4b9b --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs @@ -0,0 +1,45 @@ +īģŋusing Semmle.Util; +using Xunit; + +namespace Semmle.Extraction.Tests +{ + internal class PathCacheStub : IPathCache + { + public string GetCanonicalPath(string path) => path; + } + + public class PathTransformerTests + { + [Fact] + public void TestTransformerFile() + { + var spec = new string[] + { + @"#D:\src", + @"C:\agent*\src//", + @"-C:\agent*\src\external", + @"", + @"#empty", + @"", + @"#src2", + @"/agent*//src", + @"", + @"#optsrc", + @"opt/src//" + }; + + var pathTransformer = new PathTransformer(new PathCacheStub(), spec); + + // Windows-style matching + Assert.Equal(@"C:/bar.cs", pathTransformer.Transform(@"C:\bar.cs").Value); + Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent42\src\file.cs").Value); + Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent43\src\file.cs").Value); + Assert.Equal(@"C:/agent43/src/external/file.cs", pathTransformer.Transform(@"C:\agent43\src\external\file.cs").Value); + + // Linux-style matching + Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent/src/file.cs").Value); + Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent42/src/file.cs").Value); + Assert.Equal(@"optsrc/file.cs", pathTransformer.Transform(@"/opt/src/file.cs").Value); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj b/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj index c101a5fba830..830529d3710e 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj +++ b/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj @@ -2,9 +2,10 @@ Exe - netcoreapp3.0 + netcoreapp3.1 false win-x64;linux-x64;osx-x64 + enable diff --git a/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs b/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs index fd7f77f427b4..54e0a9db25a0 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs @@ -1,8 +1,6 @@ using Xunit; using Semmle.Util.Logging; using Semmle.Util; -using System.Runtime.InteropServices; -using System.IO; namespace Semmle.Extraction.Tests { @@ -14,7 +12,7 @@ public void NestedPaths() string tempDir = System.IO.Path.GetTempPath(); string root1, root2, root3; - if(Win32.IsWindows()) + if (Win32.IsWindows()) { root1 = "E:"; root2 = "e:"; @@ -27,41 +25,28 @@ public void NestedPaths() root3 = "/"; } - string formattedTempDir = tempDir.Replace('/', '\\').Replace(':', '_').Trim('\\'); + using var logger = new LoggerMock(); - var logger = new LoggerMock(); - System.IO.Directory.SetCurrentDirectory(tempDir); + Assert.Equal($@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs").Replace('/', '\\')); - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - // `Directory.SetCurrentDirectory()` doesn't seem to work on macOS, - // so disable this test on macOS, for now - Assert.NotEqual(Directory.GetCurrentDirectory(), tempDir); - return; - } - - Assert.Equal($@"C:\Temp\source_archive\{formattedTempDir}\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/','\\')); + Assert.Equal(@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\E_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root1}\source\def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\E_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root1}\source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\e_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root2}\source\def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\e_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root2}\source\def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\')); - - Assert.Equal(@"C:\Temp\source_archive\diskstation\share\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}{root3}diskstation\share\source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\diskstation\share\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}{root3}diskstation\share\source\def.cs").Replace('/', '\\')); } - class LoggerMock : ILogger + private sealed class LoggerMock : ILogger { public void Dispose() { } public void Log(Severity s, string text) { } - - public void Log(Severity s, string text, params object[] args) { } } } } diff --git a/csharp/extractor/Semmle.Extraction/CommentProcessing.cs b/csharp/extractor/Semmle.Extraction/CommentProcessing.cs index 2067f8694717..f407f5703fec 100644 --- a/csharp/extractor/Semmle.Extraction/CommentProcessing.cs +++ b/csharp/extractor/Semmle.Extraction/CommentProcessing.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CommentProcessing /// Registers locations of comments and program elements, /// then generates binding information. /// - class CommentProcessor : ICommentGenerator + internal class CommentProcessor : ICommentGenerator { public void AddComment(ICommentLine comment) { @@ -33,9 +33,9 @@ public void AddComment(ICommentLine comment) return null; } - class LocationComparer : IComparer + private class LocationComparer : IComparer { - public int Compare(Location l1, Location l2) => CommentProcessor.Compare(l1, l2); + public int Compare(Location? l1, Location? l2) => CommentProcessor.Compare(l1, l2); } /// @@ -44,12 +44,21 @@ class LocationComparer : IComparer /// First location /// Second location /// <0 if l1 before l2, >0 if l1 after l2, else 0. - static int Compare(Location l1, Location l2) + private static int Compare(Location? l1, Location? l2) { - int diff = l1.SourceTree == l2.SourceTree ? 0 : l1.SourceTree.FilePath.CompareTo(l2.SourceTree.FilePath); - if (diff != 0) return diff; + if (object.ReferenceEquals(l1, l2)) + return 0; + if (l1 == null) + return -1; + if (l2 == null) + return 1; + + var diff = l1.SourceTree == l2.SourceTree ? 0 : l1.SourceTree.FilePath.CompareTo(l2.SourceTree.FilePath); + if (diff != 0) + return diff; diff = l1.SourceSpan.Start - l2.SourceSpan.Start; - if (diff != 0) return diff; + if (diff != 0) + return diff; return l1.SourceSpan.End - l2.SourceSpan.End; } @@ -69,7 +78,7 @@ public void AddElement(Label elementLabel, Key? duplicationGuardKey, Location lo // Ensure that commentBlock and element refer to the same file // which can happen when processing multiple files. - void EnsureSameFile(ICommentBlock commentBlock, ref KeyValuePair? element) + private static void EnsureSameFile(ICommentBlock commentBlock, ref KeyValuePair? element) { if (element != null && element.Value.Key.SourceTree != commentBlock.Location.SourceTree) element = null; @@ -85,7 +94,7 @@ void EnsureSameFile(ICommentBlock commentBlock, ref KeyValuePairThe element after the comment block. /// The parent element of the comment block. /// Output binding information. - void GenerateBindings( + private void GenerateBindings( ICommentBlock commentBlock, KeyValuePair? previousElement, KeyValuePair? nextElement, @@ -171,7 +180,7 @@ CommentBindingCallback callback private class ElementStack { // Invariant: the top of the stack must be contained by items below it. - readonly Stack> elementStack = new Stack>(); + private readonly Stack> elementStack = new Stack>(); /// /// Add a new element to the stack. @@ -202,9 +211,9 @@ public void Push(KeyValuePair value) /// The element before l, or null. public KeyValuePair? FindBefore(Location l) { - return elementStack. - Where(v => v.Key.SourceSpan.End < l.SourceSpan.Start). - LastOrNull(); + return elementStack + .Where(v => v.Key.SourceSpan.End < l.SourceSpan.Start) + .LastOrNull(); } /// @@ -243,13 +252,13 @@ CommentBindingCallback cb /// Process comments up until nextElement. /// Group comments into blocks, and associate blocks with elements. /// - /// + /// /// Enumerator for all comments in the program. /// The next element in the list. /// A stack of nested program elements. /// Where to send the results. /// true if there are more comments to process, false otherwise. - bool GenerateBindings( + private bool GenerateBindings( IEnumerator> commentEnumerator, KeyValuePair? nextElement, ElementStack elementStack, @@ -261,7 +270,7 @@ CommentBindingCallback cb // Iterate comments until the commentEnumerator has gone past nextElement while (nextElement == null || Compare(commentEnumerator.Current.Value.Location, nextElement.Value.Key) < 0) { - if(block is null) + if (block is null) block = new CommentBlock(commentEnumerator.Current.Value); if (!block.CombinesWith(commentEnumerator.Current.Value)) @@ -284,7 +293,7 @@ CommentBindingCallback cb } } - if(!(block is null)) + if (!(block is null)) GenerateBindings(block, elementStack, nextElement, cb); return true; @@ -308,35 +317,33 @@ public void GenerateBindings(CommentBindingCallback cb) * (Note that comment processing is O(n.log n) overall due to dictionary of elements and comments.) */ - ElementStack elementStack = new ElementStack(); + var elementStack = new ElementStack(); - using (IEnumerator> elementEnumerator = elements.GetEnumerator()) - using (IEnumerator> commentEnumerator = comments.GetEnumerator()) + using IEnumerator> elementEnumerator = elements.GetEnumerator(); + using IEnumerator> commentEnumerator = comments.GetEnumerator(); + if (!commentEnumerator.MoveNext()) { - if (!commentEnumerator.MoveNext()) - { - // There are no comments to process. - return; - } + // There are no comments to process. + return; + } - while (elementEnumerator.MoveNext()) + while (elementEnumerator.MoveNext()) + { + if (!GenerateBindings(commentEnumerator, elementEnumerator.Current, elementStack, cb)) { - if (!GenerateBindings(commentEnumerator, elementEnumerator.Current, elementStack, cb)) - { - // No more comments to process. - return; - } - - elementStack.Push(elementEnumerator.Current); + // No more comments to process. + return; } - // Generate remaining comments at end of file - GenerateBindings(commentEnumerator, null, elementStack, cb); + elementStack.Push(elementEnumerator.Current); } + + // Generate remaining comments at end of file + GenerateBindings(commentEnumerator, null, elementStack, cb); } } - class CommentBlock : ICommentBlock + internal class CommentBlock : ICommentBlock { private readonly List lines; @@ -357,13 +364,14 @@ public CommentBlock(ICommentLine firstLine) /// Whether the new line should be appended to this block. public bool CombinesWith(ICommentLine newLine) { - if (!CommentLines.Any()) return true; - - bool sameFile = Location.SourceTree == newLine.Location.SourceTree; - bool sameRow = Location.EndLine() == newLine.Location.StartLine(); - bool sameColumn = Location.EndLine() + 1 == newLine.Location.StartLine(); - bool nextRow = Location.StartColumn() == newLine.Location.StartColumn(); - bool adjacent = sameFile && (sameRow || (sameColumn && nextRow)); + if (!CommentLines.Any()) + return true; + + var sameFile = Location.SourceTree == newLine.Location.SourceTree; + var sameRow = Location.EndLine() == newLine.Location.StartLine(); + var sameColumn = Location.EndLine() + 1 == newLine.Location.StartLine(); + var nextRow = Location.StartColumn() == newLine.Location.StartColumn(); + var adjacent = sameFile && (sameRow || (sameColumn && nextRow)); return newLine.Type == CommentLineType.MultilineContinuation || diff --git a/csharp/extractor/Semmle.Extraction/Context.cs b/csharp/extractor/Semmle.Extraction/Context.cs index 3e55c1068e2c..0863f4624752 100644 --- a/csharp/extractor/Semmle.Extraction/Context.cs +++ b/csharp/extractor/Semmle.Extraction/Context.cs @@ -18,7 +18,7 @@ public class Context /// /// Access various extraction functions, e.g. logger, trap writer. /// - public readonly IExtractor Extractor; + public IExtractor Extractor { get; } /// /// The program database provided by Roslyn. @@ -39,51 +39,21 @@ public SemanticModel GetModel(SyntaxNode node) /// /// Access to the trap file. /// - public readonly TrapWriter TrapWriter; - - int GetNewId() => TrapWriter.IdCounter++; + public TrapWriter TrapWriter { get; } /// - /// Creates a new entity using the factory. + /// Holds if assembly information should be prefixed to TRAP labels. /// - /// The entity factory. - /// The initializer for the entity. - /// The new/existing entity. - public Entity CreateEntity(ICachedEntityFactory factory, Type init) where Entity : ICachedEntity where Type:struct - { - return CreateNonNullEntity(factory, init); - } + public bool ShouldAddAssemblyTrapPrefix { get; } - /// - /// Creates a new entity using the factory. - /// - /// The entity factory. - /// The initializer for the entity. - /// The new/existing entity. - public Entity CreateNullableEntity(ICachedEntityFactory factory, Type init) where Entity : ICachedEntity - { - return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init); - } - - /// - /// Creates a new entity using the factory. - /// - /// The entity factory. - /// The initializer for the entity. - /// The new/existing entity. - public Entity CreateEntityFromSymbol(ICachedEntityFactory factory, Type init) - where Entity : ICachedEntity - where Type: ISymbol - { - return init == null ? CreateEntity2(factory, init) : CreateNonNullEntity(factory, init); - } + private int GetNewId() => TrapWriter.IdCounter++; // A recursion guard against writing to the trap file whilst writing an id to the trap file. - bool WritingLabel = false; + private bool writingLabel = false; public void DefineLabel(IEntity entity, TextWriter trapFile, IExtractor extractor) { - if (WritingLabel) + if (writingLabel) { // Don't define a label whilst writing a label. PopulateLater(() => DefineLabel(entity, trapFile, extractor)); @@ -92,61 +62,20 @@ public void DefineLabel(IEntity entity, TextWriter trapFile, IExtractor extracto { try { - WritingLabel = true; + writingLabel = true; entity.DefineLabel(trapFile, extractor); } finally { - WritingLabel = false; - } - } - } - - /// - /// Creates a new entity using the factory. - /// Uses a different cache to , - /// and can store null values. - /// - /// The entity factory. - /// The initializer for the entity. - /// The new/existing entity. - public Entity CreateEntity2(ICachedEntityFactory factory, Type init) where Entity : ICachedEntity - { - using (StackGuard) - { - var entity = factory.Create(this, init); - - if (entityLabelCache.TryGetValue(entity, out var label)) - { - entity.Label = label; - } - else - { - label = GetNewLabel(); - entity.Label = label; - entityLabelCache[entity] = label; - - DefineLabel(entity, TrapWriter.Writer, Extractor); - - if (entity.NeedsPopulation) - Populate(init as ISymbol, entity); -#if DEBUG_LABELS - using (var id = new StringWriter()) - { - entity.WriteId(id); - CheckEntityHasUniqueLabel(id.ToString(), entity); - } -#endif - + writingLabel = false; } - return entity; } } #if DEBUG_LABELS private void CheckEntityHasUniqueLabel(string id, ICachedEntity entity) { - if (idLabelCache.TryGetValue(id, out var originalEntity)) + if (idLabelCache.ContainsKey(id)) { ExtractionError("Label collision for " + id, entity.Label.ToString(), Entities.Location.Create(this, entity.ReportingLocation), "", Severity.Warning); } @@ -159,13 +88,28 @@ private void CheckEntityHasUniqueLabel(string id, ICachedEntity entity) public Label GetNewLabel() => new Label(GetNewId()); - public Entity CreateNonNullEntity(ICachedEntityFactory factory, Type init) - where Entity : ICachedEntity - { - if (init is null) throw new ArgumentException("Unexpected null value", nameof(init)); + public TEntity CreateEntity(ICachedEntityFactory factory, object cacheKey, TInit init) + where TEntity : ICachedEntity => + cacheKey is ISymbol s ? CreateEntity(factory, s, init, symbolEntityCache) : CreateEntity(factory, cacheKey, init, objectEntityCache); - if (objectEntityCache.TryGetValue(init, out var cached)) - return (Entity)cached; + public TEntity CreateEntityFromSymbol(ICachedEntityFactory factory, TSymbol init) + where TSymbol : ISymbol + where TEntity : ICachedEntity => CreateEntity(factory, init, init, symbolEntityCache); + + /// + /// Creates and populates a new entity, or returns the existing one from the cache. + /// + /// The entity factory. + /// The key used for caching. + /// The initializer for the entity. + /// The dictionary to use for caching. + /// The new/existing entity. + private TEntity CreateEntity(ICachedEntityFactory factory, TCacheKey cacheKey, TInit init, IDictionary dictionary) + where TCacheKey : notnull + where TEntity : ICachedEntity + { + if (dictionary.TryGetValue(cacheKey, out var cached)) + return (TEntity)cached; using (StackGuard) { @@ -173,18 +117,16 @@ public Entity CreateNonNullEntity(ICachedEntityFactory @@ -225,18 +165,19 @@ public void AddFreshLabel(IEntity entity) } #if DEBUG_LABELS - readonly Dictionary idLabelCache = new Dictionary(); + private readonly Dictionary idLabelCache = new Dictionary(); #endif - readonly Dictionary objectEntityCache = new Dictionary(); - readonly Dictionary entityLabelCache = new Dictionary(); - readonly HashSet /// If the extraction is standalone. /// The name of the output DLL/EXE, or null if not specified (standalone extraction). - public Extractor(bool standalone, string outputPath, ILogger logger) + /// The object used for logging. + /// The object used for path transformations. + public Extractor(bool standalone, string outputPath, ILogger logger, PathTransformer pathTransformer) { Standalone = standalone; OutputPath = outputPath; Logger = logger; + PathTransformer = pathTransformer; } // Limit the number of error messages in the log file // to handle pathological cases. - const int maxErrors = 1000; + private const int maxErrors = 1000; - readonly object mutex = new object(); + private readonly object mutex = new object(); public void Message(Message msg) { @@ -149,7 +152,7 @@ public void Message(Message msg) // Roslyn framework has no apparent mechanism to associate assemblies with their files. // So this lookup table needs to be populated. - readonly Dictionary referenceFilenames = new Dictionary(); + private readonly Dictionary referenceFilenames = new Dictionary(); public void SetAssemblyFile(string assembly, string file) { @@ -166,8 +169,8 @@ public int Errors get; private set; } - readonly ISet missingTypes = new SortedSet(); - readonly ISet missingNamespaces = new SortedSet(); + private readonly ISet missingTypes = new SortedSet(); + private readonly ISet missingNamespaces = new SortedSet(); public void MissingType(string fqn, bool fromSource) { @@ -187,9 +190,9 @@ public void MissingNamespace(string fqdn, bool fromSource) } } - public Context CreateContext(Compilation c, TrapWriter trapWriter, IExtractionScope scope) + public Context CreateContext(Compilation c, TrapWriter trapWriter, IExtractionScope scope, bool addAssemblyTrapPrefix) { - return new Context(this, c, trapWriter, scope); + return new Context(this, c, trapWriter, scope, addAssemblyTrapPrefix); } public IEnumerable MissingTypes => missingTypes; @@ -205,5 +208,7 @@ public string OutputPath public ILogger Logger { get; private set; } public static string Version => $"{ThisAssembly.Git.BaseTag} ({ThisAssembly.Git.Sha})"; + + public PathTransformer PathTransformer { get; } } } diff --git a/csharp/extractor/Semmle.Extraction/FilePattern.cs b/csharp/extractor/Semmle.Extraction/FilePattern.cs new file mode 100644 index 000000000000..b2b6c01faded --- /dev/null +++ b/csharp/extractor/Semmle.Extraction/FilePattern.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Diagnostics.CodeAnalysis; +using Semmle.Util; + +namespace Semmle.Extraction +{ + public sealed class InvalidFilePatternException : Exception + { + public InvalidFilePatternException(string pattern, string message) : + base($"Invalid file pattern '{pattern}': {message}") + { } + } + + /// + /// A file pattern, as used in either an extractor layout file or + /// a path transformer file. + /// + public sealed class FilePattern + { + /// + /// Whether this is an inclusion pattern. + /// + public bool Include { get; } + + public FilePattern(string pattern) + { + Include = true; + if (pattern.StartsWith("-")) + { + pattern = pattern.Substring(1); + Include = false; + } + pattern = FileUtils.ConvertToUnix(pattern.Trim()).TrimStart('/'); + RegexPattern = BuildRegex(pattern).ToString(); + } + + /// + /// Constructs a regex string from a file pattern. Throws + /// `InvalidFilePatternException` for invalid patterns. + /// + private static StringBuilder BuildRegex(string pattern) + { + bool HasCharAt(int i, Predicate p) => + i >= 0 && i < pattern.Length && p(pattern[i]); + var sb = new StringBuilder(); + var i = 0; + var seenDoubleSlash = false; + sb.Append('^'); + while (i < pattern.Length) + { + if (pattern[i] == '/') + { + if (HasCharAt(i + 1, c => c == '/')) + { + if (seenDoubleSlash) + throw new InvalidFilePatternException(pattern, "'//' is allowed at most once."); + sb.Append("(?/)"); + i += 2; + seenDoubleSlash = true; + } + else + { + sb.Append('/'); + i++; + } + } + else if (pattern[i] == '*') + { + if (HasCharAt(i + 1, c => c == '*')) + { + if (HasCharAt(i - 1, c => c != '/')) + throw new InvalidFilePatternException(pattern, "'**' preceeded by non-`/` character."); + if (HasCharAt(i + 2, c => c != '/')) + throw new InvalidFilePatternException(pattern, "'**' succeeded by non-`/` character"); + sb.Append(".*"); + i += 2; + } + else + { + sb.Append("[^/]*"); + i++; + } + } + else + { + sb.Append(Regex.Escape(pattern[i++].ToString())); + } + } + return sb.Append(".*"); + } + + + /// + /// The regex pattern compiled from this file pattern. + /// + public string RegexPattern { get; } + + /// + /// Returns `true` if the set of file patterns `patterns` match the path `path`. + /// If so, `transformerSuffix` will contain the part of `path` that needs to be + /// suffixed when using path transformers. + /// + public static bool Matches(IEnumerable patterns, string path, [NotNullWhen(true)] out string? transformerSuffix) + { + path = FileUtils.ConvertToUnix(path).TrimStart('/'); + + foreach (var pattern in patterns.Reverse()) + { + var m = new Regex(pattern.RegexPattern).Match(path); + if (m.Success) + { + if (pattern.Include) + { + transformerSuffix = m.Groups.TryGetValue("doubleslash", out var group) + ? path.Substring(group.Index) + : path; + return true; + } + + transformerSuffix = null; + return false; + } + } + + transformerSuffix = null; + return false; + } + } +} diff --git a/csharp/extractor/Semmle.Extraction/FreshEntity.cs b/csharp/extractor/Semmle.Extraction/FreshEntity.cs index d117eefc5942..e8569cd9c918 100644 --- a/csharp/extractor/Semmle.Extraction/FreshEntity.cs +++ b/csharp/extractor/Semmle.Extraction/FreshEntity.cs @@ -1,5 +1,3 @@ -using Semmle.Extraction.Entities; -using System; using System.IO; namespace Semmle.Extraction @@ -9,9 +7,9 @@ namespace Semmle.Extraction /// public abstract class FreshEntity : IEntity { - protected readonly Context cx; + protected Context cx { get; } - public FreshEntity(Context cx) + protected FreshEntity(Context cx) { this.cx = cx; cx.AddFreshLabel(this); @@ -46,11 +44,9 @@ public string DebugContents { get { - using (var writer = new StringWriter()) - { - Populate(writer); - return writer.ToString(); - } + using var writer = new StringWriter(); + Populate(writer); + return writer.ToString(); } } diff --git a/csharp/extractor/Semmle.Extraction/Id.cs b/csharp/extractor/Semmle.Extraction/Id.cs index e2a65e5206ae..3843bfb4531e 100644 --- a/csharp/extractor/Semmle.Extraction/Id.cs +++ b/csharp/extractor/Semmle.Extraction/Id.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; namespace Semmle.Extraction { @@ -25,7 +23,7 @@ public interface IId /// public class FreshId : IId { - FreshId() { } + private FreshId() { } /// /// Gets the singleton instance. @@ -50,26 +48,27 @@ public void AppendTo(TextWriter trapFile) /// public class Key : IId { - readonly StringWriter TrapBuilder = new StringWriter(); + private readonly StringWriter trapBuilder = new StringWriter(); /// /// Creates a new key by concatenating the contents of the supplied arguments. /// public Key(params object[] args) { - TrapBuilder = new StringWriter(); + trapBuilder = new StringWriter(); foreach (var arg in args) { - if (arg is IEntity) + if (arg is IEntity entity) { - var key = ((IEntity)arg).Label; - TrapBuilder.Write("{#"); - TrapBuilder.Write(key.Value.ToString()); - TrapBuilder.Write("}"); + var key = entity.Label; + trapBuilder.Write("{#"); + trapBuilder.Write(key.Value.ToString()); + trapBuilder.Write("}"); } else - TrapBuilder.Write(arg.ToString()); - + { + trapBuilder.Write(arg.ToString()); + } } } @@ -79,12 +78,12 @@ public Key(params object[] args) /// public Key(Action action) { - action(TrapBuilder); + action(trapBuilder); } public override string ToString() { - return TrapBuilder.ToString(); + return trapBuilder.ToString(); } public override bool Equals(object? obj) @@ -92,15 +91,15 @@ public override bool Equals(object? obj) if (obj is null || obj.GetType() != GetType()) return false; var id = (Key)obj; - return TrapBuilder.ToString() == id.TrapBuilder.ToString(); + return trapBuilder.ToString() == id.trapBuilder.ToString(); } - public override int GetHashCode() => TrapBuilder.ToString().GetHashCode(); + public override int GetHashCode() => trapBuilder.ToString().GetHashCode(); public void AppendTo(TextWriter trapFile) { trapFile.Write("@\""); - trapFile.Write(TrapBuilder.ToString()); + trapFile.Write(trapBuilder.ToString()); trapFile.Write("\""); } } @@ -117,14 +116,14 @@ public Label(int value) : this() public int Value { get; private set; } - static public readonly Label InvalidLabel = new Label(0); + public static Label InvalidLabel { get; } = new Label(0); public bool Valid => Value > 0; public override string ToString() { if (!Valid) - throw new NullReferenceException("Attempt to use an invalid label"); + throw new InvalidOperationException("Attempt to use an invalid label"); return "#" + Value; } @@ -135,7 +134,8 @@ public override string ToString() public override bool Equals(object? other) { - if (other is null) return false; + if (other is null) + return false; return GetType() == other.GetType() && ((Label)other).Value == Value; } @@ -148,7 +148,7 @@ public override bool Equals(object? other) public void AppendTo(System.IO.TextWriter trapFile) { if (!Valid) - throw new NullReferenceException("Attempt to use an invalid label"); + throw new InvalidOperationException("Attempt to use an invalid label"); trapFile.Write('#'); trapFile.Write(Value); diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs index 9ab7ed5738a1..4871f4c83aed 100644 --- a/csharp/extractor/Semmle.Extraction/Layout.cs +++ b/csharp/extractor/Semmle.Extraction/Layout.cs @@ -26,7 +26,7 @@ public InvalidLayoutException(string file, string message) : /// /// List of blocks in the layout file. /// - readonly List blocks; + private readonly List blocks; /// /// A subproject in the layout file. @@ -36,12 +36,12 @@ public class SubProject /// /// The trap folder, or null for current directory. /// - public readonly string? TRAP_FOLDER; + public string? TRAP_FOLDER { get; } /// /// The source archive, or null to skip. /// - public readonly string? SOURCE_ARCHIVE; + public string? SOURCE_ARCHIVE { get; } public SubProject(string? traps, string? archive) { @@ -54,18 +54,19 @@ public SubProject(string? traps, string? archive) /// /// The source file. /// The full filepath of the trap file. - public string GetTrapPath(ILogger logger, string srcFile, TrapWriter.CompressionMode trapCompression) => TrapWriter.TrapPath(logger, TRAP_FOLDER, srcFile, trapCompression); + public string GetTrapPath(ILogger logger, PathTransformer.ITransformedPath srcFile, TrapWriter.CompressionMode trapCompression) => + TrapWriter.TrapPath(logger, TRAP_FOLDER, srcFile, trapCompression); /// /// Creates a trap writer for a given source/assembly file. /// /// The source file. /// A newly created TrapWriter. - public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) => + public TrapWriter CreateTrapWriter(ILogger logger, PathTransformer.ITransformedPath srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) => new TrapWriter(logger, srcFile, TRAP_FOLDER, SOURCE_ARCHIVE, discardDuplicates, trapCompression); } - readonly SubProject DefaultProject; + private readonly SubProject defaultProject; /// /// Finds the suitable directories for a given source file. @@ -73,14 +74,15 @@ public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardD /// /// The file to look up. /// The relevant subproject, or null if not found. - public SubProject? LookupProjectOrNull(string sourceFile) + public SubProject? LookupProjectOrNull(PathTransformer.ITransformedPath sourceFile) { - if (!useLayoutFile) return DefaultProject; + if (!useLayoutFile) + return defaultProject; - return blocks. - Where(block => block.Matches(sourceFile)). - Select(block => block.Directories). - FirstOrDefault(); + return blocks + .Where(block => block.Matches(sourceFile)) + .Select(block => block.Directories) + .FirstOrDefault(); } /// @@ -89,12 +91,12 @@ public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardD /// /// The file to look up. /// The relevant subproject, or DefaultProject if not found. - public SubProject LookupProjectOrDefault(string sourceFile) + public SubProject LookupProjectOrDefault(PathTransformer.ITransformedPath sourceFile) { - return LookupProjectOrNull(sourceFile) ?? DefaultProject; + return LookupProjectOrNull(sourceFile) ?? defaultProject; } - readonly bool useLayoutFile; + private readonly bool useLayoutFile; /// /// Default constructor reads parameters from the environment. @@ -121,11 +123,11 @@ public Layout(string? traps, string? archive, string? layout) if (useLayoutFile) { ReadLayoutFile(layout!); - DefaultProject = blocks[0].Directories; + defaultProject = blocks[0].Directories; } else { - DefaultProject = new SubProject(traps, archive); + defaultProject = new SubProject(traps, archive); } } @@ -134,20 +136,20 @@ public Layout(string? traps, string? archive, string? layout) /// /// The absolute path of the file to query. /// True iff there is no layout file or the layout file specifies the file. - public bool FileInLayout(string path) => LookupProjectOrNull(path) != null; + public bool FileInLayout(PathTransformer.ITransformedPath path) => LookupProjectOrNull(path) != null; - void ReadLayoutFile(string layout) + private void ReadLayoutFile(string layout) { try { var lines = File.ReadAllLines(layout); - int i = 0; + var i = 0; while (!lines[i].StartsWith("#")) i++; while (i < lines.Length) { - LayoutBlock block = new LayoutBlock(lines, ref i); + var block = new LayoutBlock(lines, ref i); blocks.Add(block); } @@ -165,41 +167,15 @@ void ReadLayoutFile(string layout) } } - sealed class LayoutBlock + internal sealed class LayoutBlock { - struct Condition - { - private readonly bool include; - private readonly string prefix; - - public bool Include => include; + private readonly List filePatterns = new List(); - public string Prefix => prefix; - - public Condition(string line) - { - include = false; - if (line.StartsWith("-")) - line = line.Substring(1); - else - include = true; - prefix = Normalise(line.Trim()); - } + public Layout.SubProject Directories { get; } - static public string Normalise(string path) - { - path = Path.GetFullPath(path); - return path.Replace('\\', '/'); - } - } - - private readonly List conditions = new List(); - - public readonly Layout.SubProject Directories; - - string? ReadVariable(string name, string line) + private static string? ReadVariable(string name, string line) { - string prefix = name + "="; + var prefix = name + "="; if (!line.StartsWith(prefix)) return null; return line.Substring(prefix.Length).Trim(); @@ -209,32 +185,20 @@ public LayoutBlock(string[] lines, ref int i) { // first line: #name i++; - string? TRAP_FOLDER = ReadVariable("TRAP_FOLDER", lines[i++]); + var trapFolder = ReadVariable("TRAP_FOLDER", lines[i++]); // Don't care about ODASA_DB. ReadVariable("ODASA_DB", lines[i++]); - string? SOURCE_ARCHIVE = ReadVariable("SOURCE_ARCHIVE", lines[i++]); + var sourceArchive = ReadVariable("SOURCE_ARCHIVE", lines[i++]); - Directories = new Layout.SubProject(TRAP_FOLDER, SOURCE_ARCHIVE); + Directories = new Layout.SubProject(trapFolder, sourceArchive); // Don't care about ODASA_BUILD_ERROR_DIR. ReadVariable("ODASA_BUILD_ERROR_DIR", lines[i++]); while (i < lines.Length && !lines[i].StartsWith("#")) { - conditions.Add(new Condition(lines[i++])); + filePatterns.Add(new FilePattern(lines[i++])); } } - public bool Matches(string path) - { - bool matches = false; - path = Condition.Normalise(path); - foreach (Condition condition in conditions) - { - if (condition.Include) - matches |= path.StartsWith(condition.Prefix); - else - matches &= !path.StartsWith(condition.Prefix); - } - return matches; - } + public bool Matches(PathTransformer.ITransformedPath path) => FilePattern.Matches(filePatterns, path.Value, out var _); } } diff --git a/csharp/extractor/Semmle.Extraction/LocationExtensions.cs b/csharp/extractor/Semmle.Extraction/LocationExtensions.cs index 339d1695b891..5ecaae8a3fed 100644 --- a/csharp/extractor/Semmle.Extraction/LocationExtensions.cs +++ b/csharp/extractor/Semmle.Extraction/LocationExtensions.cs @@ -18,9 +18,9 @@ public static class LocationExtensions /// Whether inner is completely container in outer. public static bool Contains(this Location outer, Location inner) { - bool sameFile = outer.SourceTree == inner.SourceTree; - bool startsBefore = outer.SourceSpan.Start <= inner.SourceSpan.Start; - bool endsAfter = outer.SourceSpan.End >= inner.SourceSpan.End; + var sameFile = outer.SourceTree == inner.SourceTree; + var startsBefore = outer.SourceSpan.Start <= inner.SourceSpan.Start; + var endsAfter = outer.SourceSpan.End >= inner.SourceSpan.End; return sameFile && startsBefore && endsAfter; } @@ -32,8 +32,8 @@ public static bool Contains(this Location outer, Location inner) /// Whether 'before' comes before 'after'. public static bool Before(this Location before, Location after) { - bool sameFile = before.SourceTree == after.SourceTree; - bool endsBefore = before.SourceSpan.End <= after.SourceSpan.Start; + var sameFile = before.SourceTree == after.SourceTree; + var endsBefore = before.SourceSpan.End <= after.SourceSpan.Start; return sameFile && endsBefore; } } diff --git a/csharp/extractor/Semmle.Extraction/Message.cs b/csharp/extractor/Semmle.Extraction/Message.cs index c617efaa5ba6..fbe91fb95e63 100644 --- a/csharp/extractor/Semmle.Extraction/Message.cs +++ b/csharp/extractor/Semmle.Extraction/Message.cs @@ -11,11 +11,11 @@ namespace Semmle.Extraction /// public class Message { - public readonly Severity Severity; - public readonly string Text; - public readonly string StackTrace; - public readonly string EntityText; - public readonly Entities.Location? Location; + public Severity Severity { get; } + public string Text { get; } + public string StackTrace { get; } + public string EntityText { get; } + public Entities.Location? Location { get; } public Message(string text, string entityText, Entities.Location? location, string? stackTrace = null, Severity severity = Severity.Error) { diff --git a/csharp/extractor/Semmle.Extraction/Options.cs b/csharp/extractor/Semmle.Extraction/Options.cs index a7d186f9e92f..fffe3c88b4bd 100644 --- a/csharp/extractor/Semmle.Extraction/Options.cs +++ b/csharp/extractor/Semmle.Extraction/Options.cs @@ -12,44 +12,44 @@ public abstract class CommonOptions : ICommandLineOptions /// /// The specified number of threads, or the default if unspecified. /// - public int Threads = Extractor.DefaultNumberOfThreads; + public int Threads { get; private set; } = System.Environment.ProcessorCount; /// /// The verbosity used in output and logging. /// - public Verbosity Verbosity = Verbosity.Info; + public Verbosity Verbosity { get; protected set; } = Verbosity.Info; /// /// Whether to output to the console. /// - public bool Console = false; + public bool Console { get; private set; } = false; /// /// Holds if CIL should be extracted. /// - public bool CIL = false; + public bool CIL { get; private set; } = false; /// /// Holds if assemblies shouldn't be extracted twice. /// - public bool Cache = true; + public bool Cache { get; private set; } = true; /// /// Whether to extract PDB information. /// - public bool PDB = false; + public bool PDB { get; private set; } = false; /// /// Whether "fast extraction mode" has been enabled. /// - public bool Fast = false; + public bool Fast { get; private set; } = false; /// /// The compression algorithm used for trap files. /// - public TrapWriter.CompressionMode TrapCompression = TrapWriter.CompressionMode.Gzip; + public TrapWriter.CompressionMode TrapCompression { get; set; } = TrapWriter.CompressionMode.Gzip; - public virtual bool handleOption(string key, string value) + public virtual bool HandleOption(string key, string value) { switch (key) { @@ -64,9 +64,9 @@ public virtual bool handleOption(string key, string value) } } - public abstract bool handleArgument(string argument); + public abstract bool HandleArgument(string argument); - public virtual bool handleFlag(string flag, bool value) + public virtual bool HandleFlag(string flag, bool value) { switch (flag) { @@ -98,6 +98,6 @@ public virtual bool handleFlag(string flag, bool value) } } - public abstract void invalidArgument(string argument); + public abstract void InvalidArgument(string argument); } } diff --git a/csharp/extractor/Semmle.Extraction/PathTransformer.cs b/csharp/extractor/Semmle.Extraction/PathTransformer.cs new file mode 100644 index 000000000000..31bcc4e0be6b --- /dev/null +++ b/csharp/extractor/Semmle.Extraction/PathTransformer.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Diagnostics.CodeAnalysis; +using Semmle.Util; + +namespace Semmle.Extraction +{ + /// + /// A class for interpreting path transformers specified using the environment + /// variable `CODEQL_PATH_TRANSFORMER`. + /// + public sealed class PathTransformer + { + public class InvalidPathTransformerException : Exception + { + public InvalidPathTransformerException(string message) : + base($"Invalid path transformer specification: {message}") + { } + } + + /// + /// A transformed path. + /// + public interface ITransformedPath + { + string Value { get; } + + string Extension { get; } + + string NameWithoutExtension { get; } + + ITransformedPath? ParentDirectory { get; } + + ITransformedPath WithSuffix(string suffix); + + string DatabaseId { get; } + } + + private struct TransformedPath : ITransformedPath + { + public TransformedPath(string value) { this.value = value; } + private readonly string value; + + public string Value => value; + + public string Extension => Path.GetExtension(value)?.Substring(1) ?? ""; + + public string NameWithoutExtension => Path.GetFileNameWithoutExtension(value); + + public ITransformedPath? ParentDirectory + { + get + { + var dir = Path.GetDirectoryName(value); + if (dir is null) + return null; + var isWindowsDriveLetter = dir.Length == 2 && char.IsLetter(dir[0]) && dir[1] == ':'; + if (isWindowsDriveLetter) + return null; + return new TransformedPath(FileUtils.ConvertToUnix(dir)); + } + } + + public ITransformedPath WithSuffix(string suffix) => new TransformedPath(value + suffix); + + public string DatabaseId + { + get + { + var ret = value; + if (ret.Length >= 2 && ret[1] == ':' && Char.IsLower(ret[0])) + ret = Char.ToUpper(ret[0]) + "_" + ret.Substring(2); + return ret.Replace('\\', '/').Replace(":", "_"); + } + } + + public override int GetHashCode() => 11 * value.GetHashCode(); + + public override bool Equals(object? obj) => obj is TransformedPath tp && tp.value == value; + + public override string ToString() => value; + } + + private readonly Func transform; + + /// + /// Returns the path obtained by transforming `path`. + /// + public ITransformedPath Transform(string path) => new TransformedPath(transform(path)); + + /// + /// Default constructor reads parameters from the environment. + /// + public PathTransformer(IPathCache pathCache) : + this(pathCache, Environment.GetEnvironmentVariable("CODEQL_PATH_TRANSFORMER") is string file ? File.ReadAllLines(file) : null) + { + } + + /// + /// Creates a path transformer based on the specification in `lines`. + /// Throws `InvalidPathTransformerException` for invalid specifications. + /// + public PathTransformer(IPathCache pathCache, string[]? lines) + { + if (lines is null) + { + transform = path => FileUtils.ConvertToUnix(pathCache.GetCanonicalPath(path)); + return; + } + + var sections = ParsePathTransformerSpec(lines); + transform = path => + { + path = FileUtils.ConvertToUnix(pathCache.GetCanonicalPath(path)); + foreach (var section in sections) + { + if (section.Matches(path, out var transformed)) + return transformed; + } + return path; + }; + } + + private static IEnumerable ParsePathTransformerSpec(string[] lines) + { + var sections = new List(); + try + { + var i = 0; + while (i < lines.Length && !lines[i].StartsWith("#")) + i++; + while (i < lines.Length) + { + var section = new TransformerSection(lines, ref i); + sections.Add(section); + } + + if (sections.Count == 0) + throw new InvalidPathTransformerException("contains no sections."); + } + catch (InvalidFilePatternException ex) + { + throw new InvalidPathTransformerException(ex.Message); + } + return sections; + } + } + + internal sealed class TransformerSection + { + private readonly string name; + private readonly List filePatterns = new List(); + + public TransformerSection(string[] lines, ref int i) + { + name = lines[i++].Substring(1); // skip the '#' + for (; i < lines.Length && !lines[i].StartsWith("#"); i++) + { + var line = lines[i]; + if (!string.IsNullOrWhiteSpace(line)) + filePatterns.Add(new FilePattern(line)); + } + } + + public bool Matches(string path, [NotNullWhen(true)] out string? transformed) + { + if (FilePattern.Matches(filePatterns, path, out var suffix)) + { + transformed = FileUtils.ConvertToUnix(name) + suffix; + return true; + } + transformed = null; + return false; + } + } +} diff --git a/csharp/extractor/Semmle.Extraction/Semmle.Extraction.csproj b/csharp/extractor/Semmle.Extraction/Semmle.Extraction.csproj index b8a6d8be614b..cc32fc33b38c 100644 --- a/csharp/extractor/Semmle.Extraction/Semmle.Extraction.csproj +++ b/csharp/extractor/Semmle.Extraction/Semmle.Extraction.csproj @@ -1,7 +1,7 @@ īģŋ - netcoreapp3.0 + netcoreapp3.1 Semmle.Extraction Semmle.Extraction false @@ -15,10 +15,11 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive -all - + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/csharp/extractor/Semmle.Extraction/Symbol.cs b/csharp/extractor/Semmle.Extraction/Symbol.cs index 925fba2e3fdb..4366cff7f06e 100644 --- a/csharp/extractor/Semmle.Extraction/Symbol.cs +++ b/csharp/extractor/Semmle.Extraction/Symbol.cs @@ -1,14 +1,15 @@ using System.IO; +using Microsoft.CodeAnalysis; namespace Semmle.Extraction { /// /// An abstract symbol, which encapsulates a data type (such as a C# symbol). /// - /// The type of the symbol. - public abstract class CachedEntity : ICachedEntity + /// The type of the symbol. + public abstract class CachedEntity : ICachedEntity { - public CachedEntity(Context context, Initializer init) + protected CachedEntity(Context context, TSymbol init) { Context = context; symbol = init; @@ -29,11 +30,9 @@ public string DebugContents { get { - using (var trap = new StringWriter()) - { - Populate(trap); - return trap.ToString(); - } + using var trap = new StringWriter(); + Populate(trap); + return trap.ToString(); } } @@ -42,14 +41,14 @@ public Context Context get; } - public Initializer symbol + public TSymbol symbol { get; } object? ICachedEntity.UnderlyingObject => symbol; - public Initializer UnderlyingObject => symbol; + public TSymbol UnderlyingObject => symbol; public abstract void WriteId(System.IO.TextWriter trapFile); @@ -65,24 +64,30 @@ public abstract bool NeedsPopulation get; } - /// - /// Runs the given action , guarding for trap duplication - /// based on the ID an location of this entity. - /// - protected void WithDuplicationGuard(System.Action a, IEntity location) - { - var key = new Key(this, location); - Context.WithDuplicationGuard(key, a); - } - public override int GetHashCode() => symbol is null ? 0 : symbol.GetHashCode(); public override bool Equals(object? obj) { - var other = obj as CachedEntity; + var other = obj as CachedEntity; return other?.GetType() == GetType() && Equals(other.symbol, symbol); } public abstract TrapStackBehaviour TrapStackBehaviour { get; } } + + /// + /// A class used to wrap an `ISymbol` object, which uses `SymbolEqualityComparer.Default` + /// for comparison. + /// + public sealed class SymbolEqualityWrapper + { + public ISymbol Symbol { get; } + + public SymbolEqualityWrapper(ISymbol symbol) { Symbol = symbol; } + + public override bool Equals(object? other) => + other is SymbolEqualityWrapper sew && SymbolEqualityComparer.Default.Equals(Symbol, sew.Symbol); + + public override int GetHashCode() => 11 * Symbol.GetHashCode(); + } } diff --git a/csharp/extractor/Semmle.Extraction/TrapExtensions.cs b/csharp/extractor/Semmle.Extraction/TrapExtensions.cs index 1a25f642e7ac..11111467147e 100644 --- a/csharp/extractor/Semmle.Extraction/TrapExtensions.cs +++ b/csharp/extractor/Semmle.Extraction/TrapExtensions.cs @@ -32,7 +32,8 @@ public static void WriteSubId(this TextWriter trapFile, IEntity entity) public static void WriteSeparator(this TextWriter trapFile, string separator, ref int index) { - if (index++ > 0) trapFile.Write(separator); + if (index++ > 0) + trapFile.Write(separator); } @@ -85,16 +86,16 @@ public static TextWriter WriteColumn(this TextWriter trapFile, object o) case Enum _: return trapFile.WriteColumn((int)o); default: - throw new ArgumentException(nameof(o)); + throw new NotSupportedException($"Unsupported object type '{o.GetType()}' received"); } } - const int maxStringBytes = 1 << 20; // 1MB - static readonly System.Text.Encoding encoding = System.Text.Encoding.UTF8; + private const int maxStringBytes = 1 << 20; // 1MB + private static readonly System.Text.Encoding encoding = System.Text.Encoding.UTF8; private static bool NeedsTruncation(string s) { - // Optimization: only count the actual number of bytes if there is the possibility + // Optimization: only count the actual number of bytes if there is the possibility // of the string exceeding maxStringBytes return encoding.GetMaxByteCount(s.Length) > maxStringBytes && encoding.GetByteCount(s) > maxStringBytes; @@ -110,7 +111,7 @@ private static bool NeedsTruncation(string s) /// The truncated string. private static string TruncateString(string s, ref int bytesRemaining) { - int outputLen = encoding.GetByteCount(s); + var outputLen = encoding.GetByteCount(s); if (outputLen > bytesRemaining) { outputLen = 0; @@ -149,7 +150,7 @@ public static void WriteTrapString(this TextWriter trapFile, string s) if (NeedsTruncation(s)) { // Slow path - int remaining = maxStringBytes; + var remaining = maxStringBytes; WriteTruncatedString(trapFile, s, ref remaining); } else @@ -169,7 +170,7 @@ public static void WriteTuple(this TextWriter trapFile, string name, params obje { trapFile.Write(name); trapFile.Write('('); - int index = 0; + var index = 0; foreach (var p in @params) { trapFile.WriteSeparator(",", ref index); @@ -246,10 +247,13 @@ public static TextWriter AppendList(this TextWriter trapFile, string separato /// The original trap builder (fluent interface). public static TextWriter BuildList(this TextWriter trapFile, string separator, IEnumerable items, Action action) { - bool first = true; + var first = true; foreach (var item in items) { - if (first) first = false; else trapFile.Write(separator); + if (first) + first = false; + else + trapFile.Write(separator); action(item, trapFile); } return trapFile; diff --git a/csharp/extractor/Semmle.Extraction/TrapWriter.cs b/csharp/extractor/Semmle.Extraction/TrapWriter.cs index 7ea08eafc1c1..af7259b64e81 100644 --- a/csharp/extractor/Semmle.Extraction/TrapWriter.cs +++ b/csharp/extractor/Semmle.Extraction/TrapWriter.cs @@ -14,12 +14,6 @@ public interface ITrapEmitter public sealed class TrapWriter : IDisposable { - public enum InnerPathComputation - { - ABSOLUTE, - RELATIVE - } - public enum CompressionMode { None, @@ -31,28 +25,28 @@ public enum CompressionMode /// The location of the src_archive directory. /// private readonly string? archive; - private static readonly Encoding UTF8 = new UTF8Encoding(false); + private static readonly Encoding utf8 = new UTF8Encoding(false); private readonly bool discardDuplicates; public int IdCounter { get; set; } = 1; - readonly Lazy WriterLazy; + private readonly Lazy writerLazy; - public StreamWriter Writer => WriterLazy.Value; + public StreamWriter Writer => writerLazy.Value; - readonly ILogger Logger; + private readonly ILogger logger; - readonly CompressionMode TrapCompression; + private readonly CompressionMode trapCompression; - public TrapWriter(ILogger logger, string outputfile, string? trap, string? archive, bool discardDuplicates, CompressionMode trapCompression) + public TrapWriter(ILogger logger, PathTransformer.ITransformedPath outputfile, string? trap, string? archive, bool discardDuplicates, CompressionMode trapCompression) { - Logger = logger; - TrapCompression = trapCompression; + this.logger = logger; + this.trapCompression = trapCompression; - TrapFile = TrapPath(Logger, trap, outputfile, trapCompression); + TrapFile = TrapPath(this.logger, trap, outputfile, trapCompression); - WriterLazy = new Lazy(() => + writerLazy = new Lazy(() => { var tempPath = trap ?? Path.GetTempPath(); @@ -87,11 +81,11 @@ public TrapWriter(ILogger logger, string outputfile, string? trap, string? archi compressionStream = fileStream; break; default: - throw new ArgumentException(nameof(trapCompression)); + throw new ArgumentOutOfRangeException(nameof(trapCompression), trapCompression, "Unsupported compression type"); } - return new StreamWriter(compressionStream, UTF8, 2000000); + return new StreamWriter(compressionStream, utf8, 2000000); }); this.archive = archive; this.discardDuplicates = discardDuplicates; @@ -100,23 +94,25 @@ public TrapWriter(ILogger logger, string outputfile, string? trap, string? archi /// /// The output filename of the trap. /// - public readonly string TrapFile; - string tmpFile = ""; // The temporary file which is moved to trapFile once written. + public string TrapFile { get; } + private string tmpFile = ""; // The temporary file which is moved to trapFile once written. /// /// Adds the specified input file to the source archive. It may end up in either the normal or long path area /// of the source archive, depending on the length of its full path. /// - /// The path to the input file. + /// The path to the input file. + /// The transformed path to the input file. /// The encoding used by the input file. - public void Archive(string inputPath, Encoding inputEncoding) + public void Archive(string originalPath, PathTransformer.ITransformedPath transformedPath, Encoding inputEncoding) { - if (string.IsNullOrEmpty(archive)) return; + if (string.IsNullOrEmpty(archive)) + return; // Calling GetFullPath makes this use the canonical capitalisation, if the file exists. - string fullInputPath = Path.GetFullPath(inputPath); + var fullInputPath = Path.GetFullPath(originalPath); - ArchivePath(fullInputPath, inputEncoding); + ArchivePath(fullInputPath, transformedPath, inputEncoding); } /// @@ -124,14 +120,12 @@ public void Archive(string inputPath, Encoding inputEncoding) /// /// The path of the file. /// The contents of the file. - public void Archive(string inputPath, string contents) + public void Archive(PathTransformer.ITransformedPath inputPath, string contents) { - if (string.IsNullOrEmpty(archive)) return; + if (string.IsNullOrEmpty(archive)) + return; - // Calling GetFullPath makes this use the canonical capitalisation, if the file exists. - string fullInputPath = Path.GetFullPath(inputPath); - - ArchiveContents(fullInputPath, contents); + ArchiveContents(inputPath, contents); } /// @@ -142,7 +136,7 @@ public void Archive(string inputPath, string contents) /// The source filename. /// The destination filename. /// true if the file was moved. - static bool TryMove(string sourceFile, string destFile) + private static bool TryMove(string sourceFile, string destFile) { try { @@ -169,9 +163,9 @@ public void Dispose() { try { - if (WriterLazy.IsValueCreated) + if (writerLazy.IsValueCreated) { - WriterLazy.Value.Close(); + writerLazy.Value.Close(); if (TryMove(tmpFile, TrapFile)) return; @@ -186,16 +180,16 @@ public void Dispose() if (existingHash != hash) { var root = TrapFile.Substring(0, TrapFile.Length - 8); // Remove trailing ".trap.gz" - if (TryMove(tmpFile, $"{root}-{hash}.trap{TrapExtension(TrapCompression)}")) + if (TryMove(tmpFile, $"{root}-{hash}.trap{TrapExtension(trapCompression)}")) return; } - Logger.Log(Severity.Info, "Identical trap file for {0} already exists", TrapFile); + logger.Log(Severity.Info, "Identical trap file for {0} already exists", TrapFile); FileUtils.TryDelete(tmpFile); } } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { - Logger.Log(Severity.Error, "Failed to move the trap file from {0} to {1} because {2}", tmpFile, TrapFile, ex); + logger.Log(Severity.Error, "Failed to move the trap file from {0} to {1} because {2}", tmpFile, TrapFile, ex); } } @@ -210,20 +204,21 @@ public void Emit(ITrapEmitter emitter) /// source archive less than the system path limit of 260 characters. /// /// The full path to the input file. + /// The transformed path to the input file. /// The encoding used by the input file. /// If the output path in the source archive would /// exceed the system path limit of 260 characters. - private void ArchivePath(string fullInputPath, Encoding inputEncoding) + private void ArchivePath(string fullInputPath, PathTransformer.ITransformedPath transformedPath, Encoding inputEncoding) { - string contents = File.ReadAllText(fullInputPath, inputEncoding); - ArchiveContents(fullInputPath, contents); + var contents = File.ReadAllText(fullInputPath, inputEncoding); + ArchiveContents(transformedPath, contents); } - private void ArchiveContents(string fullInputPath, string contents) + private void ArchiveContents(PathTransformer.ITransformedPath transformedPath, string contents) { - string dest = NestPaths(Logger, archive, fullInputPath, InnerPathComputation.ABSOLUTE); - string tmpSrcFile = Path.GetTempFileName(); - File.WriteAllText(tmpSrcFile, contents, UTF8); + var dest = NestPaths(logger, archive, transformedPath.Value); + var tmpSrcFile = Path.GetTempFileName(); + File.WriteAllText(tmpSrcFile, contents, utf8); try { FileUtils.MoveOrReplace(tmpSrcFile, dest); @@ -232,18 +227,15 @@ private void ArchiveContents(string fullInputPath, string contents) { // If this happened, it was probably because the same file was compiled multiple times. // In any case, this is not a fatal error. - Logger.Log(Severity.Warning, "Problem archiving " + dest + ": " + ex); + logger.Log(Severity.Warning, "Problem archiving " + dest + ": " + ex); } } - public static string NestPaths(ILogger logger, string? outerpath, string innerpath, InnerPathComputation innerPathComputation) + public static string NestPaths(ILogger logger, string? outerpath, string innerpath) { - string nested = innerpath; + var nested = innerpath; if (!string.IsNullOrEmpty(outerpath)) { - if (!Path.IsPathRooted(innerpath) && innerPathComputation == InnerPathComputation.ABSOLUTE) - innerpath = Path.GetFullPath(innerpath); - // Remove all leading path separators / or \ // For example, UNC paths have two leading \\ innerpath = innerpath.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); @@ -265,24 +257,24 @@ public static string NestPaths(ILogger logger, string? outerpath, string innerpa return nested; } - static string TrapExtension(CompressionMode compression) + private static string TrapExtension(CompressionMode compression) { switch (compression) { case CompressionMode.None: return ""; case CompressionMode.Gzip: return ".gz"; case CompressionMode.Brotli: return ".br"; - default: throw new ArgumentException(nameof(compression)); + default: throw new ArgumentOutOfRangeException(nameof(compression), compression, "Unsupported compression type"); } } - public static string TrapPath(ILogger logger, string? folder, string filename, TrapWriter.CompressionMode trapCompression) + public static string TrapPath(ILogger logger, string? folder, PathTransformer.ITransformedPath path, TrapWriter.CompressionMode trapCompression) { - filename = $"{Path.GetFullPath(filename)}.trap{TrapExtension(trapCompression)}"; + var filename = $"{path.Value}.trap{TrapExtension(trapCompression)}"; if (string.IsNullOrEmpty(folder)) folder = Directory.GetCurrentDirectory(); - return NestPaths(logger, folder, filename, InnerPathComputation.ABSOLUTE); ; + return NestPaths(logger, folder, filename); } } } diff --git a/csharp/extractor/Semmle.Extraction/Tuple.cs b/csharp/extractor/Semmle.Extraction/Tuple.cs index 85d95d7e031b..bfe660926d6d 100644 --- a/csharp/extractor/Semmle.Extraction/Tuple.cs +++ b/csharp/extractor/Semmle.Extraction/Tuple.cs @@ -7,13 +7,13 @@ namespace Semmle.Extraction /// public struct Tuple : ITrapEmitter { - readonly string Name; - readonly object[] Args; + private readonly string name; + private readonly object[] args; public Tuple(string name, params object[] args) { - Name = name; - Args = args; + this.name = name; + this.args = args; } /// @@ -22,17 +22,15 @@ public Tuple(string name, params object[] args) /// The trap file to write to. public void EmitTrap(TextWriter trapFile) { - trapFile.WriteTuple(Name, Args); + trapFile.WriteTuple(name, args); } public override string ToString() { // Only implemented for debugging purposes - using (var writer = new StringWriter()) - { - EmitTrap(writer); - return writer.ToString(); - } + using var writer = new StringWriter(); + EmitTrap(writer); + return writer.ToString(); } } } diff --git a/csharp/extractor/Semmle.Extraction/Tuples.cs b/csharp/extractor/Semmle.Extraction/Tuples.cs index 3297b705f585..0a71ba6b3d90 100644 --- a/csharp/extractor/Semmle.Extraction/Tuples.cs +++ b/csharp/extractor/Semmle.Extraction/Tuples.cs @@ -6,7 +6,7 @@ namespace Semmle.Extraction /// /// Methods for creating DB tuples. /// - static class Tuples + internal static class Tuples { public static void assemblies(this System.IO.TextWriter trapFile, Assembly assembly, File file, string identifier, string name, string version) { diff --git a/csharp/extractor/Semmle.Util.Tests/ActionMap.cs b/csharp/extractor/Semmle.Util.Tests/ActionMap.cs index 0cc815a80c81..5c2b210834ae 100644 --- a/csharp/extractor/Semmle.Util.Tests/ActionMap.cs +++ b/csharp/extractor/Semmle.Util.Tests/ActionMap.cs @@ -51,5 +51,3 @@ public void TestMultipleActions() } } - - diff --git a/csharp/extractor/Semmle.Util.Tests/CanonicalPathCache.cs b/csharp/extractor/Semmle.Util.Tests/CanonicalPathCache.cs index c76256ef9b24..2fe8eb12d71d 100644 --- a/csharp/extractor/Semmle.Util.Tests/CanonicalPathCache.cs +++ b/csharp/extractor/Semmle.Util.Tests/CanonicalPathCache.cs @@ -6,12 +6,11 @@ namespace SemmleTests.Semmle.Util { - public class CanonicalPathCacheTest : IDisposable + public sealed class CanonicalPathCacheTest : IDisposable { - readonly ILogger Logger = new LoggerMock(); - readonly string root; - - CanonicalPathCache cache; + private readonly ILogger Logger = new LoggerMock(); + private readonly string root; + private CanonicalPathCache cache; public CanonicalPathCacheTest() { @@ -21,20 +20,13 @@ public CanonicalPathCacheTest() // Change directories to a directory that is in canonical form. Directory.SetCurrentDirectory(cache.GetCanonicalPath(Path.GetTempPath())); - if (Win32.IsWindows()) - { - root = @"X:\"; - } - else - { - root = "/"; - } - + root = Win32.IsWindows() ? @"X:\" : "/"; } - void IDisposable.Dispose() + public void Dispose() { File.Delete("abc"); + Logger.Dispose(); } [Fact] @@ -143,10 +135,10 @@ public void CanonicalPathCacheSize() Assert.Equal(0, cache.CacheSize); // The file "ABC" will fill the cache with parent directory info. - string cp = cache.GetCanonicalPath("ABC"); + cache.GetCanonicalPath("ABC"); Assert.True(cache.CacheSize == 2); - cp = cache.GetCanonicalPath("def"); + string cp = cache.GetCanonicalPath("def"); Assert.Equal(2, cache.CacheSize); Assert.Equal(Path.GetFullPath("def"), cp); } @@ -165,7 +157,7 @@ public void CanonicalPathPreserveLinksTests() RunAllTests(); } - void RunAllTests() + private void RunAllTests() { CanonicalPathRelativeFile(); CanonicalPathAbsoluteFile(); @@ -180,13 +172,11 @@ void RunAllTests() CanonicalPathDots(); } - class LoggerMock : ILogger + private sealed class LoggerMock : ILogger { public void Dispose() { } public void Log(Severity s, string text) { } - - public void Log(Severity s, string text, params object[] args) { } } } } diff --git a/csharp/extractor/Semmle.Util.Tests/LongPaths.cs b/csharp/extractor/Semmle.Util.Tests/LongPaths.cs index c2a3f500de6d..44b047b6d8dd 100644 --- a/csharp/extractor/Semmle.Util.Tests/LongPaths.cs +++ b/csharp/extractor/Semmle.Util.Tests/LongPaths.cs @@ -10,38 +10,38 @@ namespace SemmleTests.Semmle.Util /// Ensure that the Extractor works with long paths. /// These should be handled by .NET Core. /// - public class LongPaths : IDisposable + public sealed class LongPaths : IDisposable { - static readonly string tmpDir = Path.GetTempPath(); - static readonly string shortPath = Path.Combine(tmpDir, "test.txt"); - static readonly string longPath = Path.Combine(tmpDir, "aaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + private static readonly string tmpDir = Path.GetTempPath(); + private static readonly string shortPath = Path.Combine(tmpDir, "test.txt"); + private static readonly string longPath = Path.Combine(tmpDir, "aaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "ccccccccccccccccccccccccccccccc", "ddddddddddddddddddddddddddddddddddddd", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", "fffffffffffffffffffffffffffffffff", - "ggggggggggggggggggggggggggggggggggg","hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh","iiiiiiiiiiiiiiii.txt"); + "ggggggggggggggggggggggggggggggggggg", "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", "iiiiiiiiiiiiiiii.txt"); public LongPaths() { CleanUp(); } - void IDisposable.Dispose() + public void Dispose() { CleanUp(); } - void CleanUp() + private static void CleanUp() { try { File.Delete(shortPath); } - catch(DirectoryNotFoundException) + catch (DirectoryNotFoundException) { } try { File.Delete(longPath); } - catch(DirectoryNotFoundException) + catch (DirectoryNotFoundException) { } } @@ -93,7 +93,7 @@ public void Replace() Assert.Equal("def", File.ReadAllText(shortPath)); } - byte[] buffer1 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + private readonly byte[] buffer1 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; [Fact] public void CreateShortStream() diff --git a/csharp/extractor/Semmle.Util.Tests/Semmle.Util.Tests.csproj b/csharp/extractor/Semmle.Util.Tests/Semmle.Util.Tests.csproj index a82997aea637..3acf1a1fa42b 100644 --- a/csharp/extractor/Semmle.Util.Tests/Semmle.Util.Tests.csproj +++ b/csharp/extractor/Semmle.Util.Tests/Semmle.Util.Tests.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 false win-x64;linux-x64;osx-x64 enable diff --git a/csharp/extractor/Semmle.Util.Tests/TextTest.cs b/csharp/extractor/Semmle.Util.Tests/TextTest.cs index 618f1f1fede5..a24d20c06c54 100644 --- a/csharp/extractor/Semmle.Util.Tests/TextTest.cs +++ b/csharp/extractor/Semmle.Util.Tests/TextTest.cs @@ -62,7 +62,7 @@ public void GetPortionTest() Assert.Equal("poke" + NL + "Until something bad broke," + NL + "And then" + NL, text.GetPortion(2, 19, 4, 8)); // An invalid but recoverable range (to test that a best effort is made rather than crashing). - Assert.Equal(NL + "Who couldn't leave software to fester -" + NL, text.GetPortion(0, Int32.MaxValue, 1, Int32.MaxValue)); + Assert.Equal(NL + "Who couldn't leave software to fester -" + NL, text.GetPortion(0, int.MaxValue, 1, int.MaxValue)); // Some quite definitely dodgy ranges (to test that exceptions are thrown). Assert.Throws(() => text.GetPortion(-1, 0, 0, 0)); @@ -70,7 +70,7 @@ public void GetPortionTest() Assert.Throws(() => text.GetPortion(0, 0, -1, 0)); Assert.Throws(() => text.GetPortion(0, 0, 0, -1)); Assert.Throws(() => text.GetPortion(3, 5, 2, 5)); - Assert.Throws(() => text.GetPortion(2, 5, Int32.MaxValue, 5)); + Assert.Throws(() => text.GetPortion(2, 5, int.MaxValue, 5)); } #endregion diff --git a/csharp/extractor/Semmle.Util/ActionMap.cs b/csharp/extractor/Semmle.Util/ActionMap.cs index c9fecbf9da69..afcda9bb4944 100644 --- a/csharp/extractor/Semmle.Util/ActionMap.cs +++ b/csharp/extractor/Semmle.Util/ActionMap.cs @@ -7,11 +7,11 @@ namespace Semmle.Util /// A dictionary which performs an action when items are added to the dictionary. /// The order in which keys and actions are added does not matter. /// - /// - /// - public class ActionMap where Key : notnull + /// + /// + public class ActionMap where TKey : notnull { - public void Add(Key key, Value value) + public void Add(TKey key, TValue value) { if (actions.TryGetValue(key, out var a)) @@ -19,7 +19,7 @@ public void Add(Key key, Value value) values[key] = value; } - public void OnAdd(Key key, Action action) + public void OnAdd(TKey key, Action action) { if (actions.TryGetValue(key, out var a)) { @@ -30,17 +30,16 @@ public void OnAdd(Key key, Action action) actions.Add(key, action); } - Value val; - if (values.TryGetValue(key, out val)) + if (values.TryGetValue(key, out var val)) { action(val); } } // Action associated with each key. - readonly Dictionary> actions = new Dictionary>(); + private readonly Dictionary> actions = new Dictionary>(); // Values associated with each key. - readonly Dictionary values = new Dictionary(); + private readonly Dictionary values = new Dictionary(); } } diff --git a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs index bbc8ab995b47..46759ad33477 100644 --- a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs +++ b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs @@ -40,7 +40,7 @@ public abstract class PathStrategy /// A canonical path. protected static string ConstructCanonicalPath(string path, IPathCache cache) { - DirectoryInfo parent = Directory.GetParent(path); + var parent = Directory.GetParent(path); return parent != null ? Path.Combine(cache.GetCanonicalPath(parent.FullName), Path.GetFileName(path)) : @@ -52,7 +52,7 @@ protected static string ConstructCanonicalPath(string path, IPathCache cache) /// Determine canonical paths using the Win32 function /// GetFinalPathNameByHandle(). Follows symlinks. /// - class GetFinalPathNameByHandleStrategy : PathStrategy + internal class GetFinalPathNameByHandleStrategy : PathStrategy { /// /// Call GetFinalPathNameByHandle() to get a canonical filename. @@ -70,48 +70,45 @@ class GetFinalPathNameByHandleStrategy : PathStrategy /// The canonical path. public override string GetCanonicalPath(string path, IPathCache cache) { - using (var hFile = Win32.CreateFile( // lgtm[cs/call-to-unmanaged-code] + using var hFile = Win32.CreateFile( // lgtm[cs/call-to-unmanaged-code] path, 0, Win32.FILE_SHARE_READ | Win32.FILE_SHARE_WRITE, IntPtr.Zero, Win32.OPEN_EXISTING, Win32.FILE_FLAG_BACKUP_SEMANTICS, - IntPtr.Zero)) - { + IntPtr.Zero); - if (hFile.IsInvalid) - { - // File/directory does not exist. - return ConstructCanonicalPath(path, cache); - } - else - { - StringBuilder outPath = new StringBuilder(Win32.MAX_PATH); - int length = Win32.GetFinalPathNameByHandle(hFile, outPath, outPath.Capacity, 0); // lgtm[cs/call-to-unmanaged-code] - if (length >= outPath.Capacity) - { - // Path length exceeded MAX_PATH. - // Possible if target has a long path. - outPath = new StringBuilder(length + 1); - length = Win32.GetFinalPathNameByHandle(hFile, outPath, outPath.Capacity, 0); // lgtm[cs/call-to-unmanaged-code] - } + if (hFile.IsInvalid) + { + // File/directory does not exist. + return ConstructCanonicalPath(path, cache); + } - const int PREAMBLE = 4; // outPath always starts \\?\ + var outPath = new StringBuilder(Win32.MAX_PATH); + var length = Win32.GetFinalPathNameByHandle(hFile, outPath, outPath.Capacity, 0); // lgtm[cs/call-to-unmanaged-code] - if (length <= PREAMBLE) - { - // Failed. GetFinalPathNameByHandle() failed somehow. - return ConstructCanonicalPath(path, cache); - } + if (length >= outPath.Capacity) + { + // Path length exceeded MAX_PATH. + // Possible if target has a long path. + outPath = new StringBuilder(length + 1); + length = Win32.GetFinalPathNameByHandle(hFile, outPath, outPath.Capacity, 0); // lgtm[cs/call-to-unmanaged-code] + } - string result = outPath.ToString(PREAMBLE, length - PREAMBLE); // Trim off leading \\?\ + const int preamble = 4; // outPath always starts \\?\ - return result.StartsWith("UNC") ? - @"\" + result.Substring(3) : - result; - } + if (length <= preamble) + { + // Failed. GetFinalPathNameByHandle() failed somehow. + return ConstructCanonicalPath(path, cache); } + + var result = outPath.ToString(preamble, length - preamble); // Trim off leading \\?\ + + return result.StartsWith("UNC") + ? @"\" + result.Substring(3) + : result; } } @@ -119,35 +116,34 @@ public override string GetCanonicalPath(string path, IPathCache cache) /// Determine file case by querying directory contents. /// Preserves symlinks. /// - class QueryDirectoryStrategy : PathStrategy + internal class QueryDirectoryStrategy : PathStrategy { public override string GetCanonicalPath(string path, IPathCache cache) { - DirectoryInfo parent = Directory.GetParent(path); + var parent = Directory.GetParent(path); - if (parent != null) - { - var name = Path.GetFileName(path); - var parentPath = cache.GetCanonicalPath(parent.FullName); - try - { - string[] entries = Directory.GetFileSystemEntries(parentPath, name); - return entries.Length == 1 ? - entries[0] : - Path.Combine(parentPath, name); - } - catch // lgtm[cs/catch-of-all-exceptions] - { - // IO error or security error querying directory. - return Path.Combine(parentPath, name); - } - } - else + if (parent == null) { // We are at a root of the filesystem. // Convert drive letters, UNC paths etc. to uppercase. // On UNIX, this should be "/" or "". return path.ToUpperInvariant(); + + } + + var name = Path.GetFileName(path); + var parentPath = cache.GetCanonicalPath(parent.FullName); + try + { + var entries = Directory.GetFileSystemEntries(parentPath, name); + return entries.Length == 1 + ? entries[0] + : Path.Combine(parentPath, name); + } + catch // lgtm[cs/catch-of-all-exceptions] + { + // IO error or security error querying directory. + return Path.Combine(parentPath, name); } } } @@ -156,14 +152,14 @@ public override string GetCanonicalPath(string path, IPathCache cache) /// Uses Mono.Unix.UnixPath to resolve symlinks. /// Not available on Windows. /// - class PosixSymlinkStrategy : PathStrategy + internal class PosixSymlinkStrategy : PathStrategy { public PosixSymlinkStrategy() { GetRealPath("."); // Test that it works } - string GetRealPath(string path) + private static string GetRealPath(string path) { path = UnixPath.GetFullPath(path); return UnixPath.GetCompleteRealPath(path); @@ -192,7 +188,7 @@ public class CanonicalPathCache : IPathCache /// /// The maximum number of items in the cache. /// - readonly int maxCapacity; + private readonly int maxCapacity; /// /// How to handle symlinks. @@ -206,7 +202,7 @@ public enum Symlinks /// /// Algorithm for computing the canonical path. /// - readonly PathStrategy pathStrategy; + private readonly PathStrategy pathStrategy; /// /// Create cache with a given capacity. @@ -216,12 +212,35 @@ public enum Symlinks public CanonicalPathCache(int maxCapacity, PathStrategy pathStrategy) { if (maxCapacity <= 0) - throw new ArgumentOutOfRangeException("Invalid cache size specified"); + throw new ArgumentOutOfRangeException(nameof(maxCapacity), maxCapacity, "Invalid cache size specified"); this.maxCapacity = maxCapacity; this.pathStrategy = pathStrategy; } + + /// + /// Create a CanonicalPathCache. + /// + /// + /// + /// Creates the appropriate PathStrategy object which encapsulates + /// the correct algorithm. Falls back to different implementations + /// depending on platform. + /// + /// + /// Size of the cache. + /// Policy for following symlinks. + /// A new CanonicalPathCache. + public static CanonicalPathCache Create(ILogger logger, int maxCapacity) + { + var preserveSymlinks = + Environment.GetEnvironmentVariable("CODEQL_PRESERVE_SYMLINKS") == "true" || + Environment.GetEnvironmentVariable("SEMMLE_PRESERVE_SYMLINKS") == "true"; + return Create(logger, maxCapacity, preserveSymlinks ? CanonicalPathCache.Symlinks.Preserve : CanonicalPathCache.Symlinks.Follow); + + } + /// /// Create a CanonicalPathCache. /// @@ -259,7 +278,7 @@ public static CanonicalPathCache Create(ILogger logger, int maxCapacity, Symlink pathStrategy = new QueryDirectoryStrategy(); break; default: - throw new ArgumentOutOfRangeException("Invalid symlinks option"); + throw new ArgumentOutOfRangeException(nameof(symlinks), symlinks, "Invalid symlinks option"); } return new CanonicalPathCache(maxCapacity, pathStrategy); @@ -268,12 +287,12 @@ public static CanonicalPathCache Create(ILogger logger, int maxCapacity, Symlink /// /// Map of path to canonical path. /// - readonly IDictionary cache = new Dictionary(); + private readonly IDictionary cache = new Dictionary(); /// /// Used to evict random cache items when the cache is full. /// - readonly Random random = new Random(); + private readonly Random random = new Random(); /// /// The current number of items in the cache. @@ -293,7 +312,7 @@ public int CacheSize /// /// The path. /// The canonical form of path. - void AddToCache(string path, string canonical) + private void AddToCache(string path, string canonical) { if (cache.Count >= maxCapacity) { diff --git a/csharp/extractor/Semmle.Util/CommandLineExtensions.cs b/csharp/extractor/Semmle.Util/CommandLineExtensions.cs index 01a581d612d9..884a00074183 100644 --- a/csharp/extractor/Semmle.Util/CommandLineExtensions.cs +++ b/csharp/extractor/Semmle.Util/CommandLineExtensions.cs @@ -19,9 +19,9 @@ public static bool WriteCommandLine(this IEnumerable commandLineArgument foreach (var arg in commandLineArguments.Where(arg => arg.StartsWith('@')).Select(arg => arg.Substring(1))) { string? line; - using (StreamReader file = new StreamReader(arg)) - while ((line = file.ReadLine()) != null) - textWriter.WriteLine(line); + using var file = new StreamReader(arg); + while ((line = file.ReadLine()) != null) + textWriter.WriteLine(line); found = true; } return found; diff --git a/csharp/extractor/Semmle.Util/CommandLineOptions.cs b/csharp/extractor/Semmle.Util/CommandLineOptions.cs index c85f68a8c6d5..8f148219ce4e 100644 --- a/csharp/extractor/Semmle.Util/CommandLineOptions.cs +++ b/csharp/extractor/Semmle.Util/CommandLineOptions.cs @@ -13,7 +13,7 @@ public interface ICommandLineOptions /// The name of the key. This is case sensitive. /// The supplied value. /// True if the option was handled, or false otherwise. - bool handleOption(string key, string value); + bool HandleOption(string key, string value); /// /// Handle a flag of the form "--cil" or "--nocil" @@ -21,52 +21,52 @@ public interface ICommandLineOptions /// The name of the flag. This is case sensitive. /// True if set, or false if prefixed by "--no" /// True if the flag was handled, or false otherwise. - bool handleFlag(string key, bool value); + bool HandleFlag(string key, bool value); /// /// Handle an argument, not prefixed by "--". /// /// The command line argument. /// True if the argument was handled, or false otherwise. - bool handleArgument(string argument); + bool HandleArgument(string argument); /// /// Process an unhandled option, or an unhandled argument. /// /// The argument. - void invalidArgument(string argument); + void InvalidArgument(string argument); } public static class OptionsExtensions { public static void ParseArguments(this ICommandLineOptions options, IReadOnlyList arguments) { - for (int i = 0; i < arguments.Count; ++i) + for (var i = 0; i < arguments.Count; ++i) { - string arg = arguments[i]; + var arg = arguments[i]; if (arg.StartsWith("--")) { var colon = arg.IndexOf(':'); - if (colon > 0 && options.handleOption(arg.Substring(2, colon - 2), arg.Substring(colon + 1))) + if (colon > 0 && options.HandleOption(arg.Substring(2, colon - 2), arg.Substring(colon + 1))) { } - else if (arg.StartsWith("--no") && options.handleFlag(arg.Substring(4), false)) + else if (arg.StartsWith("--no") && options.HandleFlag(arg.Substring(4), false)) { } - else if (options.handleFlag(arg.Substring(2), true)) + else if (options.HandleFlag(arg.Substring(2), true)) { } - else if (i + 1 < arguments.Count && options.handleOption(arg.Substring(2), arguments[i + 1])) + else if (i + 1 < arguments.Count && options.HandleOption(arg.Substring(2), arguments[i + 1])) { ++i; } else { - options.invalidArgument(arg); + options.InvalidArgument(arg); } } else { - if (!options.handleArgument(arg)) + if (!options.HandleArgument(arg)) { - options.invalidArgument(arg); + options.InvalidArgument(arg); } } } diff --git a/csharp/extractor/Semmle.Util/DictionaryExtensions.cs b/csharp/extractor/Semmle.Util/DictionaryExtensions.cs index bb0d732a17ff..95c5443585f3 100644 --- a/csharp/extractor/Semmle.Util/DictionaryExtensions.cs +++ b/csharp/extractor/Semmle.Util/DictionaryExtensions.cs @@ -9,7 +9,7 @@ public static class DictionaryExtensions /// dictionary. If a list does not already exist, a new list is /// created. /// - public static void AddAnother(this Dictionary> dict, T1 key, T2 element) where T1:notnull + public static void AddAnother(this Dictionary> dict, T1 key, T2 element) where T1 : notnull { if (!dict.TryGetValue(key, out var list)) { diff --git a/csharp/extractor/Semmle.Util/FileRenamer.cs b/csharp/extractor/Semmle.Util/FileRenamer.cs index ad5001f7e135..494e46856f83 100644 --- a/csharp/extractor/Semmle.Util/FileRenamer.cs +++ b/csharp/extractor/Semmle.Util/FileRenamer.cs @@ -10,8 +10,8 @@ namespace Semmle.Util /// public sealed class FileRenamer : IDisposable { - readonly string[] files; - const string suffix = ".codeqlhidden"; + private readonly string[] files; + private const string suffix = ".codeqlhidden"; public FileRenamer(IEnumerable oldFiles) { diff --git a/csharp/extractor/Semmle.Util/FileUtils.cs b/csharp/extractor/Semmle.Util/FileUtils.cs index 32e2ed88e601..89e35055fee8 100644 --- a/csharp/extractor/Semmle.Util/FileUtils.cs +++ b/csharp/extractor/Semmle.Util/FileUtils.cs @@ -86,15 +86,13 @@ public static void TryDelete(string file) /// public static string ComputeFileHash(string filePath) { - using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) - using (var shaAlg = new SHA256Managed()) - { - var sha = shaAlg.ComputeHash(fileStream); - var hex = new StringBuilder(sha.Length * 2); - foreach (var b in sha) - hex.AppendFormat("{0:x2}", b); - return hex.ToString(); - } + using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var shaAlg = new SHA256Managed(); + var sha = shaAlg.ComputeHash(fileStream); + var hex = new StringBuilder(sha.Length * 2); + foreach (var b in sha) + hex.AppendFormat("{0:x2}", b); + return hex.ToString(); } } } diff --git a/csharp/extractor/Semmle.Util/FuzzyDictionary.cs b/csharp/extractor/Semmle.Util/FuzzyDictionary.cs index 6f364904b06b..9f61fa1ffa9e 100644 --- a/csharp/extractor/Semmle.Util/FuzzyDictionary.cs +++ b/csharp/extractor/Semmle.Util/FuzzyDictionary.cs @@ -37,10 +37,10 @@ namespace Semmle.Util /// /// /// The value type. - public class FuzzyDictionary where T:class + public class FuzzyDictionary where T : class { // All data items indexed by the "base string" (stripped of numbers) - readonly Dictionary>> index = new Dictionary>>(); + private readonly Dictionary>> index = new Dictionary>>(); /// /// Stores a new KeyValuePair in the data structure. @@ -51,7 +51,7 @@ public void Add(string k, T v) { var kv = new KeyValuePair(k, v); - string root = StripDigits(k); + var root = StripDigits(k); index.AddAnother(root, kv); } @@ -61,7 +61,7 @@ public void Add(string k, T v) /// Vector 1 /// Vector 2 /// The Hamming Distance. - static int HammingDistance(IEnumerable v1, IEnumerable v2) where U: notnull + private static int HammingDistance(IEnumerable v1, IEnumerable v2) where TElement : notnull { return v1.Zip(v2, (x, y) => x.Equals(y) ? 0 : 1).Sum(); } @@ -74,7 +74,7 @@ static int HammingDistance(IEnumerable v1, IEnumerable v2) where U: not /// The best match, or null (default). public T? FindMatch(string query, out int distance) { - string root = StripDigits(query); + var root = StripDigits(query); if (!index.TryGetValue(root, out var list)) { distance = 0; @@ -92,16 +92,17 @@ static int HammingDistance(IEnumerable v1, IEnumerable v2) where U: not /// The distance function. /// The distance between the query and the stored string. /// The stored value. - static T? BestMatch(string query, IEnumerable> candidates, Func distance, out int bestDistance) + private static T? BestMatch(string query, IEnumerable> candidates, Func distance, out int bestDistance) { - T? bestMatch = default(T); + var bestMatch = default(T); bestDistance = 0; - bool first = true; + var first = true; foreach (var candidate in candidates) { - int d = distance(query, candidate.Key); - if (d == 0) return candidate.Value; + var d = distance(query, candidate.Key); + if (d == 0) + return candidate.Value; if (first || d < bestDistance) { @@ -119,10 +120,10 @@ static int HammingDistance(IEnumerable v1, IEnumerable v2) where U: not /// /// The input string. /// String with digits removed. - static string StripDigits(string input) + private static string StripDigits(string input) { - StringBuilder result = new StringBuilder(); - foreach (char c in input.Where(c => !char.IsDigit(c))) + var result = new StringBuilder(); + foreach (var c in input.Where(c => !char.IsDigit(c))) result.Append(c); return result.ToString(); } @@ -132,11 +133,11 @@ static string StripDigits(string input) /// /// The string to enumerate. /// The sequence of integers. - public static IEnumerable ExtractIntegers(string input) + private static IEnumerable ExtractIntegers(string input) { - bool inNumber = false; - int value = 0; - foreach (char c in input) + var inNumber = false; + var value = 0; + foreach (var c in input) { if (char.IsDigit(c)) { diff --git a/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs b/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs index 7665dedfa70e..c97a4ffdb3d9 100644 --- a/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs +++ b/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs @@ -33,30 +33,29 @@ public static class IEnumerableExtensions /// public static IEnumerable Interleave(this IEnumerable first, IEnumerable second) { - using (IEnumerator enumerator1 = first.GetEnumerator(), enumerator2 = second.GetEnumerator()) + using var enumerator1 = first.GetEnumerator(); + using var enumerator2 = second.GetEnumerator(); + bool moveNext1; + while ((moveNext1 = enumerator1.MoveNext()) && enumerator2.MoveNext()) { - bool moveNext1; - while ((moveNext1 = enumerator1.MoveNext()) && enumerator2.MoveNext()) - { - yield return enumerator1.Current; - yield return enumerator2.Current; - } + yield return enumerator1.Current; + yield return enumerator2.Current; + } - if (moveNext1) + if (moveNext1) + { + // `first` has more elements than `second` + yield return enumerator1.Current; + while (enumerator1.MoveNext()) { - // `first` has more elements than `second` yield return enumerator1.Current; - while (enumerator1.MoveNext()) - { - yield return enumerator1.Current; - } } + } - while (enumerator2.MoveNext()) - { - // `second` has more elements than `first` - yield return enumerator2.Current; - } + while (enumerator2.MoveNext()) + { + // `second` has more elements than `first` + yield return enumerator2.Current; } } @@ -64,9 +63,10 @@ public static IEnumerable Interleave(this IEnumerable first, IEnumerabl /// Enumerates a possibly null enumerable. /// If the enumerable is null, the list is empty. /// - public static IEnumerable EnumerateNull(this IEnumerable items) + public static IEnumerable EnumerateNull(this IEnumerable? items) { - if (items == null) yield break; + if (items == null) + yield break; foreach (var item in items) yield return item; } @@ -93,9 +93,9 @@ public static void Enumerate(this IEnumerable items) /// The type of the item. /// The list of items to hash. /// The hash code. - public static int SequenceHash(this IEnumerable items) where T: notnull + public static int SequenceHash(this IEnumerable items) where T : notnull { - int h = 0; + var h = 0; foreach (var i in items) h = h * 7 + i.GetHashCode(); return h; diff --git a/csharp/extractor/Semmle.Util/LineCounter.cs b/csharp/extractor/Semmle.Util/LineCounter.cs index f4f6758a2500..d3add0c1b9df 100644 --- a/csharp/extractor/Semmle.Util/LineCounter.cs +++ b/csharp/extractor/Semmle.Util/LineCounter.cs @@ -33,8 +33,10 @@ public sealed class LineCounts public override bool Equals(object? other) { - var rhs = other as LineCounts; - return rhs != null && Total == rhs.Total && Code == rhs.Code && Comment == rhs.Comment; + return other is LineCounts rhs && + Total == rhs.Total && + Code == rhs.Code && + Comment == rhs.Comment; } public override int GetHashCode() @@ -194,7 +196,8 @@ private static void ReadEOLComment(string input, Context context) // If we reached the end of a line (as opposed to reaching the end of the text), // put the '\n' back so that it can be handled by the normal newline processing // code. - if (IsNewLine(c)) --context.CurIndex; + if (IsNewLine(c)) + --context.CurIndex; } /// @@ -240,11 +243,13 @@ private static void ReadRestOfChar(string input, Context context) private static void ReadRestOfString(string input, Context context) { char? cur = '\0'; - int numSlashes = 0; + var numSlashes = 0; while (cur != null && ((cur = GetNext(input, context)) != '"' || (numSlashes % 2 != 0))) { - if (cur == '\\') ++numSlashes; - else numSlashes = 0; + if (cur == '\\') + ++numSlashes; + else + numSlashes = 0; } } diff --git a/csharp/extractor/Semmle.Util/Logger.cs b/csharp/extractor/Semmle.Util/Logger.cs index cd353bd4f0fd..7e0bc34cc2f4 100644 --- a/csharp/extractor/Semmle.Util/Logger.cs +++ b/csharp/extractor/Semmle.Util/Logger.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Security.AccessControl; namespace Semmle.Util.Logging { @@ -56,10 +55,10 @@ public static void Log(this ILogger logger, Severity s, string text, params obje /// A logger that outputs to a csharp.log /// file. /// - public class FileLogger : ILogger + public sealed class FileLogger : ILogger { - readonly StreamWriter writer; - readonly Verbosity verbosity; + private readonly StreamWriter writer; + private readonly Verbosity verbosity; public FileLogger(Verbosity verbosity, string outputFile) { @@ -67,11 +66,11 @@ public FileLogger(Verbosity verbosity, string outputFile) try { - string? dir = Path.GetDirectoryName(outputFile); + var dir = Path.GetDirectoryName(outputFile); if (!string.IsNullOrEmpty(dir) && !System.IO.Directory.Exists(dir)) Directory.CreateDirectory(dir); - writer = new PidStreamWriter(new FileStream(outputFile, FileMode.Append, FileAccess.Write, - FileShare.ReadWrite, 8192)); + writer = new PidStreamWriter( + new FileStream(outputFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 8192)); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { @@ -86,7 +85,7 @@ public void Dispose() writer.Dispose(); } - static string GetSeverityPrefix(Severity s) + private static string GetSeverityPrefix(Severity s) { return "[" + s.ToString().ToUpper() + "] "; } @@ -101,9 +100,9 @@ public void Log(Severity s, string text) /// /// A logger that outputs to stdout/stderr. /// - public class ConsoleLogger : ILogger + public sealed class ConsoleLogger : ILogger { - readonly Verbosity verbosity; + private readonly Verbosity verbosity; public ConsoleLogger(Verbosity verbosity) { @@ -112,12 +111,12 @@ public ConsoleLogger(Verbosity verbosity) public void Dispose() { } - static TextWriter GetConsole(Severity s) + private static TextWriter GetConsole(Severity s) { return s == Severity.Error ? Console.Error : Console.Out; } - static string GetSeverityPrefix(Severity s) + private static string GetSeverityPrefix(Severity s) { switch (s) { @@ -130,7 +129,7 @@ static string GetSeverityPrefix(Severity s) case Severity.Error: return "Error: "; default: - throw new ArgumentOutOfRangeException("s"); + throw new ArgumentOutOfRangeException(nameof(s)); } } @@ -144,10 +143,10 @@ public void Log(Severity s, string text) /// /// A combined logger. /// - public class CombinedLogger : ILogger + public sealed class CombinedLogger : ILogger { - readonly ILogger logger1; - readonly ILogger logger2; + private readonly ILogger logger1; + private readonly ILogger logger2; public CombinedLogger(ILogger logger1, ILogger logger2) { @@ -168,7 +167,7 @@ public void Log(Severity s, string text) } } - static class VerbosityExtensions + internal static class VerbosityExtensions { /// /// Whether a message with the given severity must be included @@ -189,7 +188,7 @@ public static bool Includes(this Verbosity v, Severity s) case Severity.Error: return v >= Verbosity.Error; default: - throw new ArgumentOutOfRangeException("s"); + throw new ArgumentOutOfRangeException(nameof(s)); } } } diff --git a/csharp/extractor/Semmle.Util/LoggerUtils.cs b/csharp/extractor/Semmle.Util/LoggerUtils.cs index d94ff6f189f6..1ddbdf051e80 100644 --- a/csharp/extractor/Semmle.Util/LoggerUtils.cs +++ b/csharp/extractor/Semmle.Util/LoggerUtils.cs @@ -29,11 +29,11 @@ public override void WriteLine(string? value) } } - public override void WriteLine(string? value, object?[] args) + public override void WriteLine(string? format, params object?[] args) { - WriteLine(value is null ? value : String.Format(value, args)); + WriteLine(format is null ? format : string.Format(format, args)); } - readonly object mutex = new object(); + private readonly object mutex = new object(); } } diff --git a/csharp/extractor/Semmle.Util/ProcessStartInfoExtensions.cs b/csharp/extractor/Semmle.Util/ProcessStartInfoExtensions.cs index 5252372a58b2..998b6909e10a 100644 --- a/csharp/extractor/Semmle.Util/ProcessStartInfoExtensions.cs +++ b/csharp/extractor/Semmle.Util/ProcessStartInfoExtensions.cs @@ -12,18 +12,17 @@ public static class ProcessStartInfoExtensions public static int ReadOutput(this ProcessStartInfo pi, out IList stdout) { stdout = new List(); - using (var process = Process.Start(pi)) + using var process = Process.Start(pi); + string? s; + do { - string? s; - do - { - s = process.StandardOutput.ReadLine(); - if (s != null) stdout.Add(s); - } - while (s != null); - process.WaitForExit(); - return process.ExitCode; + s = process.StandardOutput.ReadLine(); + if (s != null) + stdout.Add(s); } + while (s != null); + process.WaitForExit(); + return process.ExitCode; } } } diff --git a/csharp/extractor/Semmle.Util/Semmle.Util.csproj b/csharp/extractor/Semmle.Util/Semmle.Util.csproj index 39f0c7cdedb5..fdcd0672395e 100644 --- a/csharp/extractor/Semmle.Util/Semmle.Util.csproj +++ b/csharp/extractor/Semmle.Util/Semmle.Util.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 Semmle.Util Semmle.Util false diff --git a/csharp/extractor/Semmle.Util/StringBuilder.cs b/csharp/extractor/Semmle.Util/StringBuilder.cs index 85f9155f290e..468ce6a02206 100644 --- a/csharp/extractor/Semmle.Util/StringBuilder.cs +++ b/csharp/extractor/Semmle.Util/StringBuilder.cs @@ -30,10 +30,14 @@ public static StringBuilder AppendList(this StringBuilder builder, string sep /// The original StringBuilder (fluent interface). public static StringBuilder BuildList(this StringBuilder builder, string separator, IEnumerable items, Action action) { - bool first = true; + var first = true; foreach (var item in items) { - if (first) first = false; else builder.Append(separator); + if (first) + first = false; + else + builder.Append(separator); + action(item, builder); } return builder; diff --git a/csharp/extractor/Semmle.Util/TemporaryDirectory.cs b/csharp/extractor/Semmle.Util/TemporaryDirectory.cs index a155372c9d89..ac5653afc781 100644 --- a/csharp/extractor/Semmle.Util/TemporaryDirectory.cs +++ b/csharp/extractor/Semmle.Util/TemporaryDirectory.cs @@ -1,8 +1,5 @@ īģŋusing System; using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; namespace Semmle.Util { diff --git a/csharp/extractor/Semmle.Util/Text.cs b/csharp/extractor/Semmle.Util/Text.cs index ede910a0bdf1..38619fc0164d 100644 --- a/csharp/extractor/Semmle.Util/Text.cs +++ b/csharp/extractor/Semmle.Util/Text.cs @@ -41,14 +41,12 @@ public Text(string[] lines) /// The whole text. public string GetAll() { - using (var sw = new StringWriter()) + using var sw = new StringWriter(); + foreach (var s in lines) { - foreach (string s in lines) - { - sw.WriteLine(s); - } - return sw.ToString(); + sw.WriteLine(s); } + return sw.ToString(); } /// @@ -70,38 +68,36 @@ public string GetPortion(int startRow, int startColumn, int endRow, int endColum ); } - using (var sw = new StringWriter()) - { - string line; + using var sw = new StringWriter(); + string line; - for (int i = startRow; i <= endRow; ++i) + for (var i = startRow; i <= endRow; ++i) + { + if (i == startRow && i == endRow) { - if (i == startRow && i == endRow) - { - // This is a single-line range, so take the bit between "startColumn" and "endColumn". - line = startColumn <= lines[i].Length ? lines[i].Substring(startColumn, endColumn - startColumn) : ""; - } - else if (i == startRow) - { - // This is the first line of a multi-line range, so take the bit from "startColumn" onwards. - line = startColumn <= lines[i].Length ? lines[i].Substring(startColumn) : ""; - } - else if (i == endRow) - { - // This is the last line of a multi-line range, so take the bit up to "endColumn". - line = endColumn <= lines[i].Length ? lines[i].Substring(0, endColumn) : lines[i]; - } - else - { - // This is a line in the middle of a multi-line range, so take the whole line. - line = lines[i]; - } - - sw.WriteLine(line); + // This is a single-line range, so take the bit between "startColumn" and "endColumn". + line = startColumn <= lines[i].Length ? lines[i].Substring(startColumn, endColumn - startColumn) : ""; + } + else if (i == startRow) + { + // This is the first line of a multi-line range, so take the bit from "startColumn" onwards. + line = startColumn <= lines[i].Length ? lines[i].Substring(startColumn) : ""; + } + else if (i == endRow) + { + // This is the last line of a multi-line range, so take the bit up to "endColumn". + line = endColumn <= lines[i].Length ? lines[i].Substring(0, endColumn) : lines[i]; + } + else + { + // This is a line in the middle of a multi-line range, so take the whole line. + line = lines[i]; } - return sw.ToString(); + sw.WriteLine(line); } + + return sw.ToString(); } #endregion diff --git a/csharp/extractor/Semmle.Util/Worklist.cs b/csharp/extractor/Semmle.Util/Worklist.cs index f02a09a06f1b..0a71dbd4381e 100644 --- a/csharp/extractor/Semmle.Util/Worklist.cs +++ b/csharp/extractor/Semmle.Util/Worklist.cs @@ -48,11 +48,10 @@ public bool Add(T element) /// public LinkedList GetUnprocessedElements() { - LinkedList result = internalList; + var result = internalList; internalList = new LinkedList(); hasNewElements = false; return result; } } } - diff --git a/csharp/ql/examples/qlpack.yml b/csharp/ql/examples/qlpack.yml new file mode 100644 index 000000000000..f7b585db963e --- /dev/null +++ b/csharp/ql/examples/qlpack.yml @@ -0,0 +1,3 @@ +name: codeql-csharp-examples +version: 0.0.0 +libraryPathDependencies: codeql-csharp diff --git a/csharp/ql/src/Bad Practices/Comments/CommentedOutCode.qhelp b/csharp/ql/src/Bad Practices/Comments/CommentedOutCode.qhelp index 4ce0ee029b62..0997169f4dfc 100644 --- a/csharp/ql/src/Bad Practices/Comments/CommentedOutCode.qhelp +++ b/csharp/ql/src/Bad Practices/Comments/CommentedOutCode.qhelp @@ -3,5 +3,5 @@ "qhelp.dtd"> - + diff --git a/csharp/ql/src/Bad Practices/Comments/CommentedOutCodeQuery.qhelp b/csharp/ql/src/Bad Practices/Comments/CommentedOutCodeQuery.qhelp new file mode 100644 index 000000000000..eb40ecdb7088 --- /dev/null +++ b/csharp/ql/src/Bad Practices/Comments/CommentedOutCodeQuery.qhelp @@ -0,0 +1,25 @@ + + + + +

    +Commented-out code is distracting and confusing for developers who read the surrounding code, +and its significance is often unclear. It will not get compiled or tested when the code around +it changes, so it's likely to break over time. For these reasons, commented-out code should be +avoided. +

    + +
    + + + +

    +Remove or reinstate the commented-out code. If you want to include a snippet of example code +in a comment, consider enclosing it in quotes or marking it up as appropriate for the source +language. +

    + +
    +
    diff --git a/csharp/ql/src/Concurrency/Concurrency.qll b/csharp/ql/src/Concurrency/Concurrency.qll index d37b10e0301b..aacd83083061 100644 --- a/csharp/ql/src/Concurrency/Concurrency.qll +++ b/csharp/ql/src/Concurrency/Concurrency.qll @@ -1,7 +1,8 @@ -// Various utilities for writing concurrency queries. +/** Classes for concurrency queries. */ + import csharp -class WaitCall extends MethodCall { +private class WaitCall extends MethodCall { WaitCall() { getTarget().hasName("Wait") and getTarget().getDeclaringType().hasQualifiedName("System.Threading.Monitor") @@ -10,22 +11,24 @@ class WaitCall extends MethodCall { Expr getExpr() { result = getArgument(0) } } +/** An expression statement containing a `Wait` call. */ class WaitStmt extends ExprStmt { WaitStmt() { getExpr() instanceof WaitCall } + /** Gets the expression that this wait call is waiting on. */ Expr getLock() { result = getExpr().(WaitCall).getExpr() } - // If we are waiting on a variable + /** Gets the variable that this wait call is waiting on, if any. */ Variable getWaitVariable() { result.getAnAccess() = getLock() } - // If we are waiting on 'this' + /** Holds if this wait call waits on `this`. */ predicate isWaitThis() { getLock() instanceof ThisAccess } - // If we are waiting on a typeof() + /** Gets the type that this wait call waits on, if any. */ Type getWaitTypeObject() { result = getLock().(TypeofExpr).getTypeAccess().getTarget() } } -class SynchronizedMethodAttribute extends Attribute { +private class SynchronizedMethodAttribute extends Attribute { SynchronizedMethodAttribute() { getType().hasQualifiedName("System.Runtime.CompilerServices.MethodImplAttribute") and exists(MemberConstantAccess a, MemberConstant mc | @@ -37,22 +40,29 @@ class SynchronizedMethodAttribute extends Attribute { } } -// A method with attribute [MethodImpl(MethodImplOptions.Synchronized)] -class SynchronizedMethod extends Method { +/** A method with attribute `[MethodImpl(MethodImplOptions.Synchronized)]`. */ +private class SynchronizedMethod extends Method { SynchronizedMethod() { getAnAttribute() instanceof SynchronizedMethodAttribute } + /** Holds if this method locks `this`. */ predicate isLockThis() { not isStatic() } + /** Gets the type that is locked by this method, if any. */ Type getLockTypeObject() { isStatic() and result = getDeclaringType() } } +/** A block that is locked by a `lock` statement. */ abstract class LockedBlock extends BlockStmt { + /** Holds if the `lock` statement locks `this`. */ abstract predicate isLockThis(); + /** Gets the lock variable of the `lock` statement, if any. */ abstract Variable getLockVariable(); + /** Gets the locked type of the `lock` statement, if any. */ abstract Type getLockTypeObject(); + /** Gets a statement in the scope of this locked block. */ Stmt getALockedStmt() { // Do this instead of getParent+, because we don't want to escape // delegates and lambdas @@ -62,7 +72,7 @@ abstract class LockedBlock extends BlockStmt { } } -class LockStmtBlock extends LockedBlock { +private class LockStmtBlock extends LockedBlock { LockStmtBlock() { exists(LockStmt s | this = s.getBlock()) } override predicate isLockThis() { exists(LockStmt s | this = s.getBlock() and s.isLockThis()) } @@ -76,9 +86,7 @@ class LockStmtBlock extends LockedBlock { } } -/** - * A call which may take a lock using one of the standard library classes. - */ +/** A call that may take a lock using one of the standard library methods. */ class LockingCall extends MethodCall { LockingCall() { this.getTarget() = @@ -91,7 +99,7 @@ class LockingCall extends MethodCall { } } -class SynchronizedMethodBlock extends LockedBlock { +private class SynchronizedMethodBlock extends LockedBlock { SynchronizedMethodBlock() { exists(SynchronizedMethod m | this = m.getStatementBody()) } override predicate isLockThis() { diff --git a/csharp/ql/src/Documentation/Documentation.qll b/csharp/ql/src/Documentation/Documentation.qll index ed69cf150eac..041a385e7930 100644 --- a/csharp/ql/src/Documentation/Documentation.qll +++ b/csharp/ql/src/Documentation/Documentation.qll @@ -1,6 +1,8 @@ +/** Classes representing documentation comments. */ + import csharp -class SourceDeclaration extends Declaration { +private class SourceDeclaration extends Declaration { SourceDeclaration() { this.isSourceDeclaration() } } @@ -59,10 +61,13 @@ predicate isDocumentationNeeded(Modifiable decl) { class ReturnsXmlComment extends XmlComment { ReturnsXmlComment() { getOpenTag(_) = "returns" } + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("returns", offset) } + /** Holds if the element in this comment is an opening tag at offset `offset`. */ predicate isOpenTag(int offset) { "returns" = getOpenTag(offset) } + /** Holds if the element in this comment is an empty tag at offset `offset`. */ predicate isEmptyTag(int offset) { "returns" = getEmptyTag(offset) } } @@ -70,8 +75,10 @@ class ReturnsXmlComment extends XmlComment { class ExceptionXmlComment extends XmlComment { ExceptionXmlComment() { getOpenTag(_) = "exception" } + /** Gets a `cref` attribute at offset `offset`, if any. */ string getCref(int offset) { result = getAttribute("exception", "cref", offset) } + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("exception", offset) } } @@ -79,8 +86,10 @@ class ExceptionXmlComment extends XmlComment { class ParamXmlComment extends XmlComment { ParamXmlComment() { getOpenTag(_) = "param" } + /** Gets the name of this parameter at offset `offset`. */ string getName(int offset) { getAttribute("param", "name", offset) = result } + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("param", offset) } } @@ -88,8 +97,10 @@ class ParamXmlComment extends XmlComment { class TypeparamXmlComment extends XmlComment { TypeparamXmlComment() { getOpenTag(_) = "typeparam" } + /** Gets the `name` attribute of this element at offset `offset`. */ string getName(int offset) { getAttribute("typeparam", "name", offset) = result } + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("typeparam", offset) } } @@ -97,10 +108,13 @@ class TypeparamXmlComment extends XmlComment { class SummaryXmlComment extends XmlComment { SummaryXmlComment() { getOpenTag(_) = "summary" } + /** Holds if the element in this comment has a body at offset `offset`. */ predicate hasBody(int offset) { hasBody("summary", offset) } + /** Holds if the element in this comment has an open tag at offset `offset`. */ predicate isOpenTag(int offset) { "summary" = getOpenTag(offset) } + /** Holds if the element in this comment is empty at offset `offset`. */ predicate isEmptyTag(int offset) { "summary" = getEmptyTag(offset) } } diff --git a/csharp/ql/src/Language Abuse/ForeachCapture.ql b/csharp/ql/src/Language Abuse/ForeachCapture.ql index d4a97cdbc43a..803081dcb735 100644 --- a/csharp/ql/src/Language Abuse/ForeachCapture.ql +++ b/csharp/ql/src/Language Abuse/ForeachCapture.ql @@ -75,15 +75,13 @@ Element getAssignmentTarget(Expr e) { Element getCollectionAssignmentTarget(Expr e) { // Store into collection via method exists( - MethodCall mc, Method m, IEnumerableFlow ief, CallableFlowSourceArg source, - CallableFlowSinkQualifier sink, int i + MethodCall mc, Method m, LibraryTypeDataFlow ltdf, CallableFlowSource source, + CallableFlowSink sink | - mc.getQualifier() = result.(Variable).getAnAccess() and - ief = mc.getQualifier().getType().getSourceDeclaration() and m = mc.getTarget().getSourceDeclaration() and - ief.callableFlow(source, sink, m, _) and - source.getArgumentIndex() = i and - e = mc.getArgument(i) + ltdf.callableFlow(source, AccessPath::empty(), sink, AccessPath::element(), m, _) and + e = source.getSource(mc) and + result.(Variable).getAnAccess() = sink.getSink(mc) ) or // Array initializer diff --git a/csharp/ql/src/Language Abuse/UselessUpcast.ql b/csharp/ql/src/Language Abuse/UselessUpcast.ql index 334fd0bc1e80..3e42e0599905 100644 --- a/csharp/ql/src/Language Abuse/UselessUpcast.ql +++ b/csharp/ql/src/Language Abuse/UselessUpcast.ql @@ -31,14 +31,14 @@ class StaticCall extends Call { } /** Holds `t` has instance callable `c` as a member, with name `name`. */ -pragma[noinline] +pragma[nomagic] predicate hasInstanceCallable(ValueOrRefType t, InstanceCallable c, string name) { t.hasMember(c) and name = c.getName() } /** Holds if extension method `m` is a method on `t` with name `name`. */ -pragma[noinline] +pragma[nomagic] predicate hasExtensionMethod(ValueOrRefType t, ExtensionMethod m, string name) { t.isImplicitlyConvertibleTo(m.getExtendedType()) and name = m.getName() diff --git a/csharp/ql/src/Likely Bugs/SelfAssignment.ql b/csharp/ql/src/Likely Bugs/SelfAssignment.ql index 0f0e23e8629a..01c5d904cd40 100644 --- a/csharp/ql/src/Likely Bugs/SelfAssignment.ql +++ b/csharp/ql/src/Likely Bugs/SelfAssignment.ql @@ -20,7 +20,9 @@ class StructuralComparisonConfig extends StructuralComparisonConfiguration { exists(AssignExpr ae | // Member initializers are never self-assignments, in particular // not initializers such as `new C { F = F };` - not ae instanceof MemberInitializer + not ae instanceof MemberInitializer and + // Enum field initializers are never self assignments. `enum E { A = 42 }` + not ae.getParent().(Field).getDeclaringType() instanceof Enum | ae.getLValue() = x and ae.getRValue() = y diff --git a/csharp/ql/src/Linq/Helpers.qll b/csharp/ql/src/Linq/Helpers.qll index 09643be429a9..e1b1beb1222d 100644 --- a/csharp/ql/src/Linq/Helpers.qll +++ b/csharp/ql/src/Linq/Helpers.qll @@ -6,18 +6,32 @@ import csharp //#################### PREDICATES #################### -Stmt firstStmt(ForeachStmt fes) { +private Stmt firstStmt(ForeachStmt fes) { if fes.getBody() instanceof BlockStmt then result = fes.getBody().(BlockStmt).getStmt(0) else result = fes.getBody() } +private int numStmts(ForeachStmt fes) { + if fes.getBody() instanceof BlockStmt + then result = count(fes.getBody().(BlockStmt).getAStmt()) + else result = 1 +} + +/** Holds if the type's qualified name is "System.Linq.Enumerable" */ predicate isEnumerableType(ValueOrRefType t) { t.hasQualifiedName("System.Linq.Enumerable") } +/** Holds if the type's qualified name starts with "System.Collections.Generic.IEnumerable" */ predicate isIEnumerableType(ValueOrRefType t) { t.getQualifiedName().matches("System.Collections.Generic.IEnumerable%") } +/** + * Holds if `foreach` statement `fes` could be converted to a `.All()` call. + * That is, the `ForeachStmt` contains a single `if` with a condition that + * accesses the loop variable and with a body that assigns `false` to a variable + * and `break`s out of the `foreach`. + */ predicate missedAllOpportunity(ForeachStmt fes) { exists(IfStmt is | // The loop contains an if statement with no else case, and nothing else. @@ -36,6 +50,12 @@ predicate missedAllOpportunity(ForeachStmt fes) { ) } +/** + * Holds if `foreach` statement `fes` could be converted to a `.Cast()` call. + * That is, the loop variable is accessed only in the first statement of the + * block, and the access is a cast. The first statement needs to be a + * `LocalVariableDeclStmt`. + */ predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) { s = firstStmt(fes) and forex(VariableAccess va | va = fes.getVariable().getAnAccess() | @@ -47,6 +67,12 @@ predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) { ) } +/** + * Holds if `foreach` statement `fes` could be converted to an `.OfType()` call. + * That is, the loop variable is accessed only in the first statement of the + * block, and the access is a cast with the `as` operator. The first statement + * needs to be a `LocalVariableDeclStmt`. + */ predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) { s = firstStmt(fes) and forex(VariableAccess va | va = fes.getVariable().getAnAccess() | @@ -58,6 +84,12 @@ predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) { ) } +/** + * Holds if `foreach` statement `fes` could be converted to a `.Select()` call. + * That is, the loop variable is accessed only in the first statement of the + * block, and the access is not a cast. The first statement needs to be a + * `LocalVariableDeclStmt`. + */ predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) { s = firstStmt(fes) and forex(VariableAccess va | va = fes.getVariable().getAnAccess() | @@ -66,6 +98,12 @@ predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) { not s.getAVariableDeclExpr().getInitializer() instanceof Cast } +/** + * Holds if `foreach` statement `fes` could be converted to a `.Where()` call. + * That is, first statement of the loop is an `if`, which accesses the loop + * variable, and the body of the `if` is either a `continue` or there's nothing + * else in the loop than the `if`. + */ predicate missedWhereOpportunity(ForeachStmt fes, IfStmt is) { // The very first thing the foreach loop does is test its iteration variable. is = firstStmt(fes) and @@ -82,12 +120,6 @@ predicate missedWhereOpportunity(ForeachStmt fes, IfStmt is) { ) } -int numStmts(ForeachStmt fes) { - if fes.getBody() instanceof BlockStmt - then result = count(fes.getBody().(BlockStmt).getAStmt()) - else result = 1 -} - //#################### CLASSES #################### /** A LINQ Any(...) call. */ class AnyCall extends MethodCall { @@ -100,6 +132,17 @@ class AnyCall extends MethodCall { } } +/** A LINQ Count(...) call. */ +class CountCall extends MethodCall { + CountCall() { + exists(Method m | + m = getTarget() and + isEnumerableType(m.getDeclaringType()) and + m.hasName("Count") + ) + } +} + /** A variable of type IEnumerable<T>, for some T. */ class IEnumerableSequence extends Variable { IEnumerableSequence() { isIEnumerableType(getType()) } diff --git a/csharp/ql/src/Metrics/Dependencies/ExternalDependencies.ql b/csharp/ql/src/Metrics/Dependencies/ExternalDependencies.ql index 1c2e9aaa7210..6139262afd32 100644 --- a/csharp/ql/src/Metrics/Dependencies/ExternalDependencies.ql +++ b/csharp/ql/src/Metrics/Dependencies/ExternalDependencies.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name External dependencies * @description Count the number of dependencies a C# source file has on assembly files. * @kind treemap diff --git a/csharp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql b/csharp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql index e9cda4255920..59d2dbdf5866 100644 --- a/csharp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql +++ b/csharp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name External dependency source links * @kind source-link * @metricType externalDependency diff --git a/csharp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp b/csharp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp new file mode 100644 index 000000000000..217b6d175295 --- /dev/null +++ b/csharp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp @@ -0,0 +1,12 @@ + + + +

    +This metric counts the number of lines of commented-out code in each file. Large amounts of +commented-out code often indicate poorly maintained code. +

    + +
    +
    diff --git a/csharp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp b/csharp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp new file mode 100644 index 000000000000..462eb7795f91 --- /dev/null +++ b/csharp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp @@ -0,0 +1,12 @@ + + + + +
  • Mark Needham: The danger of commenting out code.
  • +
  • Los Techies: Commented Code == Technical Debt.
  • +
  • High Integrity C++ Coding Standard: 2.3.2 Do not comment out code.
  • + + + diff --git a/csharp/ql/src/Metrics/Files/DuplicationProblems.qhelp b/csharp/ql/src/Metrics/Files/DuplicationProblems.qhelp new file mode 100644 index 000000000000..54397da6c994 --- /dev/null +++ b/csharp/ql/src/Metrics/Files/DuplicationProblems.qhelp @@ -0,0 +1,16 @@ + + + +

    +Duplicated code increases overall code size, making the code base +harder to maintain and harder to understand. It also becomes harder to fix bugs, +since a programmer applying a fix to one copy has to always remember to update +other copies accordingly. Finally, code duplication is generally an indication of +a poorly designed or hastily written code base, which typically suffers from other +problems as well. +

    + +
    +
    diff --git a/csharp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql b/csharp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql index 3237035f897c..bf3da5e1f80f 100644 --- a/csharp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql +++ b/csharp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Duplicated lines in files * @description The number of lines in a file, including code, comment and whitespace lines, * which are duplicated in at least one other place. diff --git a/csharp/ql/src/Performance/UseTryGetValue.ql b/csharp/ql/src/Performance/UseTryGetValue.ql index dcc292ecce85..276901cf61a2 100644 --- a/csharp/ql/src/Performance/UseTryGetValue.ql +++ b/csharp/ql/src/Performance/UseTryGetValue.ql @@ -6,7 +6,7 @@ * @problem.severity recommendation * @precision high * @id cs/inefficient-containskey - * @tag maintainability efficiency + * @tags maintainability efficiency */ import csharp diff --git a/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql b/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql index 7b875faae67c..24de3f110757 100644 --- a/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql +++ b/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql @@ -27,6 +27,7 @@ GuardedExpr checkedWrite(Field f, Variable v, IfStmt check) { * The result is an unsafe write to the field `f`, where * there is no check performed within the (calling) scope of the method. */ +pragma[nomagic] Expr uncheckedWrite(Callable callable, Field f) { result = f.getAnAssignedValue() and result.getEnclosingCallable() = callable and diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp b/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp index fec61771992f..e838d8c56a4d 100644 --- a/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp +++ b/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp @@ -41,7 +41,7 @@ sent back to the user, giving them access to all the system's passwords.

  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp b/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp index 9ee9ce6d0a16..bee5d8198368 100644 --- a/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp +++ b/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp @@ -71,7 +71,7 @@ Snyk:
  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql b/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql index bcb3b7c7a774..abf3f0a55ad9 100644 --- a/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql +++ b/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql @@ -20,7 +20,7 @@ import semmle.code.csharp.frameworks.system.Web */ predicate hasWebConfigXFrameOptions(WebConfigXML webConfig) { // Looking for an entry in `webConfig` that looks like this: - // ``` + // ```xml // // // diff --git a/csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp b/csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp index 7241f6f97c44..cf50b4ee1742 100644 --- a/csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp +++ b/csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp @@ -42,7 +42,7 @@ variables in an XsltArgumentList. -
  • OWASP: Testing for XPath Injection.
  • +
  • OWASP: Testing for XPath Injection.
  • OWASP: XPath Injection.
  • MSDN: User Defined Functions and Variables.
  • diff --git a/csharp/ql/src/Security Features/Encryption using ECB.qhelp b/csharp/ql/src/Security Features/Encryption using ECB.qhelp index 96bea263ff51..0315813d7bce 100644 --- a/csharp/ql/src/Security Features/Encryption using ECB.qhelp +++ b/csharp/ql/src/Security Features/Encryption using ECB.qhelp @@ -3,8 +3,8 @@ "qhelp.dtd"> -

    ECB should not be used as a mode for encryption. It has a dangerous weaknesses. Data is encrypted the same way every time -meaning the same plaintext input will always produce the same cyphertext. This makes encrypted messages very vulnerable +

    ECB should not be used as a mode for encryption. It has dangerous weaknesses. Data is encrypted the same way every time +meaning the same plaintext input will always produce the same cyphertext. This makes encrypted messages vulnerable to replay attacks.

    diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.cs b/csharp/ql/src/Security Features/InsufficientKeySize.cs index 5a12d01c1a1a..9d12299dfb00 100644 --- a/csharp/ql/src/Security Features/InsufficientKeySize.cs +++ b/csharp/ql/src/Security Features/InsufficientKeySize.cs @@ -11,7 +11,7 @@ static public byte[] EncryptWithRSA(byte[] plaintext, RSAParameters key) { try { - RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512); // BAD + RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024); // BAD rsa.ImportParameters(key); return rsa.Encrypt(plaintext, true); } @@ -27,7 +27,7 @@ static public byte[] EncryptWithRSA2(byte[] plaintext, RSAParameters key) try { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); // BAD - rsa = new RSACryptoServiceProvider(1024); // GOOD + rsa = new RSACryptoServiceProvider(2048); // GOOD rsa.ImportParameters(key); return rsa.Encrypt(plaintext, true); } @@ -58,7 +58,7 @@ static public byte[] EncryptWithDSA2(byte[] plaintext, DSAParameters key) try { DSACryptoServiceProvider dsa = new DSACryptoServiceProvider(); // BAD - dsa = new DSACryptoServiceProvider(1024); // GOOD + dsa = new DSACryptoServiceProvider(2048); // GOOD dsa.ImportParameters(key); return dsa.SignData(plaintext); } @@ -121,7 +121,7 @@ public static byte[] DSASignHash(byte[] HashToSign, DSAParameters DSAKeyInfo, try { // Create a new instance of DSACryptoServiceProvider. - using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider(1024)) // GOOD + using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider(2048)) // GOOD { // Import the key information. DSA.ImportParameters(DSAKeyInfo); diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.qhelp b/csharp/ql/src/Security Features/InsufficientKeySize.qhelp index 2b9ee39c6108..906881cf0c25 100644 --- a/csharp/ql/src/Security Features/InsufficientKeySize.qhelp +++ b/csharp/ql/src/Security Features/InsufficientKeySize.qhelp @@ -8,7 +8,7 @@ are vulnerable to brute force attack when too small a key size is used.

    -

    The key should be at least 1024-bit long when using RSA encryption, and 128-bit long when using +

    The key should be at least 2048-bit long when using RSA encryption, and 128-bit long when using symmetric encryption.

    diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.ql b/csharp/ql/src/Security Features/InsufficientKeySize.ql index 08bdcfd6724b..04623b1d4b01 100644 --- a/csharp/ql/src/Security Features/InsufficientKeySize.ql +++ b/csharp/ql/src/Security Features/InsufficientKeySize.ql @@ -29,8 +29,8 @@ predicate incorrectUseOfDSA(ObjectCreation e, string msg) { .getTarget() .getDeclaringType() .hasQualifiedName("System.Security.Cryptography", "DSACryptoServiceProvider") and - exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 1024) and - msg = "Key size should be at least 1024 bits for DSA encryption." + exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 2048) and + msg = "Key size should be at least 2048 bits for DSA encryption." } predicate incorrectUseOfRSA(ObjectCreation e, string msg) { @@ -38,8 +38,8 @@ predicate incorrectUseOfRSA(ObjectCreation e, string msg) { .getTarget() .getDeclaringType() .hasQualifiedName("System.Security.Cryptography", "RSACryptoServiceProvider") and - exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 1024) and - msg = "Key size should be at least 1024 bits for RSA encryption." + exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 2048) and + msg = "Key size should be at least 2048 bits for RSA encryption." } from Expr e, string msg diff --git a/csharp/ql/src/Stubs/Stubs.qll b/csharp/ql/src/Stubs/Stubs.qll index 97ae6b4fd267..f8b704cba8b5 100644 --- a/csharp/ql/src/Stubs/Stubs.qll +++ b/csharp/ql/src/Stubs/Stubs.qll @@ -5,7 +5,7 @@ * This will generate stubs for all the required dependencies as well. * * Use - * ``` + * ```ql * select generatedCode() * ``` * to retrieve the generated C# code. diff --git a/csharp/ql/src/cil.qll b/csharp/ql/src/cil.qll index 7252542414c5..b46c328ab914 100644 --- a/csharp/ql/src/cil.qll +++ b/csharp/ql/src/cil.qll @@ -1 +1,5 @@ +/** + * The default QL library for modeling the Common Intermediate Language (CIL). + */ + import semmle.code.cil.CIL as CIL diff --git a/csharp/ql/src/codeql-suites/csharp-code-scanning.qls b/csharp/ql/src/codeql-suites/csharp-code-scanning.qls index 3646204da7d7..44fe11937e48 100644 --- a/csharp/ql/src/codeql-suites/csharp-code-scanning.qls +++ b/csharp/ql/src/codeql-suites/csharp-code-scanning.qls @@ -2,3 +2,5 @@ - qlpack: codeql-csharp - apply: code-scanning-selectors.yml from: codeql-suite-helpers +- apply: codeql-suites/exclude-dependency-queries.yml + from: codeql-csharp diff --git a/csharp/ql/src/codeql-suites/csharp-lgtm-full.qls b/csharp/ql/src/codeql-suites/csharp-lgtm-full.qls index 9c811406eeb0..8ba077663d03 100644 --- a/csharp/ql/src/codeql-suites/csharp-lgtm-full.qls +++ b/csharp/ql/src/codeql-suites/csharp-lgtm-full.qls @@ -7,3 +7,7 @@ tags contain: - ide-contextual-queries/local-definitions - ide-contextual-queries/local-references +- query: Metrics/Files/FLinesOfCode.ql +- query: Metrics/Files/FLinesOfCommentedCode.ql +- query: Metrics/Files/FLinesOfComment.ql +- query: Metrics/Files/FNumberOfTests.ql diff --git a/csharp/ql/src/codeql-suites/csharp-security-and-quality.qls b/csharp/ql/src/codeql-suites/csharp-security-and-quality.qls new file mode 100644 index 000000000000..f5df6527965c --- /dev/null +++ b/csharp/ql/src/codeql-suites/csharp-security-and-quality.qls @@ -0,0 +1,6 @@ +- description: Security-and-quality queries for C# +- qlpack: codeql-csharp +- apply: security-and-quality-selectors.yml + from: codeql-suite-helpers +- apply: codeql-suites/exclude-dependency-queries.yml + from: codeql-csharp diff --git a/csharp/ql/src/codeql-suites/csharp-security-extended.qls b/csharp/ql/src/codeql-suites/csharp-security-extended.qls new file mode 100644 index 000000000000..f4efe70892c4 --- /dev/null +++ b/csharp/ql/src/codeql-suites/csharp-security-extended.qls @@ -0,0 +1,6 @@ +- description: Security-extended queries for C# +- qlpack: codeql-csharp +- apply: security-extended-selectors.yml + from: codeql-suite-helpers +- apply: codeql-suites/exclude-dependency-queries.yml + from: codeql-csharp diff --git a/csharp/ql/src/codeql-suites/exclude-dependency-queries.yml b/csharp/ql/src/codeql-suites/exclude-dependency-queries.yml new file mode 100644 index 000000000000..53ad48be212e --- /dev/null +++ b/csharp/ql/src/codeql-suites/exclude-dependency-queries.yml @@ -0,0 +1,4 @@ +- description: C# queries which overlap with dependency analysis +- exclude: + query path: + - Security Features/CWE-937/VulnerablePackage.ql diff --git a/csharp/ql/src/dotnet.qll b/csharp/ql/src/dotnet.qll index 510f69671275..b583edda18a7 100644 --- a/csharp/ql/src/dotnet.qll +++ b/csharp/ql/src/dotnet.qll @@ -1 +1,5 @@ +/** + * The default QL library for modeling .NET definitions for both C# and CIL code. + */ + import semmle.code.dotnet.DotNet as DotNet diff --git a/csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp b/csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp index 77721307eda9..d7f195905a84 100644 --- a/csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp +++ b/csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp @@ -51,7 +51,7 @@ system's passwords.

  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/csharp/ql/src/experimental/Security Features/Serialization/DataSetSerialization.qhelp b/csharp/ql/src/experimental/Security Features/Serialization/DataSetSerialization.qhelp new file mode 100644 index 000000000000..7575352f4f16 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/DataSetSerialization.qhelp @@ -0,0 +1,23 @@ + + + +

    The DataSet and DataTable types are legacy .NET components that you can use to represent data sets as managed objects.

    + +

    While DataSet and DataTable do impose default limitations on the types that are allowed to be present while deserializing XML payloads, DataSet and DataTable are in general not safe when populated with untrusted input.

    + +

    Please visit DataSet and DataTable security guidance for more details.

    + +
    + + +

    Please review the DataSet and DataTable security guidance before making use of these types for serialization.

    + +
    + + +
  • Microsoft DocsDataSet and DataTable security guidance.
  • + +
    +
    diff --git a/csharp/ql/src/experimental/Security Features/Serialization/DataSetSerialization.qll b/csharp/ql/src/experimental/Security Features/Serialization/DataSetSerialization.qll new file mode 100644 index 000000000000..a6230b0ff596 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/DataSetSerialization.qll @@ -0,0 +1,100 @@ +/** + * Provides classes for `DataSet` or `DataTable` deserialization queries. + * + * Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. + */ + +import csharp + +/** + * Abstract class that depends or inherits from `DataSet` or `DataTable` types. + */ +abstract class DataSetOrTableRelatedClass extends Class { } + +/** + * `DataSet`, `DataTable` types, or any types derived from them. + */ +class DataSetOrTable extends DataSetOrTableRelatedClass { + DataSetOrTable() { + this.getABaseType*().getQualifiedName() = "System.Data.DataTable" or + this.getABaseType*().getQualifiedName() = "System.Data.DataSet" + } +} + +/** + * A Class that include a property or generic collection of type `DataSet` and `DataTable` + */ +class ClassWithDataSetOrTableMember extends DataSetOrTableRelatedClass { + ClassWithDataSetOrTableMember() { + this.getAMember().(AssignableMember).getType() instanceof DataSetOrTable + or + exists(Property p | p = this.getAProperty() | + p.getType() instanceof DataSetOrTable or + p.getType().(ConstructedGeneric).getATypeArgument() instanceof DataSetOrTable + ) + } +} + +/** + * Serializable types + */ +class SerializableClass extends Class { + SerializableClass() { + ( + this.getABaseType*().getQualifiedName() = "System.Xml.Serialization.XmlSerializer" or + this.getABaseType*().getQualifiedName() = "System.Runtime.Serialization.ISerializable" or + this.getABaseType*().getQualifiedName() = "System.Runtime.Serialization.XmlObjectSerializer" or + this.getABaseType*().getQualifiedName() = + "System.Runtime.Serialization.ISerializationSurrogateProvider" or + this.getABaseType*().getQualifiedName() = + "System.Runtime.Serialization.XmlSerializableServices" or + this.getABaseType*().getQualifiedName() = "System.Xml.Serialization.IXmlSerializable" + ) + or + exists(Attribute a | a = this.getAnAttribute() | + a.getType().getQualifiedName() = "System.SerializableAttribute" + ) + } +} + +/** + * Holds if the serializable class `c` has a property or field `m` that is of `DataSet` or `DataTable` related type + */ +predicate isClassUnsafeXmlSerializerImplementation(SerializableClass c, AssignableMember am) { + am = c.getAMember() and + am.getType() instanceof DataSetOrTableRelatedClass +} + +/** + * Serializable class that has a property or field that is of `DataSet` or `DataTable` related type + */ +class UnsafeXmlSerializerImplementation extends SerializableClass { + UnsafeXmlSerializerImplementation() { isClassUnsafeXmlSerializerImplementation(this, _) } +} + +/** + * Method that may be unsafe when used to deserialize DataSet and DataTable related types + */ +class UnsafeXmlReadMethod extends Method { + UnsafeXmlReadMethod() { + this.getQualifiedName() = "System.Data.DataTable.ReadXml" + or + this.getQualifiedName() = "System.Data.DataTable.ReadXmlSchema" + or + this.getQualifiedName() = "System.Data.DataSet.ReadXml" + or + this.getQualifiedName() = "System.Data.DataSet.ReadXmlSchema" + or + this.getName().matches("ReadXml%") and + exists(Class c | c.getAMethod() = this | + c.getABaseType*() instanceof DataSetOrTableRelatedClass + ) + } +} + +/** + * MethodCall that may be unsafe when used to deserialize DataSet and DataTable related types + */ +class UnsafeXmlReadMethodCall extends MethodCall { + UnsafeXmlReadMethodCall() { exists(UnsafeXmlReadMethod uxrm | uxrm.getACall() = this) } +} diff --git a/csharp/ql/src/experimental/Security Features/Serialization/DefiningDatasetRelatedType.qhelp b/csharp/ql/src/experimental/Security Features/Serialization/DefiningDatasetRelatedType.qhelp new file mode 100644 index 000000000000..af51bf311f1f --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/DefiningDatasetRelatedType.qhelp @@ -0,0 +1,5 @@ + + + diff --git a/csharp/ql/src/experimental/Security Features/Serialization/DefiningDatasetRelatedType.ql b/csharp/ql/src/experimental/Security Features/Serialization/DefiningDatasetRelatedType.ql new file mode 100644 index 000000000000..be79f2849ad9 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/DefiningDatasetRelatedType.ql @@ -0,0 +1,16 @@ +/** + * @name Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types + * @description Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types may lead to the usage of dangerous functionality. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. + * @kind problem + * @problem.severity warning + * @id cs/dataset-serialization/defining-dataset-related-type + * @tags security + */ + +import csharp +import DataSetSerialization + +from DataSetOrTableRelatedClass dstc +where dstc.fromSource() +select dstc, + "Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details." diff --git a/csharp/ql/src/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerialization.qhelp b/csharp/ql/src/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerialization.qhelp new file mode 100644 index 000000000000..af51bf311f1f --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerialization.qhelp @@ -0,0 +1,5 @@ + + + diff --git a/csharp/ql/src/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.ql b/csharp/ql/src/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.ql new file mode 100644 index 000000000000..320096d63015 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.ql @@ -0,0 +1,20 @@ +/** + * @name Defining a potentially unsafe XML serializer + * @description Defining an XML serializable class that includes members that derive from DataSet or DataTable type may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. + * @kind problem + * @problem.severity error + * @precision medium + * @id cs/dataset-serialization/defining-potentially-unsafe-xml-serializer + * @tags security + */ + +import csharp +import DataSetSerialization + +from UnsafeXmlSerializerImplementation c, Member m +where + c.fromSource() and + isClassUnsafeXmlSerializerImplementation(c, m) +select m, + "Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details.", + c, c.toString(), m, m.toString() diff --git a/csharp/ql/src/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.qhelp b/csharp/ql/src/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.qhelp new file mode 100644 index 000000000000..af51bf311f1f --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.qhelp @@ -0,0 +1,5 @@ + + + diff --git a/csharp/ql/src/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.ql b/csharp/ql/src/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.ql new file mode 100644 index 000000000000..f3a83b679267 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.ql @@ -0,0 +1,43 @@ +/** + * @name Unsafe type is used in data contract serializer + * @description Unsafe type is used in data contract serializer. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details." + * @kind problem + * @problem.severity error + * @precision medium + * @id cs/dataset-serialization/unsafe-type-used-data-contract-serializer + * @tags security + */ + +import csharp +import DataSetSerialization + +predicate xmlSerializerConstructorArgument(Expr e) { + exists(ObjectCreation oc, Constructor c | e = oc.getArgument(0) | + c = oc.getTarget() and + c.getDeclaringType().getABaseType*().hasQualifiedName("System.Xml.Serialization.XmlSerializer") + ) +} + +predicate unsafeDataContractTypeCreation(Expr e) { + exists(MethodCall gt | + gt.getTarget().getName() = "GetType" and + e = gt and + gt.getQualifier().getType() instanceof DataSetOrTableRelatedClass + ) + or + e.(TypeofExpr).getTypeAccess().getTarget() instanceof DataSetOrTableRelatedClass +} + +class Conf extends DataFlow::Configuration { + Conf() { this = "FlowToDataSerializerConstructor" } + + override predicate isSource(DataFlow::Node node) { unsafeDataContractTypeCreation(node.asExpr()) } + + override predicate isSink(DataFlow::Node node) { xmlSerializerConstructorArgument(node.asExpr()) } +} + +from Conf conf, DataFlow::Node source, DataFlow::Node sink +where conf.hasFlow(source, sink) +select sink, + "Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source.", + source, source.toString() diff --git a/csharp/ql/src/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.qhelp b/csharp/ql/src/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.qhelp new file mode 100644 index 000000000000..af51bf311f1f --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.qhelp @@ -0,0 +1,5 @@ + + + diff --git a/csharp/ql/src/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.ql b/csharp/ql/src/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.ql new file mode 100644 index 000000000000..d4392ca4544b --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.ql @@ -0,0 +1,16 @@ +/** + * @name XML deserialization with a type type derived from DataSet or DataTable + * @description Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details." + * @kind problem + * @problem.severity error + * @precision medium + * @id cs/dataset-serialization/xml-deserialization-with-dataset + * @tags security + */ + +import csharp +import DataSetSerialization + +from UnsafeXmlReadMethodCall mc +select mc, + "Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details." diff --git a/csharp/ql/src/semmle/code/csharp/ir/IR.qll b/csharp/ql/src/experimental/ir/IR.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/IR.qll rename to csharp/ql/src/experimental/ir/IR.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/IRConfiguration.qll b/csharp/ql/src/experimental/ir/IRConfiguration.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/IRConfiguration.qll rename to csharp/ql/src/experimental/ir/IRConfiguration.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/IRConsistency.ql b/csharp/ql/src/experimental/ir/IRConsistency.ql similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/IRConsistency.ql rename to csharp/ql/src/experimental/ir/IRConsistency.ql diff --git a/csharp/ql/src/semmle/code/csharp/ir/PrintIR.ql b/csharp/ql/src/experimental/ir/PrintIR.ql similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/PrintIR.ql rename to csharp/ql/src/experimental/ir/PrintIR.ql diff --git a/csharp/ql/src/semmle/code/csharp/ir/PrintIR.qll b/csharp/ql/src/experimental/ir/PrintIR.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/PrintIR.qll rename to csharp/ql/src/experimental/ir/PrintIR.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/Util.qll b/csharp/ql/src/experimental/ir/Util.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/Util.qll rename to csharp/ql/src/experimental/ir/Util.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/ValueNumbering.qll b/csharp/ql/src/experimental/ir/ValueNumbering.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/ValueNumbering.qll rename to csharp/ql/src/experimental/ir/ValueNumbering.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll b/csharp/ql/src/experimental/ir/implementation/EdgeKind.qll similarity index 89% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll rename to csharp/ql/src/experimental/ir/implementation/EdgeKind.qll index 54059fb5b82f..32e36bb67876 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll +++ b/csharp/ql/src/experimental/ir/implementation/EdgeKind.qll @@ -1,3 +1,7 @@ +/** + * Provides classes that specify the conditions under which control flows along a given edge. + */ + private import internal.EdgeKindInternal private newtype TEdgeKind = @@ -77,9 +81,15 @@ class CaseEdge extends EdgeKind, TCaseEdge { else result = "Case[" + minValue + ".." + maxValue + "]" } - string getMinValue() { result = minValue } + /** + * Gets the smallest value of the switch expression for which control will flow along this edge. + */ + final string getMinValue() { result = minValue } - string getMaxValue() { result = maxValue } + /** + * Gets the largest value of the switch expression for which control will flow along this edge. + */ + final string getMaxValue() { result = maxValue } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll b/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll new file mode 100644 index 000000000000..37ac2fccdd94 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll @@ -0,0 +1,45 @@ +/** + * Module used to configure the IR generation process. + */ + +private import internal.IRConfigurationInternal + +private newtype TIRConfiguration = MkIRConfiguration() + +/** + * The query can extend this class to control which functions have IR generated for them. + */ +class IRConfiguration extends TIRConfiguration { + /** Gets a textual representation of this element. */ + string toString() { result = "IRConfiguration" } + + /** + * Holds if IR should be created for function `func`. By default, holds for all functions. + */ + predicate shouldCreateIRForFunction(Language::Function func) { any() } + + /** + * Holds if the strings used as part of an IR dump should be generated for function `func`. + * + * This predicate is overridden in `PrintIR.qll` to avoid the expense of generating a large number + * of debug strings for IR that will not be dumped. We still generate the actual IR for these + * functions, however, to preserve the results of any interprocedural analysis. + */ + predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() } +} + +private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration() + +/** + * The query can extend this class to control what escape analysis is used when generating SSA. + */ +class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration { + /** Gets a textual representation of this element. */ + string toString() { result = "IREscapeAnalysisConfiguration" } + + /** + * Holds if the escape analysis done by SSA construction should be sound. By default, the SSA is + * built assuming that no variable's address ever escapes. + */ + predicate useSoundEscapeAnalysis() { none() } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll b/csharp/ql/src/experimental/ir/implementation/IRType.qll similarity index 89% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll rename to csharp/ql/src/experimental/ir/implementation/IRType.qll index d196cdce0ab2..3bf3bf2e2766 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll +++ b/csharp/ql/src/experimental/ir/implementation/IRType.qll @@ -32,6 +32,7 @@ private newtype TIRType = * all pointer types map to the same instance of `IRAddressType`. */ class IRType extends TIRType { + /** Gets a textual representation of this type. */ string toString() { none() } /** @@ -111,6 +112,8 @@ private class IRSizedType extends IRType { this = TIRFunctionAddressType(byteSize) or this = TIROpaqueType(_, byteSize) } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** @@ -128,7 +131,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType { } /** - * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and + * A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and * `IRFloatingPointType`. */ class IRNumericType extends IRSizedType { @@ -137,13 +140,33 @@ class IRNumericType extends IRSizedType { this = TIRUnsignedIntegerType(byteSize) or this = TIRFloatingPointType(byteSize, _, _) } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. +} + +/** + * An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`. + */ +class IRIntegerType extends IRNumericType { + IRIntegerType() { + this = TIRSignedIntegerType(byteSize) or + this = TIRUnsignedIntegerType(byteSize) + } + + /** Holds if this integer type is signed. */ + predicate isSigned() { none() } + + /** Holds if this integer type is unsigned. */ + predicate isUnsigned() { none() } + // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is + // overridden only in the leaf classes. } /** * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed * integer, as well as character types whose representation is signed. */ -class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType { +class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType { final override string toString() { result = "int" + byteSize.toString() } final override Language::LanguageType getCanonicalLanguageType() { @@ -152,13 +175,15 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType { pragma[noinline] final override int getByteSize() { result = byteSize } + + override predicate isSigned() { any() } } /** * An unsigned two's-complement integer. Also used to represent enums whose underlying type is an * unsigned integer, as well as character types whose representation is unsigned. */ -class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType { +class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType { final override string toString() { result = "uint" + byteSize.toString() } final override Language::LanguageType getCanonicalLanguageType() { @@ -167,6 +192,8 @@ class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType { pragma[noinline] final override int getByteSize() { result = byteSize } + + override predicate isUnsigned() { any() } } /** diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll b/csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll similarity index 93% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll rename to csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll index 6852a9654017..5e11a310e2fb 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll +++ b/csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll @@ -1,3 +1,9 @@ +/** + * Provides classes that describe how a particular `Instruction` or its operands access memory. + */ + +private import IRConfiguration + private newtype TMemoryAccessKind = TIndirectMemoryAccess() or TBufferMemoryAccess() or @@ -14,6 +20,7 @@ private newtype TMemoryAccessKind = * memory result. */ class MemoryAccessKind extends TMemoryAccessKind { + /** Gets a textual representation of this access kind. */ string toString() { none() } /** diff --git a/csharp/ql/src/experimental/ir/implementation/Opcode.qll b/csharp/ql/src/experimental/ir/implementation/Opcode.qll new file mode 100644 index 000000000000..c4134d240aba --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/Opcode.qll @@ -0,0 +1,1239 @@ +/** + * Provides `Opcode`s that specify the operation performed by an `Instruction`, as well as metadata + * about those opcodes, such as operand kinds and memory accesses. + */ + +private import internal.OpcodeImports as Imports +private import internal.OperandTag +import Imports::MemoryAccessKind + +private newtype TOpcode = + TNoOp() or + TUninitialized() or + TError() or + TInitializeParameter() or + TInitializeIndirection() or + TInitializeThis() or + TEnterFunction() or + TExitFunction() or + TReturnValue() or + TReturnVoid() or + TReturnIndirection() or + TCopyValue() or + TLoad() or + TStore() or + TAdd() or + TSub() or + TMul() or + TDiv() or + TRem() or + TNegate() or + TShiftLeft() or + TShiftRight() or + TBitAnd() or + TBitOr() or + TBitXor() or + TBitComplement() or + TLogicalNot() or + TCompareEQ() or + TCompareNE() or + TCompareLT() or + TCompareGT() or + TCompareLE() or + TCompareGE() or + TPointerAdd() or + TPointerSub() or + TPointerDiff() or + TConvert() or + TConvertToNonVirtualBase() or + TConvertToVirtualBase() or + TConvertToDerived() or + TCheckedConvertOrNull() or + TCheckedConvertOrThrow() or + TCompleteObjectAddress() or + TVariableAddress() or + TFieldAddress() or + TFunctionAddress() or + TElementsAddress() or + TConstant() or + TStringConstant() or + TConditionalBranch() or + TSwitch() or + TCall() or + TCatchByType() or + TCatchAny() or + TThrowValue() or + TReThrow() or + TUnwind() or + TAliasedDefinition() or + TInitializeNonLocal() or + TAliasedUse() or + TPhi() or + TBuiltIn() or + TVarArgsStart() or + TVarArgsEnd() or + TVarArg() or + TNextVarArg() or + TCallSideEffect() or + TCallReadSideEffect() or + TIndirectReadSideEffect() or + TIndirectMustWriteSideEffect() or + TIndirectMayWriteSideEffect() or + TBufferReadSideEffect() or + TBufferMustWriteSideEffect() or + TBufferMayWriteSideEffect() or + TSizedBufferReadSideEffect() or + TSizedBufferMustWriteSideEffect() or + TSizedBufferMayWriteSideEffect() or + TInitializeDynamicAllocation() or + TChi() or + TInlineAsm() or + TUnreached() or + TNewObj() + +/** + * An opcode that specifies the operation performed by an `Instruction`. + */ +class Opcode extends TOpcode { + /** Gets a textual representation of this element. */ + string toString() { result = "UnknownOpcode" } + + /** + * Gets the kind of memory access performed by this instruction's result. + * Holds only for opcodes with a memory result. + */ + MemoryAccessKind getWriteMemoryAccess() { none() } + + /** + * Gets the kind of memory access performed by this instruction's `MemoryOperand`. Holds only for + * opcodes that read from memory. + */ + MemoryAccessKind getReadMemoryAccess() { none() } + + /** + * Holds if the instruction has an `AddressOperand`. + */ + predicate hasAddressOperand() { none() } + + /** + * Holds if the instruction has a `BufferSizeOperand`. + */ + predicate hasBufferSizeOperand() { none() } + + /** + * Holds if the instruction's write memory access is a `may` write, as opposed to a `must` write. + */ + predicate hasMayWriteMemoryAccess() { none() } + + /** + * Holds if the instruction's read memory access is a `may` read, as opposed to a `must` read. + */ + predicate hasMayReadMemoryAccess() { none() } + + /** + * Holds if the instruction must have an operand with the specified `OperandTag`. + */ + final predicate hasOperand(OperandTag tag) { + hasOperandInternal(tag) + or + hasAddressOperand() and tag instanceof AddressOperandTag + or + hasBufferSizeOperand() and tag instanceof BufferSizeOperandTag + } + + /** + * Holds if the instruction must have an operand with the specified `OperandTag`, ignoring + * `AddressOperandTag` and `BufferSizeOperandTag`. + */ + predicate hasOperandInternal(OperandTag tag) { none() } +} + +/** + * The `Opcode` for a `UnaryInstruction`. + * + * See the `UnaryInstruction` documentation for more details. + */ +abstract class UnaryOpcode extends Opcode { + final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag } +} + +/** + * The `Opcode` for a `BinaryInstruction`. + * + * See the `BinaryInstruction` documentation for more details. + */ +abstract class BinaryOpcode extends Opcode { + final override predicate hasOperandInternal(OperandTag tag) { + tag instanceof LeftOperandTag or + tag instanceof RightOperandTag + } +} + +/** + * The `Opcode` for a `PointerArithmeticInstruction`. + * + * See the `PointerArithmeticInstruction` documentation for more details. + */ +abstract class PointerArithmeticOpcode extends BinaryOpcode { } + +/** + * The `Opcode` for a `PointerOffsetInstruction`. + * + * See the `PointerOffsetInstruction` documentation for more details. + */ +abstract class PointerOffsetOpcode extends PointerArithmeticOpcode { } + +/** + * The `Opcode` for an `ArithmeticInstruction`. + * + * See the `ArithmeticInstruction` documentation for more details. + */ +abstract class ArithmeticOpcode extends Opcode { } + +/** + * The `Opcode` for a `BinaryArithmeticInstruction`. + * + * See the `BinaryArithmeticInstruction` documentation for more details. + */ +abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode { } + +/** + * The `Opcode` for a `UnaryArithmeticInstruction`. + * + * See the `UnaryArithmeticInstruction` documentation for more details. + */ +abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode { } + +/** + * The `Opcode` for a `BitwiseInstruction`. + * + * See the `BitwiseInstruction` documentation for more details. + */ +abstract class BitwiseOpcode extends Opcode { } + +/** + * The `Opcode` for a `BinaryBitwiseInstruction`. + * + * See the `BinaryBitwiseInstruction` documentation for more details. + */ +abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode { } + +/** + * The `Opcode` for a `UnaryBitwiseInstruction`. + * + * See the `UnaryBitwiseInstruction` documentation for more details. + */ +abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode { } + +/** + * The `Opcode` for a `CompareInstruction`. + * + * See the `CompareInstruction` documentation for more details. + */ +abstract class CompareOpcode extends BinaryOpcode { } + +/** + * The `Opcode` for a `RelationalInstruction`. + * + * See the `RelationalInstruction` documentation for more details. + */ +abstract class RelationalOpcode extends CompareOpcode { } + +/** + * The `Opcode` for a `CopyInstruction`. + * + * See the `CopyInstruction` documentation for more details. + */ +abstract class CopyOpcode extends Opcode { } + +/** + * The `Opcode` for a `ConvertToBaseInstruction`. + * + * See the `ConvertToBaseInstruction` documentation for more details. + */ +abstract class ConvertToBaseOpcode extends UnaryOpcode { } + +/** + * The `Opcode` for a `ReturnInstruction`. + * + * See the `ReturnInstruction` documentation for more details. + */ +abstract class ReturnOpcode extends Opcode { } + +/** + * The `Opcode` for a `ThrowInstruction`. + * + * See the `ThrowInstruction` documentation for more details. + */ +abstract class ThrowOpcode extends Opcode { } + +/** + * The `Opcode` for a `CatchInstruction`. + * + * See the `CatchInstruction` documentation for more details. + */ +abstract class CatchOpcode extends Opcode { } + +abstract private class OpcodeWithCondition extends Opcode { + final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag } +} + +/** + * The `Opcode` for a `BuiltInOperationInstruction`. + * + * See the `BuiltInOperationInstruction` documentation for more details. + */ +abstract class BuiltInOperationOpcode extends Opcode { } + +/** + * The `Opcode` for a `SideEffectInstruction`. + * + * See the `SideEffectInstruction` documentation for more details. + */ +abstract class SideEffectOpcode extends Opcode { } + +/** + * An opcode that accesses a single memory location via an `AddressOperand`. + */ +abstract class IndirectMemoryAccessOpcode extends Opcode { + final override predicate hasAddressOperand() { any() } +} + +/** + * An opcode that writes to a single memory location via an `AddressOperand`. + */ +abstract class IndirectWriteOpcode extends IndirectMemoryAccessOpcode { + final override MemoryAccessKind getWriteMemoryAccess() { result instanceof IndirectMemoryAccess } +} + +/** + * An opcode that reads from a single memory location via an `AddressOperand`. + */ +abstract class IndirectReadOpcode extends IndirectMemoryAccessOpcode { + final override MemoryAccessKind getReadMemoryAccess() { result instanceof IndirectMemoryAccess } +} + +/** + * An opcode that accesses a memory buffer. + */ +abstract class BufferAccessOpcode extends Opcode { + final override predicate hasAddressOperand() { any() } +} + +/** + * An opcode that accesses a memory buffer of unknown size. + */ +abstract class UnsizedBufferAccessOpcode extends BufferAccessOpcode { } + +/** + * An opcode that writes to a memory buffer of unknown size. + */ +abstract class UnsizedBufferWriteOpcode extends UnsizedBufferAccessOpcode { + final override MemoryAccessKind getWriteMemoryAccess() { result instanceof BufferMemoryAccess } +} + +/** + * An opcode that reads from a memory buffer of unknown size. + */ +abstract class UnsizedBufferReadOpcode extends UnsizedBufferAccessOpcode { + final override MemoryAccessKind getReadMemoryAccess() { result instanceof BufferMemoryAccess } +} + +/** + * An opcode that access an entire memory allocation. + */ +abstract class EntireAllocationAccessOpcode extends Opcode { + final override predicate hasAddressOperand() { any() } +} + +/** + * An opcode that write to an entire memory allocation. + */ +abstract class EntireAllocationWriteOpcode extends EntireAllocationAccessOpcode { + final override MemoryAccessKind getWriteMemoryAccess() { + result instanceof EntireAllocationMemoryAccess + } +} + +/** + * An opcode that reads from an entire memory allocation. + */ +abstract class EntireAllocationReadOpcode extends EntireAllocationAccessOpcode { + final override MemoryAccessKind getReadMemoryAccess() { + result instanceof EntireAllocationMemoryAccess + } +} + +/** + * An opcode that accesses a memory buffer whose size is determined by a `BufferSizeOperand`. + */ +abstract class SizedBufferAccessOpcode extends BufferAccessOpcode { + final override predicate hasBufferSizeOperand() { any() } +} + +/** + * An opcode that writes to a memory buffer whose size is determined by a `BufferSizeOperand`. + */ +abstract class SizedBufferWriteOpcode extends SizedBufferAccessOpcode { + final override MemoryAccessKind getWriteMemoryAccess() { + result instanceof BufferMemoryAccess //TODO: SizedBufferMemoryAccess + } +} + +/** + * An opcode that reads from a memory buffer whose size is determined by a `BufferSizeOperand`. + */ +abstract class SizedBufferReadOpcode extends SizedBufferAccessOpcode { + final override MemoryAccessKind getReadMemoryAccess() { + result instanceof BufferMemoryAccess //TODO: SizedBufferMemoryAccess + } +} + +/** + * An opcode that might write to any escaped memory location. + */ +abstract class EscapedWriteOpcode extends Opcode { + final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess } +} + +/** + * An opcode that might read from any escaped memory location. + */ +abstract class EscapedReadOpcode extends Opcode { + final override MemoryAccessKind getReadMemoryAccess() { result instanceof EscapedMemoryAccess } +} + +/** + * An opcode whose write memory access is a `may` write, as opposed to a `must` write. + */ +abstract class MayWriteOpcode extends Opcode { + final override predicate hasMayWriteMemoryAccess() { any() } +} + +/** + * An opcode whose read memory access is a `may` read, as opposed to a `must` read. + */ +abstract class MayReadOpcode extends Opcode { + final override predicate hasMayReadMemoryAccess() { any() } +} + +/** + * An opcode that reads a value from memory. + */ +abstract class OpcodeWithLoad extends IndirectReadOpcode { + final override predicate hasOperandInternal(OperandTag tag) { tag instanceof LoadOperandTag } +} + +/** + * The `Opcode` for a `ReadSideEffectInstruction`. + * + * See the `ReadSideEffectInstruction` documentation for more details. + */ +abstract class ReadSideEffectOpcode extends SideEffectOpcode { + final override predicate hasOperandInternal(OperandTag tag) { + tag instanceof SideEffectOperandTag + } +} + +/** + * The `Opcode` for a `WriteSideEffectInstruction`. + * + * See the `WriteSideEffectInstruction` documentation for more details. + */ +abstract class WriteSideEffectOpcode extends SideEffectOpcode { } + +/** + * Provides `Opcode`s that specify the operation performed by an `Instruction`. + */ +module Opcode { + /** + * The `Opcode` for a `NoOpInstruction`. + * + * See the `NoOpInstruction` documentation for more details. + */ + class NoOp extends Opcode, TNoOp { + final override string toString() { result = "NoOp" } + } + + /** + * The `Opcode` for an `UninitializedInstruction`. + * + * See the `UninitializedInstruction` documentation for more details. + */ + class Uninitialized extends IndirectWriteOpcode, TUninitialized { + final override string toString() { result = "Uninitialized" } + } + + /** + * The `Opcode` for an `ErrorInstruction`. + * + * See the `ErrorInstruction` documentation for more details. + */ + class Error extends Opcode, TError { + final override string toString() { result = "Error" } + } + + /** + * The `Opcode` for an `InitializeParameterInstruction`. + * + * See the `InitializeParameterInstruction` documentation for more details. + */ + class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter { + final override string toString() { result = "InitializeParameter" } + } + + /** + * The `Opcode` for an `InitializeIndirectionInstruction`. + * + * See the `InitializeIndirectionInstruction` documentation for more details. + */ + class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { + final override string toString() { result = "InitializeIndirection" } + } + + /** + * The `Opcode` for an `InitializeThisInstruction`. + * + * See the `InitializeThisInstruction` documentation for more details. + */ + class InitializeThis extends Opcode, TInitializeThis { + final override string toString() { result = "InitializeThis" } + } + + /** + * The `Opcode` for an `EnterFunctionInstruction`. + * + * See the `EnterFunctionInstruction` documentation for more details. + */ + class EnterFunction extends Opcode, TEnterFunction { + final override string toString() { result = "EnterFunction" } + } + + /** + * The `Opcode` for an `ExitFunctionInstruction`. + * + * See the `ExitFunctionInstruction` documentation for more details. + */ + class ExitFunction extends Opcode, TExitFunction { + final override string toString() { result = "ExitFunction" } + } + + /** + * The `Opcode` for a `ReturnValueInstruction`. + * + * See the `ReturnValueInstruction` documentation for more details. + */ + class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue { + final override string toString() { result = "ReturnValue" } + } + + /** + * The `Opcode` for a `ReturnVoidInstruction`. + * + * See the `ReturnVoidInstruction` documentation for more details. + */ + class ReturnVoid extends ReturnOpcode, TReturnVoid { + final override string toString() { result = "ReturnVoid" } + } + + /** + * The `Opcode` for a `ReturnIndirectionInstruction`. + * + * See the `ReturnIndirectionInstruction` documentation for more details. + */ + class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { + final override string toString() { result = "ReturnIndirection" } + + final override predicate hasOperandInternal(OperandTag tag) { + tag instanceof SideEffectOperandTag + } + } + + /** + * The `Opcode` for a `CopyValueInstruction`. + * + * See the `CopyValueInstruction` documentation for more details. + */ + class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { + final override string toString() { result = "CopyValue" } + } + + /** + * The `Opcode` for a `LoadInstruction`. + * + * See the `LoadInstruction` documentation for more details. + */ + class Load extends CopyOpcode, OpcodeWithLoad, TLoad { + final override string toString() { result = "Load" } + } + + /** + * The `Opcode` for a `StoreInstruction`. + * + * See the `StoreInstruction` documentation for more details. + */ + class Store extends CopyOpcode, IndirectWriteOpcode, TStore { + final override string toString() { result = "Store" } + + final override predicate hasOperandInternal(OperandTag tag) { + tag instanceof StoreValueOperandTag + } + } + + /** + * The `Opcode` for an `AddInstruction`. + * + * See the `AddInstruction` documentation for more details. + */ + class Add extends BinaryArithmeticOpcode, TAdd { + final override string toString() { result = "Add" } + } + + /** + * The `Opcode` for a `SubInstruction`. + * + * See the `SubInstruction` documentation for more details. + */ + class Sub extends BinaryArithmeticOpcode, TSub { + final override string toString() { result = "Sub" } + } + + /** + * The `Opcode` for a `MulInstruction`. + * + * See the `MulInstruction` documentation for more details. + */ + class Mul extends BinaryArithmeticOpcode, TMul { + final override string toString() { result = "Mul" } + } + + /** + * The `Opcode` for a `DivInstruction`. + * + * See the `DivInstruction` documentation for more details. + */ + class Div extends BinaryArithmeticOpcode, TDiv { + final override string toString() { result = "Div" } + } + + /** + * The `Opcode` for a `RemInstruction`. + * + * See the `RemInstruction` documentation for more details. + */ + class Rem extends BinaryArithmeticOpcode, TRem { + final override string toString() { result = "Rem" } + } + + /** + * The `Opcode` for a `NegateInstruction`. + * + * See the `NegateInstruction` documentation for more details. + */ + class Negate extends UnaryArithmeticOpcode, TNegate { + final override string toString() { result = "Negate" } + } + + /** + * The `Opcode` for a `ShiftLeftInstruction`. + * + * See the `ShiftLeftInstruction` documentation for more details. + */ + class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { + final override string toString() { result = "ShiftLeft" } + } + + /** + * The `Opcode` for a `ShiftRightInstruction`. + * + * See the `ShiftRightInstruction` documentation for more details. + */ + class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { + final override string toString() { result = "ShiftRight" } + } + + /** + * The `Opcode` for a `BitAndInstruction`. + * + * See the `BitAndInstruction` documentation for more details. + */ + class BitAnd extends BinaryBitwiseOpcode, TBitAnd { + final override string toString() { result = "BitAnd" } + } + + /** + * The `Opcode` for a `BitOrInstruction`. + * + * See the `BitOrInstruction` documentation for more details. + */ + class BitOr extends BinaryBitwiseOpcode, TBitOr { + final override string toString() { result = "BitOr" } + } + + /** + * The `Opcode` for a `BitXorInstruction`. + * + * See the `BitXorInstruction` documentation for more details. + */ + class BitXor extends BinaryBitwiseOpcode, TBitXor { + final override string toString() { result = "BitXor" } + } + + /** + * The `Opcode` for a `BitComplementInstruction`. + * + * See the `BitComplementInstruction` documentation for more details. + */ + class BitComplement extends UnaryBitwiseOpcode, TBitComplement { + final override string toString() { result = "BitComplement" } + } + + /** + * The `Opcode` for a `LogicalNotInstruction`. + * + * See the `LogicalNotInstruction` documentation for more details. + */ + class LogicalNot extends UnaryOpcode, TLogicalNot { + final override string toString() { result = "LogicalNot" } + } + + /** + * The `Opcode` for a `CompareEQInstruction`. + * + * See the `CompareEQInstruction` documentation for more details. + */ + class CompareEQ extends CompareOpcode, TCompareEQ { + final override string toString() { result = "CompareEQ" } + } + + /** + * The `Opcode` for a `CompareNEInstruction`. + * + * See the `CompareNEInstruction` documentation for more details. + */ + class CompareNE extends CompareOpcode, TCompareNE { + final override string toString() { result = "CompareNE" } + } + + /** + * The `Opcode` for a `CompareLTInstruction`. + * + * See the `CompareLTInstruction` documentation for more details. + */ + class CompareLT extends RelationalOpcode, TCompareLT { + final override string toString() { result = "CompareLT" } + } + + /** + * The `Opcode` for a `CompareGTInstruction`. + * + * See the `CompareGTInstruction` documentation for more details. + */ + class CompareGT extends RelationalOpcode, TCompareGT { + final override string toString() { result = "CompareGT" } + } + + /** + * The `Opcode` for a `CompareLEInstruction`. + * + * See the `CompareLEInstruction` documentation for more details. + */ + class CompareLE extends RelationalOpcode, TCompareLE { + final override string toString() { result = "CompareLE" } + } + + /** + * The `Opcode` for a `CompareGEInstruction`. + * + * See the `CompareGEInstruction` documentation for more details. + */ + class CompareGE extends RelationalOpcode, TCompareGE { + final override string toString() { result = "CompareGE" } + } + + /** + * The `Opcode` for a `PointerAddInstruction`. + * + * See the `PointerAddInstruction` documentation for more details. + */ + class PointerAdd extends PointerOffsetOpcode, TPointerAdd { + final override string toString() { result = "PointerAdd" } + } + + /** + * The `Opcode` for a `PointerSubInstruction`. + * + * See the `PointerSubInstruction` documentation for more details. + */ + class PointerSub extends PointerOffsetOpcode, TPointerSub { + final override string toString() { result = "PointerSub" } + } + + /** + * The `Opcode` for a `PointerDiffInstruction`. + * + * See the `PointerDiffInstruction` documentation for more details. + */ + class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { + final override string toString() { result = "PointerDiff" } + } + + /** + * The `Opcode` for a `ConvertInstruction`. + * + * See the `ConvertInstruction` documentation for more details. + */ + class Convert extends UnaryOpcode, TConvert { + final override string toString() { result = "Convert" } + } + + /** + * The `Opcode` for a `ConvertToNonVirtualBaseInstruction`. + * + * See the `ConvertToNonVirtualBaseInstruction` documentation for more details. + */ + class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase { + final override string toString() { result = "ConvertToNonVirtualBase" } + } + + /** + * The `Opcode` for a `ConvertToVirtualBaseInstruction`. + * + * See the `ConvertToVirtualBaseInstruction` documentation for more details. + */ + class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase { + final override string toString() { result = "ConvertToVirtualBase" } + } + + /** + * The `Opcode` for a `ConvertToDerivedInstruction`. + * + * See the `ConvertToDerivedInstruction` documentation for more details. + */ + class ConvertToDerived extends UnaryOpcode, TConvertToDerived { + final override string toString() { result = "ConvertToDerived" } + } + + /** + * The `Opcode` for a `CheckedConvertOrNullInstruction`. + * + * See the `CheckedConvertOrNullInstruction` documentation for more details. + */ + class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { + final override string toString() { result = "CheckedConvertOrNull" } + } + + /** + * The `Opcode` for a `CheckedConvertOrThrowInstruction`. + * + * See the `CheckedConvertOrThrowInstruction` documentation for more details. + */ + class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { + final override string toString() { result = "CheckedConvertOrThrow" } + } + + /** + * The `Opcode` for a `CompleteObjectAddressInstruction`. + * + * See the `CompleteObjectAddressInstruction` documentation for more details. + */ + class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress { + final override string toString() { result = "CompleteObjectAddress" } + } + + /** + * The `Opcode` for a `VariableAddressInstruction`. + * + * See the `VariableAddressInstruction` documentation for more details. + */ + class VariableAddress extends Opcode, TVariableAddress { + final override string toString() { result = "VariableAddress" } + } + + /** + * The `Opcode` for a `FieldAddressInstruction`. + * + * See the `FieldAddressInstruction` documentation for more details. + */ + class FieldAddress extends UnaryOpcode, TFieldAddress { + final override string toString() { result = "FieldAddress" } + } + + /** + * The `Opcode` for an `ElementsAddressInstruction`. + * + * See the `ElementsAddressInstruction` documentation for more details. + */ + class ElementsAddress extends UnaryOpcode, TElementsAddress { + final override string toString() { result = "ElementsAddress" } + } + + /** + * The `Opcode` for a `FunctionAddressInstruction`. + * + * See the `FunctionAddressInstruction` documentation for more details. + */ + class FunctionAddress extends Opcode, TFunctionAddress { + final override string toString() { result = "FunctionAddress" } + } + + /** + * The `Opcode` for a `ConstantInstruction`. + * + * See the `ConstantInstruction` documentation for more details. + */ + class Constant extends Opcode, TConstant { + final override string toString() { result = "Constant" } + } + + /** + * The `Opcode` for a `StringConstantInstruction`. + * + * See the `StringConstantInstruction` documentation for more details. + */ + class StringConstant extends Opcode, TStringConstant { + final override string toString() { result = "StringConstant" } + } + + /** + * The `Opcode` for a `ConditionalBranchInstruction`. + * + * See the `ConditionalBranchInstruction` documentation for more details. + */ + class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { + final override string toString() { result = "ConditionalBranch" } + } + + /** + * The `Opcode` for a `SwitchInstruction`. + * + * See the `SwitchInstruction` documentation for more details. + */ + class Switch extends OpcodeWithCondition, TSwitch { + final override string toString() { result = "Switch" } + } + + /** + * The `Opcode` for a `CallInstruction`. + * + * See the `CallInstruction` documentation for more details. + */ + class Call extends Opcode, TCall { + final override string toString() { result = "Call" } + + final override predicate hasOperandInternal(OperandTag tag) { + tag instanceof CallTargetOperandTag + } + } + + /** + * The `Opcode` for a `CatchByTypeInstruction`. + * + * See the `CatchByTypeInstruction` documentation for more details. + */ + class CatchByType extends CatchOpcode, TCatchByType { + final override string toString() { result = "CatchByType" } + } + + /** + * The `Opcode` for a `CatchAnyInstruction`. + * + * See the `CatchAnyInstruction` documentation for more details. + */ + class CatchAny extends CatchOpcode, TCatchAny { + final override string toString() { result = "CatchAny" } + } + + /** + * The `Opcode` for a `ThrowValueInstruction`. + * + * See the `ThrowValueInstruction` documentation for more details. + */ + class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue { + final override string toString() { result = "ThrowValue" } + } + + /** + * The `Opcode` for a `ReThrowInstruction`. + * + * See the `ReThrowInstruction` documentation for more details. + */ + class ReThrow extends ThrowOpcode, TReThrow { + final override string toString() { result = "ReThrow" } + } + + /** + * The `Opcode` for an `UnwindInstruction`. + * + * See the `UnwindInstruction` documentation for more details. + */ + class Unwind extends Opcode, TUnwind { + final override string toString() { result = "Unwind" } + } + + /** + * The `Opcode` for an `AliasedDefinitionInstruction`. + * + * See the `AliasedDefinitionInstruction` documentation for more details. + */ + class AliasedDefinition extends Opcode, TAliasedDefinition { + final override string toString() { result = "AliasedDefinition" } + + final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess } + } + + /** + * The `Opcode` for an `InitializeNonLocalInstruction`. + * + * See the `InitializeNonLocalInstruction` documentation for more details. + */ + class InitializeNonLocal extends Opcode, TInitializeNonLocal { + final override string toString() { result = "InitializeNonLocal" } + + final override MemoryAccessKind getWriteMemoryAccess() { + result instanceof NonLocalMemoryAccess + } + } + + /** + * The `Opcode` for an `AliasedUseInstruction`. + * + * See the `AliasedUseInstruction` documentation for more details. + */ + class AliasedUse extends Opcode, TAliasedUse { + final override string toString() { result = "AliasedUse" } + + final override MemoryAccessKind getReadMemoryAccess() { result instanceof NonLocalMemoryAccess } + + final override predicate hasOperandInternal(OperandTag tag) { + tag instanceof SideEffectOperandTag + } + } + + /** + * The `Opcode` for a `PhiInstruction`. + * + * See the `PhiInstruction` documentation for more details. + */ + class Phi extends Opcode, TPhi { + final override string toString() { result = "Phi" } + + final override MemoryAccessKind getWriteMemoryAccess() { result instanceof PhiMemoryAccess } + } + + /** + * The `Opcode` for a `BuiltInInstruction`. + * + * See the `BuiltInInstruction` documentation for more details. + */ + class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { + final override string toString() { result = "BuiltIn" } + } + + /** + * The `Opcode` for a `VarArgsStartInstruction`. + * + * See the `VarArgsStartInstruction` documentation for more details. + */ + class VarArgsStart extends UnaryOpcode, TVarArgsStart { + final override string toString() { result = "VarArgsStart" } + } + + /** + * The `Opcode` for a `VarArgsEndInstruction`. + * + * See the `VarArgsEndInstruction` documentation for more details. + */ + class VarArgsEnd extends UnaryOpcode, TVarArgsEnd { + final override string toString() { result = "VarArgsEnd" } + } + + /** + * The `Opcode` for a `VarArgInstruction`. + * + * See the `VarArgInstruction` documentation for more details. + */ + class VarArg extends UnaryOpcode, TVarArg { + final override string toString() { result = "VarArg" } + } + + /** + * The `Opcode` for a `NextVarArgInstruction`. + * + * See the `NextVarArgInstruction` documentation for more details. + */ + class NextVarArg extends UnaryOpcode, TNextVarArg { + final override string toString() { result = "NextVarArg" } + } + + /** + * The `Opcode` for a `CallSideEffectInstruction`. + * + * See the `CallSideEffectInstruction` documentation for more details. + */ + class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode, + ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect { + final override string toString() { result = "CallSideEffect" } + } + + /** + * The `Opcode` for a `CallReadSideEffectInstruction`. + * + * See the `CallReadSideEffectInstruction` documentation for more details. + */ + class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, + TCallReadSideEffect { + final override string toString() { result = "CallReadSideEffect" } + } + + /** + * The `Opcode` for an `IndirectReadSideEffectInstruction`. + * + * See the `IndirectReadSideEffectInstruction` documentation for more details. + */ + class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode, + TIndirectReadSideEffect { + final override string toString() { result = "IndirectReadSideEffect" } + } + + /** + * The `Opcode` for an `IndirectMustWriteSideEffectInstruction`. + * + * See the `IndirectMustWriteSideEffectInstruction` documentation for more details. + */ + class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, + TIndirectMustWriteSideEffect { + final override string toString() { result = "IndirectMustWriteSideEffect" } + } + + /** + * The `Opcode` for an `IndirectMayWriteSideEffectInstruction`. + * + * See the `IndirectMayWriteSideEffectInstruction` documentation for more details. + */ + class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, + MayWriteOpcode, TIndirectMayWriteSideEffect { + final override string toString() { result = "IndirectMayWriteSideEffect" } + } + + /** + * The `Opcode` for a `BufferReadSideEffectInstruction`. + * + * See the `BufferReadSideEffectInstruction` documentation for more details. + */ + class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode, + TBufferReadSideEffect { + final override string toString() { result = "BufferReadSideEffect" } + } + + /** + * The `Opcode` for a `BufferMustWriteSideEffectInstruction`. + * + * See the `BufferMustWriteSideEffectInstruction` documentation for more details. + */ + class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, + TBufferMustWriteSideEffect { + final override string toString() { result = "BufferMustWriteSideEffect" } + } + + /** + * The `Opcode` for a `BufferMayWriteSideEffectInstruction`. + * + * See the `BufferMayWriteSideEffectInstruction` documentation for more details. + */ + class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, + MayWriteOpcode, TBufferMayWriteSideEffect { + final override string toString() { result = "BufferMayWriteSideEffect" } + } + + /** + * The `Opcode` for a `SizedBufferReadSideEffectInstruction`. + * + * See the `SizedBufferReadSideEffectInstruction` documentation for more details. + */ + class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode, + TSizedBufferReadSideEffect { + final override string toString() { result = "SizedBufferReadSideEffect" } + } + + /** + * The `Opcode` for a `SizedBufferMustWriteSideEffectInstruction`. + * + * See the `SizedBufferMustWriteSideEffectInstruction` documentation for more details. + */ + class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, + TSizedBufferMustWriteSideEffect { + final override string toString() { result = "SizedBufferMustWriteSideEffect" } + } + + /** + * The `Opcode` for a `SizedBufferMayWriteSideEffectInstruction`. + * + * See the `SizedBufferMayWriteSideEffectInstruction` documentation for more details. + */ + class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, + MayWriteOpcode, TSizedBufferMayWriteSideEffect { + final override string toString() { result = "SizedBufferMayWriteSideEffect" } + } + + /** + * The `Opcode` for an `InitializeDynamicAllocationInstruction`. + * + * See the `InitializeDynamicAllocationInstruction` documentation for more details. + */ + class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode, + TInitializeDynamicAllocation { + final override string toString() { result = "InitializeDynamicAllocation" } + } + + /** + * The `Opcode` for a `ChiInstruction`. + * + * See the `ChiInstruction` documentation for more details. + */ + class Chi extends Opcode, TChi { + final override string toString() { result = "Chi" } + + final override predicate hasOperandInternal(OperandTag tag) { + tag instanceof ChiTotalOperandTag + or + tag instanceof ChiPartialOperandTag + } + + final override MemoryAccessKind getWriteMemoryAccess() { + result instanceof ChiTotalMemoryAccess + } + } + + /** + * The `Opcode` for an `InlineAsmInstruction`. + * + * See the `InlineAsmInstruction` documentation for more details. + */ + class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode, + MayReadOpcode, TInlineAsm { + final override string toString() { result = "InlineAsm" } + + final override predicate hasOperandInternal(OperandTag tag) { + tag instanceof SideEffectOperandTag + } + } + + /** + * The `Opcode` for an `UnreachedInstruction`. + * + * See the `UnreachedInstruction` documentation for more details. + */ + class Unreached extends Opcode, TUnreached { + final override string toString() { result = "Unreached" } + } + + /** + * The `Opcode` for a `NewObjInstruction`. + * + * See the `NewObjInstruction` documentation for more details. + */ + class NewObj extends Opcode, TNewObj { + final override string toString() { result = "NewObj" } + } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll b/csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll similarity index 92% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll rename to csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll index a0c0ca675307..5f230de560d6 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll +++ b/csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll @@ -12,5 +12,6 @@ private import Imports::TempVariableTag * computed on each branch. The set of possible `TempVariableTag`s is language-dependent. */ class TempVariableTag extends TTempVariableTag { + /** Gets a textual representation of this tag. */ string toString() { result = getTempVariableTagId(this) } } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/UseSoundEscapeAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/UseSoundEscapeAnalysis.qll rename to csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll diff --git a/csharp/ql/src/experimental/ir/implementation/internal/AliasedSSAStub.qll b/csharp/ql/src/experimental/ir/implementation/internal/AliasedSSAStub.qll new file mode 100644 index 000000000000..0fedd38bfbd2 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/AliasedSSAStub.qll @@ -0,0 +1,19 @@ +/** + * Provides a stub implementation of the required aliased SSA interface until we implement aliased + * SSA construction for C#. + */ + +private import IRFunctionBase +private import TInstruction + +module SSA { + class MemoryLocation = boolean; + + predicate hasPhiInstruction(TRawInstruction blockStartInstr, MemoryLocation memoryLocation) { + none() + } + + predicate hasChiInstruction(TRawInstruction primaryInstruction) { none() } + + predicate hasUnreachedInstruction(IRFunctionBase irFunc) { none() } +} diff --git a/csharp/ql/src/experimental/ir/implementation/internal/EdgeKindInternal.qll b/csharp/ql/src/experimental/ir/implementation/internal/EdgeKindInternal.qll new file mode 100644 index 000000000000..ebcc9573bce2 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/EdgeKindInternal.qll @@ -0,0 +1 @@ +import experimental.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/experimental/ir/implementation/internal/IRConfigurationInternal.qll b/csharp/ql/src/experimental/ir/implementation/internal/IRConfigurationInternal.qll new file mode 100644 index 000000000000..ebcc9573bce2 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/IRConfigurationInternal.qll @@ -0,0 +1 @@ +import experimental.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll b/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll new file mode 100644 index 000000000000..60895ce3d266 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll @@ -0,0 +1,27 @@ +/** + * Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`. + */ + +private import IRFunctionBaseInternal + +private newtype TIRFunction = + MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } + +/** + * The IR for a function. This base class contains only the predicates that are the same between all + * phases of the IR. Each instantiation of `IRFunction` extends this class. + */ +class IRFunctionBase extends TIRFunction { + Language::Function func; + + IRFunctionBase() { this = MkIRFunction(func) } + + /** Gets a textual representation of this element. */ + final string toString() { result = "IR: " + func.toString() } + + /** Gets the function whose IR is represented. */ + final Language::Function getFunction() { result = func } + + /** Gets the location of the function. */ + final Language::Location getLocation() { result = func.getLocation() } +} diff --git a/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBaseInternal.qll b/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBaseInternal.qll new file mode 100644 index 000000000000..f2da59bbb1d4 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBaseInternal.qll @@ -0,0 +1,2 @@ +import experimental.ir.internal.IRCSharpLanguage as Language +import experimental.ir.implementation.raw.internal.IRConstruction as IRConstruction diff --git a/csharp/ql/src/experimental/ir/implementation/internal/IRTypeInternal.qll b/csharp/ql/src/experimental/ir/implementation/internal/IRTypeInternal.qll new file mode 100644 index 000000000000..ebcc9573bce2 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/IRTypeInternal.qll @@ -0,0 +1 @@ +import experimental.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/experimental/ir/implementation/internal/OpcodeImports.qll b/csharp/ql/src/experimental/ir/implementation/internal/OpcodeImports.qll new file mode 100644 index 000000000000..8bacf51d8a2b --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/OpcodeImports.qll @@ -0,0 +1 @@ +import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll b/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll similarity index 82% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll rename to csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll index ac284440648d..21dfedd95cd4 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll @@ -40,7 +40,17 @@ abstract class OperandTag extends TOperandTag { /** * Gets a label that will appear before the operand when the IR is printed. */ - string getLabel() { result = "" } + final string getLabel() { if alwaysPrintLabel() then result = getId() + ":" else result = "" } + + /** + * Gets an identifier that uniquely identifies this operand within its instruction. + */ + abstract string getId(); + + /** + * Holds if the operand should always be prefixed with its label in the dump of its instruction. + */ + predicate alwaysPrintLabel() { none() } } /** @@ -69,7 +79,9 @@ class AddressOperandTag extends RegisterOperandTag, TAddressOperand { final override int getSortOrder() { result = 0 } - final override string getLabel() { result = "&:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "&" } } AddressOperandTag addressOperand() { result = TAddressOperand() } @@ -82,6 +94,8 @@ class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand { final override string toString() { result = "BufferSize" } final override int getSortOrder() { result = 1 } + + final override string getId() { result = "size" } } BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() } @@ -93,6 +107,8 @@ class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand { final override string toString() { result = "SideEffect" } final override int getSortOrder() { result = 2 } + + final override string getId() { result = "side_effect" } } SideEffectOperandTag sideEffectOperand() { result = TSideEffectOperand() } @@ -105,6 +121,8 @@ class LoadOperandTag extends TypedOperandTag, TLoadOperand { final override string toString() { result = "Load" } final override int getSortOrder() { result = 3 } + + final override string getId() { result = "load" } } LoadOperandTag loadOperand() { result = TLoadOperand() } @@ -116,6 +134,8 @@ class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand { final override string toString() { result = "StoreValue" } final override int getSortOrder() { result = 4 } + + final override string getId() { result = "store" } } StoreValueOperandTag storeValueOperand() { result = TStoreValueOperand() } @@ -127,6 +147,8 @@ class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand { final override string toString() { result = "Unary" } final override int getSortOrder() { result = 5 } + + final override string getId() { result = "unary" } } UnaryOperandTag unaryOperand() { result = TUnaryOperand() } @@ -138,6 +160,8 @@ class LeftOperandTag extends RegisterOperandTag, TLeftOperand { final override string toString() { result = "Left" } final override int getSortOrder() { result = 6 } + + final override string getId() { result = "left" } } LeftOperandTag leftOperand() { result = TLeftOperand() } @@ -149,6 +173,8 @@ class RightOperandTag extends RegisterOperandTag, TRightOperand { final override string toString() { result = "Right" } final override int getSortOrder() { result = 7 } + + final override string getId() { result = "right" } } RightOperandTag rightOperand() { result = TRightOperand() } @@ -160,6 +186,8 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand { final override string toString() { result = "Condition" } final override int getSortOrder() { result = 8 } + + final override string getId() { result = "cond" } } ConditionOperandTag conditionOperand() { result = TConditionOperand() } @@ -172,7 +200,9 @@ class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand { final override int getSortOrder() { result = 10 } - final override string getLabel() { result = "func:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "func" } } CallTargetOperandTag callTargetOperand() { result = TCallTargetOperand() } @@ -195,7 +225,9 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand { final override int getSortOrder() { result = 11 } - final override string getLabel() { result = "this:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "this" } } ThisArgumentOperandTag thisArgumentOperand() { result = TThisArgumentOperand() } @@ -212,9 +244,11 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, TPositionalArgume final override int getSortOrder() { result = 12 + argIndex } - final override string getLabel() { result = argIndex.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } final int getArgIndex() { result = argIndex } + + final override string getId() { result = argIndex.toString() } } PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) { @@ -228,7 +262,9 @@ class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand { final override int getSortOrder() { result = 13 } - final override string getLabel() { result = "total:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "total" } } ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() } @@ -238,7 +274,9 @@ class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand { final override int getSortOrder() { result = 14 } - final override string getLabel() { result = "partial:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "partial" } } ChiPartialOperandTag chiPartialOperand() { result = TChiPartialOperand() } @@ -252,7 +290,9 @@ class AsmOperandTag extends RegisterOperandTag, TAsmOperand { final override int getSortOrder() { result = 15 + index } - final override string getLabel() { result = index.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = index.toString() } } AsmOperandTag asmOperand(int index) { result = TAsmOperand(index) } diff --git a/csharp/ql/src/experimental/ir/implementation/internal/OperandTagInternal.qll b/csharp/ql/src/experimental/ir/implementation/internal/OperandTagInternal.qll new file mode 100644 index 000000000000..ebcc9573bce2 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/OperandTagInternal.qll @@ -0,0 +1 @@ +import experimental.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll b/csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll rename to csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TIRVariableInternal.qll b/csharp/ql/src/experimental/ir/implementation/internal/TIRVariableInternal.qll new file mode 100644 index 000000000000..e2b2c408a4f6 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/TIRVariableInternal.qll @@ -0,0 +1,7 @@ +import experimental.ir.internal.IRCSharpLanguage as Language +import experimental.ir.implementation.raw.internal.IRConstruction::Raw as Construction +private import experimental.ir.implementation.TempVariableTag as TempVariableTag_ + +module Imports { + module TempVariableTag = TempVariableTag_; +} diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll new file mode 100644 index 000000000000..e16b71733b5a --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll @@ -0,0 +1,97 @@ +private import TInstructionInternal +private import IRFunctionBase +private import TInstructionImports as Imports +private import Imports::IRType +private import Imports::Opcode + +/** + * An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual + * branches of this type for instructions created directly from the AST (`TRawInstruction`) and for + * instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`, + * `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of + * all of the branches that can appear in that particular stage. The public `Instruction` class for + * each phase extends the `TStageInstruction` type for that stage. + */ +cached +newtype TInstruction = + TRawInstruction( + IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2 + ) { + IRConstruction::Raw::hasInstruction(tag1, tag2) + } or + TUnaliasedSSAPhiInstruction( + TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation + ) { + UnaliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation) + } or + TUnaliasedSSAChiInstruction(TRawInstruction primaryInstruction) { none() } or + TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { + UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc) + } or + TAliasedSSAPhiInstruction( + TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation + ) { + AliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation) + } or + TAliasedSSAChiInstruction(TRawInstruction primaryInstruction) { + AliasedSSA::SSA::hasChiInstruction(primaryInstruction) + } or + TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) { + AliasedSSA::SSA::hasUnreachedInstruction(irFunc) + } + +/** + * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the + * unaliased SSA stage. + * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via + * a class alias. + */ +module UnaliasedSSAInstructions { + class TPhiInstruction = TUnaliasedSSAPhiInstruction; + + TPhiInstruction phiInstruction( + TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation + ) { + result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation) + } + + class TChiInstruction = TUnaliasedSSAChiInstruction; + + TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { + result = TUnaliasedSSAChiInstruction(primaryInstruction) + } + + class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction; + + TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) { + result = TUnaliasedSSAUnreachedInstruction(irFunc) + } +} + +/** + * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the + * aliased SSA stage. + * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via + * a class alias. + */ +module AliasedSSAInstructions { + class TPhiInstruction = TAliasedSSAPhiInstruction; + + TPhiInstruction phiInstruction( + TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation + ) { + result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation) + } + + class TChiInstruction = TAliasedSSAChiInstruction; + + TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { + result = TAliasedSSAChiInstruction(primaryInstruction) + } + + class TUnreachedInstruction = TAliasedSSAUnreachedInstruction; + + TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) { + result = TAliasedSSAUnreachedInstruction(irFunc) + } +} diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TInstructionImports.qll b/csharp/ql/src/experimental/ir/implementation/internal/TInstructionImports.qll new file mode 100644 index 000000000000..6200f2a27966 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/TInstructionImports.qll @@ -0,0 +1,2 @@ +import experimental.ir.implementation.IRType as IRType +import experimental.ir.implementation.Opcode as Opcode diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TInstructionInternal.qll b/csharp/ql/src/experimental/ir/implementation/internal/TInstructionInternal.qll new file mode 100644 index 000000000000..978d2c41aa79 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/TInstructionInternal.qll @@ -0,0 +1,4 @@ +import experimental.ir.internal.IRCSharpLanguage as Language +import experimental.ir.implementation.raw.internal.IRConstruction as IRConstruction +import experimental.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA +import AliasedSSAStub as AliasedSSA diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TempVariableTagInternal.qll b/csharp/ql/src/experimental/ir/implementation/internal/TempVariableTagInternal.qll new file mode 100644 index 000000000000..6d9f3e1e2db1 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/internal/TempVariableTagInternal.qll @@ -0,0 +1,6 @@ +import experimental.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.internal.TempVariableTag as TempVariableTag_ + +module Imports { + module TempVariableTag = TempVariableTag_; +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll new file mode 100644 index 000000000000..c96783fe6e81 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll @@ -0,0 +1,80 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + +import IRFunction +import Instruction +import IRBlock +import IRVariable +import Operand +private import internal.IRImports as Imports +import Imports::EdgeKind +import Imports::IRType +import Imports::MemoryAccessKind + +private newtype TIRPropertyProvider = MkIRPropertyProvider() + +/** + * A class that provides additional properties to be dumped for IR instructions and blocks when using + * the PrintIR module. Libraries that compute additional facts about IR elements can extend the + * single instance of this class to specify the additional properties computed by the library. + */ +class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ + string toString() { result = "IRPropertyProvider" } + + /** + * Gets the value of the property named `key` for the specified instruction. + */ + string getInstructionProperty(Instruction instruction, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified block. + */ + string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll new file mode 100644 index 000000000000..d827ed3cf82d --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -0,0 +1,290 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + +private import internal.IRInternal +import Instruction +private import internal.IRBlockImports as Imports +import Imports::EdgeKind +private import Cached + +/** + * A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only + * incoming edges at the beginning of the sequence and the only outgoing edges at the end of the + * sequence. + * + * This class does not contain any members that query the predecessor or successor edges of the + * block. This allows different classes that extend `IRBlockBase` to expose different subsets of + * edges (e.g. ignoring unreachable edges). + * + * Most consumers should use the class `IRBlock`. + */ +class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ + final string toString() { result = getFirstInstruction(this).toString() } + + /** Gets the source location of the first non-`Phi` instruction in this block. */ + final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + + /** + * INTERNAL: Do not use. + * + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ + final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } + + /** + * INTERNAL: Do not use. + * + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. + */ + int getDisplayIndex() { + exists(IRConfiguration::IRConfiguration config | + config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction()) + ) and + this = + rank[result + 1](IRBlock funcBlock, int sortOverride | + funcBlock.getEnclosingFunction() = getEnclosingFunction() and + // Ensure that the block containing `EnterFunction` always comes first. + if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction + then sortOverride = 0 + else sortOverride = 1 + | + funcBlock order by sortOverride, funcBlock.getUniqueId() + ) + } + + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ + final Instruction getInstruction(int index) { result = getInstruction(this, index) } + + /** + * Get the `Phi` instructions that appear at the start of this block. + */ + final PhiInstruction getAPhiInstruction() { + Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() + } + + /** + * Gets an instruction in this block. This includes `Phi` instructions. + */ + final Instruction getAnInstruction() { + result = getInstruction(_) or + result = getAPhiInstruction() + } + + /** + * Gets the first non-`Phi` instruction in this block. + */ + final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + + /** + * Gets the last instruction in this block. + */ + final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + + /** + * Gets the number of non-`Phi` instructions in this block. + */ + final int getInstructionCount() { result = getInstructionCount(this) } + + /** + * Gets the `IRFunction` that contains this block. + */ + final IRFunction getEnclosingIRFunction() { + result = getFirstInstruction(this).getEnclosingIRFunction() + } + + /** + * Gets the `Function` that contains this block. + */ + final Language::Function getEnclosingFunction() { + result = getFirstInstruction(this).getEnclosingFunction() + } +} + +/** + * A basic block with additional information about its predecessor and successor edges. Each edge + * corresponds to the control flow between the last instruction of one block and the first + * instruction of another block. + */ +class IRBlock extends IRBlockBase { + /** + * Gets a block to which control flows directly from this block. + */ + final IRBlock getASuccessor() { blockSuccessor(this, result) } + + /** + * Gets a block from which control flows directly to this block. + */ + final IRBlock getAPredecessor() { blockSuccessor(result, this) } + + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ + final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ + final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ + final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ + final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ + final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + + /** + * Gets a block on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ + pragma[noinline] + final IRBlock dominanceFrontier() { + dominates(result.getAPredecessor()) and + not strictlyDominates(result) + } + + /** + * Holds if this block is reachable from the entry block of its function. + */ + final predicate isReachableFromFunctionEntry() { + this = getEnclosingIRFunction().getEntryBlock() or + getAPredecessor().isReachableFromFunctionEntry() + } +} + +private predicate startsBasicBlock(Instruction instr) { + not instr instanceof PhiInstruction and + not adjacentInBlock(_, instr) +} + +/** Holds if `i2` follows `i1` in a `IRBlock`. */ +private predicate adjacentInBlock(Instruction i1, Instruction i2) { + // - i2 must be the only successor of i1 + i2 = unique(Instruction i | i = i1.getASuccessor()) and + // - i1 must be the only predecessor of i2 + i1 = unique(Instruction i | i.getASuccessor() = i2) and + // - The edge between the two must be a GotoEdge. We just check that one + // exists since we've already checked that it's unique. + exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and + // - The edge must not be a back edge. This means we get the same back edges + // in the basic-block graph as we do in the raw CFG. + not exists(Construction::getInstructionBackEdgeSuccessor(i1, _)) + // This predicate could be simplified to remove one of the `unique`s if we + // were willing to rely on the CFG being well-formed and thus never having + // more than one successor to an instruction that has a `GotoEdge` out of it. +} + +private predicate isEntryBlock(TIRBlock block) { + block = MkIRBlock(any(EnterFunctionInstruction enter)) +} + +cached +private module Cached { + cached + newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) } + + /** Holds if `i` is the `index`th instruction the block starting with `first`. */ + private Instruction getInstructionFromFirst(Instruction first, int index) = + shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index) + + /** Holds if `i` is the `index`th instruction in `block`. */ + cached + Instruction getInstruction(TIRBlock block, int index) { + result = getInstructionFromFirst(getFirstInstruction(block), index) + } + + cached + int getInstructionCount(TIRBlock block) { result = strictcount(getInstruction(block, _)) } + + cached + predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + exists(Instruction predLast, Instruction succFirst | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and + succFirst = predLast.getSuccessor(kind) and + succ = MkIRBlock(succFirst) + ) + } + + pragma[noinline] + private predicate blockIdentity(TIRBlock b1, TIRBlock b2) { b1 = b2 } + + pragma[noopt] + cached + predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + backEdgeSuccessorRaw(pred, succ, kind) + or + // See the QLDoc on `backEdgeSuccessorRaw`. + exists(TIRBlock pred2 | + // Joining with `blockIdentity` is a performance trick to get + // `forwardEdgeRaw` on the RHS of a join, where it's fast. + blockIdentity(pred, pred2) and + forwardEdgeRaw+(pred, pred2) + ) and + blockSuccessor(pred, succ, kind) + } + + /** + * Holds if there is an edge from `pred` to `succ` that is not a back edge. + */ + private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) { + exists(EdgeKind kind | + blockSuccessor(pred, succ, kind) and + not backEdgeSuccessorRaw(pred, succ, kind) + ) + } + + /** + * Holds if the `kind`-edge from `pred` to `succ` is a back edge according to + * `Construction`. + * + * There could be loops of non-back-edges if there is a flaw in the IR + * construction or back-edge detection, and this could cause non-termination + * of subsequent analysis. To prevent that, a subsequent predicate further + * classifies all edges as back edges if they are involved in a loop of + * non-back-edges. + */ + private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + exists(Instruction predLast, Instruction succFirst | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and + succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and + succ = MkIRBlock(succFirst) + ) + } + + cached + predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { blockSuccessor(pred, succ, _) } + + cached + predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) +} + +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRConsistency.ql b/csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.ql similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRConsistency.ql rename to csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.ql diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll new file mode 100644 index 000000000000..6a87b9b4b5fd --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll @@ -0,0 +1,497 @@ +private import IR +import InstructionConsistency // module is below +import IRTypeConsistency // module is in IRType.qll + +module InstructionConsistency { + private import internal.InstructionImports as Imports + private import Imports::OperandTag + private import Imports::Overlap + private import internal.IRInternal + + private newtype TOptionalIRFunction = + TPresentIRFunction(IRFunction irFunc) or + TMissingIRFunction() + + /** + * An `IRFunction` that might not exist. This is used so that we can produce consistency failures + * for IR that also incorrectly lacks a `getEnclosingIRFunction()`. + */ + abstract private class OptionalIRFunction extends TOptionalIRFunction { + abstract string toString(); + + abstract Language::Location getLocation(); + } + + private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction { + private IRFunction irFunc; + + PresentIRFunction() { this = TPresentIRFunction(irFunc) } + + override string toString() { + result = concat(Language::getIdentityString(irFunc.getFunction()), "; ") + } + + override Language::Location getLocation() { + // To avoid an overwhelming number of results when the extractor merges functions with the + // same name, just pick a single location. + result = + rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString()) + } + } + + private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction { + override string toString() { result = "" } + + override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation } + } + + private OptionalIRFunction getInstructionIRFunction(Instruction instr) { + result = TPresentIRFunction(instr.getEnclosingIRFunction()) + or + not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) { + result = getInstructionIRFunction(instr) and + irFuncText = result.toString() + } + + private OptionalIRFunction getOperandIRFunction(Operand operand) { + result = TPresentIRFunction(operand.getEnclosingIRFunction()) + or + not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) { + result = getOperandIRFunction(operand) and + irFuncText = result.toString() + } + + private OptionalIRFunction getBlockIRFunction(IRBlock block) { + result = TPresentIRFunction(block.getEnclosingIRFunction()) + or + not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + /** + * Holds if instruction `instr` is missing an expected operand with tag `tag`. + */ + query predicate missingOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag | + instr.getOpcode().hasOperand(tag) and + not exists(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + message = + "Instruction '" + instr.getOpcode().toString() + + "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if instruction `instr` has an unexpected operand with tag `tag`. + */ + query predicate unexpectedOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag | + exists(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + not instr.getOpcode().hasOperand(tag) and + not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and + not ( + instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag + ) and + not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and + message = + "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() + + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if instruction `instr` has multiple operands with tag `tag`. + */ + query predicate duplicateOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag, int operandCount | + operandCount = + strictcount(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + operandCount > 1 and + message = + "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + + " in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if `Phi` instruction `instr` is missing an operand corresponding to + * the predecessor block `pred`. + */ + query predicate missingPhiOperand( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRBlock pred | + pred = instr.getBlock().getAPredecessor() and + not exists(PhiInputOperand operand | + operand = instr.getAnOperand() and + operand.getPredecessorBlock() = pred + ) and + message = + "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" + + pred.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + query predicate missingOperandType( + Operand operand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Instruction use | + not exists(operand.getType()) and + use = operand.getUse() and + message = + "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + + "' is missing a type in function '$@'." and + irFunc = getOperandIRFunction(operand, irFuncText) + ) + } + + query predicate duplicateChiOperand( + ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText + ) { + chi.getTotal() = chi.getPartial() and + message = + "Chi instruction for " + chi.getPartial().toString() + + " has duplicate operands in function '$@'." and + irFunc = getInstructionIRFunction(chi, irFuncText) + } + + query predicate sideEffectWithoutPrimary( + SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + not exists(instr.getPrimaryInstruction()) and + message = + "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } + + /** + * Holds if an instruction, other than `ExitFunction`, has no successors. + */ + query predicate instructionWithoutSuccessor( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + not exists(instr.getASuccessor()) and + not instr instanceof ExitFunctionInstruction and + // Phi instructions aren't linked into the instruction-level flow graph. + not instr instanceof PhiInstruction and + not instr instanceof UnreachedInstruction and + message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } + + /** + * Holds if there are multiple edges of the same kind from `source`. + */ + query predicate ambiguousSuccessors( + Instruction source, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(EdgeKind kind, int n | + n = strictcount(Instruction t | source.getSuccessor(kind) = t) and + n > 1 and + message = + "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" + + kind.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(source, irFuncText) + ) + } + + /** + * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function + * contains no element that can cause loops. + */ + query predicate unexplainedLoop( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Language::Function f | + exists(IRBlock block | + instr.getBlock() = block and + block.getEnclosingFunction() = f and + block.getASuccessor+() = block + ) and + not Language::hasPotentialLoop(f) and + message = + "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if a `Phi` instruction is present in a block with fewer than two + * predecessors. + */ + query predicate unnecessaryPhiInstruction( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int n | + n = count(instr.getBlock().getAPredecessor()) and + n < 2 and + message = + "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() + + " predecessors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if a memory operand is connected to a definition with an unmodeled result. + */ + query predicate memoryOperandDefinitionIsUnmodeled( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(MemoryOperand operand, Instruction def | + operand = instr.getAnOperand() and + def = operand.getAnyDef() and + not def.isResultModeled() and + message = + "Memory operand definition on instruction '" + instr.toString() + + "' has unmodeled result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if operand `operand` consumes a value that was defined in + * a different function. + */ + query predicate operandAcrossFunctions( + Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText, + OptionalIRFunction defIRFunc, string defIRFuncText + ) { + exists(Instruction useInstr, Instruction defInstr | + operand.getUse() = useInstr and + operand.getAnyDef() = defInstr and + useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and + defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and + useIRFunc != defIRFunc and + message = + "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() + + "' in function '$@', but is defined on instruction '" + defInstr.toString() + + "' in function '$@'." + ) + } + + /** + * Holds if instruction `instr` is not in exactly one block. + */ + query predicate instructionWithoutUniqueBlock( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int blockCount | + blockCount = count(instr.getBlock()) and + blockCount != 1 and + message = + "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() + + " blocks in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + private predicate forwardEdge(IRBlock b1, IRBlock b2) { + b1.getASuccessor() = b2 and + not b1.getBackEdgeSuccessor(_) = b2 + } + + /** + * Holds if `f` contains a loop in which no edge is a back edge. + * + * This check ensures we don't have too _few_ back edges. + */ + query predicate containsLoopOfForwardEdges(IRFunction f, string message) { + exists(IRBlock block | + forwardEdge+(block, block) and + block.getEnclosingIRFunction() = f and + message = "Function contains a loop consisting of only forward edges." + ) + } + + /** + * Holds if `block` is reachable from its function entry point but would not + * be reachable by traversing only forward edges. This check is skipped for + * functions containing `goto` statements as the property does not generally + * hold there. + * + * This check ensures we don't have too _many_ back edges. + */ + query predicate lostReachability( + IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRFunction f, IRBlock entry | + entry = f.getEntryBlock() and + entry.getASuccessor+() = block and + not forwardEdge+(entry, block) and + not Language::hasGoto(f.getFunction()) and + message = + "Block '" + block.toString() + + "' is not reachable by traversing only forward edges in function '$@'." and + irFunc = TPresentIRFunction(f) and + irFuncText = irFunc.toString() + ) + } + + /** + * Holds if the number of back edges differs between the `Instruction` graph + * and the `IRBlock` graph. + */ + query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) { + exists(int fromInstr, int fromBlock | + fromInstr = + count(Instruction i1, Instruction i2 | + getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2 + ) and + fromBlock = + count(IRBlock b1, IRBlock b2 | + getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2 + ) and + fromInstr != fromBlock and + message = + "The instruction graph for function '" + irFunc.toString() + "' contains " + + fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString() + + " back edges." + ) + } + + /** + * Gets the point in the function at which the specified operand is evaluated. For most operands, + * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point + * of evaluation is at the end of the corresponding predecessor block. + */ + private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) { + block = operand.(PhiInputOperand).getPredecessorBlock() and + index = block.getInstructionCount() + or + exists(Instruction use | + use = operand.(NonPhiOperand).getUse() and + block.getInstruction(index) = use + ) + } + + /** + * Holds if `useOperand` has a definition that does not dominate the use. + */ + query predicate useNotDominatedByDefinition( + Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | + pointOfEvaluation(useOperand, useBlock, useIndex) and + defInstr = useOperand.getAnyDef() and + ( + defInstr instanceof PhiInstruction and + defBlock = defInstr.getBlock() and + defIndex = -1 + or + defBlock.getInstruction(defIndex) = defInstr + ) and + not ( + defBlock.strictlyDominates(useBlock) + or + defBlock = useBlock and + defIndex < useIndex + ) and + message = + "Operand '" + useOperand.toString() + + "' is not dominated by its definition in function '$@'." and + irFunc = getOperandIRFunction(useOperand, irFuncText) + ) + } + + query predicate switchInstructionWithoutDefaultEdge( + SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + not exists(switchInstr.getDefaultSuccessor()) and + message = + "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and + irFunc = getInstructionIRFunction(switchInstr, irFuncText) + } + + /** + * Holds if `instr` is on the chain of chi/phi instructions for all aliased + * memory. + */ + private predicate isOnAliasedDefinitionChain(Instruction instr) { + instr instanceof AliasedDefinitionInstruction + or + isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) + or + isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) + } + + private predicate shouldBeConflated(Instruction instr) { + isOnAliasedDefinitionChain(instr) + or + instr.getOpcode() instanceof Opcode::InitializeNonLocal + } + + query predicate notMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + shouldBeConflated(instr) and + not instr.isResultConflated() and + message = + "Instruction '" + instr.toString() + + "' should be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } + + query predicate wronglyMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + instr.isResultConflated() and + not shouldBeConflated(instr) and + message = + "Instruction '" + instr.toString() + + "' should not be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } + + query predicate invalidOverlap( + MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Overlap overlap | + overlap = useOperand.getDefinitionOverlap() and + overlap instanceof MayPartiallyOverlap and + message = + "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" + + overlap.toString() + "'." and + irFunc = getOperandIRFunction(useOperand, irFuncText) + ) + } + + query predicate nonUniqueEnclosingIRFunction( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int irFuncCount | + irFuncCount = count(instr.getEnclosingIRFunction()) and + irFuncCount != 1 and + message = + "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() + + " results for `getEnclosingIRFunction()` in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll new file mode 100644 index 000000000000..5968e58f90bf --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll @@ -0,0 +1,59 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + +private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase +import Instruction + +/** + * The IR for a function. + */ +class IRFunction extends IRFunctionBase { + /** + * Gets the entry point for this function. + */ + pragma[noinline] + final EnterFunctionInstruction getEnterFunctionInstruction() { + result.getEnclosingIRFunction() = this + } + + /** + * Gets the exit point for this function. + */ + pragma[noinline] + final ExitFunctionInstruction getExitFunctionInstruction() { + result.getEnclosingIRFunction() = this + } + + /** + * Gets the single return instruction for this function. + */ + pragma[noinline] + final ReturnInstruction getReturnInstruction() { result.getEnclosingIRFunction() = this } + + /** + * Gets the variable used to hold the return value of this function. If this + * function does not return a value, this predicate does not hold. + */ + pragma[noinline] + final IRReturnVariable getReturnVariable() { result.getEnclosingIRFunction() = this } + + /** + * Gets the block containing the entry point of this function. + */ + pragma[noinline] + final IRBlock getEntryBlock() { result.getFirstInstruction() = getEnterFunctionInstruction() } + + /** + * Gets all instructions in this function. + */ + final Instruction getAnInstruction() { result.getEnclosingIRFunction() = this } + + /** + * Gets all blocks in this function. + */ + final IRBlock getABlock() { result.getEnclosingIRFunction() = this } +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll new file mode 100644 index 000000000000..146fc2707383 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll @@ -0,0 +1,327 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + +private import internal.IRInternal +import IRFunction +private import internal.IRVariableImports as Imports +import Imports::TempVariableTag +private import Imports::IRUtilities +private import Imports::TTempVariableTag +private import Imports::TIRVariable +private import Imports::IRType + +/** + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). + */ +class IRVariable extends TIRVariable { + Language::Function func; + + IRVariable() { + this = TIRUserVariable(_, _, func) or + this = TIRTempVariable(func, _, _, _) or + this = TIRStringLiteral(func, _, _, _) or + this = TIRDynamicInitializationFlag(func, _, _) + } + + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Holds if this variable's value cannot be changed within a function. Currently used for string + * literals, but could also apply to `const` global and static variables. + */ + predicate isReadOnly() { none() } + + /** + * Gets the type of the variable. + */ + final Language::Type getType() { getLanguageType().hasType(result, false) } + + /** + * Gets the language-neutral type of the variable. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the variable. + */ + Language::LanguageType getLanguageType() { none() } + + /** + * Gets the AST node that declared this variable, or that introduced this + * variable as part of the AST-to-IR translation. + */ + Language::AST getAST() { none() } + + /** + * Gets an identifier string for the variable. This identifier is unique + * within the function. + */ + string getUniqueId() { none() } + + /** + * Gets the source location of this variable. + */ + final Language::Location getLocation() { result = getAST().getLocation() } + + /** + * Gets the IR for the function that references this variable. + */ + final IRFunction getEnclosingIRFunction() { result.getFunction() = func } + + /** + * Gets the function that references this variable. + */ + final Language::Function getEnclosingFunction() { result = func } +} + +/** + * A user-declared variable referenced by the IR for a function. + */ +class IRUserVariable extends IRVariable, TIRUserVariable { + Language::Variable var; + Language::LanguageType type; + + IRUserVariable() { this = TIRUserVariable(var, type, func) } + + final override string toString() { result = getVariable().toString() } + + final override Language::AST getAST() { result = var } + + final override string getUniqueId() { + result = getVariable().toString() + " " + getVariable().getLocation().toString() + } + + final override Language::LanguageType getLanguageType() { result = type } + + /** + * Gets the original user-declared variable. + */ + Language::Variable getVariable() { result = var } +} + +/** + * A variable (user-declared or temporary) that is allocated on the stack. This includes all + * parameters, non-static local variables, and temporary variables. + */ +class IRAutomaticVariable extends IRVariable { + IRAutomaticVariable() { + exists(Language::Variable var | + this = TIRUserVariable(var, _, func) and + Language::isVariableAutomatic(var) + ) + or + this = TIRTempVariable(func, _, _, _) + } +} + +/** + * A user-declared variable that is allocated on the stack. This includes all parameters and + * non-static local variables. + */ +class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { + override Language::AutomaticVariable var; + + final override Language::AutomaticVariable getVariable() { result = var } +} + +/** + * A user-declared variable that is not allocated on the stack. This includes all global variables, + * namespace-scope variables, static fields, and static local variables. + */ +class IRStaticUserVariable extends IRUserVariable { + override Language::StaticVariable var; + + IRStaticUserVariable() { not Language::isVariableAutomatic(var) } + + final override Language::StaticVariable getVariable() { result = var } +} + +/** + * A variable that is not user-declared. This includes temporary variables generated as part of IR + * construction, as well as string literals. + */ +class IRGeneratedVariable extends IRVariable { + Language::AST ast; + Language::LanguageType type; + + IRGeneratedVariable() { + this = TIRTempVariable(func, ast, _, type) or + this = TIRStringLiteral(func, ast, type, _) or + this = TIRDynamicInitializationFlag(func, ast, type) + } + + final override Language::LanguageType getLanguageType() { result = type } + + final override Language::AST getAST() { result = ast } + + override string toString() { result = getBaseString() + getLocationString() } + + override string getUniqueId() { none() } + + /** + * INTERNAL: Do not use. + * + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ + final string getLocationString() { + result = + ast.getLocation().getStartLine().toString() + ":" + + ast.getLocation().getStartColumn().toString() + } + + /** + * INTERNAL: Do not use. + * + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ + string getBaseString() { none() } +} + +/** + * A temporary variable introduced by IR construction. The most common examples are the variable + * generated to hold the return value of a function, or the variable generated to hold the result of + * a condition operator (`a ? b : c`). + */ +class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { + TempVariableTag tag; + + IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } + + final override string getUniqueId() { + result = "Temp: " + Construction::getTempVariableUniqueId(this) + } + + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ + final TempVariableTag getTag() { result = tag } + + override string getBaseString() { result = "#temp" } +} + +/** + * A temporary variable generated to hold the return value of a function. + */ +class IRReturnVariable extends IRTempVariable { + IRReturnVariable() { tag = ReturnValueTempVar() } + + final override string toString() { result = "#return" } +} + +/** + * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. + */ +class IRThrowVariable extends IRTempVariable { + IRThrowVariable() { tag = ThrowTempVar() } + + final override string getBaseString() { result = "#throw" } +} + +/** + * A temporary variable generated to hold the contents of all arguments passed to the `...` of a + * function that accepts a variable number of arguments. + */ +class IREllipsisVariable extends IRTempVariable, IRParameter { + IREllipsisVariable() { tag = EllipsisTempVar() } + + final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } +} + +/** + * A temporary variable generated to hold the `this` pointer. + */ +class IRThisVariable extends IRTempVariable, IRParameter { + IRThisVariable() { tag = ThisTempVar() } + + final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } +} + +/** + * A variable generated to represent the contents of a string literal. This variable acts much like + * a read-only global variable. + */ +class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { + Language::StringLiteral literal; + + IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) } + + final override predicate isReadOnly() { any() } + + final override string getUniqueId() { + result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal) + } + + final override string getBaseString() { result = "#string" } + + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ + final Language::StringLiteral getLiteral() { result = literal } +} + +/** + * A variable generated to track whether a specific non-stack variable has been initialized. This is + * used to model the runtime initialization of static local variables in C++, as well as static + * fields in C#. + */ +class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag { + Language::Variable var; + + IRDynamicInitializationFlag() { + this = TIRDynamicInitializationFlag(func, var, type) and ast = var + } + + final override string toString() { result = var.toString() + "#init" } + + /** + * Gets variable whose initialization is guarded by this flag. + */ + final Language::Variable getVariable() { result = var } + + final override string getUniqueId() { + result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString() + } + + final override string getBaseString() { result = "#init:" + var.toString() + ":" } +} + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +/** + * An IR variable representing a positional parameter. + */ +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll new file mode 100644 index 000000000000..620b23b942e0 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -0,0 +1,2069 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + +private import internal.IRInternal +import IRFunction +import IRBlock +import IRVariable +import Operand +private import internal.InstructionImports as Imports +import Imports::EdgeKind +import Imports::IRType +import Imports::MemoryAccessKind +import Imports::Opcode +private import Imports::OperandTag + +/** + * Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified + * `File` and line number. Used for assigning register names when printing IR. + */ +private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) { + exists(IRConfiguration::IRConfiguration config | + config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction()) + ) and + exists(Language::Location location | + irFunc = result.getEnclosingIRFunction() and + location = result.getLocation() and + file = location.getFile() and + line = location.getStartLine() + ) +} + +/** + * A single instruction in the IR. + */ +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + + /** Gets a textual representation of this element. */ + final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } + + /** + * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what + * would be printed by PrintIR.ql. For example: + * + * `mu0_28(int) = Store r0_26, r0_27` + */ + final string getDumpString() { + result = getResultString() + " = " + getOperationString() + " " + getOperandsString() + } + + private predicate shouldGenerateDumpStrings() { + exists(IRConfiguration::IRConfiguration config | + config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction()) + ) + } + + /** + * Gets a string describing the operation of this instruction. This includes + * the opcode and the immediate value, if any. For example: + * + * VariableAddress[x] + */ + final string getOperationString() { + shouldGenerateDumpStrings() and + if exists(getImmediateString()) + then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" + else result = getOperationPrefix() + getOpcode().toString() + } + + /** + * Gets a string describing the immediate value of this instruction, if any. + */ + string getImmediateString() { none() } + + private string getOperationPrefix() { + shouldGenerateDumpStrings() and + if this instanceof SideEffectInstruction then result = "^" else result = "" + } + + private string getResultPrefix() { + shouldGenerateDumpStrings() and + if getResultIRType() instanceof IRVoidType + then result = "v" + else + if hasMemoryResult() + then if isResultModeled() then result = "m" else result = "mu" + else result = "r" + } + + /** + * Gets the zero-based index of this instruction within its block. This is + * used by debugging and printing code only. + */ + int getDisplayIndexInBlock() { + shouldGenerateDumpStrings() and + exists(IRBlock block | + this = block.getInstruction(result) + or + this = + rank[-result - 1](PhiInstruction phiInstr | + phiInstr = block.getAPhiInstruction() + | + phiInstr order by phiInstr.getUniqueId() + ) + ) + } + + private int getLineRank() { + shouldGenerateDumpStrings() and + this = + rank[result](Instruction instr | + instr = + getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), + getLocation().getStartLine()) + | + instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() + ) + } + + /** + * Gets a human-readable string that uniquely identifies this instruction + * within the function. This string is used to refer to this instruction when + * printing IR dumps. + * + * Example: `r1_1` + */ + string getResultId() { + shouldGenerateDumpStrings() and + result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() + } + + /** + * Gets a string describing the result of this instruction, suitable for + * display in IR dumps. This consists of the result ID plus the type of the + * result. + * + * Example: `r1_1(int*)` + */ + final string getResultString() { + shouldGenerateDumpStrings() and + result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + } + + /** + * Gets a string describing the operands of this instruction, suitable for + * display in IR dumps. + * + * Example: `func:r3_4, this:r3_5` + */ + string getOperandsString() { + shouldGenerateDumpStrings() and + result = + concat(Operand operand | + operand = getAnOperand() + | + operand.getDumpString(), ", " order by operand.getDumpSortOrder() + ) + } + + /** + * Gets a string identifier for this function that is unique among all + * instructions in the same function. + * + * This is used for sorting IR output for tests, and is likely to be + * inefficient for any other use. + */ + final string getUniqueId() { result = Construction::getInstructionUniqueId(this) } + + /** + * Gets the basic block that contains this instruction. + */ + final IRBlock getBlock() { result.getAnInstruction() = this } + + /** + * Gets the function that contains this instruction. + */ + final Language::Function getEnclosingFunction() { + result = getEnclosingIRFunction().getFunction() + } + + /** + * Gets the IRFunction object that contains the IR for this instruction. + */ + final IRFunction getEnclosingIRFunction() { + result = Construction::getInstructionEnclosingIRFunction(this) + } + + /** + * Gets the AST that caused this instruction to be generated. + */ + final Language::AST getAST() { result = Construction::getInstructionAST(this) } + + /** + * Gets the location of the source code for this instruction. + */ + final Language::Location getLocation() { result = getAST().getLocation() } + + /** + * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a + * conversion. + */ + final Language::Expr getConvertedResultExpression() { + result = Raw::getInstructionConvertedResultExpression(this) + } + + /** + * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. + */ + final Language::Expr getUnconvertedResultExpression() { + result = Raw::getInstructionUnconvertedResultExpression(this) + } + + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ + final Language::LanguageType getResultLanguageType() { + result = Construction::getInstructionResultType(this) + } + + /** + * Gets the type of the result produced by this instruction. If the instruction does not produce + * a result, its result type will be `IRVoidType`. + */ + cached + final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + + /** + * Gets the type of the result produced by this instruction. If the + * instruction does not produce a result, its result type will be `VoidType`. + * + * If `isGLValue()` holds, then the result type of this instruction should be + * thought of as "pointer to `getResultType()`". + */ + final Language::Type getResultType() { + exists(Language::LanguageType resultType | + resultType = getResultLanguageType() and + ( + resultType.hasUnspecifiedType(result, _) + or + not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType + ) + ) + } + + /** + * Holds if the result produced by this instruction is a glvalue. If this + * holds, the result of the instruction represents the address of a location, + * and the type of the location is given by `getResultType()`. If this does + * not hold, the result of the instruction represents a value whose type is + * given by `getResultType()`. + * + * For example, the statement `y = x;` generates the following IR: + * ``` + * r1_0(glval: int) = VariableAddress[x] + * r1_1(int) = Load r1_0, mu0_1 + * r1_2(glval: int) = VariableAddress[y] + * mu1_3(int) = Store r1_2, r1_1 + * ``` + * + * The result of each `VariableAddress` instruction is a glvalue of type + * `int`, representing the address of the corresponding integer variable. The + * result of the `Load` instruction is a prvalue of type `int`, representing + * the integer value loaded from variable `x`. + */ + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } + + /** + * Gets the size of the result produced by this instruction, in bytes. If the + * result does not have a known constant size, this predicate does not hold. + * + * If `this.isGLValue()` holds for this instruction, the value of + * `getResultSize()` will always be the size of a pointer. + */ + final int getResultSize() { result = getResultLanguageType().getByteSize() } + + /** + * Gets the opcode that specifies the operation performed by this instruction. + */ + final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } + + /** + * Gets all direct uses of the result of this instruction. The result can be + * an `Operand` for which `isDefinitionInexact` holds. + */ + final Operand getAUse() { result.getAnyDef() = this } + + /** + * Gets all of this instruction's operands. + */ + final Operand getAnOperand() { result.getUse() = this } + + /** + * Holds if this instruction produces a memory result. + */ + final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } + + /** + * Gets the kind of memory access performed by this instruction's result. + * Holds only for instructions with a memory result. + */ + pragma[inline] + final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } + + /** + * Holds if the memory access performed by this instruction's result will not always write to + * every bit in the memory location. This is most commonly used for memory accesses that may or + * may not actually occur depending on runtime state (for example, the write side effect of an + * output parameter that is not written to on all paths), or for accesses where the memory + * location is a conservative estimate of the memory that might actually be accessed at runtime + * (for example, the global side effects of a function call). + */ + pragma[inline] + final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } + + /** + * Gets the operand that holds the memory address to which this instruction stores its + * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` + * is `r1`. + */ + final AddressOperand getResultAddressOperand() { + getResultMemoryAccess().usesAddressOperand() and + result.getUse() = this + } + + /** + * Gets the instruction that holds the exact memory address to which this instruction stores its + * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` + * is the instruction that defines `r1`. + */ + final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } + + /** + * Holds if the result of this instruction is precisely modeled in SSA. Always + * holds for a register result. For a memory result, a modeled result is + * connected to its actual uses. An unmodeled result has no uses. + * + * For example: + * ``` + * int x = 1; + * int *p = &x; + * int y = *p; + * ``` + * In non-aliased SSA, `x` will not be modeled because it has its address + * taken. In that case, `isResultModeled()` would not hold for the result of + * the `Store` to `x`. + */ + final predicate isResultModeled() { + // Register results are always in SSA form. + not hasMemoryResult() or + Construction::hasModeledMemoryResult(this) + } + + /** + * Holds if this is an instruction with a memory result that represents a + * conflation of more than one memory allocation. + * + * This happens in practice when dereferencing a pointer that cannot be + * tracked back to a single local allocation. Such memory is instead modeled + * as originating on the `AliasedDefinitionInstruction` at the entry of the + * function. + */ + final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } + + /** + * Gets the successor of this instruction along the control flow edge + * specified by `kind`. + */ + final Instruction getSuccessor(EdgeKind kind) { + result = Construction::getInstructionSuccessor(this, kind) + } + + /** + * Gets the a _back-edge successor_ of this instruction along the control + * flow edge specified by `kind`. A back edge in the control-flow graph is + * intuitively the edge that goes back around a loop. If all back edges are + * removed from the control-flow graph, it becomes acyclic. + */ + final Instruction getBackEdgeSuccessor(EdgeKind kind) { + // We don't take these edges from + // `Construction::getInstructionBackEdgeSuccessor` since that relation has + // not been treated to remove any loops that might be left over due to + // flaws in the IR construction or back-edge detection. + exists(IRBlock block | + block = this.getBlock() and + this = block.getLastInstruction() and + result = block.getBackEdgeSuccessor(kind).getFirstInstruction() + ) + } + + /** + * Gets all direct successors of this instruction. + */ + final Instruction getASuccessor() { result = getSuccessor(_) } + + /** + * Gets a predecessor of this instruction such that the predecessor reaches + * this instruction along the control flow edge specified by `kind`. + */ + final Instruction getPredecessor(EdgeKind kind) { result.getSuccessor(kind) = this } + + /** + * Gets all direct predecessors of this instruction. + */ + final Instruction getAPredecessor() { result = getPredecessor(_) } +} + +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ +class VariableInstruction extends Instruction { + IRVariable var; + + VariableInstruction() { var = Raw::getInstructionVariable(this) } + + override string getImmediateString() { result = var.toString() } + + /** + * Gets the variable that this instruction references. + */ + final IRVariable getIRVariable() { result = var } + + /** + * Gets the AST variable that this instruction's IR variable refers to, if one exists. + */ + final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } +} + +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ +class FieldInstruction extends Instruction { + Language::Field field; + + FieldInstruction() { field = Raw::getInstructionField(this) } + + final override string getImmediateString() { result = field.toString() } + + /** + * Gets the field that this instruction references. + */ + final Language::Field getField() { result = field } +} + +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ +class FunctionInstruction extends Instruction { + Language::Function funcSymbol; + + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } + + final override string getImmediateString() { result = funcSymbol.toString() } + + /** + * Gets the function that this instruction references. + */ + final Language::Function getFunctionSymbol() { result = funcSymbol } +} + +/** + * An instruction whose result is a compile-time constant value. + */ +class ConstantValueInstruction extends Instruction { + string value; + + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } + + final override string getImmediateString() { result = value } + + /** + * Gets the constant value of this instruction's result. + */ + final string getValue() { result = value } +} + +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ +class IndexedInstruction extends Instruction { + int index; + + IndexedInstruction() { index = Raw::getInstructionIndex(this) } + + final override string getImmediateString() { result = index.toString() } + + /** + * Gets the zero-based index of the argument that this instruction references. + */ + final int getIndex() { result = index } +} + +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ +class EnterFunctionInstruction extends Instruction { + EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } +} + +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ +class VariableAddressInstruction extends VariableInstruction { + VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } +} + +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ +class InitializeParameterInstruction extends VariableInstruction { + InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + + /** + * Gets the parameter initialized by this instruction. + */ + final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } +} + +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ +class InitializeIndirectionInstruction extends VariableInstruction { + InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + + /** + * Gets the parameter initialized by this instruction. + */ + final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } +} + +/** + * An instruction that initializes the `this` pointer parameter of the enclosing function. + */ +class InitializeThisInstruction extends Instruction { + InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } +} + +/** + * An instruction that computes the address of a non-static field of an object. + */ +class FieldAddressInstruction extends FieldInstruction { + FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + + /** + * Gets the operand that provides the address of the object containing the field. + */ + final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ + final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } +} + +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + +/** + * An instruction that produces a well-defined but unknown result and has + * unknown side effects, including side effects that are not conservatively + * modeled in the SSA graph. + * + * This type of instruction appears when there is an `ErrorExpr` in the AST, + * meaning that the extractor could not understand the expression and therefore + * produced a partial AST. Queries that give alerts when some action is _not_ + * taken may want to ignore any function that contains an `ErrorInstruction`. + */ +class ErrorInstruction extends Instruction { + ErrorInstruction() { getOpcode() instanceof Opcode::Error } +} + +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ +class UninitializedInstruction extends VariableInstruction { + UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } + + /** + * Gets the variable that is uninitialized. + */ + final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } +} + +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ +class NoOpInstruction extends Instruction { + NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } +} + +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ +class ReturnInstruction extends Instruction { + ReturnInstruction() { getOpcode() instanceof ReturnOpcode } +} + +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ +class ReturnVoidInstruction extends ReturnInstruction { + ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } +} + +/** + * An instruction that returns control to the caller of the function, including a return value. + */ +class ReturnValueInstruction extends ReturnInstruction { + ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + + /** + * Gets the operand that provides the value being returned by the function. + */ + final LoadOperand getReturnValueOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ + final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } +} + +/** + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. + */ +class ReturnIndirectionInstruction extends VariableInstruction { + ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + + /** + * Gets the operand that provides the value of the pointed-to memory. + */ + final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ + final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + + /** + * Gets the operand that provides the address of the pointed-to memory. + */ + final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ + final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + + /** + * Gets the parameter for which this instruction reads the final pointed-to value within the + * function. + */ + final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } +} + +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ +class CopyInstruction extends Instruction { + CopyInstruction() { getOpcode() instanceof CopyOpcode } + + /** + * Gets the operand that provides the input value of the copy. + */ + Operand getSourceValueOperand() { none() } + + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ + final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } +} + +/** + * An instruction that returns a register result containing a copy of its register operand. + */ +class CopyValueInstruction extends CopyInstruction, UnaryInstruction { + CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } + + final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } +} + +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ +class LoadInstruction extends CopyInstruction { + LoadInstruction() { getOpcode() instanceof Opcode::Load } + + /** + * Gets the operand that provides the address of the value being loaded. + */ + final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ + final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + + final override LoadOperand getSourceValueOperand() { result = getAnOperand() } +} + +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ +class StoreInstruction extends CopyInstruction { + StoreInstruction() { getOpcode() instanceof Opcode::Store } + + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ + final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + + final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } +} + +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ +class ConditionalBranchInstruction extends Instruction { + ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ + final ConditionOperand getConditionOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ + final Instruction getCondition() { result = getConditionOperand().getDef() } + + /** + * Gets the instruction to which control will flow if the condition is true. + */ + final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + + /** + * Gets the instruction to which control will flow if the condition is false. + */ + final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } +} + +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no + * successors. + */ +class ExitFunctionInstruction extends Instruction { + ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } +} + +/** + * An instruction whose result is a constant value. + */ +class ConstantInstruction extends ConstantValueInstruction { + ConstantInstruction() { getOpcode() instanceof Opcode::Constant } +} + +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ +class IntegerConstantInstruction extends ConstantInstruction { + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } +} + +/** + * An instruction whose result is a constant value of floating-point type. + */ +class FloatConstantInstruction extends ConstantInstruction { + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } +} + +/** + * An instruction whose result is the address of a string literal. + */ +class StringConstantInstruction extends VariableInstruction { + override IRStringLiteral var; + + final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + + /** + * Gets the string literal whose address is returned by this instruction. + */ + final Language::StringLiteral getValue() { result = var.getLiteral() } +} + +/** + * An instruction whose result is computed from two operands. + */ +class BinaryInstruction extends Instruction { + BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + + /** + * Gets the left operand of this binary instruction. + */ + final LeftOperand getLeftOperand() { result = getAnOperand() } + + /** + * Gets the right operand of this binary instruction. + */ + final RightOperand getRightOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ + final Instruction getLeft() { result = getLeftOperand().getDef() } + + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ + final Instruction getRight() { result = getRightOperand().getDef() } + + /** + * Holds if this instruction's operands are `op1` and `op2`, in either order. + */ + final predicate hasOperands(Operand op1, Operand op2) { + op1 = getLeftOperand() and op2 = getRightOperand() + or + op1 = getRightOperand() and op2 = getLeftOperand() + } +} + +/** + * An instruction that computes the result of an arithmetic operation. + */ +class ArithmeticInstruction extends Instruction { + ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } +} + +/** + * An instruction that performs an arithmetic operation on two numeric operands. + */ +class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } + +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * numeric operand. + */ +class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } + +/** + * An instruction that computes the sum of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ +class AddInstruction extends BinaryArithmeticInstruction { + AddInstruction() { getOpcode() instanceof Opcode::Add } +} + +/** + * An instruction that computes the difference of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ +class SubInstruction extends BinaryArithmeticInstruction { + SubInstruction() { getOpcode() instanceof Opcode::Sub } +} + +/** + * An instruction that computes the product of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ +class MulInstruction extends BinaryArithmeticInstruction { + MulInstruction() { getOpcode() instanceof Opcode::Mul } +} + +/** + * An instruction that computes the quotient of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ +class DivInstruction extends BinaryArithmeticInstruction { + DivInstruction() { getOpcode() instanceof Opcode::Div } +} + +/** + * An instruction that computes the remainder of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ +class RemInstruction extends BinaryArithmeticInstruction { + RemInstruction() { getOpcode() instanceof Opcode::Rem } +} + +/** + * An instruction that negates a single numeric operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ +class NegateInstruction extends UnaryArithmeticInstruction { + NegateInstruction() { getOpcode() instanceof Opcode::Negate } +} + +/** + * An instruction that computes the result of a bitwise operation. + */ +class BitwiseInstruction extends Instruction { + BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } +} + +/** + * An instruction that performs a bitwise operation on two integer operands. + */ +class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } + +/** + * An instruction that performs a bitwise operation on a single integer operand. + */ +class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } + +/** + * An instruction that computes the bitwise "and" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ +class BitAndInstruction extends BinaryBitwiseInstruction { + BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } +} + +/** + * An instruction that computes the bitwise "or" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ +class BitOrInstruction extends BinaryBitwiseInstruction { + BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } +} + +/** + * An instruction that computes the bitwise "xor" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ +class BitXorInstruction extends BinaryBitwiseInstruction { + BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } +} + +/** + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ +class ShiftLeftInstruction extends BinaryBitwiseInstruction { + ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } +} + +/** + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ +class ShiftRightInstruction extends BinaryBitwiseInstruction { + ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } +} + +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ +class PointerArithmeticInstruction extends BinaryInstruction { + int elementSize; + + PointerArithmeticInstruction() { + getOpcode() instanceof PointerArithmeticOpcode and + elementSize = Raw::getInstructionElementSize(this) + } + + final override string getImmediateString() { result = elementSize.toString() } + + /** + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. + */ + final int getElementSize() { result = elementSize } +} + +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ +class PointerOffsetInstruction extends PointerArithmeticInstruction { + PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } +} + +/** + * An instruction that adds an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ +class PointerAddInstruction extends PointerOffsetInstruction { + PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } +} + +/** + * An instruction that subtracts an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ +class PointerSubInstruction extends PointerOffsetInstruction { + PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } +} + +/** + * An instruction that computes the difference between two pointers. + * + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. + */ +class PointerDiffInstruction extends PointerArithmeticInstruction { + PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } +} + +/** + * An instruction whose result is computed from a single operand. + */ +class UnaryInstruction extends Instruction { + UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + + /** + * Gets the sole operand of this instruction. + */ + final UnaryOperand getUnaryOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ + final Instruction getUnary() { result = getUnaryOperand().getDef() } +} + +/** + * An instruction that converts the value of its operand to a value of a different type. + */ +class ConvertInstruction extends UnaryInstruction { + ConvertInstruction() { getOpcode() instanceof Opcode::Convert } +} + +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ +class CheckedConvertOrNullInstruction extends UnaryInstruction { + CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } +} + +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. + */ +class InheritanceConversionInstruction extends UnaryInstruction { + Language::Class baseClass; + Language::Class derivedClass; + + InheritanceConversionInstruction() { + Raw::getInstructionInheritance(this, baseClass, derivedClass) + } + + final override string getImmediateString() { + result = derivedClass.toString() + " : " + baseClass.toString() + } + + /** + * Gets the `ClassDerivation` for the inheritance relationship between + * the base and derived classes. This predicate does not hold if the + * conversion is to an indirect virtual base class. + */ + final Language::ClassDerivation getDerivation() { + result.getBaseClass() = baseClass and result.getDerivedClass() = derivedClass + } + + /** + * Gets the base class of the conversion. This will be either a direct + * base class of the derived class, or a virtual base class of the + * derived class. + */ + final Language::Class getBaseClass() { result = baseClass } + + /** + * Gets the derived class of the conversion. + */ + final Language::Class getDerivedClass() { result = derivedClass } +} + +/** + * An instruction that converts from the address of a derived class to the address of a base class. + */ +class ConvertToBaseInstruction extends InheritanceConversionInstruction { + ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } +} + +/** + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. + */ +class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { + ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } +} + +/** + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. + */ +class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { + ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } +} + +/** + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. + */ +class ConvertToDerivedInstruction extends InheritanceConversionInstruction { + ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } +} + +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ +class BitComplementInstruction extends UnaryBitwiseInstruction { + BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } +} + +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ +class LogicalNotInstruction extends UnaryInstruction { + LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } +} + +/** + * An instruction that compares two numeric operands. + */ +class CompareInstruction extends BinaryInstruction { + CompareInstruction() { getOpcode() instanceof CompareOpcode } +} + +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareEQInstruction extends CompareInstruction { + CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } +} + +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ +class CompareNEInstruction extends CompareInstruction { + CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } +} + +/** + * An instruction that does a relative comparison of two values, such as `<` or `>=`. + */ +class RelationalInstruction extends CompareInstruction { + RelationalInstruction() { getOpcode() instanceof RelationalOpcode } + + /** + * Gets the operand on the "greater" (or "greater-or-equal") side + * of this relational instruction, that is, the side that is larger + * if the overall instruction evaluates to `true`; for example on + * `x <= 20` this is the `20`, and on `y > 0` it is `y`. + */ + Instruction getGreater() { none() } + + /** + * Gets the operand on the "lesser" (or "lesser-or-equal") side + * of this relational instruction, that is, the side that is smaller + * if the overall instruction evaluates to `true`; for example on + * `x <= 20` this is `x`, and on `y > 0` it is the `0`. + */ + Instruction getLesser() { none() } + + /** + * Holds if this relational instruction is strict (is not an "or-equal" instruction). + */ + predicate isStrict() { none() } +} + +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareLTInstruction extends RelationalInstruction { + CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } + + override Instruction getLesser() { result = getLeft() } + + override Instruction getGreater() { result = getRight() } + + override predicate isStrict() { any() } +} + +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareGTInstruction extends RelationalInstruction { + CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } + + override Instruction getLesser() { result = getRight() } + + override Instruction getGreater() { result = getLeft() } + + override predicate isStrict() { any() } +} + +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareLEInstruction extends RelationalInstruction { + CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } + + override Instruction getLesser() { result = getLeft() } + + override Instruction getGreater() { result = getRight() } + + override predicate isStrict() { none() } +} + +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareGEInstruction extends RelationalInstruction { + CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } + + override Instruction getLesser() { result = getRight() } + + override Instruction getGreater() { result = getLeft() } + + override predicate isStrict() { none() } +} + +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ +class SwitchInstruction extends Instruction { + SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + + /** Gets the operand that provides the integer value controlling the switch. */ + final ConditionOperand getExpressionOperand() { result = getAnOperand() } + + /** Gets the instruction whose result provides the integer value controlling the switch. */ + final Instruction getExpression() { result = getExpressionOperand().getDef() } + + /** Gets the successor instructions along the case edges of the switch. */ + final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + + /** Gets the successor instruction along the default edge of the switch, if any. */ + final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } +} + +/** + * An instruction that calls a function. + */ +class CallInstruction extends Instruction { + CallInstruction() { getOpcode() instanceof Opcode::Call } + + /** + * Gets the operand the specifies the target function of the call. + */ + final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } + + /** + * Gets the `Instruction` that computes the target function of the call. This is usually a + * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a + * function pointer. + */ + final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } + + /** + * Gets all of the argument operands of the call, including the `this` pointer, if any. + */ + final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } + + /** + * Gets the `Function` that the call targets, if this is statically known. + */ + final Language::Function getStaticCallTarget() { + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() + } + + /** + * Gets all of the arguments of the call, including the `this` pointer, if any. + */ + final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } + + /** + * Gets the `this` pointer argument operand of the call, if any. + */ + final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } + + /** + * Gets the `this` pointer argument of the call, if any. + */ + final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } + + /** + * Gets the argument operand at the specified index. + */ + final PositionalArgumentOperand getPositionalArgumentOperand(int index) { + result = getAnOperand() and + result.getIndex() = index + } + + /** + * Gets the argument at the specified index. + */ + final Instruction getPositionalArgument(int index) { + result = getPositionalArgumentOperand(index).getDef() + } + + /** + * Gets the number of arguments of the call, including the `this` pointer, if any. + */ + final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } +} + +/** + * An instruction representing a side effect of a function call. + */ +class SideEffectInstruction extends Instruction { + SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + + /** + * Gets the instruction whose execution causes this side effect. + */ + final Instruction getPrimaryInstruction() { + result = Construction::getPrimaryInstructionForSideEffect(this) + } +} + +/** + * An instruction representing the side effect of a function call on any memory that might be + * accessed by that call. + */ +class CallSideEffectInstruction extends SideEffectInstruction { + CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } +} + +/** + * An instruction representing the side effect of a function call on any memory + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. + */ +class CallReadSideEffectInstruction extends SideEffectInstruction { + CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } +} + +/** + * An instruction representing a read side effect of a function call on a + * specific parameter. + */ +class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { + ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } + + /** Gets the operand for the value that will be read from this instruction, if known. */ + final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + + /** Gets the value that will be read from this instruction, if known. */ + final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + + /** Gets the operand for the address from which this instruction may read. */ + final AddressOperand getArgumentOperand() { result = getAnOperand() } + + /** Gets the address from which this instruction may read. */ + final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } +} + +/** + * An instruction representing the read of an indirect parameter within a function call. + */ +class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { + IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } +} + +/** + * An instruction representing the read of an indirect buffer parameter within a function call. + */ +class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { + BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } +} + +/** + * An instruction representing the read of an indirect buffer parameter within a function call. + */ +class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { + SizedBufferReadSideEffectInstruction() { + getOpcode() instanceof Opcode::SizedBufferReadSideEffect + } + + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } +} + +/** + * An instruction representing a write side effect of a function call on a + * specific parameter. + */ +class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { + WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } + + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } +} + +/** + * An instruction representing the write of an indirect parameter within a function call. + */ +class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { + IndirectMustWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::IndirectMustWriteSideEffect + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ +class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { + BufferMustWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::BufferMustWriteSideEffect + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ +class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { + SizedBufferMustWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect + } + + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } +} + +/** + * An instruction representing the potential write of an indirect parameter within a function call. + * + * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. + * written. + */ +class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { + IndirectMayWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::IndirectMayWriteSideEffect + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. + * + * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. + */ +class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { + BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. + * + * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. + */ +class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { + SizedBufferMayWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect + } + + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } +} + +/** + * An instruction representing the initial value of newly allocated memory, such as the result of a + * call to `malloc`. + */ +class InitializeDynamicAllocationInstruction extends SideEffectInstruction { + InitializeDynamicAllocationInstruction() { + getOpcode() instanceof Opcode::InitializeDynamicAllocation + } + + /** + * Gets the address of the allocation this instruction is initializing. + */ + final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } + + /** + * Gets the operand for the allocation this instruction is initializing. + */ + final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } +} + +/** + * An instruction representing a GNU or MSVC inline assembly statement. + */ +class InlineAsmInstruction extends Instruction { + InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } +} + +/** + * An instruction that throws an exception. + */ +class ThrowInstruction extends Instruction { + ThrowInstruction() { getOpcode() instanceof ThrowOpcode } +} + +/** + * An instruction that throws a new exception. + */ +class ThrowValueInstruction extends ThrowInstruction { + ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } + + /** + * Gets the address operand of the exception thrown by this instruction. + */ + final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } + + /** + * Gets the address of the exception thrown by this instruction. + */ + final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } + + /** + * Gets the operand for the exception thrown by this instruction. + */ + final LoadOperand getExceptionOperand() { result = getAnOperand() } + + /** + * Gets the exception thrown by this instruction. + */ + final Instruction getException() { result = getExceptionOperand().getDef() } +} + +/** + * An instruction that re-throws the current exception. + */ +class ReThrowInstruction extends ThrowInstruction { + ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } +} + +/** + * An instruction that exits the current function by propagating an exception. + */ +class UnwindInstruction extends Instruction { + UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } +} + +/** + * An instruction that starts a `catch` handler. + */ +class CatchInstruction extends Instruction { + CatchInstruction() { getOpcode() instanceof CatchOpcode } +} + +/** + * An instruction that catches an exception of a specific type. + */ +class CatchByTypeInstruction extends CatchInstruction { + Language::LanguageType exceptionType; + + CatchByTypeInstruction() { + getOpcode() instanceof Opcode::CatchByType and + exceptionType = Raw::getInstructionExceptionType(this) + } + + final override string getImmediateString() { result = exceptionType.toString() } + + /** + * Gets the type of exception to be caught. + */ + final Language::LanguageType getExceptionType() { result = exceptionType } +} + +/** + * An instruction that catches any exception. + */ +class CatchAnyInstruction extends CatchInstruction { + CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } +} + +/** + * An instruction that initializes all escaped memory. + */ +class AliasedDefinitionInstruction extends Instruction { + AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } +} + +/** + * An instruction that consumes all escaped memory on exit from the function. + */ +class AliasedUseInstruction extends Instruction { + AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } +} + +/** + * An instruction representing the choice of one of multiple input values based on control flow. + * + * A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of + * the same variable reach that block. The `PhiInstruction` will have one operand corresponding to + * each control flow predecessor of the block, with that operand representing the version of the + * variable that flows from that predecessor. The result value of the `PhiInstruction` will be + * a copy of whichever operand corresponds to the actual predecessor that entered the block at + * runtime. + */ +class PhiInstruction extends Instruction { + PhiInstruction() { getOpcode() instanceof Opcode::Phi } + + /** + * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. + */ + final PhiInputOperand getAnInputOperand() { result = this.getAnOperand() } + + /** + * Gets an instruction that defines the input to one of the operands of this + * instruction. It's possible for more than one operand to have the same + * defining instruction, so this predicate will have the same number of + * results as `getAnInputOperand()` or fewer. + */ + pragma[noinline] + final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } +} + +/** + * An instruction representing the effect that a write to a memory may have on potential aliases of + * that memory. + * + * A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The + * `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents + * the previous state of all of the memory that might be aliased by the memory write. The second + * operand, given by `getPartialOperand()`, represents the memory that was actually modified by the + * memory write. The result of the `ChiInstruction` represents the same memory as + * `getTotalOperand()`, updated to include the changes due to the value that was actually stored by + * the memory write. + * + * As an example, suppose that variable `p` and `q` are pointers that may or may not point to the + * same memory: + * ``` + * *p = 5; + * x = *q; + * ``` + * + * The IR would look like: + * ``` + * r1_1 = VariableAddress[p] + * r1_2 = Load r1_1, m0_0 // Load the value of `p` + * r1_3 = Constant[5] + * m1_4 = Store r1_2, r1_3 // Store to `*p` + * m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory + * r1_6 = VariableAddress[x] + * r1_7 = VariableAddress[q] + * r1_8 = Load r1_7, m0_2 // Load the value of `q` + * r1_9 = Load r1_8, m1_5 // Load the value of `*q` + * m1_10 = Store r1_6, r1_9 // Store to x + * ``` + * + * Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of + * aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a + * new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of + * `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory + * pointed to by `q`. + * + * For more information about how `Chi` instructions are used to model memory side effects, see + * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. + */ +class ChiInstruction extends Instruction { + ChiInstruction() { getOpcode() instanceof Opcode::Chi } + + /** + * Gets the operand that represents the previous state of all memory that might be aliased by the + * memory write. + */ + final ChiTotalOperand getTotalOperand() { result = getAnOperand() } + + /** + * Gets the operand that represents the previous state of all memory that might be aliased by the + * memory write. + */ + final Instruction getTotal() { result = getTotalOperand().getDef() } + + /** + * Gets the operand that represents the new value written by the memory write. + */ + final ChiPartialOperand getPartialOperand() { result = getAnOperand() } + + /** + * Gets the operand that represents the new value written by the memory write. + */ + final Instruction getPartial() { result = getPartialOperand().getDef() } + + /** + * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. + */ + final predicate getUpdatedInterval(int startBit, int endBit) { + Construction::getIntervalUpdatedByChi(this, startBit, endBit) + } +} + +/** + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. + */ +class UnreachedInstruction extends Instruction { + UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } +} + +/** + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. + */ +class BuiltInOperationInstruction extends Instruction { + Language::BuiltInOperation operation; + + BuiltInOperationInstruction() { + getOpcode() instanceof BuiltInOperationOpcode and + operation = Raw::getInstructionBuiltInOperation(this) + } + + /** + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is + * performed by this instruction. + */ + final Language::BuiltInOperation getBuiltInOperation() { result = operation } +} + +/** + * An instruction representing a built-in operation that does not have a specific opcode. The + * actual operation is specified by the `getBuiltInOperation()` predicate. + */ +class BuiltInInstruction extends BuiltInOperationInstruction { + BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } + + final override string getImmediateString() { result = getBuiltInOperation().toString() } +} + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll new file mode 100644 index 000000000000..a12e35d471b8 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -0,0 +1,513 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + +private import internal.IRInternal +private import Instruction +private import IRBlock +private import internal.OperandImports as Imports +private import Imports::MemoryAccessKind +private import Imports::IRType +private import Imports::Overlap +private import Imports::OperandTag + +cached +private newtype TOperand = + TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) { + defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and + not Construction::isInCycle(useInstr) and + strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1 + } or + TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) { + useInstr.getOpcode().hasOperand(tag) + } or + TPhiOperand( + PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap + ) { + defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) + } + +/** + * Base class for all register operands. This is a placeholder for the IPA union type that we will + * eventually use for this purpose. + */ +private class RegisterOperandBase extends TRegisterOperand { + /** Gets a textual representation of this element. */ + abstract string toString(); +} + +/** + * Returns the register operand with the specified parameters. + */ +private RegisterOperandBase registerOperand( + Instruction useInstr, RegisterOperandTag tag, Instruction defInstr +) { + result = TRegisterOperand(useInstr, tag, defInstr) +} + +/** + * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we + * will eventually use for this purpose. + */ +private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand { + /** Gets a textual representation of this element. */ + abstract string toString(); +} + +/** + * Returns the non-Phi memory operand with the specified parameters. + */ +private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) { + result = TNonPhiMemoryOperand(useInstr, tag) +} + +/** + * Base class for all Phi operands. This is a placeholder for the IPA union type that we will + * eventually use for this purpose. + */ +private class PhiOperandBase extends TPhiOperand { + abstract string toString(); +} + +/** + * Returns the Phi operand with the specified parameters. + */ +private PhiOperandBase phiOperand( + Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap +) { + result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) +} + +/** + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) + */ +class Operand extends TOperand { + /** Gets a textual representation of this element. */ + string toString() { result = "Operand" } + + /** + * Gets the location of the source code for this operand. + */ + final Language::Location getLocation() { result = getUse().getLocation() } + + /** + * Gets the function that contains this operand. + */ + final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } + + /** + * Gets the `Instruction` that consumes this operand. + */ + Instruction getUse() { none() } + + /** + * Gets the `Instruction` whose result is the value of the operand. Unlike + * `getDef`, this also has a result when `isDefinitionInexact` holds, which + * means that the resulting instruction may only _partially_ or _potentially_ + * be the value of this operand. + */ + Instruction getAnyDef() { none() } + + /** + * Gets the `Instruction` whose result is the value of the operand. Unlike + * `getAnyDef`, this also has no result when `isDefinitionInexact` holds, + * which means that the resulting instruction must always be exactly the be + * the value of this operand. + */ + final Instruction getDef() { + result = this.getAnyDef() and + getDefinitionOverlap() instanceof MustExactlyOverlap + } + + /** + * DEPRECATED: renamed to `getUse`. + * + * Gets the `Instruction` that consumes this operand. + */ + deprecated final Instruction getUseInstruction() { result = getUse() } + + /** + * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this + * predicate is `getAnyDef`, but most uses of this predicate should probably + * be replaced with `getDef`. + * + * Gets the `Instruction` whose result is the value of the operand. + */ + deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } + + /** + * Gets the overlap relationship between the operand's definition and its use. + */ + Overlap getDefinitionOverlap() { none() } + + /** + * Holds if the result of the definition instruction does not exactly overlap this use. + */ + final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } + + /** + * Gets a prefix to use when dumping the operand in an operand list. + */ + string getDumpLabel() { result = "" } + + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + + /** + * Gets a string describing this operand, suitable for display in IR dumps. This consists of the + * result ID of the instruction consumed by the operand, plus a label identifying the operand + * kind. + * + * For example: `this:r3_5` + */ + final string getDumpString() { + result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() + } + + /** + * Gets a string containing the identifier of the definition of this use, or `m?` if the + * definition is not modeled in SSA. + */ + private string getDefinitionId() { + result = getAnyDef().getResultId() + or + not exists(getAnyDef()) and result = "m?" + } + + /** + * Gets a string prefix to prepend to the operand's definition ID in an IR dump, specifying whether the operand is + * an exact or inexact use of its definition. For an inexact use, the prefix is "~". For an exact use, the prefix is + * the empty string. + */ + private string getInexactSpecifier() { + if isDefinitionInexact() then result = "~" else result = "" + } + + /** + * Get the order in which the operand should be sorted in the operand list. + */ + int getDumpSortOrder() { result = -1 } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + + /** + * Gets the language-neutral type of the value consumed by this operand. This is usually the same + * as the result type of the definition instruction consumed by this operand. For register + * operands, this is always the case. For some memory operands, the operand type may be different + * from the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final Language::Type getType() { getLanguageType().hasType(result, _) } + + /** + * Holds if the value consumed by this operand is a glvalue. If this + * holds, the value of the operand represents the address of a location, + * and the type of the location is given by `getType()`. If this does + * not hold, the value of the operand represents a value whose type is + * given by `getType()`. + */ + final predicate isGLValue() { getLanguageType().hasType(_, true) } + + /** + * Gets the size of the value consumed by this operand, in bytes. If the operand does not have + * a known constant size, this predicate does not hold. + */ + final int getSize() { result = getLanguageType().getByteSize() } +} + +/** + * An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction). + */ +class MemoryOperand extends Operand { + MemoryOperand() { + this instanceof NonPhiMemoryOperandBase or + this instanceof PhiOperandBase + } + + /** + * Gets the kind of memory access performed by the operand. + */ + MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } + + /** + * Holds if the memory access performed by this operand will not always read from every bit in the + * memory location. This is most commonly used for memory accesses that may or may not actually + * occur depending on runtime state (for example, the write side effect of an output parameter + * that is not written to on all paths), or for accesses where the memory location is a + * conservative estimate of the memory that might actually be accessed at runtime (for example, + * the global side effects of a function call). + */ + predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } + + /** + * Returns the operand that holds the memory address from which the current operand loads its + * value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2` + * is `r1`. + */ + final AddressOperand getAddressOperand() { + getMemoryAccess().usesAddressOperand() and + result.getUse() = getUse() + } +} + +/** + * An operand that is not an operand of a `PhiInstruction`. + */ +class NonPhiOperand extends Operand { + Instruction useInstr; + OperandTag tag; + + NonPhiOperand() { + this = registerOperand(useInstr, tag, _) or + this = nonPhiMemoryOperand(useInstr, tag) + } + + final override Instruction getUse() { result = useInstr } + + final override string getDumpLabel() { result = tag.getLabel() } + + final override string getDumpId() { result = tag.getId() } + + final override int getDumpSortOrder() { result = tag.getSortOrder() } + + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ + final OperandTag getOperandTag() { result = tag } +} + +/** + * An operand that consumes a register (non-memory) result. + */ +class RegisterOperand extends NonPhiOperand, RegisterOperandBase { + override RegisterOperandTag tag; + Instruction defInstr; + + RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) } + + final override string toString() { result = tag.toString() } + + final override Instruction getAnyDef() { result = defInstr } + + final override Overlap getDefinitionOverlap() { + // All register results overlap exactly with their uses. + result instanceof MustExactlyOverlap + } +} + +/** + * A memory operand other than the operand of a `Phi` instruction. + */ +class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { + override MemoryOperandTag tag; + + NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) } + + final override string toString() { result = tag.toString() } + + final override Instruction getAnyDef() { + result = unique(Instruction defInstr | hasDefinition(defInstr, _)) + } + + final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } + + pragma[noinline] + private predicate hasDefinition(Instruction defInstr, Overlap overlap) { + defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and + not Construction::isInCycle(useInstr) and + strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1 + } + + /** + * Holds if the operand totally overlaps with its definition and consumes the + * bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition. + */ + predicate getUsedInterval(int startBitOffset, int endBitOffset) { + Construction::getUsedInterval(this, startBitOffset, endBitOffset) + } +} + +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ +class TypedOperand extends NonPhiMemoryOperand { + override TypedOperandTag tag; + + final override Language::LanguageType getLanguageType() { + result = Construction::getInstructionOperandType(useInstr, tag) + } +} + +/** + * The address operand of an instruction that loads or stores a value from + * memory (e.g. `Load`, `Store`). + */ +class AddressOperand extends RegisterOperand { + override AddressOperandTag tag; +} + +/** + * The buffer size operand of an instruction that represents a read or write of + * a buffer. + */ +class BufferSizeOperand extends RegisterOperand { + override BufferSizeOperandTag tag; +} + +/** + * The source value operand of an instruction that loads a value from memory (e.g. `Load`, + * `ReturnValue`, `ThrowValue`). + */ +class LoadOperand extends TypedOperand { + override LoadOperandTag tag; +} + +/** + * The source value operand of a `Store` instruction. + */ +class StoreValueOperand extends RegisterOperand { + override StoreValueOperandTag tag; +} + +/** + * The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`). + */ +class UnaryOperand extends RegisterOperand { + override UnaryOperandTag tag; +} + +/** + * The left operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class LeftOperand extends RegisterOperand { + override LeftOperandTag tag; +} + +/** + * The right operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class RightOperand extends RegisterOperand { + override RightOperandTag tag; +} + +/** + * The condition operand of a `ConditionalBranch` or `Switch` instruction. + */ +class ConditionOperand extends RegisterOperand { + override ConditionOperandTag tag; +} + +/** + * The operand representing the target function of an `Call` instruction. + */ +class CallTargetOperand extends RegisterOperand { + override CallTargetOperandTag tag; +} + +/** + * An operand representing an argument to a function call. This includes both + * positional arguments (represented by `PositionalArgumentOperand`) and the + * implicit `this` argument, if any (represented by `ThisArgumentOperand`). + */ +class ArgumentOperand extends RegisterOperand { + override ArgumentOperandTag tag; +} + +/** + * An operand representing the implicit 'this' argument to a member function + * call. + */ +class ThisArgumentOperand extends ArgumentOperand { + override ThisArgumentOperandTag tag; +} + +/** + * An operand representing an argument to a function call. + */ +class PositionalArgumentOperand extends ArgumentOperand { + override PositionalArgumentOperandTag tag; + + /** + * Gets the zero-based index of the argument. + */ + final int getIndex() { result = tag.getArgIndex() } +} + +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ +class SideEffectOperand extends TypedOperand { + override SideEffectOperandTag tag; +} + +/** + * An operand of a `PhiInstruction`. + */ +class PhiInputOperand extends MemoryOperand, PhiOperandBase { + PhiInstruction useInstr; + Instruction defInstr; + IRBlock predecessorBlock; + Overlap overlap; + + PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + + override string toString() { result = "Phi" } + + final override PhiInstruction getUse() { result = useInstr } + + final override Instruction getAnyDef() { result = defInstr } + + final override Overlap getDefinitionOverlap() { result = overlap } + + final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } + + final override string getDumpLabel() { + result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" + } + + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + + /** + * Gets the predecessor block from which this value comes. + */ + final IRBlock getPredecessorBlock() { result = predecessorBlock } + + final override MemoryAccessKind getMemoryAccess() { result instanceof PhiMemoryAccess } +} + +/** + * The total operand of a Chi node, representing the previous value of the memory. + */ +class ChiTotalOperand extends NonPhiMemoryOperand { + override ChiTotalOperandTag tag; + + final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess } +} + +/** + * The partial operand of a Chi node, representing the value being written to part of the memory. + */ +class ChiPartialOperand extends NonPhiMemoryOperand { + override ChiPartialOperandTag tag; + + final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.ql b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.ql similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.ql rename to csharp/ql/src/experimental/ir/implementation/raw/PrintIR.ql diff --git a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll new file mode 100644 index 000000000000..59dadee71545 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll @@ -0,0 +1,329 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + +private import internal.IRInternal +private import IR +private import internal.PrintIRImports as Imports +import Imports::IRConfiguration + +private newtype TPrintIRConfiguration = MkPrintIRConfiguration() + +/** + * The query can extend this class to control which functions are printed. + */ +class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ + string toString() { result = "PrintIRConfiguration" } + + /** + * Holds if the IR for `func` should be printed. By default, holds for all + * functions. + */ + predicate shouldPrintFunction(Language::Function func) { any() } +} + +/** + * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. + */ +private class FilteredIRConfiguration extends IRConfiguration { + override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { + shouldPrintFunction(func) + } +} + +private predicate shouldPrintFunction(Language::Function func) { + exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) +} + +private string getAdditionalInstructionProperty(Instruction instr, string key) { + exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key)) +} + +private string getAdditionalBlockProperty(IRBlock block, string key) { + exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) +} + +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + +private newtype TPrintableIRNode = + TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or + TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or + TPrintableInstruction(Instruction instr) { shouldPrintFunction(instr.getEnclosingFunction()) } + +/** + * A node to be emitted in the IR graph. + */ +abstract private class PrintableIRNode extends TPrintableIRNode { + abstract string toString(); + + /** + * Gets the location to be emitted for the node. + */ + abstract Language::Location getLocation(); + + /** + * Gets the label to be emitted for the node. + */ + abstract string getLabel(); + + /** + * Gets the order in which the node appears in its parent node. + */ + abstract int getOrder(); + + /** + * Gets the parent of this node. + */ + abstract PrintableIRNode getParent(); + + /** + * Gets the kind of graph represented by this node ("graph" or "tree"). + */ + string getGraphKind() { none() } + + /** + * Holds if this node should always be rendered as text, even in a graphical + * viewer. + */ + predicate forceText() { none() } + + /** + * Gets the value of the node property with the specified key. + */ + string getProperty(string key) { + key = "semmle.label" and result = getLabel() + or + key = "semmle.order" and result = getOrder().toString() + or + key = "semmle.graphKind" and result = getGraphKind() + or + key = "semmle.forceText" and forceText() and result = "true" + } +} + +/** + * An IR graph node representing a `IRFunction` object. + */ +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { + IRFunction irFunc; + + PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } + + override string toString() { result = irFunc.toString() } + + override Language::Location getLocation() { result = irFunc.getLocation() } + + override string getLabel() { result = Language::getIdentityString(irFunc.getFunction()) } + + override int getOrder() { + this = + rank[result + 1](PrintableIRFunction orderedFunc, Language::Location location | + location = orderedFunc.getIRFunction().getLocation() + | + orderedFunc + order by + location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), + orderedFunc.getLabel() + ) + } + + final override PrintableIRNode getParent() { none() } + + final IRFunction getIRFunction() { result = irFunc } +} + +/** + * An IR graph node representing an `IRBlock` object. + */ +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { + IRBlock block; + + PrintableIRBlock() { this = TPrintableIRBlock(block) } + + override string toString() { result = getLabel() } + + override Language::Location getLocation() { result = block.getLocation() } + + override string getLabel() { result = "Block " + block.getDisplayIndex().toString() } + + override int getOrder() { result = block.getDisplayIndex() } + + final override string getGraphKind() { result = "tree" } + + final override predicate forceText() { any() } + + final override PrintableIRFunction getParent() { + result.getIRFunction() = block.getEnclosingIRFunction() + } + + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalBlockProperty(block, key) + } + + final IRBlock getBlock() { result = block } +} + +/** + * An IR graph node representing an `Instruction`. + */ +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { + Instruction instr; + + PrintableInstruction() { this = TPrintableInstruction(instr) } + + override string toString() { result = instr.toString() } + + override Language::Location getLocation() { result = instr.getLocation() } + + override string getLabel() { + exists(IRBlock block | + instr = block.getAnInstruction() and + exists( + string resultString, string operationString, string operandsString, int resultWidth, + int operationWidth + | + resultString = instr.getResultString() and + operationString = instr.getOperationString() and + operandsString = getOperandsString() and + columnWidths(block, resultWidth, operationWidth) and + result = + resultString + getPaddingString(resultWidth - resultString.length()) + " = " + + operationString + getPaddingString(operationWidth - operationString.length()) + " : " + + operandsString + ) + ) + } + + override int getOrder() { result = instr.getDisplayIndexInBlock() } + + final override PrintableIRBlock getParent() { result.getBlock() = instr.getBlock() } + + final Instruction getInstruction() { result = instr } + + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalInstructionProperty(instr, key) + } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } +} + +private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { + resultWidth = max(Instruction instr | instr.getBlock() = block | instr.getResultString().length()) and + operationWidth = + max(Instruction instr | instr.getBlock() = block | instr.getOperationString().length()) +} + +private int maxColumnWidth() { + result = + max(Instruction instr, int width | + width = instr.getResultString().length() or + width = instr.getOperationString().length() or + width = instr.getOperandsString().length() + | + width + ) +} + +private string getPaddingString(int n) { + n = 0 and result = "" + or + n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " +} + +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ +query predicate nodes(PrintableIRNode node, string key, string value) { + value = node.getProperty(key) +} + +private int getSuccessorIndex(IRBlock pred, IRBlock succ) { + succ = + rank[result + 1](IRBlock aSucc, EdgeKind kind | + aSucc = pred.getSuccessor(kind) + | + aSucc order by kind.toString() + ) +} + +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ +query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { + exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | + predBlock = pred.getBlock() and + succBlock = succ.getBlock() and + predBlock.getSuccessor(kind) = succBlock and + ( + ( + key = "semmle.label" and + if predBlock.getBackEdgeSuccessor(kind) = succBlock + then value = kind.toString() + " (back edge)" + else value = kind.toString() + ) + or + key = "semmle.order" and + value = getSuccessorIndex(predBlock, succBlock).toString() + ) + ) +} + +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ +query predicate parents(PrintableIRNode child, PrintableIRNode parent) { + parent = child.getParent() +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/constant/ConstantAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/raw/constant/ConstantAnalysis.qll new file mode 100644 index 000000000000..aac2e679a97b --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/constant/ConstantAnalysis.qll @@ -0,0 +1,62 @@ +private import internal.ConstantAnalysisInternal +private import experimental.ir.internal.IntegerPartial +private import IR + +language[monotonicAggregates] +int getConstantValue(Instruction instr) { + result = instr.(IntegerConstantInstruction).getValue().toInt() + or + result = getBinaryInstructionValue(instr) + or + result = neg(getConstantValue(instr.(NegateInstruction).getUnary())) + or + result = getConstantValue(instr.(CopyInstruction).getSourceValue()) + or + exists(PhiInstruction phi | + phi = instr and + result = max(Instruction def | def = phi.getAnInput() | getConstantValueToPhi(def)) and + result = min(Instruction def | def = phi.getAnInput() | getConstantValueToPhi(def)) + ) +} + +pragma[noinline] +int getConstantValueToPhi(Instruction def) { + exists(PhiInstruction phi | + result = getConstantValue(def) and + def = phi.getAnInput() + ) +} + +pragma[noinline] +private predicate binaryInstructionOperands(BinaryInstruction instr, int left, int right) { + left = getConstantValue(instr.getLeft()) and + right = getConstantValue(instr.getRight()) +} + +pragma[noinline] +private int getBinaryInstructionValue(BinaryInstruction instr) { + exists(int left, int right | + binaryInstructionOperands(instr, left, right) and + ( + instr instanceof AddInstruction and result = add(left, right) + or + instr instanceof SubInstruction and result = sub(left, right) + or + instr instanceof MulInstruction and result = mul(left, right) + or + instr instanceof DivInstruction and result = div(left, right) + or + instr instanceof CompareEQInstruction and result = compareEQ(left, right) + or + instr instanceof CompareNEInstruction and result = compareNE(left, right) + or + instr instanceof CompareLTInstruction and result = compareLT(left, right) + or + instr instanceof CompareGTInstruction and result = compareGT(left, right) + or + instr instanceof CompareLEInstruction and result = compareLE(left, right) + or + instr instanceof CompareGEInstruction and result = compareGE(left, right) + ) + ) +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/constant/PrintConstantAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/raw/constant/PrintConstantAnalysis.qll new file mode 100644 index 000000000000..53f9295be4f9 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/constant/PrintConstantAnalysis.qll @@ -0,0 +1,11 @@ +private import internal.ConstantAnalysisInternal +private import experimental.ir.internal.IntegerConstant +private import ConstantAnalysis +import IR + +private class ConstantAnalysisPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instr, string key) { + key = "ConstantValue" and + result = getValue(getConstantValue(instr)).toString() + } +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll b/csharp/ql/src/experimental/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll new file mode 100644 index 000000000000..6e2340af7eac --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll @@ -0,0 +1 @@ +import experimental.ir.implementation.raw.IR as IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/PrintValueNumbering.qll b/csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/PrintValueNumbering.qll rename to csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll b/csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll rename to csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll diff --git a/csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll b/csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll new file mode 100644 index 000000000000..34bd754692d6 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll @@ -0,0 +1,3 @@ +import experimental.ir.internal.Overlap +import experimental.ir.internal.IRCSharpLanguage as Language +import experimental.ir.implementation.unaliased_ssa.IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll b/csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll rename to csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll new file mode 100644 index 000000000000..c80761a68cf8 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll @@ -0,0 +1 @@ +import experimental.ir.implementation.EdgeKind as EdgeKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll similarity index 84% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll index 47d9b5b973a4..c8c85acdd428 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll @@ -1,9 +1,11 @@ import csharp -import semmle.code.csharp.ir.implementation.raw.IR -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.Overlap -private import semmle.code.csharp.ir.internal.TempVariableTag +import experimental.ir.implementation.raw.IR +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.implementation.internal.IRFunctionBase +private import experimental.ir.implementation.internal.TInstruction +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.Overlap +private import experimental.ir.internal.TempVariableTag private import InstructionTag private import TranslatedCondition private import TranslatedElement @@ -11,30 +13,44 @@ private import TranslatedExpr private import TranslatedStmt private import desugar.Foreach private import TranslatedFunction -private import semmle.code.csharp.ir.Util -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.Util +private import experimental.ir.internal.IRCSharpLanguage as Language TranslatedElement getInstructionTranslatedElement(Instruction instruction) { - instruction = MkInstruction(result, _) + instruction = TRawInstruction(result, _) } -InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) } +InstructionTag getInstructionTag(Instruction instruction) { + instruction = TRawInstruction(_, result) +} -import Cached +pragma[noinline] +private predicate instructionOrigin( + Instruction instruction, TranslatedElement element, InstructionTag tag +) { + element = getInstructionTranslatedElement(instruction) and + tag = getInstructionTag(instruction) +} +class TStageInstruction = TRawInstruction; + +/** + * Provides the portion of the parameterized IR interface that is used to construct the initial + * "raw" stage of the IR. The other stages of the IR do not expose these predicates. + */ cached -private module Cached { +module Raw { + class InstructionTag1 = TranslatedElement; + + class InstructionTag2 = InstructionTag; + cached - predicate functionHasIR(Callable callable) { - exists(getTranslatedFunction(callable)) and - callable.fromSource() - } + predicate functionHasIR(Callable callable) { exists(getTranslatedFunction(callable)) } cached - newtype TInstruction = - MkInstruction(TranslatedElement element, InstructionTag tag) { - element.hasInstruction(_, tag, _) - } + predicate hasInstruction(TranslatedElement element, InstructionTag tag) { + element.hasInstruction(_, tag, _) + } cached predicate hasUserVariable(Callable callable, Variable var, CSharpType type) { @@ -66,16 +82,6 @@ private module Cached { none() } - cached - predicate hasModeledMemoryResult(Instruction instruction) { none() } - - cached - predicate hasConflatedMemoryResult(Instruction instruction) { - instruction instanceof AliasedDefinitionInstruction - or - instruction.getOpcode() instanceof Opcode::InitializeNonLocal - } - cached Expr getInstructionConvertedResultExpression(Instruction instruction) { exists(TranslatedExpr translatedExpr | @@ -92,6 +98,98 @@ private module Cached { ) } + cached + IRVariable getInstructionVariable(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + element = getInstructionTranslatedElement(instruction) and + tag = getInstructionTag(instruction) and + ( + result = element.getInstructionVariable(tag) or + result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag) + ) + ) + } + + cached + Field getInstructionField(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionField(tag) + ) + } + + cached + int getInstructionIndex(Instruction instruction) { none() } + + cached + Callable getInstructionFunction(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionFunction(getInstructionTag(instruction)) + } + + cached + string getInstructionConstantValue(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionConstantValue(getInstructionTag(instruction)) + } + + cached + CSharpType getInstructionExceptionType(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction) + .getInstructionExceptionType(getInstructionTag(instruction)) + } + + cached + predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) { + getInstructionTranslatedElement(instruction) + .getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass) + } + + cached + int getInstructionElementSize(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionElementSize(tag) + ) + } + + cached + Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instr) { none() } +} + +import Cached + +cached +private module Cached { + cached + Opcode getInstructionOpcode(TRawInstruction instr) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instr, element, tag) and + element.hasInstruction(result, tag, _) + ) + } + + cached + IRFunctionBase getInstructionEnclosingIRFunction(TRawInstruction instr) { + result.getFunction() = getInstructionTranslatedElement(instr).getFunction() + } + + cached + predicate hasInstruction(TRawInstruction instr) { any() } + + cached + predicate hasModeledMemoryResult(Instruction instruction) { none() } + + cached + predicate hasConflatedMemoryResult(Instruction instruction) { + instruction instanceof AliasedDefinitionInstruction + or + instruction.getOpcode() instanceof Opcode::InitializeNonLocal + } + cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { result = @@ -116,6 +214,20 @@ private module Cached { result = getMemoryOperandDefinition(instr, _, _) } + /** + * Holds if the partial operand of this `ChiInstruction` updates the bit range + * `[startBitOffset, endBitOffset)` of the total operand. + */ + cached + predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBit, int endBit) { none() } + + /** + * Holds if the operand totally overlaps with its definition and consumes the + * bit range `[startBitOffset, endBitOffset)`. + */ + cached + predicate getUsedInterval(Operand operand, int startBit, int endBit) { none() } + /** * Holds if `instr` is part of a cycle in the operand graph that doesn't go * through a phi instruction and therefore should be impossible. @@ -267,37 +379,6 @@ private module Cached { .hasInstruction(_, getInstructionTag(instruction), result) } - cached - Opcode getInstructionOpcode(Instruction instruction) { - getInstructionTranslatedElement(instruction) - .hasInstruction(result, getInstructionTag(instruction), _) - } - - cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - result.getFunction() = getInstructionTranslatedElement(instruction).getFunction() - } - - cached - IRVariable getInstructionVariable(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) and - ( - result = element.getInstructionVariable(tag) or - result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag) - ) - ) - } - - cached - Field getInstructionField(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionField(tag) - ) - } - cached ArrayAccess getInstructionArrayAccess(Instruction instruction) { result = @@ -305,52 +386,6 @@ private module Cached { .getInstructionArrayAccess(getInstructionTag(instruction)) } - cached - int getInstructionIndex(Instruction instruction) { none() } - - cached - Callable getInstructionFunction(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionFunction(getInstructionTag(instruction)) - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionConstantValue(getInstructionTag(instruction)) - } - - cached - CSharpType getInstructionExceptionType(Instruction instruction) { - result = - getInstructionTranslatedElement(instruction) - .getInstructionExceptionType(getInstructionTag(instruction)) - } - - cached - predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) { - getInstructionTranslatedElement(instruction) - .getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass) - } - - pragma[noinline] - private predicate instructionOrigin( - Instruction instruction, TranslatedElement element, InstructionTag tag - ) { - element = getInstructionTranslatedElement(instruction) and - tag = getInstructionTag(instruction) - } - - cached - int getInstructionElementSize(Instruction instruction) { - exists(TranslatedElement element, InstructionTag tag | - instructionOrigin(instruction, element, tag) and - result = element.getInstructionElementSize(tag) - ) - } - cached int getInstructionResultSize(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | @@ -366,9 +401,6 @@ private module Cached { result = element.getPrimaryInstructionForSideEffect(tag) ) } - - cached - Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instr) { none() } } import CachedForDebugging diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll new file mode 100644 index 000000000000..4e9a7d9f3aec --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import experimental.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll new file mode 100644 index 000000000000..14dad7400b22 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll @@ -0,0 +1,3 @@ +import experimental.ir.implementation.EdgeKind as EdgeKind +import experimental.ir.implementation.IRType as IRType +import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRInternal.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRInternal.qll new file mode 100644 index 000000000000..e44184dd76c2 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRInternal.qll @@ -0,0 +1,4 @@ +import experimental.ir.internal.IRCSharpLanguage as Language +import IRConstruction as Construction +import experimental.ir.implementation.IRConfiguration as IRConfiguration +import IRConstruction::Raw as Raw diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll new file mode 100644 index 000000000000..bdb4377cbdcc --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll @@ -0,0 +1,5 @@ +import experimental.ir.implementation.IRType as IRType +import experimental.ir.implementation.TempVariableTag as TempVariableTag +import experimental.ir.internal.IRUtilities as IRUtilities +import experimental.ir.internal.TempVariableTag as TTempVariableTag +import experimental.ir.implementation.internal.TIRVariable as TIRVariable diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll new file mode 100644 index 000000000000..4bcd2e127c12 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll @@ -0,0 +1,6 @@ +import experimental.ir.implementation.EdgeKind as EdgeKind +import experimental.ir.implementation.IRType as IRType +import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind +import experimental.ir.implementation.Opcode as Opcode +import experimental.ir.implementation.internal.OperandTag as OperandTag +import experimental.ir.internal.Overlap as Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionTag.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionTag.qll similarity index 99% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionTag.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionTag.qll index c40ce195c1a7..b97981876d4e 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionTag.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionTag.qll @@ -1,5 +1,5 @@ import csharp -import semmle.code.csharp.ir.Util +import experimental.ir.Util private predicate elementIsInitialized(int elementIndex) { exists(ArrayInitWithMod initList | initList.isInitialized(elementIndex)) diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll new file mode 100644 index 000000000000..40af4631927a --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll @@ -0,0 +1,4 @@ +import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind +import experimental.ir.implementation.IRType as IRType +import experimental.ir.internal.Overlap as Overlap +import experimental.ir.implementation.internal.OperandTag as OperandTag diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll new file mode 100644 index 000000000000..9a3e4c03646c --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll @@ -0,0 +1 @@ +import experimental.ir.IRConfiguration as IRConfiguration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCall.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCall.qll similarity index 91% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCall.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCall.qll index 81f60fd1f805..a2c6a708c72a 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCall.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCall.qll @@ -1,13 +1,13 @@ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag private import InstructionTag private import TranslatedElement private import TranslatedExpr private import TranslatedInitialization -private import semmle.code.csharp.ir.Util -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedCallBase -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.Util +private import experimental.ir.implementation.raw.internal.common.TranslatedCallBase +private import experimental.ir.internal.IRCSharpLanguage as Language /** * The IR translation of a call to a function. The function can be a normal function diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCondition.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCondition.qll similarity index 95% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCondition.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCondition.qll index cc398a86011f..a172800b3779 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCondition.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCondition.qll @@ -1,12 +1,12 @@ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.CSharpType +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.CSharpType private import InstructionTag private import TranslatedElement private import TranslatedExpr private import common.TranslatedConditionBase -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.internal.IRCSharpLanguage as Language TranslatedCondition getTranslatedCondition(Expr expr) { result.getExpr() = expr } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedDeclaration.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedDeclaration.qll similarity index 89% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedDeclaration.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedDeclaration.qll index 9cd0a0a34fce..86cbdbb4360c 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedDeclaration.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedDeclaration.qll @@ -1,12 +1,12 @@ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.internal.IRUtilities -private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import experimental.ir.implementation.Opcode +private import experimental.ir.internal.IRUtilities +private import experimental.ir.implementation.internal.OperandTag private import InstructionTag private import TranslatedElement private import TranslatedExpr private import TranslatedInitialization -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.internal.IRCSharpLanguage as Language private import common.TranslatedDeclarationBase /** diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedElement.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll similarity index 96% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedElement.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll index 141c04b9927b..0022711f79eb 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll @@ -1,17 +1,17 @@ import csharp -import semmle.code.csharp.ir.implementation.raw.IR -private import semmle.code.csharp.ir.IRConfiguration -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.TempVariableTag +import experimental.ir.implementation.raw.IR +private import experimental.ir.IRConfiguration +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.TempVariableTag private import InstructionTag private import TranslatedCondition private import TranslatedFunction private import TranslatedStmt private import IRConstruction -private import semmle.code.csharp.ir.Util -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.Util +private import experimental.ir.internal.IRCSharpLanguage as Language private import desugar.Foreach private import desugar.Delegate private import desugar.Lock @@ -21,6 +21,16 @@ ArrayType getArrayOfDim(int dim, Type type) { result.getElementType() = type } +IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { + result.getVariable() = var and + result.getEnclosingFunction() = func +} + +IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { + result.getAST() = ast and + result.getTag() = tag +} + private predicate canCreateCompilerGeneratedElement(Element generatedBy, int nth) { generatedBy instanceof ForeachStmt and nth in [0 .. ForeachElements::noGeneratedElements() - 1] or @@ -117,6 +127,7 @@ private predicate ignoreExpr(Expr expr) { private predicate translateFunction(Callable callable) { // not isInvalidFunction(callable) exists(callable.getEntryPoint()) and + callable.fromSource() and exists(IRConfiguration config | config.shouldCreateIRForFunction(callable)) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll similarity index 99% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll index 44689ba45f91..72c408a3f2ae 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll @@ -1,9 +1,9 @@ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.TempVariableTag -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.IRUtilities +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.TempVariableTag +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.IRUtilities private import InstructionTag private import TranslatedCondition private import TranslatedDeclaration @@ -16,8 +16,8 @@ private import common.TranslatedExprBase private import desugar.Delegate private import desugar.internal.TranslatedCompilerGeneratedCall import TranslatedCall -private import semmle.code.csharp.ir.Util -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.Util +private import experimental.ir.internal.IRCSharpLanguage as Language /** * Gets the TranslatedExpr for the specified expression. If `expr` is a load, diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedFunction.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedFunction.qll similarity index 96% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedFunction.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedFunction.qll index 81baffb4613c..65488a1b95d7 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedFunction.qll @@ -1,16 +1,16 @@ import csharp -import semmle.code.csharp.ir.implementation.raw.IR -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.IRUtilities -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.TempVariableTag +import experimental.ir.implementation.raw.IR +private import experimental.ir.implementation.Opcode +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.IRUtilities +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.TempVariableTag private import InstructionTag private import TranslatedElement private import TranslatedExpr private import TranslatedInitialization private import TranslatedStmt -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.internal.IRCSharpLanguage as Language /** * Gets the `TranslatedFunction` that represents function `callable`. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedInitialization.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedInitialization.qll similarity index 98% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedInitialization.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedInitialization.qll index c8576c42369a..cbe0e7c1d2a7 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -4,14 +4,14 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.CSharpType +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.CSharpType private import InstructionTag private import TranslatedElement private import TranslatedExpr private import TranslatedFunction -private import semmle.code.csharp.ir.Util +private import experimental.ir.Util private import IRInternal private import desugar.Delegate diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedStmt.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll similarity index 99% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedStmt.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll index 680d01cdcfc7..81de9a6b7c9b 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedStmt.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll @@ -1,7 +1,7 @@ import csharp -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.TempVariableTag -private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.TempVariableTag +private import experimental.ir.implementation.internal.OperandTag private import InstructionTag private import TranslatedCondition private import TranslatedDeclaration @@ -11,7 +11,7 @@ private import TranslatedFunction private import TranslatedInitialization private import common.TranslatedConditionBase private import IRInternal -private import semmle.code.csharp.ir.internal.IRUtilities +private import experimental.ir.internal.IRUtilities private import desugar.Foreach private import desugar.Lock diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedCallBase.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedCallBase.qll similarity index 90% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedCallBase.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedCallBase.qll index 2b9e039a22da..a870ed026483 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedCallBase.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedCallBase.qll @@ -4,14 +4,14 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr -private import semmle.code.csharp.ir.Util -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.implementation.raw.internal.InstructionTag +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedExpr +private import experimental.ir.Util +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.IRCSharpLanguage as Language private import TranslatedExprBase abstract class TranslatedCallBase extends TranslatedElement { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedConditionBase.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedConditionBase.qll similarity index 80% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedConditionBase.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedConditionBase.qll index 8d4c5202d34a..6f8e2df02eec 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedConditionBase.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedConditionBase.qll @@ -3,14 +3,14 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedCondition -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.implementation.raw.internal.InstructionTag +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedExpr +private import experimental.ir.implementation.raw.internal.TranslatedCondition +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.IRCSharpLanguage as Language /** * Represents the context of the condition, ie. provides diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll similarity index 81% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll index 549b554ee946..9fd47de90606 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll @@ -4,15 +4,15 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.internal.IRUtilities -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedInitialization -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.implementation.Opcode +private import experimental.ir.internal.IRUtilities +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.implementation.raw.internal.InstructionTag +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedExpr +private import experimental.ir.implementation.raw.internal.TranslatedInitialization +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.IRCSharpLanguage as Language abstract class LocalVariableDeclarationBase extends TranslatedElement { override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedExprBase.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedExprBase.qll new file mode 100644 index 000000000000..ec6a8c0ab00a --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/common/TranslatedExprBase.qll @@ -0,0 +1,14 @@ +/** + * Contains an abstract class that serves as a Base for classes that deal with the translation of exprs + * (both AST generated and compiler generated). + */ + +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.internal.IRCSharpLanguage as Language + +abstract class TranslatedExprBase extends TranslatedElement { + /** + * Gets the instruction that produces the result of the expression. + */ + abstract Instruction getResult(); +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Common.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll similarity index 90% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Common.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll index 492fd46e42b8..267cf903b00c 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Common.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll @@ -6,22 +6,22 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.TempVariableTag -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedFunction -private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.TempVariableTag +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedFunction +private import experimental.ir.implementation.raw.internal.InstructionTag private import internal.TranslatedCompilerGeneratedStmt private import internal.TranslatedCompilerGeneratedExpr private import internal.TranslatedCompilerGeneratedCondition private import internal.TranslatedCompilerGeneratedCall private import internal.TranslatedCompilerGeneratedElement private import internal.TranslatedCompilerGeneratedDeclaration -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedConditionBase -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedExprBase -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.implementation.raw.internal.common.TranslatedConditionBase +private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase +private import experimental.ir.internal.IRCSharpLanguage as Language /** * The general form of a compiler generated try stmt. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Delegate.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Delegate.qll similarity index 81% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Delegate.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Delegate.qll index 99938ec14786..939f14ba8fec 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Delegate.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Delegate.qll @@ -9,18 +9,18 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.TempVariableTag -private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedStmt -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedCondition -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.TempVariableTag +private import experimental.ir.implementation.raw.internal.InstructionTag +private import experimental.ir.implementation.raw.internal.TranslatedExpr +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedStmt +private import experimental.ir.implementation.raw.internal.TranslatedCondition +private import experimental.ir.internal.IRCSharpLanguage as Language private import Common private import internal.TranslatedCompilerGeneratedCall -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedExprBase +private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase /** * Module that exposes the functions needed for the translation of the delegate creation and call expressions. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Foreach.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll similarity index 94% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Foreach.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll index 3d01b56e49eb..9dee82212350 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Foreach.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll @@ -5,13 +5,13 @@ * Also we only deal with foreach stmts where there is only * one declaration (see below). * For example the code: - * ``` + * ```csharp * foreach(var item in some_enumerable) { * // body * } * ``` * gets desugared to: - * ``` + * ```csharp * Enumerator e = some_enumerable.GetEnumerator(); * try * { @@ -34,17 +34,17 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.TempVariableTag -private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedStmt -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedConditionBase -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedExprBase -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.TempVariableTag +private import experimental.ir.implementation.raw.internal.InstructionTag +private import experimental.ir.implementation.raw.internal.TranslatedExpr +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedStmt +private import experimental.ir.implementation.raw.internal.common.TranslatedConditionBase +private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase +private import experimental.ir.internal.IRCSharpLanguage as Language private import Common private import internal.TranslatedCompilerGeneratedStmt private import internal.TranslatedCompilerGeneratedCall diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Lock.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Lock.qll similarity index 93% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Lock.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Lock.qll index 7a0ec9d5cbca..c83957d9b944 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Lock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Lock.qll @@ -1,11 +1,11 @@ /** * File that provides the desugaring of a `lock` stmt. * The statement: - * ``` + * ```csharp * lock (anExpr) ... * ``` * gets desugared to: - * ``` + * ```csharp * SomeRefType lockedVar = anExpr; * bool __lockWasTaken = false; * try { @@ -19,17 +19,17 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.TempVariableTag -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedStmt -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedExprBase -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedConditionBase -private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.TempVariableTag +private import experimental.ir.implementation.raw.internal.TranslatedExpr +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedStmt +private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase +private import experimental.ir.implementation.raw.internal.common.TranslatedConditionBase +private import experimental.ir.implementation.raw.internal.InstructionTag +private import experimental.ir.internal.IRCSharpLanguage as Language private import Common private import internal.TranslatedCompilerGeneratedStmt private import internal.TranslatedCompilerGeneratedCall diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Using.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Using.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Using.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Using.qll diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCall.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCall.qll new file mode 100644 index 000000000000..28dfd2b4cc3c --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCall.qll @@ -0,0 +1,17 @@ +/** + * Contains an abstract class that is the super class of the classes that deal with compiler generated calls. + */ + +import csharp +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedFunction +private import experimental.ir.implementation.raw.internal.common.TranslatedCallBase +private import TranslatedCompilerGeneratedElement +private import experimental.ir.internal.IRCSharpLanguage as Language + +abstract class TranslatedCompilerGeneratedCall extends TranslatedCallBase, + TranslatedCompilerGeneratedElement { + final override string toString() { + result = "compiler generated call (" + generatedBy.toString() + ")" + } +} diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCondition.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCondition.qll new file mode 100644 index 000000000000..df0bf1b24c63 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCondition.qll @@ -0,0 +1,16 @@ +/** + * Contains an abstract class that is the super class of the classes that deal with compiler generated conditions. + */ + +import csharp +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.common.TranslatedConditionBase +private import TranslatedCompilerGeneratedElement +private import experimental.ir.internal.IRCSharpLanguage as Language + +abstract class TranslatedCompilerGeneratedValueCondition extends TranslatedCompilerGeneratedElement, + ValueConditionBase { + final override string toString() { + result = "compiler generated condition (" + generatedBy.toString() + ")" + } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll similarity index 81% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll index b13702ac1680..273c99365885 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll @@ -5,15 +5,15 @@ */ import csharp -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedFunction -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedDeclarationBase +private import experimental.ir.implementation.Opcode +private import experimental.ir.implementation.internal.OperandTag +private import experimental.ir.implementation.raw.internal.InstructionTag +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.implementation.raw.internal.TranslatedFunction +private import experimental.ir.implementation.raw.internal.common.TranslatedDeclarationBase private import TranslatedCompilerGeneratedElement -private import semmle.code.csharp.ir.internal.CSharpType -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.internal.CSharpType +private import experimental.ir.internal.IRCSharpLanguage as Language abstract class TranslatedCompilerGeneratedDeclaration extends LocalVariableDeclarationBase, TranslatedCompilerGeneratedElement { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedElement.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedElement.qll similarity index 81% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedElement.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedElement.qll index 299b2547c195..1eb7520eda4e 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedElement.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedElement.qll @@ -3,8 +3,8 @@ * which represents the element that generated the compiler generated element. */ -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.implementation.raw.internal.TranslatedElement +private import experimental.ir.internal.IRCSharpLanguage as Language abstract class TranslatedCompilerGeneratedElement extends TranslatedElement, TTranslatedCompilerGeneratedElement { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedExpr.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedExpr.qll new file mode 100644 index 000000000000..b7988c3fde85 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedExpr.qll @@ -0,0 +1,17 @@ +/** + * Contains an abstract class, which is the super class of all the classes that represent compiler + * generated expressions. + */ + +import csharp +private import TranslatedCompilerGeneratedElement +private import experimental.ir.implementation.raw.Instruction +private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase +private import experimental.ir.internal.IRCSharpLanguage as Language + +abstract class TranslatedCompilerGeneratedExpr extends TranslatedCompilerGeneratedElement, + TranslatedExprBase { + override string toString() { result = "compiler generated expr (" + generatedBy.toString() + ")" } + + abstract Type getResultType(); +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedStmt.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedStmt.qll similarity index 84% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedStmt.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedStmt.qll index 68ec2f102fe1..70955e02c9b2 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedStmt.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedStmt.qll @@ -5,7 +5,7 @@ import csharp private import TranslatedCompilerGeneratedElement -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +private import experimental.ir.internal.IRCSharpLanguage as Language abstract class TranslatedCompilerGeneratedStmt extends TranslatedCompilerGeneratedElement { final override string toString() { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/Dominance.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/Dominance.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/Dominance.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/Dominance.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/DominanceInternal.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/DominanceInternal.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/DominanceInternal.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/DominanceInternal.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/PrintDominance.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/PrintDominance.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/PrintDominance.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/PrintDominance.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/ReachableBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/ReachableBlock.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/ReachableBlock.qll rename to csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/ReachableBlock.qll diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll new file mode 100644 index 000000000000..93131e2abb5b --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll @@ -0,0 +1,2 @@ +import experimental.ir.implementation.raw.IR as IR +import experimental.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll new file mode 100644 index 000000000000..c96783fe6e81 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll @@ -0,0 +1,80 @@ +/** + * Provides classes that describe the Intermediate Representation (IR) of the program. + * + * The IR is a representation of the semantics of the program, with very little dependence on the + * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`, + * and `++i` all have the same semantic effect, but appear in the AST as three different types of + * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental + * operations similar to: + * + * ``` + * r1(int*) = VariableAddress[i] // Compute the address of variable `i` + * r2(int) = Load &:r1, m0 // Load the value of `i` + * r3(int) = Constant[1] // An integer constant with the value `1` + * r4(int) = Add r2, r3 // Add `1` to the value of `i` + * r5(int) = Store &r1, r4 // Store the new value back into the variable `i` + * ``` + * + * This allows IR-based analysis to focus on the fundamental operations, rather than having to be + * concerned with the various ways of expressing those operations in source code. + * + * The key classes in the IR are: + * + * - `IRFunction` - Contains the IR for an entire function definition, including all of that + * function's `Instruction`s, `IRBlock`s, and `IRVariables`. + * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be + * performed, the operands that produce the inputs to that operation, and the type of the result + * of the operation. Control flows from an `Instruction` to one of a set of successor + * `Instruction`s. + * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly + * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has + * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction` + * that produces its value (its "definition"). + * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is + * created for each variable directly accessed by the function. In addition, `IRVariable`s are + * created to represent certain temporary storage locations that do not have explicitly declared + * variables in the source code, such as the return value of the function. + * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a + * sequence of instructions such that control flow can only enter the block at the first + * instruction, and can only leave the block from the last instruction. + * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType` + * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all + * be represented as the `IRType` `uint4`, a four-byte unsigned integer. + */ + +import IRFunction +import Instruction +import IRBlock +import IRVariable +import Operand +private import internal.IRImports as Imports +import Imports::EdgeKind +import Imports::IRType +import Imports::MemoryAccessKind + +private newtype TIRPropertyProvider = MkIRPropertyProvider() + +/** + * A class that provides additional properties to be dumped for IR instructions and blocks when using + * the PrintIR module. Libraries that compute additional facts about IR elements can extend the + * single instance of this class to specify the additional properties computed by the library. + */ +class IRPropertyProvider extends TIRPropertyProvider { + /** Gets a textual representation of this element. */ + string toString() { result = "IRPropertyProvider" } + + /** + * Gets the value of the property named `key` for the specified instruction. + */ + string getInstructionProperty(Instruction instruction, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified block. + */ + string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll new file mode 100644 index 000000000000..d827ed3cf82d --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -0,0 +1,290 @@ +/** + * Provides classes describing basic blocks in the IR of a function. + */ + +private import internal.IRInternal +import Instruction +private import internal.IRBlockImports as Imports +import Imports::EdgeKind +private import Cached + +/** + * A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only + * incoming edges at the beginning of the sequence and the only outgoing edges at the end of the + * sequence. + * + * This class does not contain any members that query the predecessor or successor edges of the + * block. This allows different classes that extend `IRBlockBase` to expose different subsets of + * edges (e.g. ignoring unreachable edges). + * + * Most consumers should use the class `IRBlock`. + */ +class IRBlockBase extends TIRBlock { + /** Gets a textual representation of this block. */ + final string toString() { result = getFirstInstruction(this).toString() } + + /** Gets the source location of the first non-`Phi` instruction in this block. */ + final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + + /** + * INTERNAL: Do not use. + * + * Gets a string that uniquely identifies this block within its enclosing function. + * + * This predicate is used by debugging and printing code only. + */ + final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } + + /** + * INTERNAL: Do not use. + * + * Gets the zero-based index of the block within its function. + * + * This predicate is used by debugging and printing code only. + */ + int getDisplayIndex() { + exists(IRConfiguration::IRConfiguration config | + config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction()) + ) and + this = + rank[result + 1](IRBlock funcBlock, int sortOverride | + funcBlock.getEnclosingFunction() = getEnclosingFunction() and + // Ensure that the block containing `EnterFunction` always comes first. + if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction + then sortOverride = 0 + else sortOverride = 1 + | + funcBlock order by sortOverride, funcBlock.getUniqueId() + ) + } + + /** + * Gets the `index`th non-`Phi` instruction in this block. + */ + final Instruction getInstruction(int index) { result = getInstruction(this, index) } + + /** + * Get the `Phi` instructions that appear at the start of this block. + */ + final PhiInstruction getAPhiInstruction() { + Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() + } + + /** + * Gets an instruction in this block. This includes `Phi` instructions. + */ + final Instruction getAnInstruction() { + result = getInstruction(_) or + result = getAPhiInstruction() + } + + /** + * Gets the first non-`Phi` instruction in this block. + */ + final Instruction getFirstInstruction() { result = getFirstInstruction(this) } + + /** + * Gets the last instruction in this block. + */ + final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + + /** + * Gets the number of non-`Phi` instructions in this block. + */ + final int getInstructionCount() { result = getInstructionCount(this) } + + /** + * Gets the `IRFunction` that contains this block. + */ + final IRFunction getEnclosingIRFunction() { + result = getFirstInstruction(this).getEnclosingIRFunction() + } + + /** + * Gets the `Function` that contains this block. + */ + final Language::Function getEnclosingFunction() { + result = getFirstInstruction(this).getEnclosingFunction() + } +} + +/** + * A basic block with additional information about its predecessor and successor edges. Each edge + * corresponds to the control flow between the last instruction of one block and the first + * instruction of another block. + */ +class IRBlock extends IRBlockBase { + /** + * Gets a block to which control flows directly from this block. + */ + final IRBlock getASuccessor() { blockSuccessor(this, result) } + + /** + * Gets a block from which control flows directly to this block. + */ + final IRBlock getAPredecessor() { blockSuccessor(result, this) } + + /** + * Gets the block to which control flows directly from this block along an edge of kind `kind`. + */ + final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } + + /** + * Gets the block to which control flows directly from this block along a back edge of kind + * `kind`. + */ + final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } + + /** + * Holds if this block immediately dominates `block`. + * + * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B` + * is a direct successor of block `A`. + */ + final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } + + /** + * Holds if this block strictly dominates `block`. + * + * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B` + * are not the same block. + */ + final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } + + /** + * Holds if this block dominates `block`. + * + * Block `A` dominates block `B` if any control flow path from the entry block of the function to + * block `B` must pass through block `A`. A block always dominates itself. + */ + final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + + /** + * Gets a block on the dominance frontier of this block. + * + * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not + * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`. + */ + pragma[noinline] + final IRBlock dominanceFrontier() { + dominates(result.getAPredecessor()) and + not strictlyDominates(result) + } + + /** + * Holds if this block is reachable from the entry block of its function. + */ + final predicate isReachableFromFunctionEntry() { + this = getEnclosingIRFunction().getEntryBlock() or + getAPredecessor().isReachableFromFunctionEntry() + } +} + +private predicate startsBasicBlock(Instruction instr) { + not instr instanceof PhiInstruction and + not adjacentInBlock(_, instr) +} + +/** Holds if `i2` follows `i1` in a `IRBlock`. */ +private predicate adjacentInBlock(Instruction i1, Instruction i2) { + // - i2 must be the only successor of i1 + i2 = unique(Instruction i | i = i1.getASuccessor()) and + // - i1 must be the only predecessor of i2 + i1 = unique(Instruction i | i.getASuccessor() = i2) and + // - The edge between the two must be a GotoEdge. We just check that one + // exists since we've already checked that it's unique. + exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and + // - The edge must not be a back edge. This means we get the same back edges + // in the basic-block graph as we do in the raw CFG. + not exists(Construction::getInstructionBackEdgeSuccessor(i1, _)) + // This predicate could be simplified to remove one of the `unique`s if we + // were willing to rely on the CFG being well-formed and thus never having + // more than one successor to an instruction that has a `GotoEdge` out of it. +} + +private predicate isEntryBlock(TIRBlock block) { + block = MkIRBlock(any(EnterFunctionInstruction enter)) +} + +cached +private module Cached { + cached + newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) } + + /** Holds if `i` is the `index`th instruction the block starting with `first`. */ + private Instruction getInstructionFromFirst(Instruction first, int index) = + shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index) + + /** Holds if `i` is the `index`th instruction in `block`. */ + cached + Instruction getInstruction(TIRBlock block, int index) { + result = getInstructionFromFirst(getFirstInstruction(block), index) + } + + cached + int getInstructionCount(TIRBlock block) { result = strictcount(getInstruction(block, _)) } + + cached + predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + exists(Instruction predLast, Instruction succFirst | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and + succFirst = predLast.getSuccessor(kind) and + succ = MkIRBlock(succFirst) + ) + } + + pragma[noinline] + private predicate blockIdentity(TIRBlock b1, TIRBlock b2) { b1 = b2 } + + pragma[noopt] + cached + predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + backEdgeSuccessorRaw(pred, succ, kind) + or + // See the QLDoc on `backEdgeSuccessorRaw`. + exists(TIRBlock pred2 | + // Joining with `blockIdentity` is a performance trick to get + // `forwardEdgeRaw` on the RHS of a join, where it's fast. + blockIdentity(pred, pred2) and + forwardEdgeRaw+(pred, pred2) + ) and + blockSuccessor(pred, succ, kind) + } + + /** + * Holds if there is an edge from `pred` to `succ` that is not a back edge. + */ + private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) { + exists(EdgeKind kind | + blockSuccessor(pred, succ, kind) and + not backEdgeSuccessorRaw(pred, succ, kind) + ) + } + + /** + * Holds if the `kind`-edge from `pred` to `succ` is a back edge according to + * `Construction`. + * + * There could be loops of non-back-edges if there is a flaw in the IR + * construction or back-edge detection, and this could cause non-termination + * of subsequent analysis. To prevent that, a subsequent predicate further + * classifies all edges as back edges if they are involved in a loop of + * non-back-edges. + */ + private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + exists(Instruction predLast, Instruction succFirst | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and + succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and + succ = MkIRBlock(succFirst) + ) + } + + cached + predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { blockSuccessor(pred, succ, _) } + + cached + predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) +} + +private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRConsistency.ql b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.ql similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRConsistency.ql rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.ql diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll new file mode 100644 index 000000000000..6a87b9b4b5fd --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll @@ -0,0 +1,497 @@ +private import IR +import InstructionConsistency // module is below +import IRTypeConsistency // module is in IRType.qll + +module InstructionConsistency { + private import internal.InstructionImports as Imports + private import Imports::OperandTag + private import Imports::Overlap + private import internal.IRInternal + + private newtype TOptionalIRFunction = + TPresentIRFunction(IRFunction irFunc) or + TMissingIRFunction() + + /** + * An `IRFunction` that might not exist. This is used so that we can produce consistency failures + * for IR that also incorrectly lacks a `getEnclosingIRFunction()`. + */ + abstract private class OptionalIRFunction extends TOptionalIRFunction { + abstract string toString(); + + abstract Language::Location getLocation(); + } + + private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction { + private IRFunction irFunc; + + PresentIRFunction() { this = TPresentIRFunction(irFunc) } + + override string toString() { + result = concat(Language::getIdentityString(irFunc.getFunction()), "; ") + } + + override Language::Location getLocation() { + // To avoid an overwhelming number of results when the extractor merges functions with the + // same name, just pick a single location. + result = + rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString()) + } + } + + private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction { + override string toString() { result = "" } + + override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation } + } + + private OptionalIRFunction getInstructionIRFunction(Instruction instr) { + result = TPresentIRFunction(instr.getEnclosingIRFunction()) + or + not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) { + result = getInstructionIRFunction(instr) and + irFuncText = result.toString() + } + + private OptionalIRFunction getOperandIRFunction(Operand operand) { + result = TPresentIRFunction(operand.getEnclosingIRFunction()) + or + not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + pragma[inline] + private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) { + result = getOperandIRFunction(operand) and + irFuncText = result.toString() + } + + private OptionalIRFunction getBlockIRFunction(IRBlock block) { + result = TPresentIRFunction(block.getEnclosingIRFunction()) + or + not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction() + } + + /** + * Holds if instruction `instr` is missing an expected operand with tag `tag`. + */ + query predicate missingOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag | + instr.getOpcode().hasOperand(tag) and + not exists(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + message = + "Instruction '" + instr.getOpcode().toString() + + "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if instruction `instr` has an unexpected operand with tag `tag`. + */ + query predicate unexpectedOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag | + exists(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + not instr.getOpcode().hasOperand(tag) and + not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and + not ( + instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag + ) and + not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and + message = + "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() + + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if instruction `instr` has multiple operands with tag `tag`. + */ + query predicate duplicateOperand( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(OperandTag tag, int operandCount | + operandCount = + strictcount(NonPhiOperand operand | + operand = instr.getAnOperand() and + operand.getOperandTag() = tag + ) and + operandCount > 1 and + message = + "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + + " in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if `Phi` instruction `instr` is missing an operand corresponding to + * the predecessor block `pred`. + */ + query predicate missingPhiOperand( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRBlock pred | + pred = instr.getBlock().getAPredecessor() and + not exists(PhiInputOperand operand | + operand = instr.getAnOperand() and + operand.getPredecessorBlock() = pred + ) and + message = + "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" + + pred.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + query predicate missingOperandType( + Operand operand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Instruction use | + not exists(operand.getType()) and + use = operand.getUse() and + message = + "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + + "' is missing a type in function '$@'." and + irFunc = getOperandIRFunction(operand, irFuncText) + ) + } + + query predicate duplicateChiOperand( + ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText + ) { + chi.getTotal() = chi.getPartial() and + message = + "Chi instruction for " + chi.getPartial().toString() + + " has duplicate operands in function '$@'." and + irFunc = getInstructionIRFunction(chi, irFuncText) + } + + query predicate sideEffectWithoutPrimary( + SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + not exists(instr.getPrimaryInstruction()) and + message = + "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } + + /** + * Holds if an instruction, other than `ExitFunction`, has no successors. + */ + query predicate instructionWithoutSuccessor( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + not exists(instr.getASuccessor()) and + not instr instanceof ExitFunctionInstruction and + // Phi instructions aren't linked into the instruction-level flow graph. + not instr instanceof PhiInstruction and + not instr instanceof UnreachedInstruction and + message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } + + /** + * Holds if there are multiple edges of the same kind from `source`. + */ + query predicate ambiguousSuccessors( + Instruction source, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(EdgeKind kind, int n | + n = strictcount(Instruction t | source.getSuccessor(kind) = t) and + n > 1 and + message = + "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" + + kind.toString() + "' in function '$@'." and + irFunc = getInstructionIRFunction(source, irFuncText) + ) + } + + /** + * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function + * contains no element that can cause loops. + */ + query predicate unexplainedLoop( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Language::Function f | + exists(IRBlock block | + instr.getBlock() = block and + block.getEnclosingFunction() = f and + block.getASuccessor+() = block + ) and + not Language::hasPotentialLoop(f) and + message = + "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if a `Phi` instruction is present in a block with fewer than two + * predecessors. + */ + query predicate unnecessaryPhiInstruction( + PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int n | + n = count(instr.getBlock().getAPredecessor()) and + n < 2 and + message = + "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() + + " predecessors in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if a memory operand is connected to a definition with an unmodeled result. + */ + query predicate memoryOperandDefinitionIsUnmodeled( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(MemoryOperand operand, Instruction def | + operand = instr.getAnOperand() and + def = operand.getAnyDef() and + not def.isResultModeled() and + message = + "Memory operand definition on instruction '" + instr.toString() + + "' has unmodeled result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + /** + * Holds if operand `operand` consumes a value that was defined in + * a different function. + */ + query predicate operandAcrossFunctions( + Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText, + OptionalIRFunction defIRFunc, string defIRFuncText + ) { + exists(Instruction useInstr, Instruction defInstr | + operand.getUse() = useInstr and + operand.getAnyDef() = defInstr and + useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and + defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and + useIRFunc != defIRFunc and + message = + "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() + + "' in function '$@', but is defined on instruction '" + defInstr.toString() + + "' in function '$@'." + ) + } + + /** + * Holds if instruction `instr` is not in exactly one block. + */ + query predicate instructionWithoutUniqueBlock( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int blockCount | + blockCount = count(instr.getBlock()) and + blockCount != 1 and + message = + "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() + + " blocks in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } + + private predicate forwardEdge(IRBlock b1, IRBlock b2) { + b1.getASuccessor() = b2 and + not b1.getBackEdgeSuccessor(_) = b2 + } + + /** + * Holds if `f` contains a loop in which no edge is a back edge. + * + * This check ensures we don't have too _few_ back edges. + */ + query predicate containsLoopOfForwardEdges(IRFunction f, string message) { + exists(IRBlock block | + forwardEdge+(block, block) and + block.getEnclosingIRFunction() = f and + message = "Function contains a loop consisting of only forward edges." + ) + } + + /** + * Holds if `block` is reachable from its function entry point but would not + * be reachable by traversing only forward edges. This check is skipped for + * functions containing `goto` statements as the property does not generally + * hold there. + * + * This check ensures we don't have too _many_ back edges. + */ + query predicate lostReachability( + IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRFunction f, IRBlock entry | + entry = f.getEntryBlock() and + entry.getASuccessor+() = block and + not forwardEdge+(entry, block) and + not Language::hasGoto(f.getFunction()) and + message = + "Block '" + block.toString() + + "' is not reachable by traversing only forward edges in function '$@'." and + irFunc = TPresentIRFunction(f) and + irFuncText = irFunc.toString() + ) + } + + /** + * Holds if the number of back edges differs between the `Instruction` graph + * and the `IRBlock` graph. + */ + query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) { + exists(int fromInstr, int fromBlock | + fromInstr = + count(Instruction i1, Instruction i2 | + getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2 + ) and + fromBlock = + count(IRBlock b1, IRBlock b2 | + getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2 + ) and + fromInstr != fromBlock and + message = + "The instruction graph for function '" + irFunc.toString() + "' contains " + + fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString() + + " back edges." + ) + } + + /** + * Gets the point in the function at which the specified operand is evaluated. For most operands, + * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point + * of evaluation is at the end of the corresponding predecessor block. + */ + private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) { + block = operand.(PhiInputOperand).getPredecessorBlock() and + index = block.getInstructionCount() + or + exists(Instruction use | + use = operand.(NonPhiOperand).getUse() and + block.getInstruction(index) = use + ) + } + + /** + * Holds if `useOperand` has a definition that does not dominate the use. + */ + query predicate useNotDominatedByDefinition( + Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | + pointOfEvaluation(useOperand, useBlock, useIndex) and + defInstr = useOperand.getAnyDef() and + ( + defInstr instanceof PhiInstruction and + defBlock = defInstr.getBlock() and + defIndex = -1 + or + defBlock.getInstruction(defIndex) = defInstr + ) and + not ( + defBlock.strictlyDominates(useBlock) + or + defBlock = useBlock and + defIndex < useIndex + ) and + message = + "Operand '" + useOperand.toString() + + "' is not dominated by its definition in function '$@'." and + irFunc = getOperandIRFunction(useOperand, irFuncText) + ) + } + + query predicate switchInstructionWithoutDefaultEdge( + SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + not exists(switchInstr.getDefaultSuccessor()) and + message = + "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and + irFunc = getInstructionIRFunction(switchInstr, irFuncText) + } + + /** + * Holds if `instr` is on the chain of chi/phi instructions for all aliased + * memory. + */ + private predicate isOnAliasedDefinitionChain(Instruction instr) { + instr instanceof AliasedDefinitionInstruction + or + isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) + or + isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) + } + + private predicate shouldBeConflated(Instruction instr) { + isOnAliasedDefinitionChain(instr) + or + instr.getOpcode() instanceof Opcode::InitializeNonLocal + } + + query predicate notMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + shouldBeConflated(instr) and + not instr.isResultConflated() and + message = + "Instruction '" + instr.toString() + + "' should be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } + + query predicate wronglyMarkedAsConflated( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + instr.isResultConflated() and + not shouldBeConflated(instr) and + message = + "Instruction '" + instr.toString() + + "' should not be marked as having a conflated result in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } + + query predicate invalidOverlap( + MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(Overlap overlap | + overlap = useOperand.getDefinitionOverlap() and + overlap instanceof MayPartiallyOverlap and + message = + "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" + + overlap.toString() + "'." and + irFunc = getOperandIRFunction(useOperand, irFuncText) + ) + } + + query predicate nonUniqueEnclosingIRFunction( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(int irFuncCount | + irFuncCount = count(instr.getEnclosingIRFunction()) and + irFuncCount != 1 and + message = + "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() + + " results for `getEnclosingIRFunction()` in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + ) + } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll new file mode 100644 index 000000000000..5968e58f90bf --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll @@ -0,0 +1,59 @@ +/** + * Provides the class `IRFunction`, which represents the Intermediate Representation for the + * definition of a function. + */ + +private import internal.IRInternal +private import internal.IRFunctionImports as Imports +import Imports::IRFunctionBase +import Instruction + +/** + * The IR for a function. + */ +class IRFunction extends IRFunctionBase { + /** + * Gets the entry point for this function. + */ + pragma[noinline] + final EnterFunctionInstruction getEnterFunctionInstruction() { + result.getEnclosingIRFunction() = this + } + + /** + * Gets the exit point for this function. + */ + pragma[noinline] + final ExitFunctionInstruction getExitFunctionInstruction() { + result.getEnclosingIRFunction() = this + } + + /** + * Gets the single return instruction for this function. + */ + pragma[noinline] + final ReturnInstruction getReturnInstruction() { result.getEnclosingIRFunction() = this } + + /** + * Gets the variable used to hold the return value of this function. If this + * function does not return a value, this predicate does not hold. + */ + pragma[noinline] + final IRReturnVariable getReturnVariable() { result.getEnclosingIRFunction() = this } + + /** + * Gets the block containing the entry point of this function. + */ + pragma[noinline] + final IRBlock getEntryBlock() { result.getFirstInstruction() = getEnterFunctionInstruction() } + + /** + * Gets all instructions in this function. + */ + final Instruction getAnInstruction() { result.getEnclosingIRFunction() = this } + + /** + * Gets all blocks in this function. + */ + final IRBlock getABlock() { result.getEnclosingIRFunction() = this } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll new file mode 100644 index 000000000000..146fc2707383 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll @@ -0,0 +1,327 @@ +/** + * Provides classes that represent variables accessed by the IR. + */ + +private import internal.IRInternal +import IRFunction +private import internal.IRVariableImports as Imports +import Imports::TempVariableTag +private import Imports::IRUtilities +private import Imports::TTempVariableTag +private import Imports::TIRVariable +private import Imports::IRType + +/** + * A variable referenced by the IR for a function. + * + * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated + * by the AST-to-IR translation (`IRTempVariable`). + */ +class IRVariable extends TIRVariable { + Language::Function func; + + IRVariable() { + this = TIRUserVariable(_, _, func) or + this = TIRTempVariable(func, _, _, _) or + this = TIRStringLiteral(func, _, _, _) or + this = TIRDynamicInitializationFlag(func, _, _) + } + + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Holds if this variable's value cannot be changed within a function. Currently used for string + * literals, but could also apply to `const` global and static variables. + */ + predicate isReadOnly() { none() } + + /** + * Gets the type of the variable. + */ + final Language::Type getType() { getLanguageType().hasType(result, false) } + + /** + * Gets the language-neutral type of the variable. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the variable. + */ + Language::LanguageType getLanguageType() { none() } + + /** + * Gets the AST node that declared this variable, or that introduced this + * variable as part of the AST-to-IR translation. + */ + Language::AST getAST() { none() } + + /** + * Gets an identifier string for the variable. This identifier is unique + * within the function. + */ + string getUniqueId() { none() } + + /** + * Gets the source location of this variable. + */ + final Language::Location getLocation() { result = getAST().getLocation() } + + /** + * Gets the IR for the function that references this variable. + */ + final IRFunction getEnclosingIRFunction() { result.getFunction() = func } + + /** + * Gets the function that references this variable. + */ + final Language::Function getEnclosingFunction() { result = func } +} + +/** + * A user-declared variable referenced by the IR for a function. + */ +class IRUserVariable extends IRVariable, TIRUserVariable { + Language::Variable var; + Language::LanguageType type; + + IRUserVariable() { this = TIRUserVariable(var, type, func) } + + final override string toString() { result = getVariable().toString() } + + final override Language::AST getAST() { result = var } + + final override string getUniqueId() { + result = getVariable().toString() + " " + getVariable().getLocation().toString() + } + + final override Language::LanguageType getLanguageType() { result = type } + + /** + * Gets the original user-declared variable. + */ + Language::Variable getVariable() { result = var } +} + +/** + * A variable (user-declared or temporary) that is allocated on the stack. This includes all + * parameters, non-static local variables, and temporary variables. + */ +class IRAutomaticVariable extends IRVariable { + IRAutomaticVariable() { + exists(Language::Variable var | + this = TIRUserVariable(var, _, func) and + Language::isVariableAutomatic(var) + ) + or + this = TIRTempVariable(func, _, _, _) + } +} + +/** + * A user-declared variable that is allocated on the stack. This includes all parameters and + * non-static local variables. + */ +class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { + override Language::AutomaticVariable var; + + final override Language::AutomaticVariable getVariable() { result = var } +} + +/** + * A user-declared variable that is not allocated on the stack. This includes all global variables, + * namespace-scope variables, static fields, and static local variables. + */ +class IRStaticUserVariable extends IRUserVariable { + override Language::StaticVariable var; + + IRStaticUserVariable() { not Language::isVariableAutomatic(var) } + + final override Language::StaticVariable getVariable() { result = var } +} + +/** + * A variable that is not user-declared. This includes temporary variables generated as part of IR + * construction, as well as string literals. + */ +class IRGeneratedVariable extends IRVariable { + Language::AST ast; + Language::LanguageType type; + + IRGeneratedVariable() { + this = TIRTempVariable(func, ast, _, type) or + this = TIRStringLiteral(func, ast, type, _) or + this = TIRDynamicInitializationFlag(func, ast, type) + } + + final override Language::LanguageType getLanguageType() { result = type } + + final override Language::AST getAST() { result = ast } + + override string toString() { result = getBaseString() + getLocationString() } + + override string getUniqueId() { none() } + + /** + * INTERNAL: Do not use. + * + * Gets a string containing the source code location of the AST that generated this variable. + * + * This is used by debugging and printing code only. + */ + final string getLocationString() { + result = + ast.getLocation().getStartLine().toString() + ":" + + ast.getLocation().getStartColumn().toString() + } + + /** + * INTERNAL: Do not use. + * + * Gets the string that is combined with the location of the variable to generate the string + * representation of this variable. + * + * This is used by debugging and printing code only. + */ + string getBaseString() { none() } +} + +/** + * A temporary variable introduced by IR construction. The most common examples are the variable + * generated to hold the return value of a function, or the variable generated to hold the result of + * a condition operator (`a ? b : c`). + */ +class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { + TempVariableTag tag; + + IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } + + final override string getUniqueId() { + result = "Temp: " + Construction::getTempVariableUniqueId(this) + } + + /** + * Gets the "tag" object that differentiates this temporary variable from other temporary + * variables generated for the same AST. + */ + final TempVariableTag getTag() { result = tag } + + override string getBaseString() { result = "#temp" } +} + +/** + * A temporary variable generated to hold the return value of a function. + */ +class IRReturnVariable extends IRTempVariable { + IRReturnVariable() { tag = ReturnValueTempVar() } + + final override string toString() { result = "#return" } +} + +/** + * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. + */ +class IRThrowVariable extends IRTempVariable { + IRThrowVariable() { tag = ThrowTempVar() } + + final override string getBaseString() { result = "#throw" } +} + +/** + * A temporary variable generated to hold the contents of all arguments passed to the `...` of a + * function that accepts a variable number of arguments. + */ +class IREllipsisVariable extends IRTempVariable, IRParameter { + IREllipsisVariable() { tag = EllipsisTempVar() } + + final override string toString() { result = "#ellipsis" } + + final override int getIndex() { result = func.getNumberOfParameters() } +} + +/** + * A temporary variable generated to hold the `this` pointer. + */ +class IRThisVariable extends IRTempVariable, IRParameter { + IRThisVariable() { tag = ThisTempVar() } + + final override string toString() { result = "#this" } + + final override int getIndex() { result = -1 } +} + +/** + * A variable generated to represent the contents of a string literal. This variable acts much like + * a read-only global variable. + */ +class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { + Language::StringLiteral literal; + + IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) } + + final override predicate isReadOnly() { any() } + + final override string getUniqueId() { + result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal) + } + + final override string getBaseString() { result = "#string" } + + /** + * Gets the AST of the string literal represented by this `IRStringLiteral`. + */ + final Language::StringLiteral getLiteral() { result = literal } +} + +/** + * A variable generated to track whether a specific non-stack variable has been initialized. This is + * used to model the runtime initialization of static local variables in C++, as well as static + * fields in C#. + */ +class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag { + Language::Variable var; + + IRDynamicInitializationFlag() { + this = TIRDynamicInitializationFlag(func, var, type) and ast = var + } + + final override string toString() { result = var.toString() + "#init" } + + /** + * Gets variable whose initialization is guarded by this flag. + */ + final Language::Variable getVariable() { result = var } + + final override string getUniqueId() { + result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString() + } + + final override string getBaseString() { result = "#init:" + var.toString() + ":" } +} + +/** + * An IR variable which acts like a function parameter, including positional parameters and the + * temporary variables generated for `this` and ellipsis parameters. + */ +class IRParameter extends IRAutomaticVariable { + IRParameter() { + this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter + or + this = TIRTempVariable(_, _, ThisTempVar(), _) + or + this = TIRTempVariable(_, _, EllipsisTempVar(), _) + } + + /** + * Gets the zero-based index of this parameter. The `this` parameter has index -1. + */ + int getIndex() { none() } +} + +/** + * An IR variable representing a positional parameter. + */ +class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable { + final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll new file mode 100644 index 000000000000..620b23b942e0 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -0,0 +1,2069 @@ +/** + * Provides classes that represent the individual instructions in the IR for a function. + */ + +private import internal.IRInternal +import IRFunction +import IRBlock +import IRVariable +import Operand +private import internal.InstructionImports as Imports +import Imports::EdgeKind +import Imports::IRType +import Imports::MemoryAccessKind +import Imports::Opcode +private import Imports::OperandTag + +/** + * Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified + * `File` and line number. Used for assigning register names when printing IR. + */ +private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) { + exists(IRConfiguration::IRConfiguration config | + config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction()) + ) and + exists(Language::Location location | + irFunc = result.getEnclosingIRFunction() and + location = result.getLocation() and + file = location.getFile() and + line = location.getStartLine() + ) +} + +/** + * A single instruction in the IR. + */ +class Instruction extends Construction::TStageInstruction { + Instruction() { + // The base `TStageInstruction` type is a superset of the actual instructions appearing in this + // stage. This call lets the stage filter out the ones that are not reused from raw IR. + Construction::hasInstruction(this) + } + + /** Gets a textual representation of this element. */ + final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } + + /** + * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what + * would be printed by PrintIR.ql. For example: + * + * `mu0_28(int) = Store r0_26, r0_27` + */ + final string getDumpString() { + result = getResultString() + " = " + getOperationString() + " " + getOperandsString() + } + + private predicate shouldGenerateDumpStrings() { + exists(IRConfiguration::IRConfiguration config | + config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction()) + ) + } + + /** + * Gets a string describing the operation of this instruction. This includes + * the opcode and the immediate value, if any. For example: + * + * VariableAddress[x] + */ + final string getOperationString() { + shouldGenerateDumpStrings() and + if exists(getImmediateString()) + then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" + else result = getOperationPrefix() + getOpcode().toString() + } + + /** + * Gets a string describing the immediate value of this instruction, if any. + */ + string getImmediateString() { none() } + + private string getOperationPrefix() { + shouldGenerateDumpStrings() and + if this instanceof SideEffectInstruction then result = "^" else result = "" + } + + private string getResultPrefix() { + shouldGenerateDumpStrings() and + if getResultIRType() instanceof IRVoidType + then result = "v" + else + if hasMemoryResult() + then if isResultModeled() then result = "m" else result = "mu" + else result = "r" + } + + /** + * Gets the zero-based index of this instruction within its block. This is + * used by debugging and printing code only. + */ + int getDisplayIndexInBlock() { + shouldGenerateDumpStrings() and + exists(IRBlock block | + this = block.getInstruction(result) + or + this = + rank[-result - 1](PhiInstruction phiInstr | + phiInstr = block.getAPhiInstruction() + | + phiInstr order by phiInstr.getUniqueId() + ) + ) + } + + private int getLineRank() { + shouldGenerateDumpStrings() and + this = + rank[result](Instruction instr | + instr = + getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), + getLocation().getStartLine()) + | + instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() + ) + } + + /** + * Gets a human-readable string that uniquely identifies this instruction + * within the function. This string is used to refer to this instruction when + * printing IR dumps. + * + * Example: `r1_1` + */ + string getResultId() { + shouldGenerateDumpStrings() and + result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() + } + + /** + * Gets a string describing the result of this instruction, suitable for + * display in IR dumps. This consists of the result ID plus the type of the + * result. + * + * Example: `r1_1(int*)` + */ + final string getResultString() { + shouldGenerateDumpStrings() and + result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + } + + /** + * Gets a string describing the operands of this instruction, suitable for + * display in IR dumps. + * + * Example: `func:r3_4, this:r3_5` + */ + string getOperandsString() { + shouldGenerateDumpStrings() and + result = + concat(Operand operand | + operand = getAnOperand() + | + operand.getDumpString(), ", " order by operand.getDumpSortOrder() + ) + } + + /** + * Gets a string identifier for this function that is unique among all + * instructions in the same function. + * + * This is used for sorting IR output for tests, and is likely to be + * inefficient for any other use. + */ + final string getUniqueId() { result = Construction::getInstructionUniqueId(this) } + + /** + * Gets the basic block that contains this instruction. + */ + final IRBlock getBlock() { result.getAnInstruction() = this } + + /** + * Gets the function that contains this instruction. + */ + final Language::Function getEnclosingFunction() { + result = getEnclosingIRFunction().getFunction() + } + + /** + * Gets the IRFunction object that contains the IR for this instruction. + */ + final IRFunction getEnclosingIRFunction() { + result = Construction::getInstructionEnclosingIRFunction(this) + } + + /** + * Gets the AST that caused this instruction to be generated. + */ + final Language::AST getAST() { result = Construction::getInstructionAST(this) } + + /** + * Gets the location of the source code for this instruction. + */ + final Language::Location getLocation() { result = getAST().getLocation() } + + /** + * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a + * conversion. + */ + final Language::Expr getConvertedResultExpression() { + result = Raw::getInstructionConvertedResultExpression(this) + } + + /** + * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. + */ + final Language::Expr getUnconvertedResultExpression() { + result = Raw::getInstructionUnconvertedResultExpression(this) + } + + /** + * Gets the language-specific type of the result produced by this instruction. + * + * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a + * less complex, language-neutral type system in which all semantically equivalent types share the + * same `IRType` instance. For example, in C++, four different `Instruction`s might have three + * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`, + * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`. + */ + final Language::LanguageType getResultLanguageType() { + result = Construction::getInstructionResultType(this) + } + + /** + * Gets the type of the result produced by this instruction. If the instruction does not produce + * a result, its result type will be `IRVoidType`. + */ + cached + final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + + /** + * Gets the type of the result produced by this instruction. If the + * instruction does not produce a result, its result type will be `VoidType`. + * + * If `isGLValue()` holds, then the result type of this instruction should be + * thought of as "pointer to `getResultType()`". + */ + final Language::Type getResultType() { + exists(Language::LanguageType resultType | + resultType = getResultLanguageType() and + ( + resultType.hasUnspecifiedType(result, _) + or + not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType + ) + ) + } + + /** + * Holds if the result produced by this instruction is a glvalue. If this + * holds, the result of the instruction represents the address of a location, + * and the type of the location is given by `getResultType()`. If this does + * not hold, the result of the instruction represents a value whose type is + * given by `getResultType()`. + * + * For example, the statement `y = x;` generates the following IR: + * ``` + * r1_0(glval: int) = VariableAddress[x] + * r1_1(int) = Load r1_0, mu0_1 + * r1_2(glval: int) = VariableAddress[y] + * mu1_3(int) = Store r1_2, r1_1 + * ``` + * + * The result of each `VariableAddress` instruction is a glvalue of type + * `int`, representing the address of the corresponding integer variable. The + * result of the `Load` instruction is a prvalue of type `int`, representing + * the integer value loaded from variable `x`. + */ + final predicate isGLValue() { getResultLanguageType().hasType(_, true) } + + /** + * Gets the size of the result produced by this instruction, in bytes. If the + * result does not have a known constant size, this predicate does not hold. + * + * If `this.isGLValue()` holds for this instruction, the value of + * `getResultSize()` will always be the size of a pointer. + */ + final int getResultSize() { result = getResultLanguageType().getByteSize() } + + /** + * Gets the opcode that specifies the operation performed by this instruction. + */ + final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } + + /** + * Gets all direct uses of the result of this instruction. The result can be + * an `Operand` for which `isDefinitionInexact` holds. + */ + final Operand getAUse() { result.getAnyDef() = this } + + /** + * Gets all of this instruction's operands. + */ + final Operand getAnOperand() { result.getUse() = this } + + /** + * Holds if this instruction produces a memory result. + */ + final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } + + /** + * Gets the kind of memory access performed by this instruction's result. + * Holds only for instructions with a memory result. + */ + pragma[inline] + final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } + + /** + * Holds if the memory access performed by this instruction's result will not always write to + * every bit in the memory location. This is most commonly used for memory accesses that may or + * may not actually occur depending on runtime state (for example, the write side effect of an + * output parameter that is not written to on all paths), or for accesses where the memory + * location is a conservative estimate of the memory that might actually be accessed at runtime + * (for example, the global side effects of a function call). + */ + pragma[inline] + final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } + + /** + * Gets the operand that holds the memory address to which this instruction stores its + * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` + * is `r1`. + */ + final AddressOperand getResultAddressOperand() { + getResultMemoryAccess().usesAddressOperand() and + result.getUse() = this + } + + /** + * Gets the instruction that holds the exact memory address to which this instruction stores its + * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` + * is the instruction that defines `r1`. + */ + final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } + + /** + * Holds if the result of this instruction is precisely modeled in SSA. Always + * holds for a register result. For a memory result, a modeled result is + * connected to its actual uses. An unmodeled result has no uses. + * + * For example: + * ``` + * int x = 1; + * int *p = &x; + * int y = *p; + * ``` + * In non-aliased SSA, `x` will not be modeled because it has its address + * taken. In that case, `isResultModeled()` would not hold for the result of + * the `Store` to `x`. + */ + final predicate isResultModeled() { + // Register results are always in SSA form. + not hasMemoryResult() or + Construction::hasModeledMemoryResult(this) + } + + /** + * Holds if this is an instruction with a memory result that represents a + * conflation of more than one memory allocation. + * + * This happens in practice when dereferencing a pointer that cannot be + * tracked back to a single local allocation. Such memory is instead modeled + * as originating on the `AliasedDefinitionInstruction` at the entry of the + * function. + */ + final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } + + /** + * Gets the successor of this instruction along the control flow edge + * specified by `kind`. + */ + final Instruction getSuccessor(EdgeKind kind) { + result = Construction::getInstructionSuccessor(this, kind) + } + + /** + * Gets the a _back-edge successor_ of this instruction along the control + * flow edge specified by `kind`. A back edge in the control-flow graph is + * intuitively the edge that goes back around a loop. If all back edges are + * removed from the control-flow graph, it becomes acyclic. + */ + final Instruction getBackEdgeSuccessor(EdgeKind kind) { + // We don't take these edges from + // `Construction::getInstructionBackEdgeSuccessor` since that relation has + // not been treated to remove any loops that might be left over due to + // flaws in the IR construction or back-edge detection. + exists(IRBlock block | + block = this.getBlock() and + this = block.getLastInstruction() and + result = block.getBackEdgeSuccessor(kind).getFirstInstruction() + ) + } + + /** + * Gets all direct successors of this instruction. + */ + final Instruction getASuccessor() { result = getSuccessor(_) } + + /** + * Gets a predecessor of this instruction such that the predecessor reaches + * this instruction along the control flow edge specified by `kind`. + */ + final Instruction getPredecessor(EdgeKind kind) { result.getSuccessor(kind) = this } + + /** + * Gets all direct predecessors of this instruction. + */ + final Instruction getAPredecessor() { result = getPredecessor(_) } +} + +/** + * An instruction that refers to a variable. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * variable. For example, it is used for `VariableAddress`, which returns the address of a specific + * variable, and `InitializeParameter`, which returns the value that was passed to the specified + * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions + * that happen to load from or store to a particular variable; in those cases, the memory location + * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be + * defined by the result of a `VariableAddress` instruction. + */ +class VariableInstruction extends Instruction { + IRVariable var; + + VariableInstruction() { var = Raw::getInstructionVariable(this) } + + override string getImmediateString() { result = var.toString() } + + /** + * Gets the variable that this instruction references. + */ + final IRVariable getIRVariable() { result = var } + + /** + * Gets the AST variable that this instruction's IR variable refers to, if one exists. + */ + final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } +} + +/** + * An instruction that refers to a field of a class, struct, or union. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * field. For example, it is used for `FieldAddress`, which computes the address of a specific + * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen + * to load from or store to a particular field; in those cases, the memory location being accessed + * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the + * result of a `FieldAddress` instruction. + */ +class FieldInstruction extends Instruction { + Language::Field field; + + FieldInstruction() { field = Raw::getInstructionField(this) } + + final override string getImmediateString() { result = field.toString() } + + /** + * Gets the field that this instruction references. + */ + final Language::Field getField() { result = field } +} + +/** + * An instruction that refers to a function. + * + * This class is used for any instruction whose operation fundamentally depends on a specific + * function. For example, it is used for `FunctionAddress`, which returns the address of a specific + * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a + * particular function; in that case, the function being called is specified by the + * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a + * `FunctionAddress` instruction. + */ +class FunctionInstruction extends Instruction { + Language::Function funcSymbol; + + FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) } + + final override string getImmediateString() { result = funcSymbol.toString() } + + /** + * Gets the function that this instruction references. + */ + final Language::Function getFunctionSymbol() { result = funcSymbol } +} + +/** + * An instruction whose result is a compile-time constant value. + */ +class ConstantValueInstruction extends Instruction { + string value; + + ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) } + + final override string getImmediateString() { result = value } + + /** + * Gets the constant value of this instruction's result. + */ + final string getValue() { result = value } +} + +/** + * An instruction that refers to an argument of a `Call` instruction. + * + * This instruction is used for side effects of a `Call` instruction that read or write memory + * pointed to by one of the arguments of the call. + */ +class IndexedInstruction extends Instruction { + int index; + + IndexedInstruction() { index = Raw::getInstructionIndex(this) } + + final override string getImmediateString() { result = index.toString() } + + /** + * Gets the zero-based index of the argument that this instruction references. + */ + final int getIndex() { result = index } +} + +/** + * An instruction representing the entry point to a function. + * + * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins + * at this instruction. This instruction has no predecessors. + */ +class EnterFunctionInstruction extends Instruction { + EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } +} + +/** + * An instruction that returns the address of a variable. + * + * This instruction returns the address of a local variable, parameter, static field, + * namespace-scope variable, or global variable. For the address of a non-static field of a class, + * struct, or union, see `FieldAddressInstruction`. + */ +class VariableAddressInstruction extends VariableInstruction { + VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } +} + +/** + * An instruction that returns the address of a function. + * + * This instruction returns the address of a function, including non-member functions, static member + * functions, and non-static member functions. + * + * The result has an `IRFunctionAddress` type. + */ +class FunctionAddressInstruction extends FunctionInstruction { + FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } +} + +/** + * An instruction that initializes a parameter of the enclosing function with the value of the + * corresponding argument passed by the caller. + * + * Each parameter of a function will have exactly one `InitializeParameter` instruction that + * initializes that parameter. + */ +class InitializeParameterInstruction extends VariableInstruction { + InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + + /** + * Gets the parameter initialized by this instruction. + */ + final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } +} + +/** + * An instruction that initializes all memory that existed before this function was called. + * + * This instruction provides a definition for memory that, because it was actually allocated and + * initialized elsewhere, would not otherwise have a definition in this function. + */ +class InitializeNonLocalInstruction extends Instruction { + InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } +} + +/** + * An instruction that initializes the memory pointed to by a parameter of the enclosing function + * with the value of that memory on entry to the function. + */ +class InitializeIndirectionInstruction extends VariableInstruction { + InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + + /** + * Gets the parameter initialized by this instruction. + */ + final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } +} + +/** + * An instruction that initializes the `this` pointer parameter of the enclosing function. + */ +class InitializeThisInstruction extends Instruction { + InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } +} + +/** + * An instruction that computes the address of a non-static field of an object. + */ +class FieldAddressInstruction extends FieldInstruction { + FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + + /** + * Gets the operand that provides the address of the object containing the field. + */ + final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the object containing the field. + */ + final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } +} + +/** + * An instruction that computes the address of the first element of a managed array. + * + * This instruction is used for element access to C# arrays. + */ +class ElementsAddressInstruction extends UnaryInstruction { + ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + + /** + * Gets the operand that provides the address of the array object. + */ + final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the array object. + */ + final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } +} + +/** + * An instruction that produces a well-defined but unknown result and has + * unknown side effects, including side effects that are not conservatively + * modeled in the SSA graph. + * + * This type of instruction appears when there is an `ErrorExpr` in the AST, + * meaning that the extractor could not understand the expression and therefore + * produced a partial AST. Queries that give alerts when some action is _not_ + * taken may want to ignore any function that contains an `ErrorInstruction`. + */ +class ErrorInstruction extends Instruction { + ErrorInstruction() { getOpcode() instanceof Opcode::Error } +} + +/** + * An instruction that returns an uninitialized value. + * + * This instruction is used to provide an initial definition for a stack variable that does not have + * an initializer, or whose initializer only partially initializes the variable. + */ +class UninitializedInstruction extends VariableInstruction { + UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } + + /** + * Gets the variable that is uninitialized. + */ + final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } +} + +/** + * An instruction that has no effect. + * + * This instruction is typically inserted to ensure that a particular AST is associated with at + * least one instruction, even when the AST has no semantic effect. + */ +class NoOpInstruction extends Instruction { + NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } +} + +/** + * An instruction that returns control to the caller of the function. + * + * This instruction represents the normal (non-exception) return from a function, either from an + * explicit `return` statement or from control flow reaching the end of the function's body. + * + * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is + * represented as an initialization of the temporary variable that holds the return value, with + * control then flowing to the common `ReturnInstruction` for that function. Exception: A function + * that never returns will not have a `ReturnInstruction`. + * + * The `ReturnInstruction` for a function will have a control-flow successor edge to a block + * containing the `ExitFunction` instruction for that function. + * + * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from + * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a + * `void`-returning function. + */ +class ReturnInstruction extends Instruction { + ReturnInstruction() { getOpcode() instanceof ReturnOpcode } +} + +/** + * An instruction that returns control to the caller of the function, without returning a value. + */ +class ReturnVoidInstruction extends ReturnInstruction { + ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } +} + +/** + * An instruction that returns control to the caller of the function, including a return value. + */ +class ReturnValueInstruction extends ReturnInstruction { + ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + + /** + * Gets the operand that provides the value being returned by the function. + */ + final LoadOperand getReturnValueOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the value being returned by the function, if an + * exact definition is available. + */ + final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } +} + +/** + * An instruction that represents the use of the value pointed to by a parameter of the function + * after the function returns control to its caller. + * + * This instruction does not itself return control to the caller. It merely represents the potential + * for a caller to use the memory pointed to by the parameter sometime after the call returns. This + * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility + * that the caller initialized the memory pointed to by the parameter before the call. + */ +class ReturnIndirectionInstruction extends VariableInstruction { + ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + + /** + * Gets the operand that provides the value of the pointed-to memory. + */ + final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the value of the pointed-to memory, if an exact + * definition is available. + */ + final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + + /** + * Gets the operand that provides the address of the pointed-to memory. + */ + final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the pointed-to memory. + */ + final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + + /** + * Gets the parameter for which this instruction reads the final pointed-to value within the + * function. + */ + final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } + + /** + * Holds if this instruction is the return indirection for `this`. + */ + final predicate isThisIndirection() { var instanceof IRThisVariable } +} + +/** + * An instruction that returns a copy of its operand. + * + * There are several different copy instructions, depending on the source and destination of the + * copy operation: + * - `CopyInstruction` - Copies a register operand to a register result. + * - `LoadInstruction` - Copies a memory operand to a register result. + * - `StoreInstruction` - Copies a register operand to a memory result. + */ +class CopyInstruction extends Instruction { + CopyInstruction() { getOpcode() instanceof CopyOpcode } + + /** + * Gets the operand that provides the input value of the copy. + */ + Operand getSourceValueOperand() { none() } + + /** + * Gets the instruction whose result provides the input value of the copy, if an exact definition + * is available. + */ + final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } +} + +/** + * An instruction that returns a register result containing a copy of its register operand. + */ +class CopyValueInstruction extends CopyInstruction, UnaryInstruction { + CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } + + final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } +} + +/** + * An instruction that returns a register result containing a copy of its memory operand. + */ +class LoadInstruction extends CopyInstruction { + LoadInstruction() { getOpcode() instanceof Opcode::Load } + + /** + * Gets the operand that provides the address of the value being loaded. + */ + final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the value being loaded. + */ + final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + + final override LoadOperand getSourceValueOperand() { result = getAnOperand() } +} + +/** + * An instruction that returns a memory result containing a copy of its register operand. + */ +class StoreInstruction extends CopyInstruction { + StoreInstruction() { getOpcode() instanceof Opcode::Store } + + /** + * Gets the operand that provides the address of the location to which the value will be stored. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the location to which the value will + * be stored, if an exact definition is available. + */ + final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + + final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } +} + +/** + * An instruction that branches to one of two successor instructions based on the value of a Boolean + * operand. + */ +class ConditionalBranchInstruction extends Instruction { + ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + + /** + * Gets the operand that provides the Boolean condition controlling the branch. + */ + final ConditionOperand getConditionOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the Boolean condition controlling the branch. + */ + final Instruction getCondition() { result = getConditionOperand().getDef() } + + /** + * Gets the instruction to which control will flow if the condition is true. + */ + final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + + /** + * Gets the instruction to which control will flow if the condition is false. + */ + final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } +} + +/** + * An instruction representing the exit point of a function. + * + * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns + * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns + * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no + * successors. + */ +class ExitFunctionInstruction extends Instruction { + ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } +} + +/** + * An instruction whose result is a constant value. + */ +class ConstantInstruction extends ConstantValueInstruction { + ConstantInstruction() { getOpcode() instanceof Opcode::Constant } +} + +/** + * An instruction whose result is a constant value of integer or Boolean type. + */ +class IntegerConstantInstruction extends ConstantInstruction { + IntegerConstantInstruction() { + exists(IRType resultType | + resultType = getResultIRType() and + (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) + ) + } +} + +/** + * An instruction whose result is a constant value of floating-point type. + */ +class FloatConstantInstruction extends ConstantInstruction { + FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } +} + +/** + * An instruction whose result is the address of a string literal. + */ +class StringConstantInstruction extends VariableInstruction { + override IRStringLiteral var; + + final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + + /** + * Gets the string literal whose address is returned by this instruction. + */ + final Language::StringLiteral getValue() { result = var.getLiteral() } +} + +/** + * An instruction whose result is computed from two operands. + */ +class BinaryInstruction extends Instruction { + BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + + /** + * Gets the left operand of this binary instruction. + */ + final LeftOperand getLeftOperand() { result = getAnOperand() } + + /** + * Gets the right operand of this binary instruction. + */ + final RightOperand getRightOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the value of the left operand of this binary + * instruction. + */ + final Instruction getLeft() { result = getLeftOperand().getDef() } + + /** + * Gets the instruction whose result provides the value of the right operand of this binary + * instruction. + */ + final Instruction getRight() { result = getRightOperand().getDef() } + + /** + * Holds if this instruction's operands are `op1` and `op2`, in either order. + */ + final predicate hasOperands(Operand op1, Operand op2) { + op1 = getLeftOperand() and op2 = getRightOperand() + or + op1 = getRightOperand() and op2 = getLeftOperand() + } +} + +/** + * An instruction that computes the result of an arithmetic operation. + */ +class ArithmeticInstruction extends Instruction { + ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } +} + +/** + * An instruction that performs an arithmetic operation on two numeric operands. + */ +class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } + +/** + * An instruction whose result is computed by performing an arithmetic operation on a single + * numeric operand. + */ +class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } + +/** + * An instruction that computes the sum of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is + * performed according to IEEE-754. + */ +class AddInstruction extends BinaryArithmeticInstruction { + AddInstruction() { getOpcode() instanceof Opcode::Add } +} + +/** + * An instruction that computes the difference of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed + * according to IEEE-754. + */ +class SubInstruction extends BinaryArithmeticInstruction { + SubInstruction() { getOpcode() instanceof Opcode::Sub } +} + +/** + * An instruction that computes the product of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is + * performed according to IEEE-754. + */ +class MulInstruction extends BinaryArithmeticInstruction { + MulInstruction() { getOpcode() instanceof Opcode::Mul } +} + +/** + * An instruction that computes the quotient of two numeric operands. + * + * Both operands must have the same numeric type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. Floating-point division is performed according + * to IEEE-754. + */ +class DivInstruction extends BinaryArithmeticInstruction { + DivInstruction() { getOpcode() instanceof Opcode::Div } +} + +/** + * An instruction that computes the remainder of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. The result of + * division by zero or integer overflow is undefined. + */ +class RemInstruction extends BinaryArithmeticInstruction { + RemInstruction() { getOpcode() instanceof Opcode::Rem } +} + +/** + * An instruction that negates a single numeric operand. + * + * The operand must have a numeric type, which will also be the result type. The result of integer + * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation + * is performed according to IEEE-754. + */ +class NegateInstruction extends UnaryArithmeticInstruction { + NegateInstruction() { getOpcode() instanceof Opcode::Negate } +} + +/** + * An instruction that computes the result of a bitwise operation. + */ +class BitwiseInstruction extends Instruction { + BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } +} + +/** + * An instruction that performs a bitwise operation on two integer operands. + */ +class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } + +/** + * An instruction that performs a bitwise operation on a single integer operand. + */ +class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } + +/** + * An instruction that computes the bitwise "and" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ +class BitAndInstruction extends BinaryBitwiseInstruction { + BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } +} + +/** + * An instruction that computes the bitwise "or" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ +class BitOrInstruction extends BinaryBitwiseInstruction { + BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } +} + +/** + * An instruction that computes the bitwise "xor" of two integer operands. + * + * Both operands must have the same integer type, which will also be the result type. + */ +class BitXorInstruction extends BinaryBitwiseInstruction { + BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } +} + +/** + * An instruction that shifts its left operand to the left by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. The + * rightmost bits are zero-filled. + */ +class ShiftLeftInstruction extends BinaryBitwiseInstruction { + ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } +} + +/** + * An instruction that shifts its left operand to the right by the number of bits specified by its + * right operand. + * + * Both operands must have an integer type. The result has the same type as the left operand. If the + * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand + * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit + * of the left operand. + */ +class ShiftRightInstruction extends BinaryBitwiseInstruction { + ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } +} + +/** + * An instruction that performs a binary arithmetic operation involving at least one pointer + * operand. + */ +class PointerArithmeticInstruction extends BinaryInstruction { + int elementSize; + + PointerArithmeticInstruction() { + getOpcode() instanceof PointerArithmeticOpcode and + elementSize = Raw::getInstructionElementSize(this) + } + + final override string getImmediateString() { result = elementSize.toString() } + + /** + * Gets the size of the elements pointed to by the pointer operands, in bytes. + * + * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer + * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the + * element size to compute the actual number of bytes added to or subtracted from the pointer + * address. When computing the integer difference between two pointers (`PointerDiffInstruction`), + * the result is computed by computing the difference between the two pointer byte addresses, then + * dividing that byte count by the element size. + */ + final int getElementSize() { result = elementSize } +} + +/** + * An instruction that adds or subtracts an integer offset from a pointer. + */ +class PointerOffsetInstruction extends PointerArithmeticInstruction { + PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } +} + +/** + * An instruction that adds an integer offset to a pointer. + * + * The result is the byte address computed by adding the value of the right (integer) operand, + * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer + * overflow is undefined. + */ +class PointerAddInstruction extends PointerOffsetInstruction { + PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } +} + +/** + * An instruction that subtracts an integer offset from a pointer. + * + * The result is the byte address computed by subtracting the value of the right (integer) operand, + * multiplied by the element size, from the value of the left (pointer) operand. The result of + * pointer underflow is undefined. + */ +class PointerSubInstruction extends PointerOffsetInstruction { + PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } +} + +/** + * An instruction that computes the difference between two pointers. + * + * Both operands must have the same pointer type. The result must have an integer type whose size is + * the same as that of the pointer operands. The result is computed by subtracting the byte address + * in the right operand from the byte address in the left operand, and dividing by the element size. + * If the difference in byte addresses is not divisible by the element size, the result is + * undefined. + */ +class PointerDiffInstruction extends PointerArithmeticInstruction { + PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } +} + +/** + * An instruction whose result is computed from a single operand. + */ +class UnaryInstruction extends Instruction { + UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + + /** + * Gets the sole operand of this instruction. + */ + final UnaryOperand getUnaryOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the sole operand of this instruction. + */ + final Instruction getUnary() { result = getUnaryOperand().getDef() } +} + +/** + * An instruction that converts the value of its operand to a value of a different type. + */ +class ConvertInstruction extends UnaryInstruction { + ConvertInstruction() { getOpcode() instanceof Opcode::Convert } +} + +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, returning a null address if the dynamic type of the + * object is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or + * `as` expression. + */ +class CheckedConvertOrNullInstruction extends UnaryInstruction { + CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } +} + +/** + * An instruction that converts the address of a polymorphic object to the address of a different + * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object + * is not compatible with the result type. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast + * expression. + */ +class CheckedConvertOrThrowInstruction extends UnaryInstruction { + CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } +} + +/** + * An instruction that returns the address of the complete object that contains the subobject + * pointed to by its operand. + * + * If the operand holds a null address, the result is a null address. + * + * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to + * the most-derived object. + */ +class CompleteObjectAddressInstruction extends UnaryInstruction { + CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } +} + +/** + * An instruction that converts the address of an object to the address of a different subobject of + * the same object, without any type checking at runtime. + */ +class InheritanceConversionInstruction extends UnaryInstruction { + Language::Class baseClass; + Language::Class derivedClass; + + InheritanceConversionInstruction() { + Raw::getInstructionInheritance(this, baseClass, derivedClass) + } + + final override string getImmediateString() { + result = derivedClass.toString() + " : " + baseClass.toString() + } + + /** + * Gets the `ClassDerivation` for the inheritance relationship between + * the base and derived classes. This predicate does not hold if the + * conversion is to an indirect virtual base class. + */ + final Language::ClassDerivation getDerivation() { + result.getBaseClass() = baseClass and result.getDerivedClass() = derivedClass + } + + /** + * Gets the base class of the conversion. This will be either a direct + * base class of the derived class, or a virtual base class of the + * derived class. + */ + final Language::Class getBaseClass() { result = baseClass } + + /** + * Gets the derived class of the conversion. + */ + final Language::Class getDerivedClass() { result = derivedClass } +} + +/** + * An instruction that converts from the address of a derived class to the address of a base class. + */ +class ConvertToBaseInstruction extends InheritanceConversionInstruction { + ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } +} + +/** + * An instruction that converts from the address of a derived class to the address of a direct + * non-virtual base class. + * + * If the operand holds a null address, the result is a null address. + */ +class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { + ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } +} + +/** + * An instruction that converts from the address of a derived class to the address of a virtual base + * class. + * + * If the operand holds a null address, the result is a null address. + */ +class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { + ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } +} + +/** + * An instruction that converts from the address of a base class to the address of a direct + * non-virtual derived class. + * + * If the operand holds a null address, the result is a null address. + */ +class ConvertToDerivedInstruction extends InheritanceConversionInstruction { + ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } +} + +/** + * An instruction that computes the bitwise complement of its operand. + * + * The operand must have an integer type, which will also be the result type. + */ +class BitComplementInstruction extends UnaryBitwiseInstruction { + BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } +} + +/** + * An instruction that computes the logical complement of its operand. + * + * The operand must have a Boolean type, which will also be the result type. + */ +class LogicalNotInstruction extends UnaryInstruction { + LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } +} + +/** + * An instruction that compares two numeric operands. + */ +class CompareInstruction extends BinaryInstruction { + CompareInstruction() { getOpcode() instanceof CompareOpcode } +} + +/** + * An instruction that returns a `true` result if its operands are equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are + * unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareEQInstruction extends CompareInstruction { + CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } +} + +/** + * An instruction that returns a `true` result if its operands are not equal. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if `left != right` or if the two operands are unordered, and `false` if + * `left == right`. Floating-point comparison is performed according to IEEE-754. + */ +class CompareNEInstruction extends CompareInstruction { + CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } +} + +/** + * An instruction that does a relative comparison of two values, such as `<` or `>=`. + */ +class RelationalInstruction extends CompareInstruction { + RelationalInstruction() { getOpcode() instanceof RelationalOpcode } + + /** + * Gets the operand on the "greater" (or "greater-or-equal") side + * of this relational instruction, that is, the side that is larger + * if the overall instruction evaluates to `true`; for example on + * `x <= 20` this is the `20`, and on `y > 0` it is `y`. + */ + Instruction getGreater() { none() } + + /** + * Gets the operand on the "lesser" (or "lesser-or-equal") side + * of this relational instruction, that is, the side that is smaller + * if the overall instruction evaluates to `true`; for example on + * `x <= 20` this is `x`, and on `y > 0` it is the `0`. + */ + Instruction getLesser() { none() } + + /** + * Holds if this relational instruction is strict (is not an "or-equal" instruction). + */ + predicate isStrict() { none() } +} + +/** + * An instruction that returns a `true` result if its left operand is less than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareLTInstruction extends RelationalInstruction { + CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } + + override Instruction getLesser() { result = getLeft() } + + override Instruction getGreater() { result = getRight() } + + override predicate isStrict() { any() } +} + +/** + * An instruction that returns a `true` result if its left operand is greater than its right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareGTInstruction extends RelationalInstruction { + CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } + + override Instruction getLesser() { result = getRight() } + + override Instruction getGreater() { result = getLeft() } + + override predicate isStrict() { any() } +} + +/** + * An instruction that returns a `true` result if its left operand is less than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareLEInstruction extends RelationalInstruction { + CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } + + override Instruction getLesser() { result = getLeft() } + + override Instruction getGreater() { result = getRight() } + + override predicate isStrict() { none() } +} + +/** + * An instruction that returns a `true` result if its left operand is greater than or equal to its + * right operand. + * + * Both operands must have the same numeric or address type. The result must have a Boolean type. + * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands + * are unordered. Floating-point comparison is performed according to IEEE-754. + */ +class CompareGEInstruction extends RelationalInstruction { + CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } + + override Instruction getLesser() { result = getRight() } + + override Instruction getGreater() { result = getLeft() } + + override predicate isStrict() { none() } +} + +/** + * An instruction that branches to one of multiple successor instructions based on the value of an + * integer operand. + * + * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each + * representing the branch that will be taken if the controlling expression is within the range + * specified for that case edge. The range of a case edge must be disjoint from the range of each + * other case edge. + * + * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`, + * representing the branch that will be taken if the controlling expression is not within the range + * of any case edge. + */ +class SwitchInstruction extends Instruction { + SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + + /** Gets the operand that provides the integer value controlling the switch. */ + final ConditionOperand getExpressionOperand() { result = getAnOperand() } + + /** Gets the instruction whose result provides the integer value controlling the switch. */ + final Instruction getExpression() { result = getExpressionOperand().getDef() } + + /** Gets the successor instructions along the case edges of the switch. */ + final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + + /** Gets the successor instruction along the default edge of the switch, if any. */ + final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } +} + +/** + * An instruction that calls a function. + */ +class CallInstruction extends Instruction { + CallInstruction() { getOpcode() instanceof Opcode::Call } + + /** + * Gets the operand the specifies the target function of the call. + */ + final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } + + /** + * Gets the `Instruction` that computes the target function of the call. This is usually a + * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a + * function pointer. + */ + final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } + + /** + * Gets all of the argument operands of the call, including the `this` pointer, if any. + */ + final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } + + /** + * Gets the `Function` that the call targets, if this is statically known. + */ + final Language::Function getStaticCallTarget() { + result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() + } + + /** + * Gets all of the arguments of the call, including the `this` pointer, if any. + */ + final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } + + /** + * Gets the `this` pointer argument operand of the call, if any. + */ + final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } + + /** + * Gets the `this` pointer argument of the call, if any. + */ + final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } + + /** + * Gets the argument operand at the specified index. + */ + final PositionalArgumentOperand getPositionalArgumentOperand(int index) { + result = getAnOperand() and + result.getIndex() = index + } + + /** + * Gets the argument at the specified index. + */ + final Instruction getPositionalArgument(int index) { + result = getPositionalArgumentOperand(index).getDef() + } + + /** + * Gets the number of arguments of the call, including the `this` pointer, if any. + */ + final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } +} + +/** + * An instruction representing a side effect of a function call. + */ +class SideEffectInstruction extends Instruction { + SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + + /** + * Gets the instruction whose execution causes this side effect. + */ + final Instruction getPrimaryInstruction() { + result = Construction::getPrimaryInstructionForSideEffect(this) + } +} + +/** + * An instruction representing the side effect of a function call on any memory that might be + * accessed by that call. + */ +class CallSideEffectInstruction extends SideEffectInstruction { + CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } +} + +/** + * An instruction representing the side effect of a function call on any memory + * that might be read by that call. + * + * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the + * call target cannot write to escaped memory. + */ +class CallReadSideEffectInstruction extends SideEffectInstruction { + CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } +} + +/** + * An instruction representing a read side effect of a function call on a + * specific parameter. + */ +class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { + ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } + + /** Gets the operand for the value that will be read from this instruction, if known. */ + final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + + /** Gets the value that will be read from this instruction, if known. */ + final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + + /** Gets the operand for the address from which this instruction may read. */ + final AddressOperand getArgumentOperand() { result = getAnOperand() } + + /** Gets the address from which this instruction may read. */ + final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } +} + +/** + * An instruction representing the read of an indirect parameter within a function call. + */ +class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { + IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } +} + +/** + * An instruction representing the read of an indirect buffer parameter within a function call. + */ +class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { + BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } +} + +/** + * An instruction representing the read of an indirect buffer parameter within a function call. + */ +class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { + SizedBufferReadSideEffectInstruction() { + getOpcode() instanceof Opcode::SizedBufferReadSideEffect + } + + /** + * Gets the operand that holds the number of bytes read from the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes read from the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } +} + +/** + * An instruction representing a write side effect of a function call on a + * specific parameter. + */ +class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { + WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } + + /** + * Get the operand that holds the address of the memory to be written. + */ + final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the address of the memory to be written. + */ + Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } +} + +/** + * An instruction representing the write of an indirect parameter within a function call. + */ +class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { + IndirectMustWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::IndirectMustWriteSideEffect + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ +class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { + BufferMustWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::BufferMustWriteSideEffect + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ +class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { + SizedBufferMustWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect + } + + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } +} + +/** + * An instruction representing the potential write of an indirect parameter within a function call. + * + * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. + * written. + */ +class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { + IndirectMayWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::IndirectMayWriteSideEffect + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. + * + * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. + */ +class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { + BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. + * + * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. + */ +class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { + SizedBufferMayWriteSideEffectInstruction() { + getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect + } + + /** + * Gets the operand that holds the number of bytes written to the buffer. + */ + final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + + /** + * Gets the instruction whose result provides the number of bytes written to the buffer. + */ + final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } +} + +/** + * An instruction representing the initial value of newly allocated memory, such as the result of a + * call to `malloc`. + */ +class InitializeDynamicAllocationInstruction extends SideEffectInstruction { + InitializeDynamicAllocationInstruction() { + getOpcode() instanceof Opcode::InitializeDynamicAllocation + } + + /** + * Gets the address of the allocation this instruction is initializing. + */ + final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } + + /** + * Gets the operand for the allocation this instruction is initializing. + */ + final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } +} + +/** + * An instruction representing a GNU or MSVC inline assembly statement. + */ +class InlineAsmInstruction extends Instruction { + InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } +} + +/** + * An instruction that throws an exception. + */ +class ThrowInstruction extends Instruction { + ThrowInstruction() { getOpcode() instanceof ThrowOpcode } +} + +/** + * An instruction that throws a new exception. + */ +class ThrowValueInstruction extends ThrowInstruction { + ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } + + /** + * Gets the address operand of the exception thrown by this instruction. + */ + final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } + + /** + * Gets the address of the exception thrown by this instruction. + */ + final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } + + /** + * Gets the operand for the exception thrown by this instruction. + */ + final LoadOperand getExceptionOperand() { result = getAnOperand() } + + /** + * Gets the exception thrown by this instruction. + */ + final Instruction getException() { result = getExceptionOperand().getDef() } +} + +/** + * An instruction that re-throws the current exception. + */ +class ReThrowInstruction extends ThrowInstruction { + ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } +} + +/** + * An instruction that exits the current function by propagating an exception. + */ +class UnwindInstruction extends Instruction { + UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } +} + +/** + * An instruction that starts a `catch` handler. + */ +class CatchInstruction extends Instruction { + CatchInstruction() { getOpcode() instanceof CatchOpcode } +} + +/** + * An instruction that catches an exception of a specific type. + */ +class CatchByTypeInstruction extends CatchInstruction { + Language::LanguageType exceptionType; + + CatchByTypeInstruction() { + getOpcode() instanceof Opcode::CatchByType and + exceptionType = Raw::getInstructionExceptionType(this) + } + + final override string getImmediateString() { result = exceptionType.toString() } + + /** + * Gets the type of exception to be caught. + */ + final Language::LanguageType getExceptionType() { result = exceptionType } +} + +/** + * An instruction that catches any exception. + */ +class CatchAnyInstruction extends CatchInstruction { + CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } +} + +/** + * An instruction that initializes all escaped memory. + */ +class AliasedDefinitionInstruction extends Instruction { + AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } +} + +/** + * An instruction that consumes all escaped memory on exit from the function. + */ +class AliasedUseInstruction extends Instruction { + AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } +} + +/** + * An instruction representing the choice of one of multiple input values based on control flow. + * + * A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of + * the same variable reach that block. The `PhiInstruction` will have one operand corresponding to + * each control flow predecessor of the block, with that operand representing the version of the + * variable that flows from that predecessor. The result value of the `PhiInstruction` will be + * a copy of whichever operand corresponds to the actual predecessor that entered the block at + * runtime. + */ +class PhiInstruction extends Instruction { + PhiInstruction() { getOpcode() instanceof Opcode::Phi } + + /** + * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. + */ + final PhiInputOperand getAnInputOperand() { result = this.getAnOperand() } + + /** + * Gets an instruction that defines the input to one of the operands of this + * instruction. It's possible for more than one operand to have the same + * defining instruction, so this predicate will have the same number of + * results as `getAnInputOperand()` or fewer. + */ + pragma[noinline] + final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } +} + +/** + * An instruction representing the effect that a write to a memory may have on potential aliases of + * that memory. + * + * A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The + * `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents + * the previous state of all of the memory that might be aliased by the memory write. The second + * operand, given by `getPartialOperand()`, represents the memory that was actually modified by the + * memory write. The result of the `ChiInstruction` represents the same memory as + * `getTotalOperand()`, updated to include the changes due to the value that was actually stored by + * the memory write. + * + * As an example, suppose that variable `p` and `q` are pointers that may or may not point to the + * same memory: + * ``` + * *p = 5; + * x = *q; + * ``` + * + * The IR would look like: + * ``` + * r1_1 = VariableAddress[p] + * r1_2 = Load r1_1, m0_0 // Load the value of `p` + * r1_3 = Constant[5] + * m1_4 = Store r1_2, r1_3 // Store to `*p` + * m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory + * r1_6 = VariableAddress[x] + * r1_7 = VariableAddress[q] + * r1_8 = Load r1_7, m0_2 // Load the value of `q` + * r1_9 = Load r1_8, m1_5 // Load the value of `*q` + * m1_10 = Store r1_6, r1_9 // Store to x + * ``` + * + * Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of + * aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a + * new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of + * `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory + * pointed to by `q`. + * + * For more information about how `Chi` instructions are used to model memory side effects, see + * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. + */ +class ChiInstruction extends Instruction { + ChiInstruction() { getOpcode() instanceof Opcode::Chi } + + /** + * Gets the operand that represents the previous state of all memory that might be aliased by the + * memory write. + */ + final ChiTotalOperand getTotalOperand() { result = getAnOperand() } + + /** + * Gets the operand that represents the previous state of all memory that might be aliased by the + * memory write. + */ + final Instruction getTotal() { result = getTotalOperand().getDef() } + + /** + * Gets the operand that represents the new value written by the memory write. + */ + final ChiPartialOperand getPartialOperand() { result = getAnOperand() } + + /** + * Gets the operand that represents the new value written by the memory write. + */ + final Instruction getPartial() { result = getPartialOperand().getDef() } + + /** + * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. + */ + final predicate getUpdatedInterval(int startBit, int endBit) { + Construction::getIntervalUpdatedByChi(this, startBit, endBit) + } +} + +/** + * An instruction representing unreachable code. + * + * This instruction is inserted in place of the original target instruction of a `ConditionalBranch` + * or `Switch` instruction where that particular edge is infeasible. + */ +class UnreachedInstruction extends Instruction { + UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } +} + +/** + * An instruction representing a built-in operation. + * + * This is used to represent a variety of intrinsic operations provided by the compiler + * implementation, such as vector arithmetic. + */ +class BuiltInOperationInstruction extends Instruction { + Language::BuiltInOperation operation; + + BuiltInOperationInstruction() { + getOpcode() instanceof BuiltInOperationOpcode and + operation = Raw::getInstructionBuiltInOperation(this) + } + + /** + * Gets the language-specific `BuiltInOperation` object that specifies the operation that is + * performed by this instruction. + */ + final Language::BuiltInOperation getBuiltInOperation() { result = operation } +} + +/** + * An instruction representing a built-in operation that does not have a specific opcode. The + * actual operation is specified by the `getBuiltInOperation()` predicate. + */ +class BuiltInInstruction extends BuiltInOperationInstruction { + BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } + + final override string getImmediateString() { result = getBuiltInOperation().toString() } +} + +/** + * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter. + * + * The operand specifies the address of the `IREllipsisVariable` used to represent the `...` + * parameter. The result is a `va_list` that initially refers to the first argument that was passed + * to the `...` parameter. + */ +class VarArgsStartInstruction extends UnaryInstruction { + VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } +} + +/** + * An instruction that cleans up a `va_list` after it is no longer in use. + * + * The operand specifies the address of the `va_list` to clean up. This instruction does not return + * a result. + */ +class VarArgsEndInstruction extends UnaryInstruction { + VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } +} + +/** + * An instruction that returns the address of the argument currently pointed to by a `va_list`. + * + * The operand is the `va_list` that points to the argument. The result is the address of the + * argument. + */ +class VarArgInstruction extends UnaryInstruction { + VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } +} + +/** + * An instruction that modifies a `va_list` to point to the next argument that was passed to the + * `...` parameter. + * + * The operand is the current `va_list`. The result is an updated `va_list` that points to the next + * argument of the `...` parameter. + */ +class NextVarArgInstruction extends UnaryInstruction { + NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } +} + +/** + * An instruction that allocates a new object on the managed heap. + * + * This instruction is used to represent the allocation of a new object in C# using the `new` + * expression. This instruction does not invoke a constructor for the object. Instead, there will be + * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the + * result of the `NewObj` as the `this` argument. + * + * The result is the address of the newly allocated object. + */ +class NewObjInstruction extends Instruction { + NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll new file mode 100644 index 000000000000..a12e35d471b8 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -0,0 +1,513 @@ +/** + * Provides classes that represent the input values of IR instructions. + */ + +private import internal.IRInternal +private import Instruction +private import IRBlock +private import internal.OperandImports as Imports +private import Imports::MemoryAccessKind +private import Imports::IRType +private import Imports::Overlap +private import Imports::OperandTag + +cached +private newtype TOperand = + TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) { + defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and + not Construction::isInCycle(useInstr) and + strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1 + } or + TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) { + useInstr.getOpcode().hasOperand(tag) + } or + TPhiOperand( + PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap + ) { + defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) + } + +/** + * Base class for all register operands. This is a placeholder for the IPA union type that we will + * eventually use for this purpose. + */ +private class RegisterOperandBase extends TRegisterOperand { + /** Gets a textual representation of this element. */ + abstract string toString(); +} + +/** + * Returns the register operand with the specified parameters. + */ +private RegisterOperandBase registerOperand( + Instruction useInstr, RegisterOperandTag tag, Instruction defInstr +) { + result = TRegisterOperand(useInstr, tag, defInstr) +} + +/** + * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we + * will eventually use for this purpose. + */ +private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand { + /** Gets a textual representation of this element. */ + abstract string toString(); +} + +/** + * Returns the non-Phi memory operand with the specified parameters. + */ +private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) { + result = TNonPhiMemoryOperand(useInstr, tag) +} + +/** + * Base class for all Phi operands. This is a placeholder for the IPA union type that we will + * eventually use for this purpose. + */ +private class PhiOperandBase extends TPhiOperand { + abstract string toString(); +} + +/** + * Returns the Phi operand with the specified parameters. + */ +private PhiOperandBase phiOperand( + Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap +) { + result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) +} + +/** + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) + */ +class Operand extends TOperand { + /** Gets a textual representation of this element. */ + string toString() { result = "Operand" } + + /** + * Gets the location of the source code for this operand. + */ + final Language::Location getLocation() { result = getUse().getLocation() } + + /** + * Gets the function that contains this operand. + */ + final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } + + /** + * Gets the `Instruction` that consumes this operand. + */ + Instruction getUse() { none() } + + /** + * Gets the `Instruction` whose result is the value of the operand. Unlike + * `getDef`, this also has a result when `isDefinitionInexact` holds, which + * means that the resulting instruction may only _partially_ or _potentially_ + * be the value of this operand. + */ + Instruction getAnyDef() { none() } + + /** + * Gets the `Instruction` whose result is the value of the operand. Unlike + * `getAnyDef`, this also has no result when `isDefinitionInexact` holds, + * which means that the resulting instruction must always be exactly the be + * the value of this operand. + */ + final Instruction getDef() { + result = this.getAnyDef() and + getDefinitionOverlap() instanceof MustExactlyOverlap + } + + /** + * DEPRECATED: renamed to `getUse`. + * + * Gets the `Instruction` that consumes this operand. + */ + deprecated final Instruction getUseInstruction() { result = getUse() } + + /** + * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this + * predicate is `getAnyDef`, but most uses of this predicate should probably + * be replaced with `getDef`. + * + * Gets the `Instruction` whose result is the value of the operand. + */ + deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } + + /** + * Gets the overlap relationship between the operand's definition and its use. + */ + Overlap getDefinitionOverlap() { none() } + + /** + * Holds if the result of the definition instruction does not exactly overlap this use. + */ + final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } + + /** + * Gets a prefix to use when dumping the operand in an operand list. + */ + string getDumpLabel() { result = "" } + + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + + /** + * Gets a string describing this operand, suitable for display in IR dumps. This consists of the + * result ID of the instruction consumed by the operand, plus a label identifying the operand + * kind. + * + * For example: `this:r3_5` + */ + final string getDumpString() { + result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() + } + + /** + * Gets a string containing the identifier of the definition of this use, or `m?` if the + * definition is not modeled in SSA. + */ + private string getDefinitionId() { + result = getAnyDef().getResultId() + or + not exists(getAnyDef()) and result = "m?" + } + + /** + * Gets a string prefix to prepend to the operand's definition ID in an IR dump, specifying whether the operand is + * an exact or inexact use of its definition. For an inexact use, the prefix is "~". For an exact use, the prefix is + * the empty string. + */ + private string getInexactSpecifier() { + if isDefinitionInexact() then result = "~" else result = "" + } + + /** + * Get the order in which the operand should be sorted in the operand list. + */ + int getDumpSortOrder() { result = -1 } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + + /** + * Gets the language-neutral type of the value consumed by this operand. This is usually the same + * as the result type of the definition instruction consumed by this operand. For register + * operands, this is always the case. For some memory operands, the operand type may be different + * from the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final Language::Type getType() { getLanguageType().hasType(result, _) } + + /** + * Holds if the value consumed by this operand is a glvalue. If this + * holds, the value of the operand represents the address of a location, + * and the type of the location is given by `getType()`. If this does + * not hold, the value of the operand represents a value whose type is + * given by `getType()`. + */ + final predicate isGLValue() { getLanguageType().hasType(_, true) } + + /** + * Gets the size of the value consumed by this operand, in bytes. If the operand does not have + * a known constant size, this predicate does not hold. + */ + final int getSize() { result = getLanguageType().getByteSize() } +} + +/** + * An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction). + */ +class MemoryOperand extends Operand { + MemoryOperand() { + this instanceof NonPhiMemoryOperandBase or + this instanceof PhiOperandBase + } + + /** + * Gets the kind of memory access performed by the operand. + */ + MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } + + /** + * Holds if the memory access performed by this operand will not always read from every bit in the + * memory location. This is most commonly used for memory accesses that may or may not actually + * occur depending on runtime state (for example, the write side effect of an output parameter + * that is not written to on all paths), or for accesses where the memory location is a + * conservative estimate of the memory that might actually be accessed at runtime (for example, + * the global side effects of a function call). + */ + predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } + + /** + * Returns the operand that holds the memory address from which the current operand loads its + * value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2` + * is `r1`. + */ + final AddressOperand getAddressOperand() { + getMemoryAccess().usesAddressOperand() and + result.getUse() = getUse() + } +} + +/** + * An operand that is not an operand of a `PhiInstruction`. + */ +class NonPhiOperand extends Operand { + Instruction useInstr; + OperandTag tag; + + NonPhiOperand() { + this = registerOperand(useInstr, tag, _) or + this = nonPhiMemoryOperand(useInstr, tag) + } + + final override Instruction getUse() { result = useInstr } + + final override string getDumpLabel() { result = tag.getLabel() } + + final override string getDumpId() { result = tag.getId() } + + final override int getDumpSortOrder() { result = tag.getSortOrder() } + + /** + * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`. + */ + final OperandTag getOperandTag() { result = tag } +} + +/** + * An operand that consumes a register (non-memory) result. + */ +class RegisterOperand extends NonPhiOperand, RegisterOperandBase { + override RegisterOperandTag tag; + Instruction defInstr; + + RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) } + + final override string toString() { result = tag.toString() } + + final override Instruction getAnyDef() { result = defInstr } + + final override Overlap getDefinitionOverlap() { + // All register results overlap exactly with their uses. + result instanceof MustExactlyOverlap + } +} + +/** + * A memory operand other than the operand of a `Phi` instruction. + */ +class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { + override MemoryOperandTag tag; + + NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) } + + final override string toString() { result = tag.toString() } + + final override Instruction getAnyDef() { + result = unique(Instruction defInstr | hasDefinition(defInstr, _)) + } + + final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } + + pragma[noinline] + private predicate hasDefinition(Instruction defInstr, Overlap overlap) { + defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and + not Construction::isInCycle(useInstr) and + strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1 + } + + /** + * Holds if the operand totally overlaps with its definition and consumes the + * bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition. + */ + predicate getUsedInterval(int startBitOffset, int endBitOffset) { + Construction::getUsedInterval(this, startBitOffset, endBitOffset) + } +} + +/** + * A memory operand whose type may be different from the type of the result of its definition. + */ +class TypedOperand extends NonPhiMemoryOperand { + override TypedOperandTag tag; + + final override Language::LanguageType getLanguageType() { + result = Construction::getInstructionOperandType(useInstr, tag) + } +} + +/** + * The address operand of an instruction that loads or stores a value from + * memory (e.g. `Load`, `Store`). + */ +class AddressOperand extends RegisterOperand { + override AddressOperandTag tag; +} + +/** + * The buffer size operand of an instruction that represents a read or write of + * a buffer. + */ +class BufferSizeOperand extends RegisterOperand { + override BufferSizeOperandTag tag; +} + +/** + * The source value operand of an instruction that loads a value from memory (e.g. `Load`, + * `ReturnValue`, `ThrowValue`). + */ +class LoadOperand extends TypedOperand { + override LoadOperandTag tag; +} + +/** + * The source value operand of a `Store` instruction. + */ +class StoreValueOperand extends RegisterOperand { + override StoreValueOperandTag tag; +} + +/** + * The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`). + */ +class UnaryOperand extends RegisterOperand { + override UnaryOperandTag tag; +} + +/** + * The left operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class LeftOperand extends RegisterOperand { + override LeftOperandTag tag; +} + +/** + * The right operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class RightOperand extends RegisterOperand { + override RightOperandTag tag; +} + +/** + * The condition operand of a `ConditionalBranch` or `Switch` instruction. + */ +class ConditionOperand extends RegisterOperand { + override ConditionOperandTag tag; +} + +/** + * The operand representing the target function of an `Call` instruction. + */ +class CallTargetOperand extends RegisterOperand { + override CallTargetOperandTag tag; +} + +/** + * An operand representing an argument to a function call. This includes both + * positional arguments (represented by `PositionalArgumentOperand`) and the + * implicit `this` argument, if any (represented by `ThisArgumentOperand`). + */ +class ArgumentOperand extends RegisterOperand { + override ArgumentOperandTag tag; +} + +/** + * An operand representing the implicit 'this' argument to a member function + * call. + */ +class ThisArgumentOperand extends ArgumentOperand { + override ThisArgumentOperandTag tag; +} + +/** + * An operand representing an argument to a function call. + */ +class PositionalArgumentOperand extends ArgumentOperand { + override PositionalArgumentOperandTag tag; + + /** + * Gets the zero-based index of the argument. + */ + final int getIndex() { result = tag.getArgIndex() } +} + +/** + * An operand representing memory read as a side effect of evaluating another instruction. + */ +class SideEffectOperand extends TypedOperand { + override SideEffectOperandTag tag; +} + +/** + * An operand of a `PhiInstruction`. + */ +class PhiInputOperand extends MemoryOperand, PhiOperandBase { + PhiInstruction useInstr; + Instruction defInstr; + IRBlock predecessorBlock; + Overlap overlap; + + PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + + override string toString() { result = "Phi" } + + final override PhiInstruction getUse() { result = useInstr } + + final override Instruction getAnyDef() { result = defInstr } + + final override Overlap getDefinitionOverlap() { result = overlap } + + final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } + + final override string getDumpLabel() { + result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" + } + + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + + /** + * Gets the predecessor block from which this value comes. + */ + final IRBlock getPredecessorBlock() { result = predecessorBlock } + + final override MemoryAccessKind getMemoryAccess() { result instanceof PhiMemoryAccess } +} + +/** + * The total operand of a Chi node, representing the previous value of the memory. + */ +class ChiTotalOperand extends NonPhiMemoryOperand { + override ChiTotalOperandTag tag; + + final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess } +} + +/** + * The partial operand of a Chi node, representing the value being written to part of the memory. + */ +class ChiPartialOperand extends NonPhiMemoryOperand { + override ChiPartialOperandTag tag; + + final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.ql b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.ql similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.ql rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.ql diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll new file mode 100644 index 000000000000..59dadee71545 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll @@ -0,0 +1,329 @@ +/** + * Outputs a representation of the IR as a control flow graph. + * + * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small + * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most + * uses, however, it is better to write a query that imports `PrintIR.qll`, extends + * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to + * dump. + */ + +private import internal.IRInternal +private import IR +private import internal.PrintIRImports as Imports +import Imports::IRConfiguration + +private newtype TPrintIRConfiguration = MkPrintIRConfiguration() + +/** + * The query can extend this class to control which functions are printed. + */ +class PrintIRConfiguration extends TPrintIRConfiguration { + /** Gets a textual representation of this configuration. */ + string toString() { result = "PrintIRConfiguration" } + + /** + * Holds if the IR for `func` should be printed. By default, holds for all + * functions. + */ + predicate shouldPrintFunction(Language::Function func) { any() } +} + +/** + * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. + */ +private class FilteredIRConfiguration extends IRConfiguration { + override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { + shouldPrintFunction(func) + } +} + +private predicate shouldPrintFunction(Language::Function func) { + exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) +} + +private string getAdditionalInstructionProperty(Instruction instr, string key) { + exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key)) +} + +private string getAdditionalBlockProperty(IRBlock block, string key) { + exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) +} + +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + +private newtype TPrintableIRNode = + TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or + TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or + TPrintableInstruction(Instruction instr) { shouldPrintFunction(instr.getEnclosingFunction()) } + +/** + * A node to be emitted in the IR graph. + */ +abstract private class PrintableIRNode extends TPrintableIRNode { + abstract string toString(); + + /** + * Gets the location to be emitted for the node. + */ + abstract Language::Location getLocation(); + + /** + * Gets the label to be emitted for the node. + */ + abstract string getLabel(); + + /** + * Gets the order in which the node appears in its parent node. + */ + abstract int getOrder(); + + /** + * Gets the parent of this node. + */ + abstract PrintableIRNode getParent(); + + /** + * Gets the kind of graph represented by this node ("graph" or "tree"). + */ + string getGraphKind() { none() } + + /** + * Holds if this node should always be rendered as text, even in a graphical + * viewer. + */ + predicate forceText() { none() } + + /** + * Gets the value of the node property with the specified key. + */ + string getProperty(string key) { + key = "semmle.label" and result = getLabel() + or + key = "semmle.order" and result = getOrder().toString() + or + key = "semmle.graphKind" and result = getGraphKind() + or + key = "semmle.forceText" and forceText() and result = "true" + } +} + +/** + * An IR graph node representing a `IRFunction` object. + */ +private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { + IRFunction irFunc; + + PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } + + override string toString() { result = irFunc.toString() } + + override Language::Location getLocation() { result = irFunc.getLocation() } + + override string getLabel() { result = Language::getIdentityString(irFunc.getFunction()) } + + override int getOrder() { + this = + rank[result + 1](PrintableIRFunction orderedFunc, Language::Location location | + location = orderedFunc.getIRFunction().getLocation() + | + orderedFunc + order by + location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), + orderedFunc.getLabel() + ) + } + + final override PrintableIRNode getParent() { none() } + + final IRFunction getIRFunction() { result = irFunc } +} + +/** + * An IR graph node representing an `IRBlock` object. + */ +private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { + IRBlock block; + + PrintableIRBlock() { this = TPrintableIRBlock(block) } + + override string toString() { result = getLabel() } + + override Language::Location getLocation() { result = block.getLocation() } + + override string getLabel() { result = "Block " + block.getDisplayIndex().toString() } + + override int getOrder() { result = block.getDisplayIndex() } + + final override string getGraphKind() { result = "tree" } + + final override predicate forceText() { any() } + + final override PrintableIRFunction getParent() { + result.getIRFunction() = block.getEnclosingIRFunction() + } + + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalBlockProperty(block, key) + } + + final IRBlock getBlock() { result = block } +} + +/** + * An IR graph node representing an `Instruction`. + */ +private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { + Instruction instr; + + PrintableInstruction() { this = TPrintableInstruction(instr) } + + override string toString() { result = instr.toString() } + + override Language::Location getLocation() { result = instr.getLocation() } + + override string getLabel() { + exists(IRBlock block | + instr = block.getAnInstruction() and + exists( + string resultString, string operationString, string operandsString, int resultWidth, + int operationWidth + | + resultString = instr.getResultString() and + operationString = instr.getOperationString() and + operandsString = getOperandsString() and + columnWidths(block, resultWidth, operationWidth) and + result = + resultString + getPaddingString(resultWidth - resultString.length()) + " = " + + operationString + getPaddingString(operationWidth - operationString.length()) + " : " + + operandsString + ) + ) + } + + override int getOrder() { result = instr.getDisplayIndexInBlock() } + + final override PrintableIRBlock getParent() { result.getBlock() = instr.getBlock() } + + final Instruction getInstruction() { result = instr } + + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalInstructionProperty(instr, key) + } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() + ) + } +} + +private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { + resultWidth = max(Instruction instr | instr.getBlock() = block | instr.getResultString().length()) and + operationWidth = + max(Instruction instr | instr.getBlock() = block | instr.getOperationString().length()) +} + +private int maxColumnWidth() { + result = + max(Instruction instr, int width | + width = instr.getResultString().length() or + width = instr.getOperationString().length() or + width = instr.getOperandsString().length() + | + width + ) +} + +private string getPaddingString(int n) { + n = 0 and result = "" + or + n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " +} + +/** + * Holds if `node` belongs to the output graph, and its property `key` has the given `value`. + */ +query predicate nodes(PrintableIRNode node, string key, string value) { + value = node.getProperty(key) +} + +private int getSuccessorIndex(IRBlock pred, IRBlock succ) { + succ = + rank[result + 1](IRBlock aSucc, EdgeKind kind | + aSucc = pred.getSuccessor(kind) + | + aSucc order by kind.toString() + ) +} + +/** + * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key` + * has the given `value`. + */ +query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { + exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | + predBlock = pred.getBlock() and + succBlock = succ.getBlock() and + predBlock.getSuccessor(kind) = succBlock and + ( + ( + key = "semmle.label" and + if predBlock.getBackEdgeSuccessor(kind) = succBlock + then value = kind.toString() + " (back edge)" + else value = kind.toString() + ) + or + key = "semmle.order" and + value = getSuccessorIndex(predBlock, succBlock).toString() + ) + ) +} + +/** + * Holds if `parent` is the parent node of `child` in the output graph. + */ +query predicate parents(PrintableIRNode child, PrintableIRNode parent) { + parent = child.getParent() +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll new file mode 100644 index 000000000000..c50e8385c99a --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll @@ -0,0 +1,54 @@ +private import internal.ConstantAnalysisInternal +private import experimental.ir.internal.IntegerPartial +private import IR + +language[monotonicAggregates] +int getConstantValue(Instruction instr) { + result = instr.(IntegerConstantInstruction).getValue().toInt() + or + result = getBinaryInstructionValue(instr) + or + result = neg(getConstantValue(instr.(NegateInstruction).getUnary())) + or + result = getConstantValue(instr.(CopyInstruction).getSourceValue()) + or + exists(PhiInstruction phi | + phi = instr and + result = max(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) and + result = min(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) + ) +} + +pragma[noinline] +private predicate binaryInstructionOperands(BinaryInstruction instr, int left, int right) { + left = getConstantValue(instr.getLeft()) and + right = getConstantValue(instr.getRight()) +} + +pragma[noinline] +private int getBinaryInstructionValue(BinaryInstruction instr) { + exists(int left, int right | + binaryInstructionOperands(instr, left, right) and + ( + instr instanceof AddInstruction and result = add(left, right) + or + instr instanceof SubInstruction and result = sub(left, right) + or + instr instanceof MulInstruction and result = mul(left, right) + or + instr instanceof DivInstruction and result = div(left, right) + or + instr instanceof CompareEQInstruction and result = compareEQ(left, right) + or + instr instanceof CompareNEInstruction and result = compareNE(left, right) + or + instr instanceof CompareLTInstruction and result = compareLT(left, right) + or + instr instanceof CompareGTInstruction and result = compareGT(left, right) + or + instr instanceof CompareLEInstruction and result = compareLE(left, right) + or + instr instanceof CompareGEInstruction and result = compareGE(left, right) + ) + ) +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll new file mode 100644 index 000000000000..53f9295be4f9 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll @@ -0,0 +1,11 @@ +private import internal.ConstantAnalysisInternal +private import experimental.ir.internal.IntegerConstant +private import ConstantAnalysis +import IR + +private class ConstantAnalysisPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instr, string key) { + key = "ConstantValue" and + result = getValue(getConstantValue(instr)).toString() + } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll new file mode 100644 index 000000000000..c70b240fe420 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll @@ -0,0 +1 @@ +import experimental.ir.implementation.unaliased_ssa.IR as IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll new file mode 100644 index 000000000000..34bd754692d6 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll @@ -0,0 +1,3 @@ +import experimental.ir.internal.Overlap +import experimental.ir.internal.IRCSharpLanguage as Language +import experimental.ir.implementation.unaliased_ssa.IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll similarity index 97% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 1612e0065b77..19fb0490f808 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -196,16 +196,17 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) { bitOffset = Ints::unknown() } -private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) { +private predicate isArgumentForParameter( + CallInstruction ci, Operand operand, InitializeParameterInstruction init +) { exists(Language::Function f | ci = operand.getUse() and f = ci.getStaticCallTarget() and ( - init.(InitializeParameterInstruction).getParameter() = - f.getParameter(operand.(PositionalArgumentOperand).getIndex()) + init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex()) or - init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and - init.getEnclosingFunction() = f and + init.getIRVariable() instanceof IRThisVariable and + unique( | | init.getEnclosingFunction()) = f and operand instanceof ThisArgumentOperand ) and not Language::isFunctionVirtual(f) and diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll similarity index 91% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll index 11d7d37063ee..7992aa9ed146 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll @@ -1,6 +1,6 @@ private import csharp -import semmle.code.csharp.ir.implementation.IRConfiguration -import semmle.code.csharp.ir.internal.IntegerConstant as Ints +import experimental.ir.implementation.IRConfiguration +import experimental.ir.internal.IntegerConstant as Ints module AliasModels { /** @@ -12,7 +12,7 @@ module AliasModels { * the function returns. * * Example: - * ``` + * ```csharp * int* g; * int* func(int* p, int* q, int* r, int* s, int n) { * *s = 1; // `s` does not escape. diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll new file mode 100644 index 000000000000..f3f2d14ab437 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll @@ -0,0 +1,3 @@ +import experimental.ir.internal.IRCSharpLanguage as Language +import experimental.ir.implementation.raw.IR as InputIR +import AliasConfiguration as Configuration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll new file mode 100644 index 000000000000..fc29c0d77dd0 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll @@ -0,0 +1 @@ +import experimental.ir.implementation.raw.IR diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll new file mode 100644 index 000000000000..c80761a68cf8 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll @@ -0,0 +1 @@ +import experimental.ir.implementation.EdgeKind as EdgeKind diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll new file mode 100644 index 000000000000..4e9a7d9f3aec --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll @@ -0,0 +1 @@ +import experimental.ir.implementation.internal.IRFunctionBase as IRFunctionBase diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll new file mode 100644 index 000000000000..14dad7400b22 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll @@ -0,0 +1,3 @@ +import experimental.ir.implementation.EdgeKind as EdgeKind +import experimental.ir.implementation.IRType as IRType +import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRInternal.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRInternal.qll new file mode 100644 index 000000000000..eaf33e0800fb --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRInternal.qll @@ -0,0 +1,4 @@ +import experimental.ir.internal.IRCSharpLanguage as Language +import SSAConstruction as Construction +import experimental.ir.implementation.IRConfiguration as IRConfiguration +import experimental.ir.implementation.raw.internal.IRConstruction::Raw as Raw diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll new file mode 100644 index 000000000000..bdb4377cbdcc --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll @@ -0,0 +1,5 @@ +import experimental.ir.implementation.IRType as IRType +import experimental.ir.implementation.TempVariableTag as TempVariableTag +import experimental.ir.internal.IRUtilities as IRUtilities +import experimental.ir.internal.TempVariableTag as TTempVariableTag +import experimental.ir.implementation.internal.TIRVariable as TIRVariable diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll new file mode 100644 index 000000000000..4bcd2e127c12 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll @@ -0,0 +1,6 @@ +import experimental.ir.implementation.EdgeKind as EdgeKind +import experimental.ir.implementation.IRType as IRType +import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind +import experimental.ir.implementation.Opcode as Opcode +import experimental.ir.implementation.internal.OperandTag as OperandTag +import experimental.ir.internal.Overlap as Overlap diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll new file mode 100644 index 000000000000..40af4631927a --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll @@ -0,0 +1,4 @@ +import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind +import experimental.ir.implementation.IRType as IRType +import experimental.ir.internal.Overlap as Overlap +import experimental.ir.implementation.internal.OperandTag as OperandTag diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll new file mode 100644 index 000000000000..9a3e4c03646c --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll @@ -0,0 +1 @@ +import experimental.ir.IRConfiguration as IRConfiguration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll similarity index 84% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 30414bb5db3a..a6cb78b2b3a7 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -1,5 +1,11 @@ import SSAConstructionInternal -private import SSAConstructionImports +private import SSAConstructionImports as Imports +private import Imports::Opcode +private import Imports::OperandTag +private import Imports::Overlap +private import Imports::TInstruction +private import Imports::RawIR as RawIR +private import SSAInstructions private import NewIR private class OldBlock = Reachability::ReachableBlock; @@ -10,52 +16,45 @@ import Cached cached private module Cached { - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached + predicate hasPhiInstructionCached( + OldInstruction blockStartInstr, Alias::MemoryLocation defLocation + ) { + exists(OldBlock oldBlock | + definitionHasPhiNode(defLocation, oldBlock) and + blockStartInstr = oldBlock.getFirstInstruction() + ) } cached - predicate functionHasIR(Language::Function func) { - exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func) + predicate hasChiInstructionCached(OldInstruction primaryInstruction) { + hasChiNode(_, primaryInstruction) } cached - OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) } - - private IRVariable getNewIRVariable(OldIR::IRVariable var) { - // This is just a type cast. Both classes derive from the same newtype. - result = var + predicate hasUnreachedInstructionCached(IRFunction irFunc) { + exists(OldInstruction oldInstruction | + irFunc = oldInstruction.getEnclosingIRFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) + ) } - cached - newtype TInstruction = - WrappedInstruction(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction - } or - Phi(OldBlock block, Alias::MemoryLocation defLocation) { - definitionHasPhiNode(defLocation, block) - } or - Chi(OldInstruction oldInstruction) { - not oldInstruction instanceof OldIR::PhiInstruction and - hasChiNode(_, oldInstruction) - } or - Unreached(Language::Function function) { - exists(OldInstruction oldInstruction | - function = oldInstruction.getEnclosingFunction() and - Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) - ) - } + class TStageInstruction = + TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; cached - predicate hasTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type - ) { - exists(OldIR::IRTempVariable var | - var.getEnclosingFunction() = func and - var.getAST() = ast and - var.getTag() = tag and - var.getLanguageType() = type - ) + predicate hasInstruction(TStageInstruction instr) { + instr instanceof TRawInstruction and instr instanceof OldInstruction + or + instr instanceof TPhiInstruction + or + instr instanceof TChiInstruction + or + instr instanceof TUnreachedInstruction + } + + private IRBlock getNewBlock(OldBlock oldBlock) { + result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } cached @@ -73,7 +72,7 @@ private module Cached { or // Chi instructions track virtual variables, and therefore a chi instruction is // conflated if it's associated with the aliased virtual variable. - exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) | + exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) | Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof Alias::AliasedVirtualVariable ) @@ -81,7 +80,7 @@ private module Cached { // Phi instructions track locations, and therefore a phi instruction is // conflated if it's associated with a conflated location. exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and + instruction = getPhi(_, location) and not exists(location.getAllocation()) ) } @@ -128,7 +127,7 @@ private module Cached { hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result) ) or - instruction = Chi(getOldInstruction(result)) and + instruction = getChi(getOldInstruction(result)) and tag instanceof ChiPartialOperandTag and overlap instanceof MustExactlyOverlap or @@ -150,6 +149,34 @@ private module Cached { ) } + /** + * Holds if the partial operand of this `ChiInstruction` updates the bit range + * `[startBitOffset, endBitOffset)` of the total operand. + */ + cached + predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBitOffset, int endBitOffset) { + exists(Alias::MemoryLocation location, OldInstruction oldInstruction | + oldInstruction = getOldInstruction(chi.getPartial()) and + location = Alias::getResultMemoryLocation(oldInstruction) and + startBitOffset = Alias::getStartBitOffset(location) and + endBitOffset = Alias::getEndBitOffset(location) + ) + } + + /** + * Holds if `operand` totally overlaps with its definition and consumes the bit range + * `[startBitOffset, endBitOffset)`. + */ + cached + predicate getUsedInterval(NonPhiMemoryOperand operand, int startBitOffset, int endBitOffset) { + exists(Alias::MemoryLocation location, OldIR::NonPhiMemoryOperand oldOperand | + oldOperand = operand.getUse().(OldInstruction).getAnOperand() and + location = Alias::getOperandMemoryLocation(oldOperand) and + startBitOffset = Alias::getStartBitOffset(location) and + endBitOffset = Alias::getEndBitOffset(location) + ) + } + /** * Holds if `instr` is part of a cycle in the operand graph that doesn't go * through a phi instruction and therefore should be impossible. @@ -172,13 +199,15 @@ private module Cached { pragma[noopt] cached - Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) { + Instruction getPhiOperandDefinition( + PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation | hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and - instr = Phi(phiBlock, useLocation) and + instr = getPhi(phiBlock, useLocation) and newPredecessorBlock = getNewBlock(predBlock) and result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) @@ -191,7 +220,7 @@ private module Cached { Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank | - chiInstr = Chi(oldInstr) and + chiInstr = getChi(oldInstr) and vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and hasUseAtRank(vvar, useBlock, useRank, oldInstr) and @@ -203,21 +232,11 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = Phi(oldBlock, _) and + instr = getPhi(oldBlock, _) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } - cached - Language::Expr getInstructionConvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getConvertedResultExpression() - } - - cached - Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) { - result = getOldInstruction(instruction).getUnconvertedResultExpression() - } - /* * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, * that node is its successor in the new successor relation, and the Chi node's successors are @@ -228,20 +247,20 @@ private module Cached { Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { if hasChiNode(_, getOldInstruction(instruction)) then - result = Chi(getOldInstruction(instruction)) and + result = getChi(getOldInstruction(instruction)) and kind instanceof GotoEdge else ( exists(OldInstruction oldInstruction | oldInstruction = getOldInstruction(instruction) and ( if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) - then result = Unreached(instruction.getEnclosingFunction()) + then result = unreachedInstruction(instruction.getEnclosingIRFunction()) else result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) or exists(OldInstruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction.getSuccessor(kind)) ) ) @@ -260,137 +279,73 @@ private module Cached { // `oldInstruction`, in which case the back edge should come out of the // chi node instead. if hasChiNode(_, oldInstruction) - then instruction = Chi(oldInstruction) + then instruction = getChi(oldInstruction) else instruction = getNewInstruction(oldInstruction) ) } cached - Language::AST getInstructionAST(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result = oldInstruction.getAST() + Language::AST getInstructionAST(Instruction instr) { + result = getOldInstruction(instr).getAST() + or + exists(RawIR::Instruction blockStartInstr | + instr = phiInstruction(blockStartInstr, _) and + result = blockStartInstr.getAST() ) or - exists(OldBlock block | - instruction = Phi(block, _) and - result = block.getFirstInstruction().getAST() + exists(RawIR::Instruction primaryInstr | + instr = chiInstruction(primaryInstr) and + result = primaryInstr.getAST() ) or - instruction = Unreached(result) + exists(IRFunctionBase irFunc | + instr = unreachedInstruction(irFunc) and result = irFunc.getFunction() + ) } cached - Language::LanguageType getInstructionResultType(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getResultLanguageType() - ) + Language::LanguageType getInstructionResultType(Instruction instr) { + result = instr.(RawIR::Instruction).getResultLanguageType() or - exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | - instruction = Chi(oldInstruction) and - hasChiNode(vvar, oldInstruction) and - result = vvar.getType() + exists(Alias::MemoryLocation defLocation | + instr = phiInstruction(_, defLocation) and + result = defLocation.getType() ) or - exists(Alias::MemoryLocation location | - instruction = Phi(_, location) and - result = location.getType() + exists(Instruction primaryInstr, Alias::VirtualVariable vvar | + instr = chiInstruction(primaryInstr) and + hasChiNode(vvar, primaryInstr) and + result = vvar.getType() ) or - instruction = Unreached(_) and - result = Language::getVoidType() + instr = unreachedInstruction(_) and result = Language::getVoidType() } cached - Opcode getInstructionOpcode(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) and - result = oldInstruction.getOpcode() - ) + Opcode getInstructionOpcode(Instruction instr) { + result = getOldInstruction(instr).getOpcode() or - instruction instanceof Chi and - result instanceof Opcode::Chi + instr = phiInstruction(_, _) and result instanceof Opcode::Phi or - instruction instanceof Phi and - result instanceof Opcode::Phi + instr = chiInstruction(_) and result instanceof Opcode::Chi or - instruction instanceof Unreached and - result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and result instanceof Opcode::Unreached } cached - IRFunction getInstructionEnclosingIRFunction(Instruction instruction) { - exists(OldInstruction oldInstruction | - instruction = WrappedInstruction(oldInstruction) - or - instruction = Chi(oldInstruction) - | - result.getFunction() = oldInstruction.getEnclosingFunction() - ) + IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) { + result = getOldInstruction(instr).getEnclosingIRFunction() or - exists(OldBlock block | - instruction = Phi(block, _) and - result.getFunction() = block.getEnclosingFunction() + exists(OldInstruction blockStartInstr | + instr = phiInstruction(blockStartInstr, _) and + result = blockStartInstr.getEnclosingIRFunction() ) or - instruction = Unreached(result.getFunction()) - } - - cached - IRVariable getInstructionVariable(Instruction instruction) { - result = - getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable()) - } - - cached - Language::Field getInstructionField(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() - } - - cached - int getInstructionIndex(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex() - } - - cached - Language::Function getInstructionFunction(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() - } - - cached - string getInstructionConstantValue(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue() - } - - cached - Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { - result = - getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation() - } - - cached - Language::LanguageType getInstructionExceptionType(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() - } - - cached - int getInstructionElementSize(Instruction instruction) { - result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() - } - - cached - predicate getInstructionInheritance( - Instruction instruction, Language::Class baseClass, Language::Class derivedClass - ) { - exists(OldIR::InheritanceConversionInstruction oldInstr | - oldInstr = getOldInstruction(instruction) and - baseClass = oldInstr.getBaseClass() and - derivedClass = oldInstr.getDerivedClass() + exists(OldInstruction primaryInstr | + instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction() ) + or + instr = unreachedInstruction(result) } cached @@ -401,7 +356,7 @@ private module Cached { ) or exists(OldIR::Instruction oldInstruction | - instruction = Chi(oldInstruction) and + instruction = getChi(oldInstruction) and result = getNewInstruction(oldInstruction) ) } @@ -409,6 +364,14 @@ private module Cached { private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } +private OldInstruction getOldInstruction(Instruction instr) { instr = result } + +private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) } + +private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) { + result = phiInstruction(defBlock.getFirstInstruction(), defLocation) +} + /** * Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition * of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the @@ -588,7 +551,7 @@ module DefUse { | // An odd offset corresponds to the `Chi` instruction. defOffset = oldOffset * 2 + 1 and - result = Chi(oldInstr) and + result = getChi(oldInstr) and ( defLocation = Alias::getResultMemoryLocation(oldInstr) or defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() @@ -607,7 +570,7 @@ module DefUse { or defOffset = -1 and hasDefinition(_, defLocation, defBlock, defOffset) and - result = Phi(defBlock, defLocation) and + result = getPhi(defBlock, defLocation) and actualDefLocation = defLocation } @@ -891,7 +854,7 @@ private module CachedForDebugging { ) or exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity | - instr = Phi(phiBlock, location) and + instr = getPhi(phiBlock, location) and result = "Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and if location instanceof Alias::VirtualVariable @@ -901,7 +864,7 @@ private module CachedForDebugging { else specificity = "s" ) or - instr = Unreached(_) and + instr = unreachedInstruction(_) and result = "Unreached" } @@ -961,3 +924,19 @@ module SSAConsistency { ) } } + +/** + * Provides the portion of the parameterized IR interface that is used to construct the SSA stages + * of the IR. The raw stage of the IR does not expose these predicates. + * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures + * that all of SSA construction will be evaluated in the same stage. + */ +module SSA { + class MemoryLocation = Alias::MemoryLocation; + + predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2; + + predicate hasChiInstruction = Cached::hasChiInstructionCached/1; + + predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll new file mode 100644 index 000000000000..bf9b18d0b17b --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll @@ -0,0 +1,5 @@ +import experimental.ir.implementation.Opcode as Opcode +import experimental.ir.implementation.internal.OperandTag as OperandTag +import experimental.ir.internal.Overlap as Overlap +import experimental.ir.implementation.internal.TInstruction as TInstruction +import experimental.ir.implementation.raw.IR as RawIR diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll new file mode 100644 index 000000000000..15eaf8045a78 --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll @@ -0,0 +1,8 @@ +import experimental.ir.implementation.raw.IR as OldIR +import experimental.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability +import experimental.ir.implementation.raw.internal.reachability.Dominance as Dominance +import experimental.ir.implementation.unaliased_ssa.IR as NewIR +import experimental.ir.implementation.raw.internal.IRConstruction as RawStage +import experimental.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions +import experimental.ir.internal.IRCSharpLanguage as Language +import SimpleSSA as Alias diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll similarity index 85% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index aee4959513ed..a7b9160bdc75 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -59,6 +59,12 @@ class MemoryLocation extends TMemoryLocation { final string getUniqueId() { result = var.getUniqueId() } } +/** + * Represents a set of `MemoryLocation`s that cannot overlap with + * `MemoryLocation`s outside of the set. The `VirtualVariable` will be + * represented by a `MemoryLocation` that totally overlaps all other + * `MemoryLocations` in the set. + */ class VirtualVariable extends MemoryLocation { } /** A virtual variable that groups all escaped memory within a function. */ @@ -79,3 +85,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { result = getMemoryLocation(getAddressOperandAllocation(operand.getAddressOperand())) } + +/** Gets the start bit offset of a `MemoryLocation`, if any. */ +int getStartBitOffset(MemoryLocation location) { none() } + +/** Gets the end bit offset of a `MemoryLocation`, if any. */ +int getEndBitOffset(MemoryLocation location) { none() } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll new file mode 100644 index 000000000000..80a1c7c36fdb --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll @@ -0,0 +1,4 @@ +import experimental.ir.implementation.raw.IR +import experimental.ir.internal.IntegerConstant as Ints +import experimental.ir.implementation.internal.OperandTag +import experimental.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSAPublicImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSAPublicImports.qll new file mode 100644 index 000000000000..047d4923039b --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSAPublicImports.qll @@ -0,0 +1 @@ +import experimental.ir.internal.Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/DominanceInternal.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/DominanceInternal.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/DominanceInternal.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/DominanceInternal.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll rename to csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll new file mode 100644 index 000000000000..e435289cbfcb --- /dev/null +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll @@ -0,0 +1,2 @@ +import experimental.ir.implementation.unaliased_ssa.IR as IR +import experimental.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/CSharpType.qll b/csharp/ql/src/experimental/ir/internal/CSharpType.qll similarity index 99% rename from csharp/ql/src/semmle/code/csharp/ir/internal/CSharpType.qll rename to csharp/ql/src/experimental/ir/internal/CSharpType.qll index 5f966dd93a13..02dee3edff9a 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/CSharpType.qll +++ b/csharp/ql/src/experimental/ir/internal/CSharpType.qll @@ -1,5 +1,5 @@ private import csharp -private import semmle.code.csharp.ir.implementation.IRType +private import experimental.ir.implementation.IRType private import IRCSharpLanguage as Language int getTypeSize(Type type) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll b/csharp/ql/src/experimental/ir/internal/IRCSharpLanguage.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll rename to csharp/ql/src/experimental/ir/internal/IRCSharpLanguage.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IRGuards.qll b/csharp/ql/src/experimental/ir/internal/IRGuards.qll similarity index 99% rename from csharp/ql/src/semmle/code/csharp/ir/internal/IRGuards.qll rename to csharp/ql/src/experimental/ir/internal/IRGuards.qll index 0cafb65d6f2f..a505e54c37e4 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/IRGuards.qll +++ b/csharp/ql/src/experimental/ir/internal/IRGuards.qll @@ -1,6 +1,6 @@ import csharp import semmle.code.csharp.controlflow.BasicBlocks -import semmle.code.csharp.ir.IR +import experimental.ir.IR /** * Holds if `block` consists of an `UnreachedInstruction`. @@ -93,7 +93,7 @@ class GuardCondition extends Expr { * implies that the truth of the child expression `part` has truth value `partIsTrue`. * * For example if the binary operation: - * ``` + * ```csharp * x && y * ``` * is true, `x` and `y` must also be true, so `impliesValue(x, true, true)` and @@ -341,7 +341,7 @@ class IRGuardCondition extends Instruction { * predecessors. For example, in the following situation, an inference can be made about the * value of `x` at the end of the `if` statement, but there is no block which is controlled by * the `if` statement when `x >= y`. - * ``` + * ```csharp * if (x < y) { * x = y; * } diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IRUtilities.qll b/csharp/ql/src/experimental/ir/internal/IRUtilities.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/internal/IRUtilities.qll rename to csharp/ql/src/experimental/ir/internal/IRUtilities.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll b/csharp/ql/src/experimental/ir/internal/IntegerConstant.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll rename to csharp/ql/src/experimental/ir/internal/IntegerConstant.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll b/csharp/ql/src/experimental/ir/internal/IntegerInterval.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll rename to csharp/ql/src/experimental/ir/internal/IntegerInterval.qll diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll b/csharp/ql/src/experimental/ir/internal/IntegerPartial.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll rename to csharp/ql/src/experimental/ir/internal/IntegerPartial.qll diff --git a/csharp/ql/src/experimental/ir/internal/Overlap.qll b/csharp/ql/src/experimental/ir/internal/Overlap.qll new file mode 100644 index 000000000000..f9a0c574f8c3 --- /dev/null +++ b/csharp/ql/src/experimental/ir/internal/Overlap.qll @@ -0,0 +1,35 @@ +private newtype TOverlap = + TMayPartiallyOverlap() or + TMustTotallyOverlap() or + TMustExactlyOverlap() + +/** + * Represents a possible overlap between two memory ranges. + */ +abstract class Overlap extends TOverlap { + abstract string toString(); +} + +/** + * Represents a partial overlap between two memory ranges, which may or may not + * actually occur in practice. + */ +class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { + final override string toString() { result = "MayPartiallyOverlap" } +} + +/** + * Represents an overlap in which the first memory range is known to include all + * bits of the second memory range, but may be larger or have a different type. + */ +class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { + final override string toString() { result = "MustTotallyOverlap" } +} + +/** + * Represents an overlap between two memory ranges that have the same extent and + * the same type. + */ +class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { + final override string toString() { result = "MustExactlyOverlap" } +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/TempVariableTag.qll b/csharp/ql/src/experimental/ir/internal/TempVariableTag.qll similarity index 100% rename from csharp/ql/src/semmle/code/csharp/ir/internal/TempVariableTag.qll rename to csharp/ql/src/experimental/ir/internal/TempVariableTag.qll diff --git a/csharp/ql/src/experimental/ir/rangeanalysis/Bound.qll b/csharp/ql/src/experimental/ir/rangeanalysis/Bound.qll new file mode 100644 index 000000000000..c79c199832bc --- /dev/null +++ b/csharp/ql/src/experimental/ir/rangeanalysis/Bound.qll @@ -0,0 +1,79 @@ +import csharp +private import experimental.ir.IR +private import experimental.ir.ValueNumbering + +private newtype TBound = + TBoundZero() or + TBoundValueNumber(ValueNumber vn) { + exists(Instruction i | + vn.getAnInstruction() = i and + ( + i.getResultType() instanceof IntegralType or + i.getResultType() instanceof PointerType + ) and + not vn.getAnInstruction() instanceof ConstantInstruction + | + i instanceof PhiInstruction + or + i instanceof InitializeParameterInstruction + or + i instanceof CallInstruction + or + i instanceof VariableAddressInstruction + or + i instanceof FieldAddressInstruction + or + i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction + or + i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction + or + i.getAUse() instanceof ArgumentOperand + ) + } + +/** + * A bound that may be inferred for an expression plus/minus an integer delta. + */ +abstract class Bound extends TBound { + abstract string toString(); + + /** Gets an expression that equals this bound plus `delta`. */ + abstract Instruction getInstruction(int delta); + + /** Gets an expression that equals this bound. */ + Instruction getInstruction() { result = getInstruction(0) } + + abstract Location getLocation(); +} + +/** + * The bound that corresponds to the integer 0. This is used to represent all + * integer bounds as bounds are always accompanied by an added integer delta. + */ +class ZeroBound extends Bound, TBoundZero { + override string toString() { result = "0" } + + override Instruction getInstruction(int delta) { + result.(ConstantValueInstruction).getValue().toInt() = delta + } + + override Location getLocation() { result instanceof EmptyLocation } +} + +/** + * A bound corresponding to the value of an `Instruction`. + */ +class ValueNumberBound extends Bound, TBoundValueNumber { + ValueNumber vn; + + ValueNumberBound() { this = TBoundValueNumber(vn) } + + /** Gets the SSA variable that equals this bound. */ + override Instruction getInstruction(int delta) { + this = TBoundValueNumber(valueNumber(result)) and delta = 0 + } + + override string toString() { result = vn.getExampleInstruction().toString() } + + override Location getLocation() { result = vn.getLocation() } +} diff --git a/csharp/ql/src/experimental/ir/rangeanalysis/RangeAnalysis.qll b/csharp/ql/src/experimental/ir/rangeanalysis/RangeAnalysis.qll new file mode 100644 index 000000000000..ccc6b9ea30e4 --- /dev/null +++ b/csharp/ql/src/experimental/ir/rangeanalysis/RangeAnalysis.qll @@ -0,0 +1,635 @@ +/** + * Provides classes and predicates for range analysis. + * + * An inferred bound can either be a specific integer or a `ValueNumber` + * representing the abstract value of a set of `Instruction`s. + * + * If an inferred bound relies directly on a condition, then this condition is + * reported as the reason for the bound. + */ + +/* + * This library tackles range analysis as a flow problem. Consider e.g.: + * ```csharp + * len = arr.length; + * if (x < len) { ... y = x-1; ... y ... } + * ``` + * In this case we would like to infer `y <= arr.length - 2`, and this is + * accomplished by tracking the bound through a sequence of steps: + * ``` + * arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y + * ``` + * + * In its simplest form the step relation `I1 --> I2` relates two `Instruction`s + * such that `I1 <= B` implies `I2 <= B` for any `B` (with a second separate + * step relation handling lower bounds). Examples of such steps include + * assignments `I2 = I1` and conditions `x <= I1` where `I2` is a use of `x` + * guarded by the condition. + * + * In order to handle subtractions and additions with constants, and strict + * comparisons, the step relation is augmented with an integer delta. With this + * generalization `I1 --(delta)--> I2` relates two `Instruction`s and an integer + * such that `I1 <= B` implies `I2 <= B + delta` for any `B`. This corresponds + * to the predicate `boundFlowStep`. + * + * The complete range analysis is then implemented as the transitive closure of + * the step relation summing the deltas along the way. If `I1` transitively + * steps to `I2`, `delta` is the sum of deltas along the path, and `B` is an + * interesting bound equal to the value of `I1` then `I2 <= B + delta`. This + * corresponds to the predicate `boundedInstruction`. + * + * Bounds come in two forms: either they are relative to zero (and thus provide + * a constant bound), or they are relative to some program value. This value is + * represented by the `ValueNumber` class, each instance of which represents a + * set of `Instructions` that must have the same value. + * + * Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`. + * There are essentially two cases: + * - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`. + * - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`. + * The first case is for whenever a bound can be proven without taking looping + * into account. The second case is relevant when `x2` comes from a back-edge + * where we can prove that the variable has been non-increasing through the + * loop-iteration as this means that any upper bound that holds prior to the + * loop also holds for the variable during the loop. + * This generalizes to a phi node with `n` inputs, so if + * `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we + * also have `x0 <= B + delta` if we can prove either: + * - `xj <= B + d` with `d <= delta` or + * - `xj <= x0 + d` with `d <= 0` + * for each input `xj`. + * + * As all inferred bounds can be related directly to a path in the source code + * the only source of non-termination is if successive redundant (and thereby + * increasingly worse) bounds are calculated along a loop in the source code. + * We prevent this by weakening the bound to a small finite set of bounds when + * a path follows a second back-edge (we postpone weakening till the second + * back-edge as a precise bound might require traversing a loop once). + */ + +import csharp +private import experimental.ir.IR +private import experimental.ir.internal.IRGuards +private import experimental.ir.ValueNumbering +private import RangeUtils +private import SignAnalysis +import Bound + +cached +private module RangeAnalysisCache { + cached + module RangeAnalysisPublic { + /** + * Holds if `b + delta` is a valid bound for `i`. + * - `upper = true` : `i <= b + delta` + * - `upper = false` : `i >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ + cached + predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) { + boundedInstruction(i, b, delta, upper, _, _, reason) + } + + /** + * Holds if `b + delta` is a valid bound for `op`. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ + cached + predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) { + boundedNonPhiOperand(op, b, delta, upper, _, _, reason) + or + boundedPhiOperand(op, b, delta, upper, _, _, reason) + } + } + + /** + * Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`. + */ + cached + predicate possibleReason(IRGuardCondition guard) { + guard = boundFlowCond(_, _, _, _, _) + or + guard = eqFlowCond(_, _, _, _, _) + } +} + +private import RangeAnalysisCache +import RangeAnalysisPublic + +/** + * Gets a condition that tests whether `vn` equals `bound + delta`. + * + * If the condition evaluates to `testIsTrue`: + * - `isEq = true` : `vn == bound + delta` + * - `isEq = false` : `vn != bound + delta` + */ +private IRGuardCondition eqFlowCond( + ValueNumber vn, Operand bound, int delta, boolean isEq, boolean testIsTrue +) { + result.comparesEq(vn.getAUse(), bound, delta, isEq, testIsTrue) +} + +/** + * Holds if `op1 + delta` is a valid bound for `op2`. + * - `upper = true` : `op2 <= op1 + delta` + * - `upper = false` : `op2 >= op1 + delta` + */ +private predicate boundFlowStepSsa( + NonPhiOperand op2, Operand op1, int delta, boolean upper, Reason reason +) { + exists(IRGuardCondition guard, boolean testIsTrue | + guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and + guard.controls(op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +/** + * Gets a condition that tests whether `vn` is bounded by `bound + delta`. + * + * If the condition evaluates to `testIsTrue`: + * - `upper = true` : `vn <= bound + delta` + * - `upper = false` : `vn >= bound + delta` + */ +private IRGuardCondition boundFlowCond( + ValueNumber vn, NonPhiOperand bound, int delta, boolean upper, boolean testIsTrue +) { + exists(int d | + result.comparesLt(vn.getAUse(), bound, d, upper, testIsTrue) and + // `comparesLt` provides bounds of the form `x < y + k` or `x >= y + k`, but we need + // `x <= y + k` so we strengthen here. `testIsTrue` has the same semantics in `comparesLt` as + // it does here, so we don't need to account for it. + if upper = true then delta = d - 1 else delta = d + ) + or + result = eqFlowCond(vn, bound, delta, true, testIsTrue) and + (upper = true or upper = false) +} + +private newtype TReason = + TNoReason() or + TCondReason(IRGuardCondition guard) { possibleReason(guard) } + +/** + * A reason for an inferred bound. This can either be `CondReason` if the bound + * is due to a specific condition, or `NoReason` if the bound is inferred + * without going through a bounding condition. + */ +abstract class Reason extends TReason { + abstract string toString(); +} + +class NoReason extends Reason, TNoReason { + override string toString() { result = "NoReason" } +} + +class CondReason extends Reason, TCondReason { + IRGuardCondition getCond() { this = TCondReason(result) } + + override string toString() { result = getCond().toString() } +} + +/** + * Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of + * range analysis. + */ +pragma[inline] +private predicate safeCast(IntegralType fromtyp, IntegralType totyp) { + fromtyp.getSize() < totyp.getSize() and + ( + fromtyp instanceof UnsignedIntegralType + or + totyp instanceof SignedIntegralType + ) + or + fromtyp.getSize() <= totyp.getSize() and + ( + fromtyp instanceof SignedIntegralType and + totyp instanceof SignedIntegralType + or + fromtyp instanceof UnsignedIntegralType and + totyp instanceof UnsignedIntegralType + ) +} + +private class SafeCastInstruction extends ConvertInstruction { + SafeCastInstruction() { + safeCast(getResultType(), getUnary().getResultType()) + or + getResultType() instanceof PointerType and + getUnary().getResultType() instanceof PointerType + } +} + +/** + * Holds if `typ` is a small integral type with the given lower and upper bounds. + */ +private predicate typeBound(IntegralType typ, int lowerbound, int upperbound) { + typ instanceof SignedIntegralType and + typ.getSize() = 1 and + lowerbound = typ.minValue() and + upperbound = typ.maxValue() + or + typ instanceof UnsignedIntegralType and + typ.getSize() = 1 and + lowerbound = typ.minValue() and + upperbound = typ.maxValue() + or + typ instanceof SignedIntegralType and + typ.getSize() = 2 and + lowerbound = typ.minValue() and + upperbound = typ.maxValue() + or + typ instanceof UnsignedIntegralType and + typ.getSize() = 2 and + lowerbound = typ.minValue() and + upperbound = typ.maxValue() +} + +/** + * A cast to a small integral type that may overflow or underflow. + */ +private class NarrowingCastInstruction extends ConvertInstruction { + NarrowingCastInstruction() { + not this instanceof SafeCastInstruction and + typeBound(getResultType(), _, _) + } + + /** Gets the lower bound of the resulting type. */ + int getLowerBound() { typeBound(getResultType(), result, _) } + + /** Gets the upper bound of the resulting type. */ + int getUpperBound() { typeBound(getResultType(), _, result) } +} + +/** + * Holds if `op + delta` is a valid bound for `i`. + * - `upper = true` : `i <= op + delta` + * - `upper = false` : `i >= op + delta` + */ +private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, boolean upper) { + valueFlowStep(i, op, delta) and + (upper = true or upper = false) + or + i.(SafeCastInstruction).getAnOperand() = op and + delta = 0 and + (upper = true or upper = false) + or + exists(Operand x | + i.(AddInstruction).getAnOperand() = op and + i.(AddInstruction).getAnOperand() = x and + op != x + | + not exists(getValue(getConstantValue(op.getUse()))) and + not exists(getValue(getConstantValue(x.getUse()))) and + if strictlyPositive(x) + then upper = false and delta = 1 + else + if positive(x) + then upper = false and delta = 0 + else + if strictlyNegative(x) + then upper = true and delta = -1 + else + if negative(x) + then upper = true and delta = 0 + else none() + ) + or + exists(Operand x | + exists(SubInstruction sub | + i = sub and + sub.getLeftOperand() = op and + sub.getRightOperand() = x + ) + | + // `x` with constant value is covered by valueFlowStep + not exists(getValue(getConstantValue(x.getUse()))) and + if strictlyPositive(x) + then upper = true and delta = -1 + else + if positive(x) + then upper = true and delta = 0 + else + if strictlyNegative(x) + then upper = false and delta = 1 + else + if negative(x) + then upper = false and delta = 0 + else none() + ) + or + i.(RemInstruction).getRightOperand() = op and positive(op) and delta = -1 and upper = true + or + i.(RemInstruction).getLeftOperand() = op and positive(op) and delta = 0 and upper = true + or + i.(BitAndInstruction).getAnOperand() = op and positive(op) and delta = 0 and upper = true + or + i.(BitOrInstruction).getAnOperand() = op and + positiveInstruction(i) and + delta = 0 and + upper = false + // TODO: min, max, rand +} + +private predicate boundFlowStepMul(Instruction i1, Operand op, int factor) { + exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | + i1.(MulInstruction).hasOperands(op, c.getAUse()) and factor = k + or + exists(ShiftLeftInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRightOperand() = c.getAUse() and factor = 2.pow(k) + ) + ) +} + +private predicate boundFlowStepDiv(Instruction i1, Operand op, int factor) { + exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | + exists(DivInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = k + ) + or + exists(ShiftRightInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = 2.pow(k) + ) + ) +} + +/** + * Holds if `b` is a valid bound for `op` + */ +pragma[noinline] +private predicate boundedNonPhiOperand( + NonPhiOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(NonPhiOperand op2, int d1, int d2 | + boundFlowStepSsa(op, op2, d1, upper, reason) and + boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, _) and + delta = d1 + d2 + ) + or + boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) + or + exists(int d, Reason r1, Reason r2 | + boundedNonPhiOperand(op, b, d, upper, fromBackEdge, origdelta, r2) + | + unequalOperand(op, b, d, r1) and + ( + upper = true and delta = d - 1 + or + upper = false and delta = d + 1 + ) and + ( + reason = r1 + or + reason = r2 and not r2 instanceof NoReason + ) + ) +} + +/** + * Holds if `op1 + delta` is a valid bound for `op2`. + * - `upper = true` : `op2 <= op1 + delta` + * - `upper = false` : `op2 >= op1 + delta` + */ +private predicate boundFlowStepPhi( + PhiInputOperand op2, Operand op1, int delta, boolean upper, Reason reason +) { + op2.getDef().(CopyInstruction).getSourceValueOperand() = op1 and + (upper = true or upper = false) and + reason = TNoReason() and + delta = 0 + or + exists(IRGuardCondition guard, boolean testIsTrue | + guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and + guard.controlsEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +private predicate boundedPhiOperand( + PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(NonPhiOperand op2, int d1, int d2, Reason r1, Reason r2 | + boundFlowStepPhi(op, op2, d1, upper, r1) and + boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, r2) and + delta = d1 + d2 and + (if r1 instanceof NoReason then reason = r2 else reason = r1) + ) + or + boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) + or + exists(int d, Reason r1, Reason r2 | + boundedInstruction(op.getDef(), b, d, upper, fromBackEdge, origdelta, r2) + | + unequalOperand(op, b, d, r1) and + ( + upper = true and delta = d - 1 + or + upper = false and delta = d + 1 + ) and + ( + reason = r1 + or + reason = r2 and not r2 instanceof NoReason + ) + ) +} + +/** Holds if `op2 != op1 + delta` at `pos`. */ +private predicate unequalFlowStep(Operand op2, Operand op1, int delta, Reason reason) { + exists(IRGuardCondition guard, boolean testIsTrue | + guard = eqFlowCond(valueNumberOfOperand(op2), op1, delta, false, testIsTrue) and + guard.controls(op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +/** + * Holds if `op != b + delta` at `pos`. + */ +private predicate unequalOperand(Operand op, Bound b, int delta, Reason reason) { + exists(Operand op2, int d1, int d2 | + unequalFlowStep(op, op2, d1, reason) and + boundedNonPhiOperand(op2, b, d2, true, _, _, _) and + boundedNonPhiOperand(op2, b, d2, false, _, _, _) and + delta = d1 + d2 + ) +} + +private predicate boundedPhiCandValidForEdge( + PhiInstruction phi, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason, PhiInputOperand op +) { + boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and + ( + exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = true and d <= delta) + or + exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = false and d >= delta) + or + selfBoundedPhiInp(phi, op, upper) + ) +} + +/** Weakens a delta to lie in the range `[-1..1]`. */ +bindingset[delta, upper] +private int weakenDelta(boolean upper, int delta) { + delta in [-1 .. 1] and result = delta + or + upper = true and result = -1 and delta < -1 + or + upper = false and result = 1 and delta > 1 +} + +private predicate boundedPhiInp( + PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, + int origdelta, Reason reason +) { + phi.getAnOperand() = op and + exists(int d, boolean fromBackEdge0 | + boundedPhiOperand(op, b, d, upper, fromBackEdge0, origdelta, reason) + or + b.(ValueNumberBound).getInstruction() = op.getDef() and + d = 0 and + (upper = true or upper = false) and + fromBackEdge0 = false and + origdelta = 0 and + reason = TNoReason() + | + if backEdge(phi, op) + then + fromBackEdge = true and + ( + fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta + or + fromBackEdge0 = false and delta = d + ) + else ( + delta = d and fromBackEdge = fromBackEdge0 + ) + ) +} + +pragma[noinline] +private predicate boundedPhiInp1( + PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper +) { + boundedPhiInp(phi, op, b, delta, upper, _, _, _) +} + +private predicate selfBoundedPhiInp(PhiInstruction phi, PhiInputOperand op, boolean upper) { + exists(int d, ValueNumberBound phibound | + phibound.getInstruction() = phi and + boundedPhiInp(phi, op, phibound, d, upper, _, _, _) and + ( + upper = true and d <= 0 + or + upper = false and d >= 0 + ) + ) +} + +pragma[noinline] +private predicate boundedPhiCand( + PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(PhiInputOperand op | + boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason) + ) +} + +/** + * Holds if the value being cast has an upper (for `upper = true`) or lower + * (for `upper = false`) bound within the bounds of the resulting type. + * For `upper = true` this means that the cast will not overflow and for + * `upper = false` this means that the cast will not underflow. + */ +private predicate safeNarrowingCast(NarrowingCastInstruction cast, boolean upper) { + exists(int bound | + boundedNonPhiOperand(cast.getAnOperand(), any(ZeroBound zb), bound, upper, _, _, _) + | + upper = true and bound <= cast.getUpperBound() + or + upper = false and bound >= cast.getLowerBound() + ) +} + +pragma[noinline] +private predicate boundedCastExpr( + NarrowingCastInstruction cast, Bound b, int delta, boolean upper, boolean fromBackEdge, + int origdelta, Reason reason +) { + boundedNonPhiOperand(cast.getAnOperand(), b, delta, upper, fromBackEdge, origdelta, reason) +} + +/** + * Holds if `b + delta` is a valid bound for `i`. + * - `upper = true` : `i <= b + delta` + * - `upper = false` : `i >= b + delta` + */ +private predicate boundedInstruction( + Instruction i, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + i instanceof PhiInstruction and + forex(PhiInputOperand op | op = i.getAnOperand() | + boundedPhiCandValidForEdge(i, b, delta, upper, fromBackEdge, origdelta, reason, op) + ) + or + i = b.getInstruction(delta) and + (upper = true or upper = false) and + fromBackEdge = false and + origdelta = delta and + reason = TNoReason() + or + exists(Operand mid, int d1, int d2 | + boundFlowStep(i, mid, d1, upper) and + boundedNonPhiOperand(mid, b, d2, upper, fromBackEdge, origdelta, reason) and + delta = d1 + d2 and + not exists(getValue(getConstantValue(i))) + ) + or + exists(Operand mid, int factor, int d | + boundFlowStepMul(i, mid, factor) and + boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and + b instanceof ZeroBound and + delta = d * factor and + not exists(getValue(getConstantValue(i))) + ) + or + exists(Operand mid, int factor, int d | + boundFlowStepDiv(i, mid, factor) and + boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and + d >= 0 and + b instanceof ZeroBound and + delta = d / factor and + not exists(getValue(getConstantValue(i))) + ) + or + exists(NarrowingCastInstruction cast | + cast = i and + safeNarrowingCast(cast, upper.booleanNot()) and + boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason) + ) + or + exists(PropertyAccess pa | + i.(CallInstruction).getAST() = pa and + pa.getProperty().getName() = "Length" and + b instanceof ZeroBound and + delta = origdelta and + (upper = true or upper = false) and + fromBackEdge = false and + delta = getArrayDim(pa.getQualifier().(VariableAccess).getTarget()) and + reason = TNoReason() + ) +} diff --git a/csharp/ql/src/experimental/ir/rangeanalysis/RangeUtils.qll b/csharp/ql/src/experimental/ir/rangeanalysis/RangeUtils.qll new file mode 100644 index 000000000000..4a7f1d698406 --- /dev/null +++ b/csharp/ql/src/experimental/ir/rangeanalysis/RangeUtils.qll @@ -0,0 +1,99 @@ +import csharp +private import experimental.ir.IR +// TODO: move this dependency +import experimental.ir.internal.IntegerConstant + +// TODO: move this out of test code +language[monotonicAggregates] +IntValue getConstantValue(Instruction instr) { + result = instr.(IntegerConstantInstruction).getValue().toInt() + or + exists(BinaryInstruction binInstr, IntValue left, IntValue right | + binInstr = instr and + left = getConstantValue(binInstr.getLeft()) and + right = getConstantValue(binInstr.getRight()) and + ( + binInstr instanceof AddInstruction and result = add(left, right) + or + binInstr instanceof SubInstruction and result = sub(left, right) + or + binInstr instanceof MulInstruction and result = mul(left, right) + or + binInstr instanceof DivInstruction and result = div(left, right) + ) + ) + or + result = getConstantValue(instr.(CopyInstruction).getSourceValue()) + or + exists(PhiInstruction phi | + phi = instr and + result = + max(PhiInputOperand operand | + operand = phi.getAnOperand() + | + getConstantValue(operand.getDef()) + ) and + result = + min(PhiInputOperand operand | + operand = phi.getAnOperand() + | + getConstantValue(operand.getDef()) + ) + ) +} + +/** + * Gets the dimension of the array (either the declared size, or the + * size of the initializer); if no size is declared and no initializer used, + * the predicate does not hold. + */ +IntValue getArrayDim(Variable arr) { + exists(ArrayCreation ac | + arr.getInitializer() = ac and + if exists(ac.getLengthArgument(0)) + then result = ac.getLengthArgument(0).getValue().toInt() + else + if exists(ac.getInitializer()) + then result = ac.getInitializer().getNumberOfElements() + else none() + ) +} + +predicate valueFlowStep(Instruction i, Operand op, int delta) { + i.(CopyInstruction).getSourceValueOperand() = op and delta = 0 + or + exists(Operand x | + i.(AddInstruction).getAnOperand() = op and + i.(AddInstruction).getAnOperand() = x and + op != x + | + delta = getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(SubInstruction).getLeftOperand() = op and + i.(SubInstruction).getRightOperand() = x + | + delta = -getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(PointerAddInstruction).getAnOperand() = op and + i.(PointerAddInstruction).getAnOperand() = x and + op != x + | + delta = i.(PointerAddInstruction).getElementSize() * getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(PointerSubInstruction).getLeftOperand() = op and + i.(PointerSubInstruction).getRightOperand() = x + | + delta = i.(PointerSubInstruction).getElementSize() * -getValue(getConstantValue(x.getDef())) + ) +} + +predicate backEdge(PhiInstruction phi, PhiInputOperand op) { + phi.getAnOperand() = op and + phi.getBlock() = op.getPredecessorBlock().getBackEdgeSuccessor(_) +} diff --git a/csharp/ql/src/experimental/ir/rangeanalysis/SignAnalysis.qll b/csharp/ql/src/experimental/ir/rangeanalysis/SignAnalysis.qll new file mode 100644 index 000000000000..44548e0517a3 --- /dev/null +++ b/csharp/ql/src/experimental/ir/rangeanalysis/SignAnalysis.qll @@ -0,0 +1,583 @@ +/** + * Provides sign analysis to determine whether expression are always positive + * or negative. + * + * The analysis is implemented as an abstract interpretation over the + * three-valued domain `{negative, zero, positive}`. + */ + +import csharp +private import experimental.ir.IR +private import experimental.ir.internal.IRGuards +private import experimental.ir.ValueNumbering +private import SignAnalysisCached + +private newtype TSign = + TNeg() or + TZero() or + TPos() + +private class Sign extends TSign { + string toString() { + result = "-" and this = TNeg() + or + result = "0" and this = TZero() + or + result = "+" and this = TPos() + } + + Sign inc() { + this = TNeg() and result = TNeg() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TPos() + or + this = TPos() and result = TPos() + } + + Sign dec() { result.inc() = this } + + Sign neg() { + this = TNeg() and result = TPos() + or + this = TZero() and result = TZero() + or + this = TPos() and result = TNeg() + } + + Sign bitnot() { + this = TNeg() and result = TPos() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TNeg() + or + this = TPos() and result = TNeg() + } + + Sign add(Sign s) { + this = TZero() and result = s + or + s = TZero() and result = this + or + this = s and this = result + or + this = TPos() and s = TNeg() + or + this = TNeg() and s = TPos() + } + + Sign mul(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign div(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign rem(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = this and s = TNeg() + or + result = this and s = TPos() + } + + Sign bitand(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TZero() and this = TPos() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TNeg() + or + result = TPos() and this = TPos() and s = TPos() + } + + Sign bitor(Sign s) { + result = TZero() and this = TZero() and s = TZero() + or + result = TNeg() and this = TNeg() + or + result = TNeg() and s = TNeg() + or + result = TPos() and this = TPos() and s = TZero() + or + result = TPos() and this = TZero() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + } + + Sign bitxor(Sign s) { + result = TZero() and this = s + or + result = this and s = TZero() + or + result = s and this = TZero() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign lshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + this != TZero() and s != TZero() + } + + Sign rshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result = TNeg() and this = TNeg() + or + result != TNeg() and this = TPos() and s != TZero() + } + + Sign urshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result != TZero() and this = TNeg() and s != TZero() + or + result != TNeg() and this = TPos() and s != TZero() + } +} + +private Sign certainInstructionSign(Instruction inst) { + exists(int i | inst.(IntegerConstantInstruction).getValue().toInt() = i | + i < 0 and result = TNeg() + or + i = 0 and result = TZero() + or + i > 0 and result = TPos() + ) + or + exists(float f | f = inst.(FloatConstantInstruction).getValue().toFloat() | + f < 0 and result = TNeg() + or + f = 0 and result = TZero() + or + f > 0 and result = TPos() + ) +} + +private newtype CastKind = + TWiden() or + TSame() or + TNarrow() + +private CastKind getCastKind(ConvertInstruction ci) { + exists(int fromSize, int toSize | + toSize = ci.getResultSize() and + fromSize = ci.getUnary().getResultSize() + | + fromSize < toSize and + result = TWiden() + or + fromSize = toSize and + result = TSame() + or + fromSize > toSize and + result = TNarrow() + ) +} + +private predicate bindBool(boolean bool) { + bool = true or + bool = false +} + +private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck) { + result = TZero() and + ( + bindBool(fromSigned) and + bindBool(toSigned) and + s = TZero() + or + bindBool(fromSigned) and + bindBool(toSigned) and + ck = TNarrow() + ) + or + result = TPos() and + ( + bindBool(fromSigned) and + bindBool(toSigned) and + s = TPos() + or + bindBool(fromSigned) and + bindBool(toSigned) and + s = TNeg() and + ck = TNarrow() + or + fromSigned = true and + toSigned = false and + s = TNeg() + ) + or + result = TNeg() and + ( + fromSigned = true and + toSigned = true and + s = TNeg() + or + fromSigned = false and + toSigned = true and + s = TPos() and + ck != TWiden() + ) +} + +/** Holds if the sign of `e` is too complicated to determine. */ +private predicate unknownSign(Instruction i) { + // REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than + // the ones we don't understand. Currently, if we try to compute the sign of an instruction that + // we don't understand, and it isn't on this list, we incorrectly compute the sign as "none" + // instead of "+,0,-". + // Even better, we could track the state of each instruction as a power set of {non-negative, + // non-positive, non-zero}, which would mean that the representation of the sign of an unknown + // value would be the empty set. + ( + i instanceof UninitializedInstruction + or + i instanceof InitializeParameterInstruction + or + i instanceof BuiltInOperationInstruction + or + i instanceof CallInstruction + or + i instanceof ChiInstruction + ) +} + +/** + * Holds if `lowerbound` is a lower bound for `bounded`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate lowerBound( + IRGuardCondition comp, Operand lowerbound, Operand bounded, boolean isStrict +) { + exists(int adjustment, Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + ( + isStrict = true and + adjustment = 0 + or + isStrict = false and + adjustment = 1 + ) and + comp.ensuresLt(lowerbound, compared, adjustment, bounded.getUse().getBlock(), true) + ) +} + +/** + * Holds if `upperbound` is an upper bound for `bounded` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate upperBound( + IRGuardCondition comp, Operand upperbound, Operand bounded, boolean isStrict +) { + exists(int adjustment, Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + ( + isStrict = true and + adjustment = 0 + or + isStrict = false and + adjustment = 1 + ) and + comp.ensuresLt(compared, upperbound, adjustment, bounded.getUse().getBlock(), true) + ) +} + +/** + * Holds if `eqbound` is an equality/inequality for `bounded` at `pos`. This is + * restricted to only include bounds for which we might determine a sign. The + * boolean `isEq` gives the polarity: + * - `isEq = true` : `bounded = eqbound` + * - `isEq = false` : `bounded != eqbound` + */ +private predicate eqBound(IRGuardCondition guard, Operand eqbound, Operand bounded, boolean isEq) { + exists(Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + guard.ensuresEq(compared, eqbound, 0, bounded.getUse().getBlock(), isEq) + ) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in + * order for `v` to be positive. + */ +private predicate posBound(IRGuardCondition comp, Operand bound, Operand op) { + upperBound(comp, bound, op, _) or + eqBound(comp, bound, op, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in + * order for `v` to be negative. + */ +private predicate negBound(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) or + eqBound(comp, bound, op, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` + * can be zero. + */ +private predicate zeroBound(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) or + upperBound(comp, bound, op, _) or + eqBound(comp, bound, op, _) +} + +/** Holds if `bound` allows `v` to be positive at `pos`. */ +private predicate posBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + posBound(comp, bound, op) and TPos() = operandSign(bound) +} + +/** Holds if `bound` allows `v` to be negative at `pos`. */ +private predicate negBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + negBound(comp, bound, op) and TNeg() = operandSign(bound) +} + +/** Holds if `bound` allows `v` to be zero at `pos`. */ +private predicate zeroBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) and TNeg() = operandSign(bound) + or + lowerBound(comp, bound, op, false) and TZero() = operandSign(bound) + or + upperBound(comp, bound, op, _) and TPos() = operandSign(bound) + or + upperBound(comp, bound, op, false) and TZero() = operandSign(bound) + or + eqBound(comp, bound, op, true) and TZero() = operandSign(bound) + or + eqBound(comp, bound, op, false) and TZero() != operandSign(bound) +} + +private Sign binaryOpLhsSign(BinaryInstruction i) { result = operandSign(i.getLeftOperand()) } + +private Sign binaryOpRhsSign(BinaryInstruction i) { result = operandSign(i.getRightOperand()) } + +pragma[noinline] +private predicate binaryOpSigns(BinaryInstruction i, Sign lhs, Sign rhs) { + lhs = binaryOpLhsSign(i) and + rhs = binaryOpRhsSign(i) +} + +private Sign unguardedOperandSign(Operand operand) { + result = instructionSign(operand.getDef()) and + not hasGuard(operand, result) +} + +private Sign guardedOperandSign(Operand operand) { + result = instructionSign(operand.getDef()) and + hasGuard(operand, result) +} + +private Sign guardedOperandSignOk(Operand operand) { + result = TPos() and + forex(IRGuardCondition guard, Operand bound | posBound(guard, bound, operand) | + posBoundOk(guard, bound, operand) + ) + or + result = TNeg() and + forex(IRGuardCondition guard, Operand bound | negBound(guard, bound, operand) | + negBoundOk(guard, bound, operand) + ) + or + result = TZero() and + forex(IRGuardCondition guard, Operand bound | zeroBound(guard, bound, operand) | + zeroBoundOk(guard, bound, operand) + ) +} + +/** + * Holds if there is a bound that might restrict whether `v` has the sign `s` + * at `pos`. + */ +private predicate hasGuard(Operand op, Sign s) { + s = TPos() and posBound(_, _, op) + or + s = TNeg() and negBound(_, _, op) + or + s = TZero() and zeroBound(_, _, op) +} + +cached +module SignAnalysisCached { + /** + * Gets a sign that `operand` may have at `pos`, taking guards into account. + */ + cached + Sign operandSign(Operand operand) { + result = unguardedOperandSign(operand) + or + result = guardedOperandSign(operand) and + result = guardedOperandSignOk(operand) + or + // `result` is unconstrained if the definition is inexact. Then any sign is possible. + operand.isDefinitionInexact() + } + + cached + Sign instructionSign(Instruction i) { + result = certainInstructionSign(i) + or + not exists(certainInstructionSign(i)) and + not ( + result = TNeg() and + i.getResultType() instanceof UnsignedIntegralType + ) and + ( + unknownSign(i) + or + exists(ConvertInstruction ci, Instruction prior, boolean fromSigned, boolean toSigned | + i = ci and + prior = ci.getUnary() and + ( + if ci.getResultType() instanceof SignedIntegralType + then toSigned = true + else toSigned = false + ) and + ( + if prior.getResultType() instanceof SignedIntegralType + then fromSigned = true + else fromSigned = false + ) and + result = castSign(operandSign(ci.getAnOperand()), fromSigned, toSigned, getCastKind(ci)) + ) + or + result = operandSign(i.(CopyInstruction).getSourceValueOperand()) + or + result = operandSign(i.(BitComplementInstruction).getAnOperand()).bitnot() + or + result = operandSign(i.(NegateInstruction).getAnOperand()).neg() + or + exists(Sign s1, Sign s2 | binaryOpSigns(i, s1, s2) | + i instanceof AddInstruction and result = s1.add(s2) + or + i instanceof SubInstruction and result = s1.add(s2.neg()) + or + i instanceof MulInstruction and result = s1.mul(s2) + or + i instanceof DivInstruction and result = s1.div(s2) + or + i instanceof RemInstruction and result = s1.rem(s2) + or + i instanceof BitAndInstruction and result = s1.bitand(s2) + or + i instanceof BitOrInstruction and result = s1.bitor(s2) + or + i instanceof BitXorInstruction and result = s1.bitxor(s2) + or + i instanceof ShiftLeftInstruction and result = s1.lshift(s2) + or + i instanceof ShiftRightInstruction and + i.getResultType().(IntegralType) instanceof SignedIntegralType and + result = s1.rshift(s2) + or + i instanceof ShiftRightInstruction and + not i.getResultType().(IntegralType) instanceof SignedIntegralType and + result = s1.urshift(s2) + ) + or + // use hasGuard here? + result = operandSign(i.(PhiInstruction).getAnOperand()) + ) + } +} + +/** Holds if `i` can be positive and cannot be negative. */ +predicate positiveInstruction(Instruction i) { + instructionSign(i) = TPos() and + not instructionSign(i) = TNeg() +} + +/** Holds if `i` at `pos` can be positive at and cannot be negative. */ +predicate positive(Operand op) { + operandSign(op) = TPos() and + not operandSign(op) = TNeg() +} + +/** Holds if `i` can be negative and cannot be positive. */ +predicate negativeInstruction(Instruction i) { + instructionSign(i) = TNeg() and + not instructionSign(i) = TPos() +} + +/** Holds if `i` at `pos` can be negative and cannot be positive. */ +predicate negative(Operand op) { + operandSign(op) = TNeg() and + not operandSign(op) = TPos() +} + +/** Holds if `i` is strictly positive. */ +predicate strictlyPositiveInstruction(Instruction i) { + instructionSign(i) = TPos() and + not instructionSign(i) = TNeg() and + not instructionSign(i) = TZero() +} + +/** Holds if `i` is strictly positive at `pos`. */ +predicate strictlyPositive(Operand op) { + operandSign(op) = TPos() and + not operandSign(op) = TNeg() and + not operandSign(op) = TZero() +} + +/** Holds if `i` is strictly negative. */ +predicate strictlyNegativeInstruction(Instruction i) { + instructionSign(i) = TNeg() and + not instructionSign(i) = TPos() and + not instructionSign(i) = TZero() +} + +/** Holds if `i` is strictly negative at `pos`. */ +predicate strictlyNegative(Operand op) { + operandSign(op) = TNeg() and + not operandSign(op) = TPos() and + not operandSign(op) = TZero() +} diff --git a/csharp/ql/src/external/CodeDuplication.qll b/csharp/ql/src/external/CodeDuplication.qll index 53ff9e91c1cc..f4165e599e8f 100644 --- a/csharp/ql/src/external/CodeDuplication.qll +++ b/csharp/ql/src/external/CodeDuplication.qll @@ -66,9 +66,9 @@ class SimilarBlock extends Copy, @similarity { } } -Method sourceMethod() { method_location(result, _) and numlines(result, _, _, _) } +private Method sourceMethod() { method_location(result, _) and numlines(result, _, _, _) } -int numberOfSourceMethods(Class c) { +private int numberOfSourceMethods(Class c) { result = count(Method m | m = sourceMethod() and m.getDeclaringType() = c) } @@ -97,6 +97,7 @@ private predicate duplicateStatement(Method m1, Method m2, Stmt s1, Stmt s2) { ) } +/** Holds if `duplicate` number of statements are duplicated in the methods. */ predicate duplicateStatements(Method m1, Method m2, int duplicate, int total) { duplicate = strictcount(Stmt s | duplicateStatement(m1, m2, s, _)) and total = strictcount(statementInMethod(m1)) @@ -109,7 +110,7 @@ predicate duplicateMethod(Method m, Method other) { exists(int total | duplicateStatements(m, other, total, total)) } -predicate similarLines(File f, int line) { +private predicate similarLines(File f, int line) { exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]) } @@ -148,7 +149,7 @@ private predicate similarLinesCovered(File f, int coveredLines, File otherFile) ) } -predicate duplicateLines(File f, int line) { +private predicate duplicateLines(File f, int line) { exists(DuplicateBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()] ) @@ -189,6 +190,7 @@ private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile ) } +/** Holds if the two files are not duplicated but have more than 80% similar lines. */ predicate similarFiles(File f, File other, int percent) { exists(int covered, int total | similarLinesCovered(f, covered, other) and @@ -199,6 +201,7 @@ predicate similarFiles(File f, File other, int percent) { not duplicateFiles(f, other, _) } +/** Holds if the two files have more than 70% duplicated lines. */ predicate duplicateFiles(File f, File other, int percent) { exists(int covered, int total | duplicateLinesCovered(f, covered, other) and @@ -209,7 +212,7 @@ predicate duplicateFiles(File f, File other, int percent) { } pragma[noopt] -predicate duplicateAnonymousClass(AnonymousClass c, AnonymousClass other) { +private predicate duplicateAnonymousClass(AnonymousClass c, AnonymousClass other) { exists(int numDup | numDup = strictcount(Method m1 | @@ -248,6 +251,7 @@ private predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int not other instanceof AnonymousClass } +/** Holds if the methods in the two classes are more than 80% duplicated. */ predicate mostlyDuplicateClass(Class c, Class other, string message) { exists(int numDup, int total | mostlyDuplicateClassBase(c, other, numDup, total) and @@ -272,19 +276,28 @@ predicate mostlyDuplicateClass(Class c, Class other, string message) { ) } +/** Holds if the two files are similar or duplicated. */ predicate fileLevelDuplication(File f, File other) { similarFiles(f, other, _) or duplicateFiles(f, other, _) } +/** + * Holds if the two classes are duplicated anonymous classes or more than 80% of + * their methods are duplicated. + */ predicate classLevelDuplication(Class c, Class other) { duplicateAnonymousClass(c, other) or mostlyDuplicateClass(c, other, _) } -Element whitelistedDuplicateElement() { +private Element whitelistedDuplicateElement() { result instanceof UsingNamespaceDirective or result instanceof UsingStaticDirective } +/** + * Holds if the `line` in the `file` contains an element, such as a `using` + * directive, that is not considered for code duplication. + */ predicate whitelistedLineForDuplication(File file, int line) { exists(Location loc | loc = whitelistedDuplicateElement().getLocation() | line = loc.getStartLine() and file = loc.getFile() diff --git a/csharp/ql/src/external/DuplicateMethod.ql b/csharp/ql/src/external/DuplicateMethod.ql index 57255ce51d01..371315cd4e66 100644 --- a/csharp/ql/src/external/DuplicateMethod.ql +++ b/csharp/ql/src/external/DuplicateMethod.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Duplicate method * @description There is another identical implementation of this method. Extract the code to a common superclass or delegate to improve sharing. * @kind problem diff --git a/csharp/ql/src/external/MostlyDuplicateClass.ql b/csharp/ql/src/external/MostlyDuplicateClass.ql index fd4fc87c176b..7b7d5ef3eff0 100644 --- a/csharp/ql/src/external/MostlyDuplicateClass.ql +++ b/csharp/ql/src/external/MostlyDuplicateClass.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Duplicate class * @description More than 80% of the methods in this class are duplicated in another class. Create a common supertype to improve code sharing. * @kind problem diff --git a/csharp/ql/src/external/MostlyDuplicateFile.ql b/csharp/ql/src/external/MostlyDuplicateFile.ql index 0100e94bc7c2..2dbc2e17ffbe 100644 --- a/csharp/ql/src/external/MostlyDuplicateFile.ql +++ b/csharp/ql/src/external/MostlyDuplicateFile.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Mostly duplicate file * @description There is another file that shares a lot of the code with this file. Merge the two files to improve maintainability. * @kind problem diff --git a/csharp/ql/src/external/MostlyDuplicateMethod.ql b/csharp/ql/src/external/MostlyDuplicateMethod.ql index a7d01ed0ed99..aa0003c113b7 100644 --- a/csharp/ql/src/external/MostlyDuplicateMethod.ql +++ b/csharp/ql/src/external/MostlyDuplicateMethod.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Mostly duplicate method * @description There is another method that shares a lot of the code with this method. Extract the code to a common superclass or delegate to improve sharing. * @kind problem diff --git a/csharp/ql/src/external/MostlySimilarFile.ql b/csharp/ql/src/external/MostlySimilarFile.ql index 1c175a72bbd0..a66df834aca6 100644 --- a/csharp/ql/src/external/MostlySimilarFile.ql +++ b/csharp/ql/src/external/MostlySimilarFile.ql @@ -1,4 +1,5 @@ /** + * @deprecated * @name Mostly similar file * @description There is another file that shares a lot of the code with this file. Notice that names of variables and types may have been changed. Merge the two files to improve maintainability. * @kind problem diff --git a/csharp/ql/src/external/examples/filters/BumpMetricBy10.ql b/csharp/ql/src/external/examples/filters/BumpMetricBy10.ql deleted file mode 100644 index 209d580bc19b..000000000000 --- a/csharp/ql/src/external/examples/filters/BumpMetricBy10.ql +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @name Edit the value of a metric - * @description Add 10 to a metric's value - * @deprecated - */ - -import csharp -import external.MetricFilter - -from MetricResult res -select res, res.getValue() + 10 diff --git a/csharp/ql/src/external/examples/filters/EditDefectMessage.ql b/csharp/ql/src/external/examples/filters/EditDefectMessage.ql deleted file mode 100644 index 4fbf41465504..000000000000 --- a/csharp/ql/src/external/examples/filters/EditDefectMessage.ql +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @name Edit the message of a query - * @description Change the string in the select to edit the message - * @deprecated - */ - -import csharp -import external.DefectFilter - -from DefectResult res -select res, "Filtered query result: " + res.getMessage() diff --git a/csharp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql b/csharp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql deleted file mode 100644 index 2a1257427b25..000000000000 --- a/csharp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @name Filter: removed results from generated code - * @description Shows how to exclude certain files or folders from results. - * @deprecated - */ - -import csharp -import external.DefectFilter - -predicate generatedFile(File f) { f.getAbsolutePath().matches("%generated%") } - -from DefectResult res -where not generatedFile(res.getFile()) -select res, res.getMessage() diff --git a/csharp/ql/src/external/examples/filters/FromSource.ql b/csharp/ql/src/external/examples/filters/FromSource.ql deleted file mode 100644 index 2f714ec2225a..000000000000 --- a/csharp/ql/src/external/examples/filters/FromSource.ql +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @name Filter: only keep results from source - * @description Shows how to filter for only certain files - * @deprecated - */ - -import csharp -import external.DefectFilter - -from DefectResult res -where res.getFile().fromSource() -select res, res.getMessage() diff --git a/csharp/ql/src/external/tests/DefectFromExternalData.ql b/csharp/ql/src/external/tests/DefectFromExternalData.ql deleted file mode 100644 index 70557f357a38..000000000000 --- a/csharp/ql/src/external/tests/DefectFromExternalData.ql +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @name Defect from external data - * @description Insert description here... - * @kind problem - * @problem.severity warning - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -// custom://[FileUtil][2011-01-02][false][1.1][6][Message 2] -from ExternalData d, File u -where - d.getQueryPath() = "external-data.ql" and - u.getStem() = d.getField(0) -select u, - d.getField(5) + ", " + d.getFieldAsDate(1) + ", " + d.getField(2) + ", " + d.getFieldAsFloat(3) + - ", " + d.getFieldAsInt(4) + ": " + d.getNumFields() diff --git a/csharp/ql/src/external/tests/DefectFromExternalDefect.ql b/csharp/ql/src/external/tests/DefectFromExternalDefect.ql deleted file mode 100644 index 57138a6879ff..000000000000 --- a/csharp/ql/src/external/tests/DefectFromExternalDefect.ql +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @name Defect from external defect - * @description Create a defect from external data - * @kind problem - * @problem.severity warning - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -class DuplicateCode extends ExternalDefect { - DuplicateCode() { getQueryPath() = "duplicate-code/duplicateCode.ql" } -} - -from DuplicateCode d -select d, "External Defect " + d.getMessage() diff --git a/csharp/ql/src/external/tests/DefectFromExternalMetric.ql b/csharp/ql/src/external/tests/DefectFromExternalMetric.ql deleted file mode 100644 index 88d38d5f00b6..000000000000 --- a/csharp/ql/src/external/tests/DefectFromExternalMetric.ql +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @name Defect from external metric - * @description Create a defect from external data - * @kind problem - * @problem.severity warning - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -from ExternalMetric m, File f -where - m.getQueryPath() = "filesBuilt.ql" and - m.getValue() = 1.0 and - m.getFile() = f -select f, "File is built" diff --git a/csharp/ql/src/external/tests/MetricFilter.ql b/csharp/ql/src/external/tests/MetricFilter.ql deleted file mode 100644 index 9f79465be75e..000000000000 --- a/csharp/ql/src/external/tests/MetricFilter.ql +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @name Metric filter - * @description Only include results in large files (200) lines of code. - * @kind treemap - * @deprecated - */ - -import csharp -import external.MetricFilter - -from MetricResult res -where res.getFile().getNumberOfLinesOfCode() > 200 -select res, res.getValue() diff --git a/csharp/ql/src/external/tests/MetricFromExternalDefect.ql b/csharp/ql/src/external/tests/MetricFromExternalDefect.ql deleted file mode 100644 index 4894de287618..000000000000 --- a/csharp/ql/src/external/tests/MetricFromExternalDefect.ql +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @name Metric from external defect - * @description Find number of duplicate code entries in a file - * @treemap.warnOn lowValues - * @metricType file - * @kind treemap - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -class DuplicateCode extends ExternalDefect { - DuplicateCode() { getQueryPath() = "duplicate-code/duplicateCode.ql" } -} - -predicate numDuplicateEntries(File f, int i) { i = count(DuplicateCode d | d.getFile() = f) } - -from File f, int i -where numDuplicateEntries(f, i) -select f, i diff --git a/csharp/ql/src/external/tests/MetricFromExternalMetric.ql b/csharp/ql/src/external/tests/MetricFromExternalMetric.ql deleted file mode 100644 index 4d2ab4088ae5..000000000000 --- a/csharp/ql/src/external/tests/MetricFromExternalMetric.ql +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @name Metric from external metric - * @description Each file in a folder gets as metric value the number of files built in that folder - * @treemap.warnOn lowValues - * @metricType file - * @kind treemap - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -predicate numBuiltFiles(Folder fold, int i) { - i = - count(File f | - exists(ExternalMetric m | - m.getQueryPath() = "filesBuilt.ql" and - m.getValue() = 1.0 and - m.getFile() = f - ) and - f.getParentContainer() = fold - ) -} - -from File f, int i -where numBuiltFiles(f.getParentContainer(), i) -select f, i diff --git a/csharp/ql/src/printAst.ql b/csharp/ql/src/printAst.ql new file mode 100644 index 000000000000..b02193b257ac --- /dev/null +++ b/csharp/ql/src/printAst.ql @@ -0,0 +1,28 @@ +/** + * @name Print AST + * @description Outputs a representation of a file's Abstract Syntax Tree. This + * query is used by the VS Code extension. + * @id csharp/print-ast + * @kind graph + * @tags ide-contextual-queries/print-ast + */ + +import csharp +import semmle.code.csharp.PrintAst +import definitions + +/** + * The source file to generate an AST from. + */ +external string selectedSourceFile(); + +class PrintAstConfigurationOverride extends PrintAstConfiguration { + /** + * Holds if the location matches the selected file in the VS Code extension and + * the element is `fromSource`. + */ + override predicate shouldPrint(Element e, Location l) { + super.shouldPrint(e, l) and + l.getFile() = getEncodedFile(selectedSourceFile()) + } +} diff --git a/csharp/ql/src/semmle/code/asp/AspNet.qll b/csharp/ql/src/semmle/code/asp/AspNet.qll index c7ed51bb4842..0666c5eb782d 100644 --- a/csharp/ql/src/semmle/code/asp/AspNet.qll +++ b/csharp/ql/src/semmle/code/asp/AspNet.qll @@ -32,7 +32,7 @@ class AspAttribute extends AspElement, @asp_attribute { } /** * An open tag, for example the tag on line 1 in * - * ``` + * ```html * @@ -67,7 +67,7 @@ class AspOpenTag extends AspElement, @asp_open_tag { /** * A close tag, for example the tag on line 3 in * - * ``` + * ```html * @@ -123,7 +123,7 @@ class AspServerComment extends AspComment { /** * A data-binding expression, for example `<%# myArray %>` in * - * ``` + * ```html * * ``` */ diff --git a/csharp/ql/src/semmle/code/cil/Access.qll b/csharp/ql/src/semmle/code/cil/Access.qll index 263e527dfec9..e42a883a2e5f 100644 --- a/csharp/ql/src/semmle/code/cil/Access.qll +++ b/csharp/ql/src/semmle/code/cil/Access.qll @@ -31,6 +31,7 @@ class ReadRefAccess extends ReadAccess, ReadRef { } /** An instruction that writes a variable. */ class WriteAccess extends VariableAccess, @cil_write_access { + /** Gets the expression whose value is used in this variable write. */ Expr getExpr() { none() } } diff --git a/csharp/ql/src/semmle/code/cil/BasicBlock.qll b/csharp/ql/src/semmle/code/cil/BasicBlock.qll index ef584e73bbee..459bb667e6f1 100644 --- a/csharp/ql/src/semmle/code/cil/BasicBlock.qll +++ b/csharp/ql/src/semmle/code/cil/BasicBlock.qll @@ -23,7 +23,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (x < 0) * x = -x; * ``` @@ -41,7 +41,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (!(x >= 0)) * x = -x; * ``` @@ -75,7 +75,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -97,7 +97,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -124,7 +124,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (x < 0) { * x = -x; * if (x > 10) @@ -158,7 +158,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -182,7 +182,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * try { * return s.Length; @@ -207,7 +207,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * try { * return s.Length; @@ -353,7 +353,7 @@ class ConditionBlock extends BasicBlock { * all predecessors of `this.getATrueSuccessor()` are either `this` or dominated by `this.getATrueSuccessor()`. * * For example, in the following C# snippet: - * ``` + * ```csharp * if (x) * controlled; * false_successor; @@ -361,7 +361,7 @@ class ConditionBlock extends BasicBlock { * ``` * `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`) * or dominated by itself. Whereas in the following code: - * ``` + * ```csharp * if (x) * while (controlled) * also_controlled; diff --git a/csharp/ql/src/semmle/code/cil/Instructions.qll b/csharp/ql/src/semmle/code/cil/Instructions.qll index 3f3011f10b3d..438c16518f89 100644 --- a/csharp/ql/src/semmle/code/cil/Instructions.qll +++ b/csharp/ql/src/semmle/code/cil/Instructions.qll @@ -1,90 +1,106 @@ /** * Provides classes representing individual opcodes. + * + * See ECMA-335 (https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf) + * pages 32-101 for a detailed explanation of these instructions. */ private import CIL private import semmle.code.dotnet.Variable as DotNet module Opcodes { - // Literals + /** An `ldc.i4.m1` instruction. */ class Ldc_i4_m1 extends IntLiteral, @cil_ldc_i4_m1 { override string getOpcodeName() { result = "ldc.i4.m1" } override string getValue() { result = "-1" } } + /** An `ldc.i4.0` instruction. */ class Ldc_i4_0 extends IntLiteral, @cil_ldc_i4_0 { override string getOpcodeName() { result = "ldc.i4.0" } override string getValue() { result = "0" } } + /** An `ldc.i4.1` instruction. */ class Ldc_i4_1 extends IntLiteral, @cil_ldc_i4_1 { override string getOpcodeName() { result = "ldc.i4.1" } override string getValue() { result = "1" } } + /** An `ldc.i4.2` instruction. */ class Ldc_i4_2 extends IntLiteral, @cil_ldc_i4_2 { override string getOpcodeName() { result = "ldc.i4.2" } override string getValue() { result = "2" } } + /** An `ldc.i4.3` instruction. */ class Ldc_i4_3 extends IntLiteral, @cil_ldc_i4_3 { override string getOpcodeName() { result = "ldc.i4.3" } override string getValue() { result = "3" } } + /** An `ldc.i4.4` instruction. */ class Ldc_i4_4 extends IntLiteral, @cil_ldc_i4_4 { override string getOpcodeName() { result = "ldc.i4.4" } override string getValue() { result = "4" } } + /** An `ldc.i4.5` instruction. */ class Ldc_i4_5 extends IntLiteral, @cil_ldc_i4_5 { override string getOpcodeName() { result = "ldc.i4.5" } override string getValue() { result = "5" } } + /** An `ldc.i4.6` instruction. */ class Ldc_i4_6 extends IntLiteral, @cil_ldc_i4_6 { override string getOpcodeName() { result = "ldc.i4.6" } override string getValue() { result = "6" } } + /** An `ldc.i4.7` instruction. */ class Ldc_i4_7 extends IntLiteral, @cil_ldc_i4_7 { override string getOpcodeName() { result = "ldc.i4.7" } override string getValue() { result = "7" } } + /** An `ldc.i4.8` instruction. */ class Ldc_i4_8 extends IntLiteral, @cil_ldc_i4_8 { override string getOpcodeName() { result = "ldc.i4.8" } override string getValue() { result = "8" } } + /** An `ldc.i4` instruction. */ class Ldc_i4 extends IntLiteral, @cil_ldc_i4 { override string getOpcodeName() { result = "ldc.i4" } override string getExtra() { result = getValue() } } + /** An `ldc.i8` instruction. */ class Ldc_i8 extends IntLiteral, @cil_ldc_i8 { override string getOpcodeName() { result = "ldc.i8" } override string getExtra() { result = getValue() } } + /** An `ldc.i4.s` instruction. */ class Ldc_i4_s extends IntLiteral, @cil_ldc_i4_s { override string getOpcodeName() { result = "ldc.i4.s" } override string getExtra() { result = getValue() } } + /** An `ldnull` instruction. */ class Ldnull extends Literal, @cil_ldnull { override string getOpcodeName() { result = "ldnull" } @@ -95,6 +111,7 @@ module Opcodes { override Type getType() { result instanceof ObjectType } } + /** An `ldc.r4` instruction. */ class Ldc_r4 extends FloatLiteral, @cil_ldc_r4 { override string getOpcodeName() { result = "ldc.r4" } @@ -103,6 +120,7 @@ module Opcodes { override Type getType() { result instanceof FloatType } } + /** An `ldc.r8` instruction. */ class Ldc_r8 extends FloatLiteral, @cil_ldc_r8 { override string getOpcodeName() { result = "ldc.r8" } @@ -111,59 +129,72 @@ module Opcodes { override Type getType() { result instanceof DoubleType } } - // Arithmetic operations + /** An `add` instruction. */ class Add extends BinaryArithmeticExpr, @cil_add { override string getOpcodeName() { result = "add" } } + /** An `add.ovf` instruction. */ class Add_ovf extends BinaryArithmeticExpr, @cil_add_ovf { override string getOpcodeName() { result = "add.ovf" } } + /** An `add.ovf.un` instruction. */ class Add_ovf_un extends BinaryArithmeticExpr, @cil_add_ovf_un { override string getOpcodeName() { result = "add.ovf.un" } } + /** A `sub` instruction. */ class Sub extends BinaryArithmeticExpr, @cil_sub { override string getOpcodeName() { result = "sub" } } + /** A `sub.ovf` instruction. */ class Sub_ovf extends BinaryArithmeticExpr, @cil_sub_ovf { override string getOpcodeName() { result = "sub.ovf" } } + /** A `sub.ovf.un` instruction. */ class Sub_ovf_un extends BinaryArithmeticExpr, @cil_sub_ovf_un { override string getOpcodeName() { result = "sub.ovf.un" } } + /** A `mul` instruction. */ class Mul extends BinaryArithmeticExpr, @cil_mul { override string getOpcodeName() { result = "mul" } } + /** A `mul.ovf` instruction. */ class Mul_ovf extends BinaryArithmeticExpr, @cil_mul_ovf { override string getOpcodeName() { result = "mul.ovf" } } + /** A `mul.ovf.un` instruction. */ class Mul_ovf_un extends BinaryArithmeticExpr, @cil_mul_ovf_un { override string getOpcodeName() { result = "mul.ovf.un" } } + /** A `div` instruction. */ class Div extends BinaryArithmeticExpr, @cil_div { override string getOpcodeName() { result = "div" } } + /** A `div.un` instruction. */ class Div_un extends BinaryArithmeticExpr, @cil_div_un { override string getOpcodeName() { result = "div.un" } } + /** A `rem` instruction. */ class Rem extends BinaryArithmeticExpr, @cil_rem { override string getOpcodeName() { result = "rem" } } + /** A `rem.un` instruction. */ class Rem_un extends BinaryArithmeticExpr, @cil_rem_un { override string getOpcodeName() { result = "rem.un" } } + /** A `neg` instruction. */ class Neg extends UnaryExpr, @cil_neg { override string getOpcodeName() { result = "neg" } @@ -174,46 +205,54 @@ module Opcodes { } } - // Binary operations + /** An `and` instruction. */ class And extends BinaryBitwiseOperation, @cil_and { override string getOpcodeName() { result = "and" } } + /** An `or` instruction. */ class Or extends BinaryBitwiseOperation, @cil_or { override string getOpcodeName() { result = "or" } } + /** An `xor` instruction. */ class Xor extends BinaryBitwiseOperation, @cil_xor { override string getOpcodeName() { result = "xor" } } + /** A `not` instruction. */ class Not extends UnaryBitwiseOperation, @cil_not { override string getOpcodeName() { result = "not" } } + /** A `shl` instruction. */ class Shl extends BinaryBitwiseOperation, @cil_shl { override string getOpcodeName() { result = "shl" } } + /** A `shr` instruction. */ class Shr extends BinaryBitwiseOperation, @cil_shr { override string getOpcodeName() { result = "shr" } } + /** A `shr.un` instruction. */ class Shr_un extends BinaryBitwiseOperation, @cil_shr_un { override string getOpcodeName() { result = "shr.un" } } - // Binary comparison operations + /** A `ceq` instruction. */ class Ceq extends ComparisonOperation, @cil_ceq { override string getOpcodeName() { result = "ceq" } } + /** A `pop` instruction. */ class Pop extends Instruction, @cil_pop { override string getOpcodeName() { result = "pop" } override int getPopCount() { result = 1 } } + /** A `dup` instruction. */ class Dup extends Expr, @cil_dup { override string getOpcodeName() { result = "dup" } @@ -224,6 +263,7 @@ module Opcodes { override Type getType() { result = getOperand(0).getType() } } + /** A `ret` instruction. */ class Ret extends Return, @cil_ret { override string getOpcodeName() { result = "ret" } @@ -234,10 +274,12 @@ module Opcodes { } } + /** A `nop` instruction. */ class Nop extends Instruction, @cil_nop { override string getOpcodeName() { result = "nop" } } + /** An `ldstr` instruction. */ class Ldstr extends StringLiteral, @cil_ldstr { override string getOpcodeName() { result = "ldstr" } @@ -246,111 +288,137 @@ module Opcodes { override Type getType() { result instanceof StringType } } - // Control flow + /** A `br` instruction. */ class Br extends UnconditionalBranch, @cil_br { override string getOpcodeName() { result = "br" } } + /** A `br.s` instruction. */ class Br_s extends UnconditionalBranch, @cil_br_s { override string getOpcodeName() { result = "br.s" } } + /** A `brfalse.s` instruction. */ class Brfalse_s extends UnaryBranch, @cil_brfalse_s { override string getOpcodeName() { result = "brfalse.s" } } + /** A `brfalse` instruction. */ class Brfalse extends UnaryBranch, @cil_brfalse { override string getOpcodeName() { result = "brfalse" } } + /** A `brtrue.s` instruction. */ class Brtrue_s extends UnaryBranch, @cil_brtrue_s { override string getOpcodeName() { result = "brtrue.s" } } + /** A `brtrue` instruction. */ class Brtrue extends UnaryBranch, @cil_brtrue { override string getOpcodeName() { result = "brtrue" } } + /** A `blt.s` instruction. */ class Blt_s extends BinaryBranch, @cil_blt_s { override string getOpcodeName() { result = "blt.s" } } + /** A `blt` instruction. */ class Blt extends BinaryBranch, @cil_blt { override string getOpcodeName() { result = "blt" } } + /** A `blt.un.s` instruction. */ class Blt_un_s extends BinaryBranch, @cil_blt_un_s { override string getOpcodeName() { result = "blt.un.s" } } + /** A `blt.un` instruction. */ class Blt_un extends BinaryBranch, @cil_blt_un { override string getOpcodeName() { result = "blt.un" } } + /** A `bgt.un` instruction. */ class Bgt_un extends BinaryBranch, @cil_bgt_un { override string getOpcodeName() { result = "bgt.un" } } + /** A `ble.un.s` instruction. */ class Ble_un_s extends BinaryBranch, @cil_ble_un_s { override string getOpcodeName() { result = "ble.un.s" } } + /** A `ble.un` instruction. */ class Ble_un extends BinaryBranch, @cil_ble_un { override string getOpcodeName() { result = "ble.un" } } + /** A `bge.s` instruction. */ class Bge_s extends BinaryBranch, @cil_bge_s { override string getOpcodeName() { result = "bge.s" } } + /** A `ble.un` instruction. */ class Bge_un extends BinaryBranch, @cil_bge_un { override string getOpcodeName() { result = "bge.un" } } + /** A `bge` instruction. */ class Bge extends BinaryBranch, @cil_bge { override string getOpcodeName() { result = "bge" } } + /** A `bne.un.s` instruction. */ class Bne_un_s extends BinaryBranch, @cil_bne_un_s { override string getOpcodeName() { result = "bne.un.s" } } + /** A `bne.un` instruction. */ class Bne_un extends BinaryBranch, @cil_bne_un { override string getOpcodeName() { result = "bne.un" } } + /** A `beq` instruction. */ class Beq extends BinaryBranch, @cil_beq { override string getOpcodeName() { result = "beq" } } + /** A `beq.s` instruction. */ class Beq_s extends BinaryBranch, @cil_beq_s { override string getOpcodeName() { result = "beq.s" } } + /** A `ble.s` instruction. */ class Ble_s extends BinaryBranch, @cil_ble_s { override string getOpcodeName() { result = "ble.s" } } + /** A `ble` instruction. */ class Ble extends BinaryBranch, @cil_ble { override string getOpcodeName() { result = "ble" } } + /** A `bgt.s` instruction. */ class Bgt_s extends BinaryBranch, @cil_bgt_s { override string getOpcodeName() { result = "bgt.s" } } + /** A `bgt` instruction. */ class Bgt extends BinaryBranch, @cil_bgt { override string getOpcodeName() { result = "bgt" } } + /** A `bgt.in.s` instruction. */ class Bgt_in_s extends BinaryBranch, @cil_bgt_un_s { - override string getOpcodeName() { result = "bgt.un.s" } + override string getOpcodeName() { result = "bgt.in.s" } } + /** A `bge.in.s` instruction. */ class Bge_in_s extends BinaryBranch, @cil_bge_un_s { override string getOpcodeName() { result = "bge.un.s" } } + /** A `switch` instruction. */ class Switch extends ConditionalBranch, @cil_switch { override string getOpcodeName() { result = "switch" } @@ -367,62 +435,73 @@ module Opcodes { } } + /** A `leave` instruction. */ class Leave_ extends Leave, @cil_leave { override string getOpcodeName() { result = "leave" } } + /** A `leave.s` instruction. */ class Leave_s extends Leave, @cil_leave_s { override string getOpcodeName() { result = "leave.s" } } + /** An `endfilter` instruction. */ class Endfilter extends Instruction, @cil_endfilter { override string getOpcodeName() { result = "endfilter" } } + /** An `endfinally` instruction. */ class Endfinally extends Instruction, @cil_endfinally { override string getOpcodeName() { result = "endfinally" } override predicate canFlowNext() { none() } } - // Comparisons (not jumps) + /** A `cgt.un` instruction. */ class Cgt_un extends ComparisonOperation, @cil_cgt_un { override string getOpcodeName() { result = "cgt.un" } } + /** A `cgt` instruction. */ class Cgt extends ComparisonOperation, @cil_cgt { override string getOpcodeName() { result = "cgt" } } + /** A `clt.un` instruction. */ class Clt_un extends ComparisonOperation, @cil_clt_un { - override string getOpcodeName() { result = "cgt.un" } + override string getOpcodeName() { result = "clt.un" } } + /** A `clt` instruction. */ class Clt extends ComparisonOperation, @cil_clt { override string getOpcodeName() { result = "clt" } } - // Calls + /** A `call` instruction. */ class Call_ extends Call, @cil_call { override string getOpcodeName() { result = "call" } } + /** A `callvirt` instruction. */ class Callvirt extends Call, @cil_callvirt { override string getOpcodeName() { result = "callvirt" } override predicate isVirtual() { any() } } + /** A `tail.` instruction. */ class Tail extends Instruction, @cil_tail { override string getOpcodeName() { result = "tail." } } + /** A `jmp` instruction. */ class Jmp extends Call, @cil_jmp { override string getOpcodeName() { result = "jmp" } override predicate isTailCall() { any() } } + /** An `isinst` instruction. */ class Isinst extends UnaryExpr, @cil_isinst { override string getOpcodeName() { result = "isinst" } @@ -434,6 +513,7 @@ module Opcodes { override string getExtra() { result = getTestedType().getQualifiedName() } } + /** A `castclass` instruction. */ class Castclass extends UnaryExpr, @cil_castclass { override string getOpcodeName() { result = "castclass" } @@ -445,67 +525,77 @@ module Opcodes { override string getExtra() { result = getTestedType().getQualifiedName() } } - // Locals + /** An `stloc.0` instruction. */ class Stloc_0 extends LocalVariableWriteAccess, @cil_stloc_0 { override string getOpcodeName() { result = "stloc.0" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(0) } } + /** An `stloc.1` instruction. */ class Stloc_1 extends LocalVariableWriteAccess, @cil_stloc_1 { override string getOpcodeName() { result = "stloc.1" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(1) } } + /** An `stloc.2` instruction. */ class Stloc_2 extends LocalVariableWriteAccess, @cil_stloc_2 { override string getOpcodeName() { result = "stloc.2" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(2) } } + /** An `stloc.3` instruction. */ class Stloc_3 extends LocalVariableWriteAccess, @cil_stloc_3 { override string getOpcodeName() { result = "stloc.3" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(3) } } + /** An `stloc.s` instruction. */ class Stloc_s extends LocalVariableWriteAccess, @cil_stloc_s { override string getOpcodeName() { result = "stloc.s" } override LocalVariable getTarget() { cil_access(this, result) } } + /** An `stloc` instruction. */ class Stloc extends LocalVariableWriteAccess, @cil_stloc { override string getOpcodeName() { result = "stloc" } override LocalVariable getTarget() { cil_access(this, result) } } + /** An `ldloc.0` instruction. */ class Ldloc_0 extends LocalVariableReadAccess, @cil_ldloc_0 { override string getOpcodeName() { result = "ldloc.0" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(0) } } + /** An `ldloc.1` instruction. */ class Ldloc_1 extends LocalVariableReadAccess, @cil_ldloc_1 { override string getOpcodeName() { result = "ldloc.1" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(1) } } + /** An `ldloc.2` instruction. */ class Ldloc_2 extends LocalVariableReadAccess, @cil_ldloc_2 { override string getOpcodeName() { result = "ldloc.2" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(2) } } + /** An `ldloc.3` instruction. */ class Ldloc_3 extends LocalVariableReadAccess, @cil_ldloc_3 { override string getOpcodeName() { result = "ldloc.3" } override LocalVariable getTarget() { result = getImplementation().getLocalVariable(3) } } + /** An `ldloc.s` instruction. */ class Ldloc_s extends LocalVariableReadAccess, @cil_ldloc_s { override string getOpcodeName() { result = "ldloc.s" } @@ -514,6 +604,7 @@ module Opcodes { override string getExtra() { result = "L" + getTarget().getIndex() } } + /** An `ldloca.s` instruction. */ class Ldloca_s extends LocalVariableReadAccess, ReadRefAccess, @cil_ldloca_s { override string getOpcodeName() { result = "ldloca.s" } @@ -522,6 +613,7 @@ module Opcodes { override string getExtra() { result = "L" + getTarget().getIndex() } } + /** An `ldloc` instruction. */ class Ldloc extends LocalVariableReadAccess, @cil_ldloc { override string getOpcodeName() { result = "ldloc" } @@ -530,31 +622,35 @@ module Opcodes { override string getExtra() { result = "L" + getTarget().getIndex() } } - // Arguments + /** An `ldarg.0` instruction. */ class Ldarg_0 extends ParameterReadAccess, @cil_ldarg_0 { override string getOpcodeName() { result = "ldarg.0" } override Parameter getTarget() { result = getImplementation().getMethod().getRawParameter(0) } } + /** An `ldarg.1` instruction. */ class Ldarg_1 extends ParameterReadAccess, @cil_ldarg_1 { override string getOpcodeName() { result = "ldarg.1" } override Parameter getTarget() { result = getImplementation().getMethod().getRawParameter(1) } } + /** An `ldarg.2` instruction. */ class Ldarg_2 extends ParameterReadAccess, @cil_ldarg_2 { override string getOpcodeName() { result = "ldarg.2" } override Parameter getTarget() { result = getImplementation().getMethod().getRawParameter(2) } } + /** An `ldarg.3` instruction. */ class Ldarg_3 extends ParameterReadAccess, @cil_ldarg_3 { override string getOpcodeName() { result = "ldarg.3" } override Parameter getTarget() { result = getImplementation().getMethod().getRawParameter(3) } } + /** An `ldarg.s` instruction. */ class Ldarg_s extends ParameterReadAccess, @cil_ldarg_s { override string getOpcodeName() { result = "ldarg.s" } @@ -563,25 +659,28 @@ module Opcodes { override string getExtra() { result = this.getTarget().getIndex().toString() } } + /** An `ldarg` instruction. */ class Ldarg extends ParameterReadAccess, @cil_ldarg { override string getOpcodeName() { result = "ldarg" } override Parameter getTarget() { cil_access(this, result) } } + /** An `ldarga.s` instruction. */ class Ldarga_s extends ParameterReadAccess, ReadRefAccess, @cil_ldarga_s { override string getOpcodeName() { result = "ldarga.s" } override Parameter getTarget() { cil_access(this, result) } } + /** An `starg.s` instruction. */ class Starg_s extends ParameterWriteAccess, @cil_starg_s { override string getOpcodeName() { result = "starg.s" } override Parameter getTarget() { cil_access(this, result) } } - // Fields + /** An `ldfld` instruction. */ class Ldfld extends FieldReadAccess, @cil_ldfld { override string getOpcodeName() { result = "ldfld" } @@ -590,6 +689,7 @@ module Opcodes { override Expr getQualifier() { result = getOperand(0) } } + /** An `ldflda` instruction. */ class Ldflda extends FieldReadAccess, ReadRefAccess, @cil_ldflda { override string getOpcodeName() { result = "ldflda" } @@ -598,6 +698,7 @@ module Opcodes { override Expr getQualifier() { result = getOperand(0) } } + /** An `ldsfld` instruction. */ class Ldsfld extends FieldReadAccess, @cil_ldsfld { override string getOpcodeName() { result = "ldsfld" } @@ -606,6 +707,7 @@ module Opcodes { override Expr getQualifier() { none() } } + /** An `ldsflda` instruction. */ class Ldsflda extends FieldReadAccess, ReadRefAccess, @cil_ldsflda { override string getOpcodeName() { result = "ldsflda" } @@ -614,6 +716,7 @@ module Opcodes { override Expr getQualifier() { none() } } + /** An `stfld` instruction. */ class Stfld extends FieldWriteAccess, @cil_stfld { override string getOpcodeName() { result = "stfld" } @@ -624,6 +727,7 @@ module Opcodes { override Expr getExpr() { result = getOperand(0) } } + /** An `stsfld` instruction. */ class Stsfld extends FieldWriteAccess, @cil_stsfld { override string getOpcodeName() { result = "stsfld" } @@ -634,6 +738,7 @@ module Opcodes { override Expr getExpr() { result = getOperand(0) } } + /** A `newobj` instruction. */ class Newobj extends Call, @cil_newobj { override string getOpcodeName() { result = "newobj" } @@ -656,30 +761,35 @@ module Opcodes { } } + /** An `initobj` instruction. */ class Initobj extends Instruction, @cil_initobj { override string getOpcodeName() { result = "initobj" } override int getPopCount() { result = 1 } // ?? } + /** A `box` instruction. */ class Box extends UnaryExpr, @cil_box { override string getOpcodeName() { result = "box" } override Type getType() { result = getAccess() } } + /** An `unbox.any` instruction. */ class Unbox_any extends UnaryExpr, @cil_unbox_any { override string getOpcodeName() { result = "unbox.any" } override Type getType() { result = getAccess() } } + /** An `unbox` instruction. */ class Unbox extends UnaryExpr, @cil_unbox { override string getOpcodeName() { result = "unbox" } override Type getType() { result = getAccess() } } + /** An `ldobj` instruction. */ class Ldobj extends UnaryExpr, @cil_ldobj { override string getOpcodeName() { result = "ldobj" } @@ -689,6 +799,7 @@ module Opcodes { override Type getType() { result = getAccess() } } + /** An `ldtoken` instruction. */ class Ldtoken extends Expr, @cil_ldtoken { override string getOpcodeName() { result = "ldtoken" } @@ -696,27 +807,31 @@ module Opcodes { override ObjectType getType() { exists(result) } } + /** A `constrained.` instruction. */ class Constrained extends Instruction, @cil_constrained { override string getOpcodeName() { result = "constrained." } } + /** A `throw` instruction. */ class Throw_ extends Throw, @cil_throw { override string getOpcodeName() { result = "throw" } override int getPopCount() { result = 1 } } + /** A `rethrow` instruction. */ class Rethrow extends Throw, @cil_rethrow { override string getOpcodeName() { result = "rethrow" } } + /** A `ldlen` instruction. */ class Ldlen extends UnaryExpr, @cil_ldlen { override string getOpcodeName() { result = "ldlen" } override IntType getType() { exists(result) } } - // Arrays + /** A `newarr` instruction. */ class Newarr extends Expr, @cil_newarr { override string getOpcodeName() { result = "newarr" } @@ -734,449 +849,524 @@ module Opcodes { override string getExtra() { result = getType().getQualifiedName() } } + /** An `ldelem` instruction. */ class Ldelem extends ReadArrayElement, @cil_ldelem { override string getOpcodeName() { result = "ldelem" } override Type getType() { result = getAccess() } } + /** An `ldelem.ref` instruction. */ class Ldelem_ref extends ReadArrayElement, @cil_ldelem_ref { override string getOpcodeName() { result = "ldelem.ref" } override Type getType() { result = getArray().getType() } } + /** An `ldelema` instruction. */ class Ldelema extends ReadArrayElement, ReadRef, @cil_ldelema { override string getOpcodeName() { result = "ldelema" } override Type getType() { result = getAccess() } } + /** An `stelem.ref` instruction. */ class Stelem_ref extends WriteArrayElement, @cil_stelem_ref { override string getOpcodeName() { result = "stelem.ref" } } + /** An `stelem` instruction. */ class Stelem extends WriteArrayElement, @cil_stelem { override string getOpcodeName() { result = "stelem" } } + /** An `stelem.i` instruction. */ class Stelem_i extends WriteArrayElement, @cil_stelem_i { override string getOpcodeName() { result = "stelem.i" } } + /** An `stelem.i1` instruction. */ class Stelem_i1 extends WriteArrayElement, @cil_stelem_i1 { override string getOpcodeName() { result = "stelem.i1" } } + /** An `stelem.i2` instruction. */ class Stelem_i2 extends WriteArrayElement, @cil_stelem_i2 { override string getOpcodeName() { result = "stelem.i2" } } + /** An `stelem.i4` instruction. */ class Stelem_i4 extends WriteArrayElement, @cil_stelem_i4 { override string getOpcodeName() { result = "stelem.i4" } } + /** An `stelem.i8` instruction. */ class Stelem_i8 extends WriteArrayElement, @cil_stelem_i8 { override string getOpcodeName() { result = "stelem.i8" } } + /** An `stelem.r4` instruction. */ class Stelem_r4 extends WriteArrayElement, @cil_stelem_r4 { override string getOpcodeName() { result = "stelem.r4" } } + /** An `stelem.r8` instruction. */ class Stelem_r8 extends WriteArrayElement, @cil_stelem_r8 { override string getOpcodeName() { result = "stelem.r8" } } + /** An `ldelem.i` instruction. */ class Ldelem_i extends ReadArrayElement, @cil_ldelem_i { override string getOpcodeName() { result = "ldelem.i" } override IntType getType() { exists(result) } } + /** An `ldelem.i1` instruction. */ class Ldelem_i1 extends ReadArrayElement, @cil_ldelem_i1 { override string getOpcodeName() { result = "ldelem.i1" } override SByteType getType() { exists(result) } } + /** An `ldelem.i2` instruction. */ class Ldelem_i2 extends ReadArrayElement, @cil_ldelem_i2 { override string getOpcodeName() { result = "ldelem.i2" } override ShortType getType() { exists(result) } } + /** An `ldelem.i4` instruction. */ class Ldelem_i4 extends ReadArrayElement, @cil_ldelem_i4 { override string getOpcodeName() { result = "ldelem.i4" } override IntType getType() { exists(result) } } + /** An `ldelem.i8` instruction. */ class Ldelem_i8 extends ReadArrayElement, @cil_ldelem_i8 { override string getOpcodeName() { result = "ldelem.i8" } override LongType getType() { exists(result) } } + /** An `ldelem.r4` instruction. */ class Ldelem_r4 extends ReadArrayElement, @cil_ldelem_r4 { override string getOpcodeName() { result = "ldelem.r4" } override FloatType getType() { exists(result) } } + /** An `ldelem.r8` instruction. */ class Ldelem_r8 extends ReadArrayElement, @cil_ldelem_r8 { override string getOpcodeName() { result = "ldelem.r8" } override DoubleType getType() { exists(result) } } + /** An `ldelem.u1` instruction. */ class Ldelem_u1 extends ReadArrayElement, @cil_ldelem_u1 { override string getOpcodeName() { result = "ldelem.u1" } override ByteType getType() { exists(result) } } + /** An `ldelem.u2` instruction. */ class Ldelem_u2 extends ReadArrayElement, @cil_ldelem_u2 { override string getOpcodeName() { result = "ldelem.u2" } override UShortType getType() { exists(result) } } + /** An `ldelem.u4` instruction. */ class Ldelem_u4 extends ReadArrayElement, @cil_ldelem_u4 { override string getOpcodeName() { result = "ldelem.u4" } override UIntType getType() { exists(result) } } - // Conversions + /** A `conv.i` instruction. */ class Conv_i extends Conversion, @cil_conv_i { override string getOpcodeName() { result = "conv.i" } override IntType getType() { exists(result) } } + /** A `conv.ovf.i` instruction. */ class Conv_ovf_i extends Conversion, @cil_conv_ovf_i { override string getOpcodeName() { result = "conv.ovf.i" } override IntType getType() { exists(result) } } + /** A `conv.ovf.i.un` instruction. */ class Conv_ovf_i_un extends Conversion, @cil_conv_ovf_i_un { override string getOpcodeName() { result = "conv.ovf.i.un" } override UIntType getType() { exists(result) } } + /** A `conv.i1` instruction. */ class Conv_i1 extends Conversion, @cil_conv_i1 { override string getOpcodeName() { result = "conv.i1" } override SByteType getType() { exists(result) } } + /** A `conv.ovf.i1` instruction. */ class Conv_ovf_i1 extends Conversion, @cil_conv_ovf_i1 { override string getOpcodeName() { result = "conv.ovf.i1" } override SByteType getType() { exists(result) } } + /** A `conv.ovf.i1.un` instruction. */ class Conv_ovf_i1_un extends Conversion, @cil_conv_ovf_i1_un { override string getOpcodeName() { result = "conv.ovf.i1.un" } override SByteType getType() { exists(result) } } + /** A `conv.i2` instruction. */ class Conv_i2 extends Conversion, @cil_conv_i2 { override string getOpcodeName() { result = "conv.i2" } override ShortType getType() { exists(result) } } + /** A `conv.ovf.i2` instruction. */ class Conv_ovf_i2 extends Conversion, @cil_conv_ovf_i2 { override string getOpcodeName() { result = "conv.ovf.i2" } override ShortType getType() { exists(result) } } + /** A `conv.ovf.i2.un` instruction. */ class Conv_ovf_i2_un extends Conversion, @cil_conv_ovf_i2_un { override string getOpcodeName() { result = "conv.ovf.i2.un" } override ShortType getType() { exists(result) } } + /** A `conv.i4` instruction. */ class Conv_i4 extends Conversion, @cil_conv_i4 { override string getOpcodeName() { result = "conv.i4" } override IntType getType() { exists(result) } } + /** A `conv.ovf.i4` instruction. */ class Conv_ovf_i4 extends Conversion, @cil_conv_ovf_i4 { override string getOpcodeName() { result = "conv.ovf.i4" } override IntType getType() { exists(result) } } + /** A `conv.ovf.i4.un` instruction. */ class Conv_ovf_i4_un extends Conversion, @cil_conv_ovf_i4_un { override string getOpcodeName() { result = "conv.ovf.i4.un" } override IntType getType() { exists(result) } } + /** A `conv.i8` instruction. */ class Conv_i8 extends Conversion, @cil_conv_i8 { override string getOpcodeName() { result = "conv.i8" } override LongType getType() { exists(result) } } + /** A `conv.ovf.i8` instruction. */ class Conv_ovf_i8 extends Conversion, @cil_conv_ovf_i8 { override string getOpcodeName() { result = "conv.ovf.i8" } override LongType getType() { exists(result) } } + /** A `conv.ovf.i8.un` instruction. */ class Conv_ovf_i8_un extends Conversion, @cil_conv_ovf_i8_un { override string getOpcodeName() { result = "conv.ovf.i8.un" } override LongType getType() { exists(result) } } - // Unsigned conversions + /** A `conv.u` instruction. */ class Conv_u extends Conversion, @cil_conv_u { override string getOpcodeName() { result = "conv.u" } override UIntType getType() { exists(result) } } + /** A `conv.ovf.u` instruction. */ class Conv_ovf_u extends Conversion, @cil_conv_ovf_u { override string getOpcodeName() { result = "conv.ovf.u" } override UIntType getType() { exists(result) } } + /** A `conv.ovf.u.un` instruction. */ class Conv_ovf_u_un extends Conversion, @cil_conv_ovf_u_un { override string getOpcodeName() { result = "conv.ovf.u.un" } override UIntType getType() { exists(result) } } + /** A `conv.u1` instruction. */ class Conv_u1 extends Conversion, @cil_conv_u1 { override string getOpcodeName() { result = "conv.u1" } override ByteType getType() { exists(result) } } + /** A `conv.ovf.u1` instruction. */ class Conv_ovf_u1 extends Conversion, @cil_conv_ovf_u1 { override string getOpcodeName() { result = "conv.ovf.u1" } override ByteType getType() { exists(result) } } + /** A `conv.ovf.u1.un` instruction. */ class Conv_ovf_u1_un extends Conversion, @cil_conv_ovf_u1_un { override string getOpcodeName() { result = "conv.ovf.u1.un" } override ByteType getType() { exists(result) } } + /** A `conv.u2` instruction. */ class Conv_u2 extends Conversion, @cil_conv_u2 { override string getOpcodeName() { result = "conv.u2" } override UShortType getType() { exists(result) } } + /** A `conv.ovf.u2` instruction. */ class Conv_ovf_u2 extends Conversion, @cil_conv_ovf_u2 { override string getOpcodeName() { result = "conv.ovf.u2" } override UShortType getType() { exists(result) } } + /** A `conv.ovf.u2.un` instruction. */ class Conv_ovf_u2_un extends Conversion, @cil_conv_ovf_u2_un { override string getOpcodeName() { result = "conv.ovf.u2.un" } override UShortType getType() { exists(result) } } + /** A `conv.u4` instruction. */ class Conv_u4 extends Conversion, @cil_conv_u4 { override string getOpcodeName() { result = "conv.u4" } override UIntType getType() { exists(result) } } + /** A `conv.ovf.u4` instruction. */ class Conv_ovf_u4 extends Conversion, @cil_conv_ovf_u4 { override string getOpcodeName() { result = "conv.ovf.u4" } override UIntType getType() { exists(result) } } + /** A `conv.ovf.u4.un` instruction. */ class Conv_ovf_u4_un extends Conversion, @cil_conv_ovf_u4_un { override string getOpcodeName() { result = "conv.ovf.u4.un" } override UIntType getType() { exists(result) } } + /** A `conv.u8` instruction. */ class Conv_u8 extends Conversion, @cil_conv_u8 { override string getOpcodeName() { result = "conv.u8" } override ULongType getType() { exists(result) } } + /** A `conv.ovf.u8` instruction. */ class Conv_ovf_u8 extends Conversion, @cil_conv_ovf_u8 { override string getOpcodeName() { result = "conv.ovf.u8" } override ULongType getType() { exists(result) } } + /** A `conv.ovf.u8.un` instruction. */ class Conv_ovf_u8_un extends Conversion, @cil_conv_ovf_u8_un { override string getOpcodeName() { result = "conv.ovf.u8.un" } override ULongType getType() { exists(result) } } - // Floating point conversions + /** A `conv.r4` instruction. */ class Conv_r4 extends Conversion, @cil_conv_r4 { override string getOpcodeName() { result = "conv.r4" } override FloatType getType() { exists(result) } } + /** A `conv.r8` instruction. */ class Conv_r8 extends Conversion, @cil_conv_r8 { override string getOpcodeName() { result = "conv.r8" } override DoubleType getType() { exists(result) } } + /** A `conv.r.un` instruction. */ class Conv_r_un extends Conversion, @cil_conv_r_un { override string getOpcodeName() { result = "conv.r.un" } override DoubleType getType() { exists(result) } // ?? } + /** A `volatile.` instruction. */ class Volatile extends Instruction, @cil_volatile { override string getOpcodeName() { result = "volatile." } } - // Indirections + /** An `ldind.i` instruction. */ class Ldind_i extends LoadIndirect, @cil_ldind_i { override string getOpcodeName() { result = "ldind.i" } override IntType getType() { exists(result) } } + /** An `ldind.i1` instruction. */ class Ldind_i1 extends LoadIndirect, @cil_ldind_i1 { override string getOpcodeName() { result = "ldind.i1" } override SByteType getType() { exists(result) } } + /** An `ldind.i2` instruction. */ class Ldind_i2 extends LoadIndirect, @cil_ldind_i2 { override string getOpcodeName() { result = "ldind.i2" } override ShortType getType() { exists(result) } } + /** An `ldind.i4` instruction. */ class Ldind_i4 extends LoadIndirect, @cil_ldind_i4 { override string getOpcodeName() { result = "ldind.i4" } override IntType getType() { exists(result) } } + /** An `ldind.i8` instruction. */ class Ldind_i8 extends LoadIndirect, @cil_ldind_i8 { override string getOpcodeName() { result = "ldind.i8" } override LongType getType() { exists(result) } } + /** An `ldind.r4` instruction. */ class Ldind_r4 extends LoadIndirect, @cil_ldind_r4 { override string getOpcodeName() { result = "ldind.r4" } override FloatType getType() { exists(result) } } + /** An `ldind.r8` instruction. */ class Ldind_r8 extends LoadIndirect, @cil_ldind_r8 { override string getOpcodeName() { result = "ldind.r8" } override DoubleType getType() { exists(result) } } + /** An `ldind.ref` instruction. */ class Ldind_ref extends LoadIndirect, @cil_ldind_ref { override string getOpcodeName() { result = "ldind.ref" } override ObjectType getType() { exists(result) } } + /** An `ldind.u1` instruction. */ class Ldind_u1 extends LoadIndirect, @cil_ldind_u1 { override string getOpcodeName() { result = "ldind.u1" } override ByteType getType() { exists(result) } } + /** An `ldind.u2` instruction. */ class Ldind_u2 extends LoadIndirect, @cil_ldind_u2 { override string getOpcodeName() { result = "ldind.u2" } override UShortType getType() { exists(result) } } + /** An `ldind.u4` instruction. */ class Ldind_u4 extends LoadIndirect, @cil_ldind_u4 { override string getOpcodeName() { result = "ldind.u4" } override UIntType getType() { exists(result) } } + /** An `stind.i` instruction. */ class Stind_i extends StoreIndirect, @cil_stind_i { override string getOpcodeName() { result = "stind.i" } } + /** An `stind.i1` instruction. */ class Stind_i1 extends StoreIndirect, @cil_stind_i1 { override string getOpcodeName() { result = "stind.i1" } } + /** An `stind.i2` instruction. */ class Stind_i2 extends StoreIndirect, @cil_stind_i2 { override string getOpcodeName() { result = "stind.i2" } } + /** An `stind.i4` instruction. */ class Stind_i4 extends StoreIndirect, @cil_stind_i4 { override string getOpcodeName() { result = "stind.i4" } } + /** An `stind.i8` instruction. */ class Stind_i8 extends StoreIndirect, @cil_stind_i8 { override string getOpcodeName() { result = "stind.i8" } } + /** An `stind.r4` instruction. */ class Stind_r4 extends StoreIndirect, @cil_stind_r4 { override string getOpcodeName() { result = "stind.r4" } } + /** An `stind.r8` instruction. */ class Stind_r8 extends StoreIndirect, @cil_stind_r8 { - override string getOpcodeName() { result = "stind.r4" } + override string getOpcodeName() { result = "stind.r8" } } + /** An `stind.ref` instruction. */ class Stind_ref extends StoreIndirect, @cil_stind_ref { override string getOpcodeName() { result = "stind.ref" } } - // Miscellaneous + /** An `stobj` instruction. */ class Stobj extends Instruction, @cil_stobj { override string getOpcodeName() { result = "stobj" } override int getPopCount() { result = 2 } } + /** An `ldftn` instruction. */ class Ldftn extends Expr, @cil_ldftn { override string getOpcodeName() { result = "ldftn" } override int getPopCount() { result = 0 } } + /** An `ldvirtftn` instruction. */ class Ldvirtftn extends Expr, @cil_ldvirtftn { override string getOpcodeName() { result = "ldvirtftn" } override int getPopCount() { result = 1 } } + /** A `sizeof` instruction. */ class Sizeof extends Expr, @cil_sizeof { override string getOpcodeName() { result = "sizeof" } override IntType getType() { exists(result) } } + /** A `localloc` instruction. */ class Localloc extends Expr, @cil_localloc { override string getOpcodeName() { result = "localloc" } @@ -1185,10 +1375,12 @@ module Opcodes { override PointerType getType() { result.getReferentType() instanceof ByteType } } + /** A `readonly.` instruction. */ class Readonly extends Instruction, @cil_readonly { override string getOpcodeName() { result = "readonly." } } + /** A `mkrefany` instruction. */ class Mkrefany extends Expr, @cil_mkrefany { override string getOpcodeName() { result = "mkrefany" } @@ -1197,6 +1389,7 @@ module Opcodes { override Type getType() { result = getAccess() } } + /** A `refanytype` instruction. */ class Refanytype extends Expr, @cil_refanytype { override string getOpcodeName() { result = "refanytype" } @@ -1205,6 +1398,7 @@ module Opcodes { override SystemType getType() { exists(result) } } + /** An `arglist` instruction. */ class Arglist extends Expr, @cil_arglist { override string getOpcodeName() { result = "arglist" } } diff --git a/csharp/ql/src/semmle/code/csharp/AnnotatedType.qll b/csharp/ql/src/semmle/code/csharp/AnnotatedType.qll index 413e04f50177..0e5c512057a7 100644 --- a/csharp/ql/src/semmle/code/csharp/AnnotatedType.qll +++ b/csharp/ql/src/semmle/code/csharp/AnnotatedType.qll @@ -7,6 +7,7 @@ */ import csharp +private import TypeRef private module Annotations { newtype TAnnotation = diff --git a/csharp/ql/src/semmle/code/csharp/Assignable.qll b/csharp/ql/src/semmle/code/csharp/Assignable.qll index 3dbcd7cbdc3e..42ae43c37481 100644 --- a/csharp/ql/src/semmle/code/csharp/Assignable.qll +++ b/csharp/ql/src/semmle/code/csharp/Assignable.qll @@ -29,8 +29,10 @@ class Assignable extends Declaration, @assignable { * An assignable that is also a member. Either a field (`Field`), a * property (`Property`), an indexer (`Indexer`), or an event (`Event`). */ -class AssignableMember extends Member, Assignable { +class AssignableMember extends Member, Assignable, Attributable { override AssignableMemberAccess getAnAccess() { result = Assignable.super.getAnAccess() } + + override string toString() { result = Assignable.super.toString() } } /** @@ -55,7 +57,7 @@ private predicate nameOfChild(NameOfExpr noe, Expr child) { * * For example, the last occurrence of `Length` in * - * ``` + * ```csharp * class C { * int Length; * @@ -89,7 +91,7 @@ class AssignableRead extends AssignableAccess { * that can be reached from this read without passing through any other reads, * and which is guaranteed to read the same value. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -131,7 +133,7 @@ class AssignableRead extends AssignableAccess { * * For example, the last occurrence of `Length` in * - * ``` + * ```csharp * class C { * int Length; * @@ -454,7 +456,7 @@ class AssignableDefinition extends TAssignableDefinition { * reads, and which is guaranteed to read the value assigned in this * definition. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -720,7 +722,7 @@ module AssignableDefinitions { * An initializer definition for a field or a property, for example * line 2 in * - * ``` + * ```csharp * class C { * int Field = 0; * } diff --git a/csharp/ql/src/semmle/code/csharp/Attribute.qll b/csharp/ql/src/semmle/code/csharp/Attribute.qll index a7d7729903a5..9ca168192a19 100644 --- a/csharp/ql/src/semmle/code/csharp/Attribute.qll +++ b/csharp/ql/src/semmle/code/csharp/Attribute.qll @@ -4,6 +4,7 @@ import Type private import semmle.code.csharp.ExprOrStmtParent +private import TypeRef /** * An element that can have attributes. Either an assembly (`Assembly`), a field (`Field`), @@ -38,7 +39,7 @@ class Attributable extends @attributable { /** * An attribute, for example `[...]` on line 1 in * - * ``` + * ```csharp * [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] * public static extern int GetFinalPathNameByHandle( * SafeHandle handle, @@ -64,7 +65,7 @@ class Attribute extends TopLevelExprParent, @attribute { * Gets the `i`th constructor argument of this attribute. For example, only * `true` is a constructor argument in * - * ``` + * ```csharp * MyAttribute[true, Foo = 0] * ``` */ @@ -76,7 +77,7 @@ class Attribute extends TopLevelExprParent, @attribute { * Gets the named argument `name` of this attribute. For example, only * `0` is a named argument in * - * ``` + * ```csharp * MyAttribute[true, Foo = 0] * ``` */ @@ -93,4 +94,6 @@ class Attribute extends TopLevelExprParent, @attribute { result = "[" + name + "(...)]" ) } + + override string getAPrimaryQlClass() { result = "Attribute" } } diff --git a/csharp/ql/src/semmle/code/csharp/Caching.qll b/csharp/ql/src/semmle/code/csharp/Caching.qll index 4443ec9bc7c8..95ffbf9ffc91 100644 --- a/csharp/ql/src/semmle/code/csharp/Caching.qll +++ b/csharp/ql/src/semmle/code/csharp/Caching.qll @@ -39,7 +39,7 @@ module Stages { cached private predicate forceCachingInSameStageRev() { - any(ControlFlowElement cfe).controlsBlock(_, _) + any(ControlFlowElement cfe).controlsBlock(_, _, _) or exists(GuardedExpr ge) or @@ -58,7 +58,7 @@ module Stages { cached private predicate forceCachingInSameStageRev() { - localAdditionalTaintStep(_, _) + defaultAdditionalTaintStep(_, _) or any(ArgumentNode n).argumentOf(_, _) or @@ -68,7 +68,7 @@ module Stages { or exists(any(DataFlow::Node n).getType()) or - exists(any(DataFlow::Node n).getTypeBound()) + exists(any(NodeImpl n).getDataFlowType()) or exists(any(DataFlow::Node n).getLocation()) or diff --git a/csharp/ql/src/semmle/code/csharp/Callable.qll b/csharp/ql/src/semmle/code/csharp/Callable.qll index 2ed9e5cf03a4..2153524ac9e4 100644 --- a/csharp/ql/src/semmle/code/csharp/Callable.qll +++ b/csharp/ql/src/semmle/code/csharp/Callable.qll @@ -3,13 +3,14 @@ * such as methods and operators. */ -import Type import Member import Stmt +import Type import exprs.Call private import dotnet private import semmle.code.csharp.ExprOrStmtParent private import semmle.code.csharp.metrics.Complexity +private import TypeRef /** * An element that can be called. @@ -31,45 +32,42 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * Gets the body of this callable, if any. * * The body is either a `BlockStmt` or an `Expr`. - */ - final ControlFlowElement getBody() { - result = this.getStatementBody() or - result = this.getExpressionBody() - } - - /** - * Gets a body of this callable, if any. * - * Unlike `getBody()`, this predicate may return multiple bodies, in the case - * where the same callable is compiled multiple times. For example, if we - * compile both `A.cs` + * Normally, each callable will have at most one body, except in the case where + * the same callable is compiled multiple times. For example, if we compile + * both `A.cs` * - * ``` + * ```csharp * namespaces N { * public class C { - * public int M() => 0; + * public int M() { return 0; } * } * } * ``` * * and later `B.cs` * - * ``` + * ```csharp * namespaces N { * public class C { - * public int M() { return 1; } + * public int M() => 1; * } * } * ``` * - * to the same assembly, then both `0` and `{ return 1; }` are bodies of `N.C.M()`. + * then both `{ return 0; }` and `1` are bodies of `N.C.M()`. */ - final ControlFlowElement getABody() { - result = this.getAStatementBody() or - result = this.getAnExpressionBody() + final ControlFlowElement getBody() { + result = this.getStatementBody() or + result = this.getExpressionBody() } - override predicate hasBody() { exists(getBody()) } + /** + * DEPRECATED: Use `getBody()` instead. + */ + deprecated final ControlFlowElement getABody() { result = this.getBody() } + + override predicate hasBody() { exists(this.getBody()) } /** * Holds if this callable has a non-empty body. That is, either it has @@ -78,21 +76,17 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal predicate hasNonEmptyBody() { this.hasExpressionBody() or - this.hasStatementBody() and - not this.getStatementBody().stripSingletonBlocks().(BlockStmt).isEmpty() + this.getStatementBody().stripSingletonBlocks() = any(Stmt s | not s.(BlockStmt).isEmpty()) } - /** Gets the statement body of this callable, if any. */ - final BlockStmt getStatementBody() { result = this.getAChildStmt() } - /** - * Gets a statement body of this callable, if any. + * Gets the statement body of this callable, if any. * - * Unlike `getStatementBody()`, this predicate may return multiple bodies, in - * the case where the same callable is compiled multiple times. For example, - * if we compile both `A.cs` + * Normally, each callable will have at most one statement body, except in the + * case where the same callable is compiled multiple times. For example, if + * we compile both `A.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() { return 0; } @@ -102,7 +96,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * * and later `B.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() { return 1; } @@ -110,25 +104,27 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * } * ``` * - * to the same assembly, then both `{ return 0; }` and `{ return 1; }` are - * statement bodies of `N.C.M()`. + * then both `{ return 0; }` and `{ return 1; }` are statement bodies of + * `N.C.M()`. */ - final BlockStmt getAStatementBody() { stmt_parent_top_level(result, _, this) } + final BlockStmt getStatementBody() { result = this.getAChildStmt() } + + /** + * DEPRECATED: Use `getStatementBody` instead. + */ + final BlockStmt getAStatementBody() { result = this.getStatementBody() } /** Holds if this callable has a statement body. */ final predicate hasStatementBody() { exists(getStatementBody()) } - /** Gets the expression body of this callable (if any), specified by `=>`. */ - final Expr getExpressionBody() { result = this.getChildExpr(0) } - /** - * Gets an expression body of this callable (if any), specified by `=>`. + * Gets the expression body of this callable (if any), specified by `=>`. * - * Unlike `getExpressionBody()`, this predicate may return multiple bodies, in - * the case where the same callable is compiled multiple times. For example, - * if we compile both `A.cs` + * Normally, each callable will have at most one expression body, except in the + * case where the same callable is compiled multiple times. For example, if + * we compile both `A.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() => 0; @@ -138,7 +134,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * * and later `B.cs` * - * ``` + * ```csharp * namespaces N { * public class C { * public int M() => 1; @@ -146,9 +142,17 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal * } * ``` * - * to the same assembly, then both `0` and `1` are expression bodies of `N.C.M()`. + * then both `0` and `1` are expression bodies of `N.C.M()`. */ - final Expr getAnExpressionBody() { expr_parent_top_level_adjusted(result, 0, this) } + final Expr getExpressionBody() { + result = this.getAChildExpr() and + not result = this.(Constructor).getInitializer() + } + + /** + * DEPRECATED: Use `getExpressionBody()` instead. + */ + deprecated final Expr getAnExpressionBody() { result = this.getExpressionBody() } /** Holds if this callable has an expression body. */ final predicate hasExpressionBody() { exists(getExpressionBody()) } @@ -223,7 +227,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal /** * A method, for example * - * ``` + * ```csharp * public override bool Equals(object other) { * ... * } @@ -284,12 +288,14 @@ class Method extends Callable, Virtualizable, Attributable, @method { override Parameter getRawParameter(int i) { if this.isStatic() then result = this.getParameter(i) else result = this.getParameter(i - 1) } + + override string getAPrimaryQlClass() { result = "Method" } } /** * An extension method, for example * - * ``` + * ```csharp * static bool IsDefined(this Widget w) { * ... * } @@ -302,12 +308,14 @@ class ExtensionMethod extends Method { /** Gets the type being extended by this method. */ Type getExtendedType() { result = getParameter(0).getType() } + + override string getAPrimaryQlClass() { result = "ExtensionMethod" } } /** * A constructor, for example `public C() { }` on line 2 in * - * ``` + * ```csharp * class C { * public C() { } * } @@ -326,7 +334,7 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @ * the initializer of the constructor on line 2 is `this(null)` * on line 3 in * - * ``` + * ```csharp * class C { * public C() * : this(null) { ... } @@ -361,7 +369,7 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @ * A static constructor (as opposed to an instance constructor), * for example `static public C() { }` on line 2 in * - * ``` + * ```csharp * class C { * static public C() { } * } @@ -371,13 +379,15 @@ class StaticConstructor extends Constructor { StaticConstructor() { this.isStatic() } override string getUndecoratedName() { result = ".cctor" } + + override string getAPrimaryQlClass() { result = "StaticConstructor" } } /** * An instance constructor (as opposed to a static constructor), * for example `public C() { }` on line 2 in * - * ``` + * ```csharp * class C { * public C() { } * } @@ -385,12 +395,14 @@ class StaticConstructor extends Constructor { */ class InstanceConstructor extends Constructor { InstanceConstructor() { not this.isStatic() } + + override string getAPrimaryQlClass() { result = "InstanceConstructor" } } /** * A destructor, for example `~C() { }` on line 2 in * - * ``` + * ```csharp * class C { * ~C() { } * } @@ -413,6 +425,8 @@ class Destructor extends DotNet::Destructor, Callable, Member, Attributable, @de override Location getALocation() { destructor_location(this, result) } override string toString() { result = Callable.super.toString() } + + override string getAPrimaryQlClass() { result = "Destructor" } } /** @@ -427,6 +441,9 @@ class Operator extends Callable, Member, Attributable, @operator { override string getName() { operators(this, _, result, _, _, _) } + /** + * Gets the metadata name of the operator, such as `op_implicit` or `op_RightShift`. + */ string getFunctionName() { none() } override ValueOrRefType getDeclaringType() { operators(this, _, _, result, _, _) } @@ -461,7 +478,7 @@ class UnaryOperator extends Operator { /** * A user-defined plus operator (`+`), for example * - * ``` + * ```csharp * public static Widget operator +(Widget w) { * ... * } @@ -471,12 +488,14 @@ class PlusOperator extends UnaryOperator { PlusOperator() { this.getName() = "+" } override string getFunctionName() { result = "op_UnaryPlus" } + + override string getAPrimaryQlClass() { result = "PlusOperator" } } /** * A user-defined minus operator (`-`), for example * - * ``` + * ```csharp * public static Widget operator -(Widget w) { * ... * } @@ -486,12 +505,14 @@ class MinusOperator extends UnaryOperator { MinusOperator() { this.getName() = "-" } override string getFunctionName() { result = "op_UnaryNegation" } + + override string getAPrimaryQlClass() { result = "MinusOperator" } } /** * A user-defined not operator (`!`), for example * - * ``` + * ```csharp * public static bool operator !(Widget w) { * ... * } @@ -501,12 +522,14 @@ class NotOperator extends UnaryOperator { NotOperator() { this.getName() = "!" } override string getFunctionName() { result = "op_LogicalNot" } + + override string getAPrimaryQlClass() { result = "NotOperator" } } /** * A user-defined complement operator (`~`), for example * - * ``` + * ```csharp * public static Widget operator ~(Widget w) { * ... * } @@ -516,12 +539,14 @@ class ComplementOperator extends UnaryOperator { ComplementOperator() { this.getName() = "~" } override string getFunctionName() { result = "op_OnesComplement" } + + override string getAPrimaryQlClass() { result = "ComplementOperator" } } /** * A user-defined increment operator (`++`), for example * - * ``` + * ```csharp * public static Widget operator ++(Widget w) { * ... * } @@ -531,12 +556,14 @@ class IncrementOperator extends UnaryOperator { IncrementOperator() { this.getName() = "++" } override string getFunctionName() { result = "op_Increment" } + + override string getAPrimaryQlClass() { result = "IncrementOperator" } } /** * A user-defined decrement operator (`--`), for example * - * ``` + * ```csharp * public static Widget operator --(Widget w) { * ... * } @@ -546,12 +573,14 @@ class DecrementOperator extends UnaryOperator { DecrementOperator() { this.getName() = "--" } override string getFunctionName() { result = "op_Decrement" } + + override string getAPrimaryQlClass() { result = "DecrementOperator" } } /** * A user-defined false operator (`false`), for example * - * ``` + * ```csharp * public static bool operator false(Widget w) { * ... * } @@ -561,12 +590,14 @@ class FalseOperator extends UnaryOperator { FalseOperator() { this.getName() = "false" } override string getFunctionName() { result = "op_False" } + + override string getAPrimaryQlClass() { result = "FalseOperator" } } /** * A user-defined true operator (`true`), for example * - * ``` + * ```csharp * public static bool operator true(Widget w) { * ... * } @@ -576,6 +607,8 @@ class TrueOperator extends UnaryOperator { TrueOperator() { this.getName() = "true" } override string getFunctionName() { result = "op_True" } + + override string getAPrimaryQlClass() { result = "TrueOperator" } } /** @@ -598,7 +631,7 @@ class BinaryOperator extends Operator { /** * A user-defined addition operator (`+`), for example * - * ``` + * ```csharp * public static Widget operator +(Widget lhs, Widget rhs) { * ... * } @@ -608,12 +641,14 @@ class AddOperator extends BinaryOperator { AddOperator() { this.getName() = "+" } override string getFunctionName() { result = "op_Addition" } + + override string getAPrimaryQlClass() { result = "AddOperator" } } /** * A user-defined subtraction operator (`-`), for example * - * ``` + * ```csharp * public static Widget operator -(Widget lhs, Widget rhs) { * ... * } @@ -623,12 +658,14 @@ class SubOperator extends BinaryOperator { SubOperator() { this.getName() = "-" } override string getFunctionName() { result = "op_Subtraction" } + + override string getAPrimaryQlClass() { result = "SubOperator" } } /** * A user-defined multiplication operator (`*`), for example * - * ``` + * ```csharp * public static Widget operator *(Widget lhs, Widget rhs) { * ... * } @@ -638,12 +675,14 @@ class MulOperator extends BinaryOperator { MulOperator() { this.getName() = "*" } override string getFunctionName() { result = "op_Multiply" } + + override string getAPrimaryQlClass() { result = "MulOperator" } } /** * A user-defined division operator (`/`), for example * - * ``` + * ```csharp * public static Widget operator /(Widget lhs, Widget rhs) { * ... * } @@ -653,12 +692,14 @@ class DivOperator extends BinaryOperator { DivOperator() { this.getName() = "/" } override string getFunctionName() { result = "op_Division" } + + override string getAPrimaryQlClass() { result = "DivOperator" } } /** * A user-defined remainder operator (`%`), for example * - * ``` + * ```csharp * public static Widget operator %(Widget lhs, Widget rhs) { * ... * } @@ -668,12 +709,14 @@ class RemOperator extends BinaryOperator { RemOperator() { this.getName() = "%" } override string getFunctionName() { result = "op_Modulus" } + + override string getAPrimaryQlClass() { result = "RemOperator" } } /** * A user-defined and operator (`&`), for example * - * ``` + * ```csharp * public static Widget operator &(Widget lhs, Widget rhs) { * ... * } @@ -683,12 +726,14 @@ class AndOperator extends BinaryOperator { AndOperator() { this.getName() = "&" } override string getFunctionName() { result = "op_BitwiseAnd" } + + override string getAPrimaryQlClass() { result = "AndOperator" } } /** * A user-defined or operator (`|`), for example * - * ``` + * ```csharp * public static Widget operator |(Widget lhs, Widget rhs) { * ... * } @@ -698,12 +743,14 @@ class OrOperator extends BinaryOperator { OrOperator() { this.getName() = "|" } override string getFunctionName() { result = "op_BitwiseOr" } + + override string getAPrimaryQlClass() { result = "OrOperator" } } /** * A user-defined xor operator (`^`), for example * - * ``` + * ```csharp * public static Widget operator ^(Widget lhs, Widget rhs) { * ... * } @@ -713,12 +760,14 @@ class XorOperator extends BinaryOperator { XorOperator() { this.getName() = "^" } override string getFunctionName() { result = "op_ExclusiveOr" } + + override string getAPrimaryQlClass() { result = "XorOperator" } } /** * A user-defined left shift operator (`<<`), for example * - * ``` + * ```csharp * public static Widget operator <<(Widget lhs, Widget rhs) { * ... * } @@ -728,12 +777,14 @@ class LShiftOperator extends BinaryOperator { LShiftOperator() { this.getName() = "<<" } override string getFunctionName() { result = "op_LeftShift" } + + override string getAPrimaryQlClass() { result = "LShiftOperator" } } /** * A user-defined right shift operator (`>>`), for example * - * ``` + * ```csharp * public static Widget operator >>(Widget lhs, Widget rhs) { * ... * } @@ -743,12 +794,14 @@ class RShiftOperator extends BinaryOperator { RShiftOperator() { this.getName() = ">>" } override string getFunctionName() { result = "op_RightShift" } + + override string getAPrimaryQlClass() { result = "RShiftOperator" } } /** * A user-defined equals operator (`==`), for example * - * ``` + * ```csharp * public static bool operator ==(Widget lhs, Widget rhs) { * ... * } @@ -758,12 +811,14 @@ class EQOperator extends BinaryOperator { EQOperator() { this.getName() = "==" } override string getFunctionName() { result = "op_Equality" } + + override string getAPrimaryQlClass() { result = "EQOperator" } } /** * A user-defined not equals operator (`!=`), for example * - * ``` + * ```csharp * public static bool operator !=(Widget lhs, Widget rhs) { * ... * } @@ -773,12 +828,14 @@ class NEOperator extends BinaryOperator { NEOperator() { this.getName() = "!=" } override string getFunctionName() { result = "op_Inequality" } + + override string getAPrimaryQlClass() { result = "NEOperator" } } /** * A user-defined lesser than operator (`<`), for example * - * ``` + * ```csharp * public static bool operator <(Widget lhs, Widget rhs) { * ... * } @@ -788,12 +845,14 @@ class LTOperator extends BinaryOperator { LTOperator() { this.getName() = "<" } override string getFunctionName() { result = "op_LessThan" } + + override string getAPrimaryQlClass() { result = "LTOperator" } } /** * A user-defined greater than operator (`>`), for example * - * ``` + * ```csharp * public static bool operator >(Widget lhs, Widget rhs) { * ... * } @@ -803,12 +862,14 @@ class GTOperator extends BinaryOperator { GTOperator() { this.getName() = ">" } override string getFunctionName() { result = "op_GreaterThan" } + + override string getAPrimaryQlClass() { result = "GTOperator" } } /** * A user-defined less than or equals operator (`<=`), for example * - * ``` + * ```csharp * public static bool operator <=(Widget lhs, Widget rhs) { * ... * } @@ -818,12 +879,14 @@ class LEOperator extends BinaryOperator { LEOperator() { this.getName() = "<=" } override string getFunctionName() { result = "op_LessThanOrEqual" } + + override string getAPrimaryQlClass() { result = "LEOperator" } } /** * A user-defined greater than or equals operator (`>=`), for example * - * ``` + * ```csharp * public static bool operator >=(Widget lhs, Widget rhs) { * ... * } @@ -833,12 +896,14 @@ class GEOperator extends BinaryOperator { GEOperator() { this.getName() = ">=" } override string getFunctionName() { result = "op_GreaterThanOrEqual" } + + override string getAPrimaryQlClass() { result = "GEOperator" } } /** * A user-defined conversion operator, for example * - * ``` + * ```csharp * public static implicit operator int(BigInteger i) { * ... * } @@ -860,7 +925,7 @@ class ConversionOperator extends Operator { /** * A user-defined implicit conversion operator, for example * - * ``` + * ```csharp * public static implicit operator int(BigInteger i) { * ... * } @@ -870,12 +935,14 @@ class ImplicitConversionOperator extends ConversionOperator { ImplicitConversionOperator() { this.getName() = "implicit conversion" } override string getFunctionName() { result = "op_Implicit" } + + override string getAPrimaryQlClass() { result = "ImplicitConversionOperator" } } /** * A user-defined explicit conversion operator, for example * - * ``` + * ```csharp * public static explicit operator int(BigInteger i) { * ... * } @@ -885,13 +952,15 @@ class ExplicitConversionOperator extends ConversionOperator { ExplicitConversionOperator() { this.getName() = "explicit conversion" } override string getFunctionName() { result = "op_Explicit" } + + override string getAPrimaryQlClass() { result = "ExplicitConversionOperator" } } /** * A local function, defined within the scope of another callable. * For example, `Fac` on lines 2--4 in * - * ``` + * ```csharp * int Choose(int n, int m) { * int Fac(int x) { * return x > 1 ? x * Fac(x - 1) : 1; @@ -923,4 +992,6 @@ class LocalFunction extends Callable, Modifiable, @local_function { override Location getALocation() { result = getStatement().getALocation() } override Parameter getRawParameter(int i) { result = getParameter(i) } + + override string getAPrimaryQlClass() { result = "LocalFunction" } } diff --git a/csharp/ql/src/semmle/code/csharp/Comments.qll b/csharp/ql/src/semmle/code/csharp/Comments.qll index 1e81fb1fc1c9..41f4e5b0be89 100644 --- a/csharp/ql/src/semmle/code/csharp/Comments.qll +++ b/csharp/ql/src/semmle/code/csharp/Comments.qll @@ -34,7 +34,7 @@ class CommentLine extends @commentline { /** * A single-line comment, for example line 1 in * - * ``` + * ```csharp * // This method returns the successor of its argument * public int Succ(int x) => x + 1; * ``` @@ -47,7 +47,7 @@ class SinglelineComment extends CommentLine, @singlelinecomment { * A line of comment in a multiline style, for example each of the * lines in * - * ``` + * ```csharp * /* This is * a comment * / * ``` @@ -60,7 +60,7 @@ class MultilineComment extends CommentLine, @multilinecomment { * A line of XML documentation comment, for example each of the * lines in * - * ``` + * ```csharp * /// * /// This method ... * /// @@ -148,7 +148,7 @@ class XmlComment extends CommentLine, @xmldoccomment { /** * A collection of adjacent comment lines, for example * - * ``` + * ```csharp * /// * /// Represents a named tuple. * /// diff --git a/csharp/ql/src/semmle/code/csharp/Conversion.qll b/csharp/ql/src/semmle/code/csharp/Conversion.qll index 1bfc1eb8f615..75c24c896572 100644 --- a/csharp/ql/src/semmle/code/csharp/Conversion.qll +++ b/csharp/ql/src/semmle/code/csharp/Conversion.qll @@ -362,7 +362,7 @@ private module Identity { IdentityConvertibleGenericType fromType, IdentityConvertibleGenericType toType ) { // Semantically equivalent with - // ``` + // ```ql // ugt = fromType.getUnboundGeneric() // and // forex(int i | @@ -514,6 +514,11 @@ predicate convNullableType(ValueOrRefType fromType, NullableType toType) { ) } +/** + * Holds if `fromType` is `NullType`, and `toType` is a type that can represent + * the `null` value, such as a reference type, `Nullable` or a type parameter + * with contraints that restrict it to a reference type. + */ // This is a deliberate, small Cartesian product, so we have manually lifted it to force the // evaluator to evaluate it in its entirety, rather than trying to optimize it in context. pragma[noinline] @@ -773,7 +778,7 @@ predicate convConversionOperator(Type fromType, Type toType) { /** 13.1.3.2: Variance conversion. */ private predicate convVariance(GenericType fromType, GenericType toType) { // Semantically equivalent with - // ``` + // ```ql // ugt = fromType.getUnboundGeneric() // and // forex(int i | diff --git a/csharp/ql/src/semmle/code/csharp/Element.qll b/csharp/ql/src/semmle/code/csharp/Element.qll index 521138db1b50..f9ebbc5bef90 100644 --- a/csharp/ql/src/semmle/code/csharp/Element.qll +++ b/csharp/ql/src/semmle/code/csharp/Element.qll @@ -48,4 +48,17 @@ class Element extends DotNet::Element, @element { * other children (zero-based). */ int getIndex() { exists(Element parent | parent.getChild(result) = this) } + + /** + * 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 always has a result. If no primary class can be + * determined, the result is `"???"`. If multiple primary classes match, + * this predicate can have multiple results. + */ + string getAPrimaryQlClass() { result = "???" } } diff --git a/csharp/ql/src/semmle/code/csharp/Event.qll b/csharp/ql/src/semmle/code/csharp/Event.qll index 38f89428b6f4..b2c624544fe2 100644 --- a/csharp/ql/src/semmle/code/csharp/Event.qll +++ b/csharp/ql/src/semmle/code/csharp/Event.qll @@ -4,11 +4,12 @@ import Member import Type +private import TypeRef /** * An event, for example `E` on line 3 in * - * ``` + * ```csharp * class C { * delegate void D(); * public event D E; @@ -61,13 +62,15 @@ class Event extends DeclarationWithAccessors, @event { } override Location getALocation() { event_location(this, result) } + + override string getAPrimaryQlClass() { result = "Event" } } /** * An event accessor, for example `add` on line 4 or `remove` * on line 5 in * - * ``` + * ```csharp * class C { * delegate void D(); * public event D E { @@ -95,7 +98,7 @@ class EventAccessor extends Accessor, @event_accessor { /** * An add event accessor, for example `add` on line 4 in * - * ``` + * ```csharp * class C { * delegate void D(); * public event D E { @@ -107,12 +110,14 @@ class EventAccessor extends Accessor, @event_accessor { */ class AddEventAccessor extends EventAccessor, @add_event_accessor { override string getName() { result = "add" + "_" + getDeclaration().getName() } + + override string getAPrimaryQlClass() { result = "AddEventAccessor" } } /** * A remove event accessor, for example `remove` on line 5 in * - * ``` + * ```csharp * class C { * delegate void D(); * public event D E { @@ -124,4 +129,6 @@ class AddEventAccessor extends EventAccessor, @add_event_accessor { */ class RemoveEventAccessor extends EventAccessor, @remove_event_accessor { override string getName() { result = "remove" + "_" + getDeclaration().getName() } + + override string getAPrimaryQlClass() { result = "RemoveEventAccessor" } } diff --git a/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll b/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll index 0cceb3cb8c24..80029612a7c5 100644 --- a/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll +++ b/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll @@ -6,74 +6,6 @@ import csharp -/** - * INTERNAL: Do not use. - * - * The `expr_parent_top_level()` relation extended to include a relation - * between getters and expression bodies in properties such as `int P => 0`. - */ -predicate expr_parent_top_level_adjusted(Expr child, int i, @top_level_exprorstmt_parent parent) { - expr_parent_top_level(child, i, parent) - or - parent = any(Getter g | expr_parent_top_level(child, i, g.getDeclaration())) and - i = 0 -} - -/** - * The `expr_parent()` relation adjusted for expandable assignments. For example, - * the assignment `x += y` is extracted as - * - * ``` - * += - * | - * 2 - * | - * = - * / \ - * 1 0 - * / \ - * x + - * / \ - * 1 0 - * / \ - * x y - * ``` - * - * in order to be able to retrieve the expanded assignment `x = x + y` as the 2nd - * child. This predicate changes the diagram above into - * - * ``` - * += - * / \ - * 1 0 - * / \ - * x y - * ``` - */ -private predicate expr_parent_adjusted(Expr child, int i, ControlFlowElement parent) { - if parent instanceof AssignOperation - then - parent = - any(AssignOperation ao | - exists(AssignExpr ae | ae = ao.getExpandedAssignment() | - i = 0 and - exists(Expr right | - // right = `x + y` - expr_parent(right, 0, ae) - | - expr_parent(child, 1, right) - ) - or - i = 1 and - expr_parent(child, 1, ae) - ) - or - not ao.hasExpandedAssignment() and - expr_parent(child, i, parent) - ) - else expr_parent(child, i, parent) -} - /** * INTERNAL: Do not use. * @@ -88,7 +20,7 @@ class ExprOrStmtParent extends Element, @exprorstmt_parent { /** Gets the `i`th child expression of this element (zero-based). */ final Expr getChildExpr(int i) { expr_parent_adjusted(result, i, this) or - result = getTopLevelChild(this, i) + expr_parent_top_level_adjusted(result, i, this) } /** Gets a child expression of this element, if any. */ @@ -97,7 +29,7 @@ class ExprOrStmtParent extends Element, @exprorstmt_parent { /** Gets the `i`th child statement of this element (zero-based). */ final Stmt getChildStmt(int i) { stmt_parent(result, i, this) or - result = getTopLevelChild(this, i) + stmt_parent_top_level(result, i, this) } /** Gets a child statement of this element, if any. */ @@ -113,242 +45,19 @@ class TopLevelExprParent extends Element, @top_level_expr_parent { final override Expr getChild(int i) { result = this.getChildExpr(i) } /** Gets the `i`th child expression of this element (zero-based). */ - final Expr getChildExpr(int i) { result = getTopLevelChild(this, i) } + final Expr getChildExpr(int i) { expr_parent_top_level_adjusted(result, i, this) } /** Gets a child expression of this element, if any. */ final Expr getAChildExpr() { result = this.getChildExpr(_) } } -/** - * INTERNAL: Do not use. - * - * An element that can have a child statement or expression, and where we have - * encountered multiple potential implementations at compile-time. For example, - * if we compile both `A.cs` - * - * ``` - * namespaces N { - * public class C { - * public int M() => 0; - * } - * } - * ``` - * - * and later `B.cs` - * - * ``` - * namespaces N { - * public class C { - * public int M() => 1; - * } - * } - * ``` - * - * then the method `N.C.M` has two implementations, returning `0` and `1`, - * respectively. - * - * The implementation used at run-time is in this case unknown (indeed, it could - * be a third implementation not encountered during compilation), so we make a - * guess for the "most likely" implementation in `getBestChild()`. - */ -class MultiImplementationsParent extends ExprOrStmtParent { - MultiImplementationsParent() { - exists(int i | - strictcount(File f | - exists(ControlFlowElement implementation, Location l | f = l.getFile() | - stmt_parent_top_level(implementation, i, this) and - stmt_location(implementation, l) - or - expr_parent_top_level_adjusted(implementation, i, this) and - expr_location(implementation, l) - ) - or - hasAccessorAutoImplementation(this, f) and - i = 0 - ) > 1 - ) - } - - /** - * Gets a file that contains an implementation `cfe` for the `i`th child of this - * element. - */ - private File getAnImplementation(int i, ControlFlowElement cfe) { - exists(Location l | result = l.getFile() | - stmt_parent_top_level(cfe, i, this) and - stmt_location(cfe, l) - or - expr_parent_top_level_adjusted(cfe, i, this) and - expr_location(cfe, l) - ) - } - - /** - * Gets a file that contains an implementation `cfe` for the `i`th child of this - * element, where `t` is the top-level type containing this element (that is, - * `t` is not a nested type). - */ - File getAnImplementationInTopLevelType(int i, ControlFlowElement cfe, ValueOrRefType t) { - result = this.getAnImplementation(i, cfe) and - t = getTopLevelDeclaringType(this) - } - - /** - * Gets a file that contains an auto-implementation for this element, where - * `t` is the top-level type containing this element (that is, `t` is not a - * nested type). - */ - File getAnAutoImplementationFileInTopLevelType(ValueOrRefType t) { - hasAccessorAutoImplementation(this, result) and - t = getTopLevelDeclaringType(this) - } - - private File getAnImplementationFileInTopLevelType(int i, ValueOrRefType t) { - result = getAnImplementationInTopLevelType(i, _, t) - or - result = this.getAnAutoImplementationFileInTopLevelType(t) and - i = 0 - } - - /** - * Gets the file containing the "best" implementation of this element, that is, the - * file considered most likely to contain the actual run-time implementation. - * - * The heuristics we use is to choose the implementation belonging to the top-level type - * with the most control flow elements (excluding `throw` elements). In the case of a tie, - * we arbitrarily choose the implementation belonging to the last file (in lexicographic - * order). - * - * By counting elements for the top-level type, we ensure that all definitions belonging - * to the same top-level type will get implementations belonging to the same file. - */ - File getBestFile() { - exists(ValueOrRefType t | - result = - max(this.getAnImplementationFileInTopLevelType(_, t) as file - order by - getImplementationSize(t, file), file.toString() - ) - ) - } - - /** - * Gets the i`th child of this element. Only the "best" child among all the possible - * run-time implementations is returned, namely the child considered most likely to - * be the actual run-time implementation. - */ - ControlFlowElement getBestChild(int i) { - exists(File f, ValueOrRefType t | f = getBestFile() | - f = this.getAnImplementationInTopLevelType(i, result, t) - ) - } -} - -/** Gets the top-level type containing declaration `d`. */ -private ValueOrRefType getTopLevelDeclaringType(Declaration d) { - result = getDeclaringType+(d) and - not result instanceof NestedType -} - -/** Gets the declaring type of element `e`. */ -private ValueOrRefType getDeclaringType(Declaration d) { - methods(d, _, result, _, _) - or - constructors(d, _, result, _) - or - destructors(d, _, result, _) - or - operators(d, _, _, result, _, _) - or - properties(d, _, result, _, _) - or - indexers(d, _, result, _, _) - or - nested_types(d, result, _) - or - fields(d, _, _, result, _, _) - or - exists(DeclarationWithAccessors decl | d = decl.getAnAccessor() | result = getDeclaringType(decl)) - or - exists(Parameterizable p | params(d, _, _, _, _, p, _) | result = getDeclaringType(p)) -} - -private ControlFlowElement getAChild(ControlFlowElement cfe) { - expr_parent_adjusted(result, _, cfe) or - stmt_parent(result, _, cfe) -} - -private int getImplementationSize0(ValueOrRefType t, File f) { - result = - strictcount(ControlFlowElement cfe | - exists(MultiImplementationsParent p, ControlFlowElement child | - cfe = getAChild*(child) and - not cfe = getAChild*(any(ThrowElement te)) - | - f = p.getAnImplementationInTopLevelType(_, child, t) - or - // Merge stats for partial implementations belonging to the same folder - t.isPartial() and - f = p.getAnImplementationInTopLevelType(_, _, t) and - exists(File fOther, MultiImplementationsParent pOther | - f.getParentContainer() = fOther.getParentContainer() - | - fOther = pOther.getAnImplementationInTopLevelType(_, child, t) - ) - ) - ) -} - -private int getImplementationSize1(ValueOrRefType t, File f) { - result = - strictsum(MultiImplementationsParent p, int c | - // Count each auto-implemented accessor as size 4 (getter) or 5 (setter) - f = p.getAnAutoImplementationFileInTopLevelType(t) and - if p instanceof Getter then c = 4 else c = 5 - | - c - ) -} - -private int getImplementationSize(ValueOrRefType t, File f) { - if exists(getImplementationSize0(t, f)) - then - if exists(getImplementationSize1(t, f)) - then result = getImplementationSize0(t, f) + getImplementationSize1(t, f) - else result = getImplementationSize0(t, f) - else result = getImplementationSize1(t, f) -} - -/** - * Holds if declaration `d` should have a location in file `f`, because it is part of a - * type with multiple implementations, where the most likely run-time implementation is - * in `f`. - */ -private predicate mustHaveLocationInFile(Declaration d, File f) { - exists(MultiImplementationsParent p, ValueOrRefType t | - t = getTopLevelDeclaringType(p) and - f = p.getBestFile() - | - t = getTopLevelDeclaringType(d) or d = t or d = p - ) -} - private predicate hasNoSourceLocation(Element e) { not e.getALocation() instanceof SourceLocation } cached private module Cached { - cached - ControlFlowElement getTopLevelChild(ExprOrStmtParent p, int i) { - result = p.(MultiImplementationsParent).getBestChild(i) - or - not p instanceof MultiImplementationsParent and - (stmt_parent_top_level(result, i, p) or expr_parent_top_level_adjusted(result, i, p)) - } - cached Location bestLocation(Element e) { - result = e.getALocation().(SourceLocation) and - (mustHaveLocationInFile(e, _) implies mustHaveLocationInFile(e, result.getFile())) + result = e.getALocation().(SourceLocation) or hasNoSourceLocation(e) and result = min(Location l | l = e.getALocation() | l order by l.getFile().toString()) @@ -360,20 +69,71 @@ private module Cached { /** * INTERNAL: Do not use. * - * Holds if accessor `a` has an auto-implementation in file `f`. + * The `expr_parent_top_level()` relation extended to include a relation + * between getters and expression bodies in properties such as `int P => 0`. + */ + cached + predicate expr_parent_top_level_adjusted(Expr child, int i, @top_level_exprorstmt_parent parent) { + expr_parent_top_level(child, i, parent) + or + parent = any(Getter g | expr_parent_top_level(child, i, g.getDeclaration())) and + i = 0 + } + + /** + * The `expr_parent()` relation adjusted for expandable assignments. For example, + * the assignment `x += y` is extracted as + * + * ``` + * += + * | + * 2 + * | + * = + * / \ + * 1 0 + * / \ + * x + + * / \ + * 1 0 + * / \ + * x y + * ``` + * + * in order to be able to retrieve the expanded assignment `x = x + y` as the 2nd + * child. This predicate changes the diagram above into + * + * ``` + * += + * / \ + * 1 0 + * / \ + * x y + * ``` */ cached - predicate hasAccessorAutoImplementation(Accessor a, File f) { - exists(SourceLocation sl | sl = a.getALocation() | - f = sl.getFile() and - not exists(ControlFlowElement cfe, Location l | sl.getFile() = l.getFile() | - stmt_parent_top_level(cfe, _, a) and - stmt_location(cfe, l) - or - expr_parent_top_level_adjusted(cfe, 0, a) and - expr_location(cfe, l) - ) - ) + predicate expr_parent_adjusted(Expr child, int i, ControlFlowElement parent) { + if parent instanceof AssignOperation + then + parent = + any(AssignOperation ao | + exists(AssignExpr ae | ae = ao.getExpandedAssignment() | + i = 0 and + exists(Expr right | + // right = `x + y` + expr_parent(right, 0, ae) + | + expr_parent(child, 1, right) + ) + or + i = 1 and + expr_parent(child, 1, ae) + ) + or + not ao.hasExpandedAssignment() and + expr_parent(child, i, parent) + ) + else expr_parent(child, i, parent) } } diff --git a/csharp/ql/src/semmle/code/csharp/Generics.qll b/csharp/ql/src/semmle/code/csharp/Generics.qll index 5aec92ebb988..52fe9c7b6996 100644 --- a/csharp/ql/src/semmle/code/csharp/Generics.qll +++ b/csharp/ql/src/semmle/code/csharp/Generics.qll @@ -16,6 +16,7 @@ import Location import Namespace private import dotnet +private import TypeRef /** * A generic declaration. Either an unbound generic (`UnboundGeneric`) or a @@ -183,6 +184,8 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter { /** Gets the generic that defines this type parameter. */ UnboundGeneric getGeneric() { type_parameters(this, _, result, _) } + + override string getAPrimaryQlClass() { result = "TypeParameter" } } /** @@ -190,7 +193,7 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter { * * For example, `where` on line 2 in * - * ``` + * ```csharp * class Factory * where T : ICloneable { * } @@ -233,7 +236,7 @@ class TypeParameterConstraints extends Element, @type_parameter_constraints { * * For example, * - * ``` + * ```csharp * struct KeyValuePair { * ... * } @@ -256,7 +259,7 @@ class UnboundGenericStruct extends Struct, UnboundGenericType { /** * An unbound generic class, for example * - * ``` + * ```csharp * class List { * ... * } @@ -279,7 +282,7 @@ class UnboundGenericClass extends Class, UnboundGenericType { /** * An unbound generic interface, for example * - * ``` + * ```csharp * interface IEnumerable { * ... * } @@ -305,7 +308,7 @@ class UnboundGenericInterface extends Interface, UnboundGenericType { * * For example * - * ``` + * ```csharp * delegate void F(T t); * ``` */ @@ -375,7 +378,7 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric { * * For example, `KeyValuePair` on line 4 in * - * ``` + * ```csharp * struct KeyValuePair { ... } * * class C { @@ -398,7 +401,7 @@ class ConstructedStruct extends Struct, ConstructedType { * * For example, `List` on line 4 in * - * ``` + * ```csharp * class List { ... } * * class C { @@ -421,7 +424,7 @@ class ConstructedClass extends Class, ConstructedType { * * For example, `IEnumerable` on line 4 in * - * ``` + * ```csharp * interface IEnumerable { ... } * * class C { @@ -444,7 +447,7 @@ class ConstructedInterface extends Interface, ConstructedType { * * For example, `F` on line 4 in * - * ``` + * ```csharp * delegate void F(T t); * * class C { @@ -466,7 +469,7 @@ class ConstructedDelegateType extends DelegateType, ConstructedType { * An unbound generic method. This is a generic method whose signature involves formal type parameters, * For example `M` on line 2 in * - * ``` + * ```csharp * class C { * void M() { ... } * } @@ -492,7 +495,7 @@ class UnboundGenericMethod extends Method, UnboundGeneric { * A constructed (bound) method, for example the target `M` of the call on * line 5 in * - * ``` + * ```csharp * class C { * void M() { ... } * @@ -526,8 +529,8 @@ class ConstructedMethod extends Method, ConstructedGeneric { /** * An unbound generic local function, for example `f` on line 3 in * - * ``` - * class { + * ```csharp + * class C { * void M() { * void f(T t) { ... } * } @@ -544,8 +547,8 @@ class UnboundLocalFunction extends LocalFunction, UnboundGeneric { * A constructed generic local function, for example the target `f` * of the function call `f(5)` on line 4 in * - * ``` - * class { + * ```csharp + * class C { * void M() { * void f(T t) { ... } * f(5); @@ -580,7 +583,7 @@ class NonConstructedMethod extends Method { * * Example: * - * ``` + * ```csharp * class A { * void M1(T1 x1) { } * void M2(T1 x1, T2 x) { } diff --git a/csharp/ql/src/semmle/code/csharp/Implements.qll b/csharp/ql/src/semmle/code/csharp/Implements.qll index 1007ed2aaf97..7b5358170391 100644 --- a/csharp/ql/src/semmle/code/csharp/Implements.qll +++ b/csharp/ql/src/semmle/code/csharp/Implements.qll @@ -21,7 +21,7 @@ private import Conversion * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -52,7 +52,7 @@ predicate implements(Virtualizable m1, Virtualizable m2, ValueOrRefType t) { * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -143,10 +143,10 @@ private predicate getACompatibleInterfaceAccessorAux( * of the interface `i`. Note that the class or struct need not be a * sub type of the interface in the inheritance hierarchy: * - * ``` - * interface I { void M() } + * ```csharp + * interface I { void M(); } * - * class A { public void M() } + * class A { public void M() { } } * * class B { } * @@ -259,7 +259,7 @@ private module Gvn { private newtype TGvnType = TLeafGvnType(LeafType t) or TMethodTypeParameterGvnType(int i) { i = any(MethodTypeParameter p).getIndex() } or - TConstructedGvnType(ConstructedGvnTypeList l) + TConstructedGvnType(ConstructedGvnTypeList l) { l.isFullyConstructed() } private newtype TConstructedGvnTypeList = TConstructedGvnTypeNil(Unification::CompoundTypeKind k) or @@ -334,6 +334,10 @@ private module Gvn { ) } + predicate isFullyConstructed() { + this.getKind().getNumberOfTypeParameters() - 1 = this.length() + } + private GvnType getArg(int i) { exists(GvnType head, ConstructedGvnTypeList tail | this = TConstructedGvnTypeCons(head, tail) @@ -345,47 +349,71 @@ private module Gvn { ) } + private Unification::GenericType getConstructedGenericDeclaringTypeAt(int i) { + i = 0 and + result = this.getKind().getConstructedSourceDeclaration() + or + result = this.getConstructedGenericDeclaringTypeAt(i - 1).getGenericDeclaringType() + } + + private predicate isDeclaringTypeAt(int i) { + exists(this.getConstructedGenericDeclaringTypeAt(i - 1)) + } + /** - * Gets a textual representation of this constructed type, restricted - * to the prefix `t` of the underlying source declaration type. - * - * The `toString()` calculation needs to be split up into prefixes, in - * order to apply the type arguments correctly. For example, a source - * declaration type `A<>.B.C<,>` applied to types `int, string, bool` - * needs to be printed as `A.B.C`. + * Gets the `j`th `toString()` part of the `i`th nested component of this + * constructed type, if any. The nested components are sorted in reverse + * order, while the individual parts are sorted in normal order. */ language[monotonicAggregates] - private string toStringConstructed(Unification::GenericType t) { - t = this.getKind().getConstructedSourceDeclaration().getGenericDeclaringType*() and - exists(int offset, int children, string name, string nameArgs | - offset = t.getNumberOfDeclaringArguments() and - children = t.getNumberOfArgumentsSelf() and - name = Unification::getNameNested(t) and - if children = 0 - then nameArgs = name - else - exists(string offsetArgs | - offsetArgs = - concat(int i | - i in [offset .. offset + children - 1] - | - this.getArg(i).toString(), "," order by i - ) and - nameArgs = name.prefix(name.length() - children - 1) + "<" + offsetArgs + ">" + private string toStringConstructedPart(int i, int j) { + this.isFullyConstructed() and + exists(Unification::GenericType t | + t = this.getConstructedGenericDeclaringTypeAt(i) and + exists(int offset, int children, string name | + offset = t.getNumberOfDeclaringArguments() and + children = t.getNumberOfArgumentsSelf() and + name = Unification::getNameNested(t) and + if children = 0 + then + j = 0 and result = name + or + this.isDeclaringTypeAt(i) and j = 1 and result = "." + else ( + j = 0 and result = name.prefix(name.length() - children - 1) + "<" + or + j in [1 .. 2 * children - 1] and + if j % 2 = 0 + then result = "," + else result = this.getArg((j + 1) / 2 + offset - 1).toString() + or + j = 2 * children and + result = ">" + or + this.isDeclaringTypeAt(i) and + j = 2 * children + 1 and + result = "." ) - | - offset = 0 and result = nameArgs - or - result = this.toStringConstructed(t.getGenericDeclaringType()) + "." + nameArgs + ) ) } language[monotonicAggregates] string toString() { + this.isFullyConstructed() and exists(Unification::CompoundTypeKind k | k = this.getKind() | result = k.toStringBuiltin(this.getArg(0).toString()) or - result = this.toStringConstructed(k.getConstructedSourceDeclaration()) + result = + strictconcat(int i, int j | + exists(Unification::GenericType t, int children | + t = this.getConstructedGenericDeclaringTypeAt(i) and + children = t.getNumberOfArgumentsSelf() and + if children = 0 then j = 0 else j in [0 .. 2 * children] + ) + | + this.toStringConstructedPart(i, j) order by i desc, j + ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/Location.qll b/csharp/ql/src/semmle/code/csharp/Location.qll index 178eddc1d4d7..99df294ae636 100644 --- a/csharp/ql/src/semmle/code/csharp/Location.qll +++ b/csharp/ql/src/semmle/code/csharp/Location.qll @@ -38,16 +38,16 @@ class Location extends @location { /** Gets a textual representation of this location. */ string toString() { none() } - /** Gets the start line of this location. */ + /** Gets the 1-based line number (inclusive) where this location starts. */ final int getStartLine() { this.hasLocationInfo(_, result, _, _, _) } - /** Gets the end line of this location. */ + /** Gets the 1-based line number (inclusive) where this location ends. */ final int getEndLine() { this.hasLocationInfo(_, _, _, result, _) } - /** Gets the start column of this location. */ + /** Gets the 1-based column number (inclusive) where this location starts. */ final int getStartColumn() { this.hasLocationInfo(_, _, result, _, _) } - /** Gets the end column of this location. */ + /** Gets the 1-based column number (inclusive) where this location ends. */ final int getEndColumn() { this.hasLocationInfo(_, _, _, _, result) } } diff --git a/csharp/ql/src/semmle/code/csharp/Member.qll b/csharp/ql/src/semmle/code/csharp/Member.qll index dd2a64c47ae9..6df943a51b9a 100644 --- a/csharp/ql/src/semmle/code/csharp/Member.qll +++ b/csharp/ql/src/semmle/code/csharp/Member.qll @@ -1,11 +1,12 @@ /** Provides classes relating to declarations and type members. */ -import Element -import Variable import Callable +import Element import Modifier -private import Implements +import Variable private import dotnet +private import Implements +private import TypeRef /** * A declaration. @@ -24,7 +25,7 @@ class Declaration extends DotNet::Declaration, Element, @declaration { * Gets the fully qualified name of this declaration, including types, for example * the fully qualified name with types of `M` on line 3 is `N.C.M(int, string)` in * - * ``` + * ```csharp * namespace N { * class C { * void M(int i, string s) { } @@ -195,7 +196,7 @@ class Virtualizable extends Member, @virtualizable { * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -223,7 +224,7 @@ class Virtualizable extends Member, @virtualizable { * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -251,7 +252,7 @@ class Virtualizable extends Member, @virtualizable { * Note that this is generally *not* equivalent with * `getOverridee*().getImplementee()`, as the example below illustrates: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public virtual void M() { } } diff --git a/csharp/ql/src/semmle/code/csharp/Namespace.qll b/csharp/ql/src/semmle/code/csharp/Namespace.qll index 0c94d182bee8..ed1fece2ae64 100644 --- a/csharp/ql/src/semmle/code/csharp/Namespace.qll +++ b/csharp/ql/src/semmle/code/csharp/Namespace.qll @@ -12,7 +12,7 @@ class TypeContainer extends DotNet::NamedElement, Element, @type_container { } /** * A namespace, for example * - * ``` + * ```csharp * namespace System.IO { * ... * } @@ -37,7 +37,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets a type directly declared in this namespace, if any. * For example, the class `File` in * - * ``` + * ```csharp * namespace System.IO { * public class File { ... } * } @@ -49,7 +49,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets a class directly declared in this namespace, if any. * For example, the class `File` in * - * ``` + * ```csharp * namespace System.IO { * public class File { ... } * } @@ -61,7 +61,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets an interface directly declared in this namespace, if any. * For example, the interface `IEnumerable` in * - * ``` + * ```csharp * namespace System.Collections { * public interface IEnumerable { ... } * } @@ -73,7 +73,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets a struct directly declared in this namespace, if any. * For example, the struct `Timespan` in * - * ``` + * ```csharp * namespace System { * public struct Timespan { ... } * } @@ -85,7 +85,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets an enum directly declared in this namespace, if any. * For example, the enum `DayOfWeek` in * - * ``` + * ```csharp * namespace System { * public enum DayOfWeek { ... } * } @@ -97,7 +97,7 @@ class Namespace extends DotNet::Namespace, TypeContainer, Declaration, @namespac * Gets a delegate directly declared in this namespace, if any. * For example, the delegate `AsyncCallback` in * - * ``` + * ```csharp * namespace System { * public delegate void AsyncCallback(IAsyncResult ar); * } @@ -135,7 +135,7 @@ class GlobalNamespace extends Namespace { /** * An explicit namespace declaration in a source file. For example: * - * ``` + * ```csharp * namespace N1.N2 { * ... * } @@ -145,7 +145,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration { /** * Gets the declared namespace, for example `N1.N2` in * - * ``` + * ```csharp * namespace N1.N2 { * ... * } @@ -159,7 +159,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration { * declaration `namespace N1` on line 1, but `namespace N1` on line 1 and * `namespace N1.N2` on line 7 do not have parent namespace declarations. * - * ``` + * ```csharp * namespace N1 { * namespace N2 { * ... @@ -180,7 +180,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration { * `namespace N2` on line 2 is a child namespace declaration of * `namespace N1` on line 1. * - * ``` + * ```csharp * namespace N1 { * namespace N2 { * ... @@ -196,7 +196,7 @@ class NamespaceDeclaration extends Element, @namespace_declaration { * Gets a type directly declared within this namespace declaration. * For example, class `C` in * - * ``` + * ```csharp * namespace N { * class C { ... } * } @@ -207,4 +207,6 @@ class NamespaceDeclaration extends Element, @namespace_declaration { override Location getALocation() { namespace_declaration_location(this, result) } override string toString() { result = "namespace ... { ... }" } + + override string getAPrimaryQlClass() { result = "NamespaceDeclaration" } } diff --git a/csharp/ql/src/semmle/code/csharp/PrintAst.ql b/csharp/ql/src/semmle/code/csharp/PrintAst.ql new file mode 100644 index 000000000000..3867fd2990fb --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/PrintAst.ql @@ -0,0 +1,20 @@ +/** + * @name Print AST + * @description Outputs a representation of the Abstract Syntax Tree. + * @id csharp/print-ast + * @kind graph + */ + +import csharp +import PrintAst + +/** + * Temporarily tweak this class or make a copy to control which functions are + * printed. + */ +class PrintAstConfigurationOverride extends PrintAstConfiguration { + /** + * TWEAK THIS PREDICATE AS NEEDED. + */ + override predicate shouldPrint(Element e, Location l) { super.shouldPrint(e, l) } +} diff --git a/csharp/ql/src/semmle/code/csharp/PrintAst.qll b/csharp/ql/src/semmle/code/csharp/PrintAst.qll new file mode 100644 index 000000000000..c8001f443aaa --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/PrintAst.qll @@ -0,0 +1,647 @@ +/** + * Provides queries to pretty-print a C# AST as a graph. + * + * By default, this will print the AST for all elements in the database. To change this behavior, + * extend `PrintAstConfiguration` and override `shouldPrint` to hold for only the elements + * you wish to view the AST for. + */ + +import csharp + +private newtype TPrintAstConfiguration = MkPrintAstConfiguration() + +/** + * The query can extend this class to control which elements are printed. + */ +class PrintAstConfiguration extends TPrintAstConfiguration { + /** + * Gets a textual representation of this `PrintAstConfiguration`. + */ + string toString() { result = "PrintAstConfiguration" } + + /** + * Controls whether the `Element` should be considered for AST printing. + * By default it checks whether the `Element` `e` belongs to `Location` `l`. + */ + predicate shouldPrint(Element e, Location l) { e.fromSource() and l = e.getLocation() } +} + +private predicate shouldPrint(Element e, Location l) { + exists(PrintAstConfiguration config | config.shouldPrint(e, l)) +} + +private predicate isImplicitExpression(ControlFlowElement element) { + element.(Expr).isImplicit() and not exists(element.getAChild()) +} + +private predicate isFilteredCompilerGenerated(Declaration d) { + d.isCompilerGenerated() and + not d instanceof Accessor +} + +private predicate isNotNeeded(Element e) { + isFilteredCompilerGenerated(e) + or + e instanceof ConstructedGeneric + or + e instanceof AnonymousClass + or + e instanceof TupleType + or + isNotNeeded(e.(Declaration).getDeclaringType()) + or + isNotNeeded(e.(Parameter).getDeclaringElement()) + or + isNotNeeded(e.(Attribute).getTarget()) +} + +/** + * Retrieves the canonical QL class(es) for entity `el` + */ +private string getQlClass(Element el) { + result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] " + // Alternative implementation -- do not delete. It is useful for QL class discovery. + // result = "["+ concat(el.getAQlClass(), ",") + "] " +} + +/** + * An `Element`, such as a `namespace` and a `partial class`, might have multiple locations. + * The locations are ordered by line, column, and then the first one is selected. + */ +private Location getRepresentativeLocation(Element ast) { + result = + min(Location loc | + shouldPrint(ast, loc) + | + loc order by loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn() + ) +} + +private predicate locationSortKeys(Element ast, string file, int line, int column) { + exists(Location loc | + loc = getRepresentativeLocation(ast) and + file = loc.getFile().toString() and + line = loc.getStartLine() and + column = loc.getStartColumn() + ) + or + not exists(getRepresentativeLocation(ast)) and + file = "" and + line = 0 and + column = 0 +} + +private predicate hasInterestingBaseTypes(ValueOrRefType type) { + exists(getAnInterestingBaseType(type)) +} + +private ValueOrRefType getAnInterestingBaseType(ValueOrRefType type) { + not type instanceof TupleType and + not type instanceof ArrayType and + not type instanceof NullableType and + result = type.getABaseType() and + isInterestingBaseType(result) +} + +private predicate isInterestingBaseType(ValueOrRefType base) { + not base instanceof ObjectType and + not base.getQualifiedName() = "System.ValueType" and + not base.getQualifiedName() = "System.Delegate" and + not base.getQualifiedName() = "System.MulticastDelegate" and + not base.getQualifiedName() = "System.Enum" +} + +/** + * Printed AST nodes are mostly `Element`s of the underlying AST. + * There are extra AST nodes generated for parameters of `Parameterizable`s, + * attributes of `Attributable`s, type parameters of `UnboundGeneric` and + * base types of `ValueOrRefType` declarations. These extra nodes are used + * as containers to organize the tree a bit better. + */ +private newtype TPrintAstNode = + TElementNode(Element element) { shouldPrint(element, _) } or + TParametersNode(Parameterizable parameterizable) { + shouldPrint(parameterizable, _) and + parameterizable.getNumberOfParameters() > 0 and + not isNotNeeded(parameterizable) + } or + TAttributesNode(Attributable attributable) { + shouldPrint(attributable, _) and + exists(attributable.getAnAttribute()) and + not isNotNeeded(attributable) + } or + TTypeParametersNode(UnboundGeneric unboundGeneric) { + shouldPrint(unboundGeneric, _) and + unboundGeneric.getNumberOfTypeParameters() > 0 and + not isNotNeeded(unboundGeneric) + } or + TBaseTypesNode(ValueOrRefType type) { + shouldPrint(type, _) and + hasInterestingBaseTypes(type) and + not isNotNeeded(type) + } or + TBaseTypeNode(ValueOrRefType derived, ValueOrRefType base) { + shouldPrint(derived, _) and + base = getAnInterestingBaseType(derived) and + not isNotNeeded(derived) + } + +/** + * A node in the output tree. + */ +class PrintAstNode extends TPrintAstNode { + /** + * Gets a textual representation of this node in the PrintAst output tree. + */ + string toString() { none() } + + /** + * Gets the child node at index `childIndex`. Child indices must be unique, + * but need not be contiguous. + */ + PrintAstNode getChild(int childIndex) { none() } + + /** + * Gets a child of this node. + */ + final PrintAstNode getAChild() { result = getChild(_) } + + /** + * Gets the parent of this node, if any. + */ + final PrintAstNode getParent() { result.getAChild() = this } + + /** + * Gets the location of this node in the source code. + */ + Location getLocation() { none() } + + /** + * Gets the value of the property of this node, where the name of the property + * is `key`. + */ + string getProperty(string key) { + key = "semmle.label" and + result = toString() + } + + /** + * Gets the label for the edge from this node to the specified child. By + * default, this is just the index of the child, but subclasses can override + * this. + */ + string getChildEdgeLabel(int childIndex) { + exists(getChild(childIndex)) and + result = childIndex.toString() + } +} + +/** A top-level AST node. */ +class TopLevelPrintAstNode extends PrintAstNode { + TopLevelPrintAstNode() { not exists(this.getParent()) } + + private int getOrder() { + this = + rank[result](TopLevelPrintAstNode n, Location l | + l = n.getLocation() + | + n + order by + l.getFile().getRelativePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(), + l.getEndColumn() + ) + } + + override string getProperty(string key) { + result = super.getProperty(key) + or + key = "semmle.order" and + result = this.getOrder().toString() + } +} + +/** + * A node representing an AST node with an underlying `Element`. + */ +abstract class ElementNode extends PrintAstNode, TElementNode { + Element element; + + ElementNode() { this = TElementNode(element) } + + override string toString() { result = getQlClass(element) + element.toString() } + + override Location getLocation() { result = getRepresentativeLocation(element) } + + /** + * Gets the `Element` represented by this node. + */ + final Element getElement() { result = element } +} + +/** + * A node representing a `ControlFlowElement` (`Expr` or `Stmt`). + */ +class ControlFlowElementNode extends ElementNode { + ControlFlowElement controlFlowElement; + + ControlFlowElementNode() { + controlFlowElement = element and + // Removing implicit expressions + not isImplicitExpression(element) and + // Removing extra nodes that are generated for an `AssignOperation` + not exists(AssignOperation ao | + ao.hasExpandedAssignment() and + ( + ao.getExpandedAssignment() = controlFlowElement or + ao.getExpandedAssignment().getRValue() = controlFlowElement or + ao.getExpandedAssignment().getRValue().(BinaryOperation).getLeftOperand() = + controlFlowElement.getParent*() or + ao.getExpandedAssignment().getRValue().(OperatorCall).getChild(0) = + controlFlowElement.getParent*() + ) + ) and + not isNotNeeded(element.getParent+()) + } + + override ElementNode getChild(int childIndex) { + result.getElement() = controlFlowElement.getChild(childIndex) + } +} + +/** + * A node representing a `LocalFunctionStmt`. + * Each `LocalFunction` is displayed below its corresponding `LocalFunctionStmt`. + */ +final class LocalFunctionStmtNode extends ControlFlowElementNode { + LocalFunctionStmt stmt; + + LocalFunctionStmtNode() { stmt = element } + + override CallableNode getChild(int childIndex) { + childIndex = 0 and + result.getElement() = stmt.getLocalFunction() + } +} + +/** + * A node representing a `Callable`, such as method declaration. + */ +final class CallableNode extends ElementNode { + Callable callable; + + CallableNode() { + callable = element and + not isNotNeeded(callable) + } + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and + result.(AttributesNode).getAttributable() = callable + or + childIndex = 1 and + result.(TypeParametersNode).getUnboundGeneric() = callable + or + childIndex = 2 and + result.(ParametersNode).getParameterizable() = callable + or + childIndex = 3 and + result.(ElementNode).getElement() = callable.(Constructor).getInitializer() + or + childIndex = 4 and + result.(ElementNode).getElement() = callable.getBody() + } +} + +/** + * A node representing a `DeclarationWithAccessors`, such as property declaration. + */ +final class DeclarationWithAccessorsNode extends ElementNode { + DeclarationWithAccessors declaration; + + DeclarationWithAccessorsNode() { + declaration = element and + not isNotNeeded(declaration.getDeclaringType()) + } + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and + result.(AttributesNode).getAttributable() = declaration + or + childIndex = 1 and + result.(ParametersNode).getParameterizable() = declaration + or + childIndex = 2 and + result.(ElementNode).getElement() = declaration.(Property).getInitializer().getParent() + or + result.(ElementNode).getElement() = + rank[childIndex - 2](Element a, string file, int line, int column, string name | + a = declaration.getAnAccessor() and + locationSortKeys(a, file, line, column) and + name = a.toString() + | + a order by file, line, column, name + ) + } +} + +/** + * A node representing a `Field` declaration. + */ +final class FieldNode extends ElementNode { + Field field; + + FieldNode() { + field = element and + not field.getDeclaringType() instanceof TupleType and + not isNotNeeded(field.getDeclaringType()) + } + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and + result.(AttributesNode).getAttributable() = field + or + childIndex = 1 and + field.hasInitializer() and + ( + if field.getDeclaringType() instanceof Enum + then result.(ElementNode).getElement() = field.getInitializer() + else result.(ElementNode).getElement() = field.getInitializer().getParent() + ) + } +} + +/** + * A node representing a `Parameter` declaration. + */ +final class ParameterNode extends ElementNode { + Parameter param; + + ParameterNode() { + param = element and + not isNotNeeded(param.getDeclaringElement()) + } + + override Location getLocation() { + not param.hasExtensionMethodModifier() and result = super.getLocation() + or + // for extension method first parameters, we're choosing the shorter location of the two + param.hasExtensionMethodModifier() and + result = + min(Location loc | + shouldPrint(param, loc) and + loc.getStartLine() = loc.getEndLine() + | + loc order by loc.getEndColumn() - loc.getStartColumn() + ) + } + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and + result.(AttributesNode).getAttributable() = param + or + childIndex = 1 and + result.(ElementNode).getElement() = param.getDefaultValue() + } +} + +/** + * A node representing an `Attribute`. + */ +final class AttributeNode extends ElementNode { + Attribute attr; + + AttributeNode() { + attr = element and + not isNotNeeded(attr.getTarget()) + } + + override ElementNode getChild(int childIndex) { result.getElement() = attr.getChild(childIndex) } +} + +/** + * A node representing a `TypeParameter`. + */ +final class TypeParameterNode extends ElementNode { + TypeParameter typeParameter; + + TypeParameterNode() { + typeParameter = element and + not isNotNeeded(typeParameter.getDeclaringGeneric()) + } + + override ElementNode getChild(int childIndex) { none() } +} + +/** + * A node representing a `ValueOrRefType`. + */ +final class TypeNode extends ElementNode { + ValueOrRefType type; + + TypeNode() { + type = element and + not type instanceof TupleType and + not type instanceof ArrayType and + not type instanceof NullableType and + not isNotNeeded(type) + } + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and + result.(AttributesNode).getAttributable() = type + or + childIndex = 1 and + result.(TypeParametersNode).getUnboundGeneric() = type + or + childIndex = 2 and + result.(ParametersNode).getParameterizable() = type + or + childIndex = 3 and + result.(BaseTypesNode).getValueOrRefType() = type + or + result.(ElementNode).getElement() = + rank[childIndex - 3](Member m, string file, int line, int column | + m = type.getAMember() and + locationSortKeys(m, file, line, column) + | + m order by file, line, column + ) + } +} + +/** + * A node representing a `NamespaceDeclaration`. + */ +final class NamespaceNode extends ElementNode { + NamespaceDeclaration namespace; + + NamespaceNode() { namespace = element } + + override PrintAstNode getChild(int childIndex) { + result.(ElementNode).getElement() = + rank[childIndex](Element a, string file, int line, int column | + (a = namespace.getAChildNamespaceDeclaration() or a = namespace.getATypeDeclaration()) and + locationSortKeys(a, file, line, column) + | + a order by file, line, column + ) + } +} + +/** + * A node representing the parameters of a `Parameterizable`. + * Only rendered if there's at least one parameter and if the + * `Parameterizable` is not compiler generated or is of type + * `Accessor`. + */ +final class ParametersNode extends PrintAstNode, TParametersNode { + Parameterizable parameterizable; + + ParametersNode() { this = TParametersNode(parameterizable) } + + override string toString() { result = "(Parameters)" } + + override Location getLocation() { none() } + + override ParameterNode getChild(int childIndex) { + result.getElement() = parameterizable.getParameter(childIndex) + } + + /** + * Gets the underlying `Parameterizable` + */ + Parameterizable getParameterizable() { result = parameterizable } +} + +/** + * A node representing the attributes of an `Attributable`. + * Only rendered if there's at least one attribute. + */ +final class AttributesNode extends PrintAstNode, TAttributesNode { + Attributable attributable; + + AttributesNode() { this = TAttributesNode(attributable) } + + override string toString() { result = "(Attributes)" } + + override Location getLocation() { none() } + + override AttributeNode getChild(int childIndex) { + result.getElement() = + rank[childIndex](Attribute a, string file, int line, int column | + a = attributable.getAnAttribute() and + locationSortKeys(a, file, line, column) + | + a order by file, line, column + ) + } + + /** + * Gets the underlying `Attributable` + */ + Attributable getAttributable() { result = attributable } +} + +/** + * A node representing the type parameters of an `UnboundGeneric`. + */ +final class TypeParametersNode extends PrintAstNode, TTypeParametersNode { + UnboundGeneric unboundGeneric; + + TypeParametersNode() { this = TTypeParametersNode(unboundGeneric) } + + override string toString() { result = "(Type parameters)" } + + override Location getLocation() { none() } + + override TypeParameterNode getChild(int childIndex) { + result.getElement() = unboundGeneric.getTypeParameter(childIndex) + } + + /** + * Gets the underlying `UnboundGeneric` + */ + UnboundGeneric getUnboundGeneric() { result = unboundGeneric } +} + +/** + * A node representing the base types of a `ValueOrRefType`. + */ +final class BaseTypesNode extends PrintAstNode, TBaseTypesNode { + ValueOrRefType valueOrRefType; + + BaseTypesNode() { this = TBaseTypesNode(valueOrRefType) } + + override string toString() { result = "(Base types)" } + + override Location getLocation() { none() } + + override BaseTypeNode getChild(int childIndex) { + childIndex = 0 and + result.getBaseType() = valueOrRefType.getBaseClass() and + result.getDerivedType() = valueOrRefType + or + result.getBaseType() = + rank[childIndex](ValueOrRefType base, string name | + base = valueOrRefType.getABaseInterface() and + name = base.toString() + | + base order by name + ) and + result.getDerivedType() = valueOrRefType + } + + /** + * Gets the underlying `ValueOrRefType` + */ + ValueOrRefType getValueOrRefType() { result = valueOrRefType } +} + +/** + * A node representing a base type reference of a `ValueOrRefType` declaration. + */ +final class BaseTypeNode extends PrintAstNode, TBaseTypeNode { + ValueOrRefType derived; + ValueOrRefType base; + + BaseTypeNode() { this = TBaseTypeNode(derived, base) } + + override string toString() { result = getQlClass(base) + base.toString() } + + override Location getLocation() { result = derived.getLocation() } + + override BaseTypeNode getChild(int childIndex) { none() } + + /** + * Gets the underlying derived `ValueOrRefType` + */ + ValueOrRefType getDerivedType() { result = derived } + + /** + * Gets the underlying base `ValueOrRefType` + */ + ValueOrRefType getBaseType() { result = base } +} + +/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */ +query predicate nodes(PrintAstNode node, string key, string value) { value = node.getProperty(key) } + +/** + * Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the + * given `value`. + */ +query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) { + exists(int childIndex | + target = source.getChild(childIndex) and + ( + key = "semmle.label" and value = source.getChildEdgeLabel(childIndex) + or + key = "semmle.order" and value = childIndex.toString() + ) + ) +} + +/** Holds if property `key` of the graph has the given `value`. */ +query predicate graphProperties(string key, string value) { + key = "semmle.graphKind" and value = "tree" +} diff --git a/csharp/ql/src/semmle/code/csharp/Property.qll b/csharp/ql/src/semmle/code/csharp/Property.qll index d7013927846d..f50daf58f901 100644 --- a/csharp/ql/src/semmle/code/csharp/Property.qll +++ b/csharp/ql/src/semmle/code/csharp/Property.qll @@ -2,12 +2,13 @@ * Provides classes for properties, indexers, and accessors. */ -import Type import Member import Stmt -private import semmle.code.csharp.ExprOrStmtParent -private import dotnet +import Type private import cil +private import dotnet +private import semmle.code.csharp.ExprOrStmtParent +private import TypeRef /** * A declaration that may have accessors. Either an event (`Event`), a property @@ -106,7 +107,7 @@ class DeclarationWithGetSetAccessors extends DeclarationWithAccessors, TopLevelE /** * A property, for example `P` on line 2 in * - * ``` + * ```csharp * class C { * public int P { get; set; } * } @@ -123,7 +124,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * Holds if this property is automatically implemented. For example, `P1` * on line 2 is automatically implemented, while `P2` on line 5 is not in * - * ``` + * ```csharp * class C { * public int P1 { get; set; } * @@ -195,7 +196,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * Gets the initial value of this property, if any. For example, the initial * value of `P` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int P { get; set; } = 20; * } @@ -207,7 +208,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * Holds if this property has an initial value. For example, the initial * value of `P` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int P { get; set; } = 20; * } @@ -219,7 +220,7 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * Gets the expression body of this property, if any. For example, the expression * body of `P` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int P => 20; * } @@ -232,12 +233,14 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper override Getter getGetter() { result = DeclarationWithGetSetAccessors.super.getGetter() } override Setter getSetter() { result = DeclarationWithGetSetAccessors.super.getSetter() } + + override string getAPrimaryQlClass() { result = "Property" } } /** * An indexer, for example `string this[int i]` on line 2 in * - * ``` + * ```csharp * class C { * public string this[int i] { * get { return i.ToString(); } @@ -261,7 +264,7 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer * Gets the expression body of this indexer, if any. For example, the * expression body of the indexer on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int this[int i] => 20; * } @@ -298,6 +301,8 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer override Location getALocation() { indexer_location(this, result) } override string toStringWithTypes() { result = getName() + "[" + parameterTypesToString() + "]" } + + override string getAPrimaryQlClass() { result = "Indexer" } } /** @@ -314,7 +319,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { * Gets the declaration that this accessor belongs to. For example, both * accessors on lines 3 and 4 belong to the property `P` on line 2 in * - * ``` + * ```csharp * class C { * public int P { * get; @@ -330,7 +335,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { * the `get` accessor on line 3 has no access modifier and the `set` accessor * on line 4 has a `private` access modifier in * - * ``` + * ```csharp * class C { * public int P { * get; @@ -349,7 +354,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { * has the modifiers `public` and `virtual`, and the `set` accessor on line 4 * has the modifiers `private` and `virtual` in * - * ``` + * ```csharp * class C { * public virtual int P { * get; @@ -375,7 +380,7 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { /** * A `get` accessor, for example `get { return p; }` in * - * ``` + * ```csharp * public class C { * int p; * public int P { @@ -394,7 +399,7 @@ class Getter extends Accessor, @getter { * Gets the field used in the trival implementation of this getter, if any. * For example, the field `p` in * - * ``` + * ```csharp * public class C { * int p; * public int P { @@ -415,12 +420,14 @@ class Getter extends Accessor, @getter { override DeclarationWithGetSetAccessors getDeclaration() { result = Accessor.super.getDeclaration() } + + override string getAPrimaryQlClass() { result = "Getter" } } /** * A `set` accessor, for example `set { p = value; }` in * - * ``` + * ```csharp * public class C { * int p; * public int P { @@ -442,7 +449,7 @@ class Setter extends Accessor, @setter { * Gets the field used in the trival implementation of this setter, if any. * For example, the field `p` in * - * ``` + * ```csharp * public class C { * int p; * public int P { @@ -464,6 +471,8 @@ class Setter extends Accessor, @setter { override DeclarationWithGetSetAccessors getDeclaration() { result = Accessor.super.getDeclaration() } + + override string getAPrimaryQlClass() { result = "Setter" } } /** @@ -478,7 +487,7 @@ private ParameterAccess accessToValue() { * A property with a trivial getter and setter. For example, properties `P1` * and `P2` are trivial, while `P3` is not, in * - * ``` + * ```csharp * public class C { * int p1; * public int P1 { @@ -536,7 +545,7 @@ class IndexerProperty extends Property { // too many indexer calls, for example the call to the indexer // setter at `dict[0]` in // - // ``` + // ```csharp // class A // { // Dictionary dict; @@ -554,4 +563,6 @@ class IndexerProperty extends Property { // ``` result.getIndexer() = i } + + override string getAPrimaryQlClass() { result = "IndexerProperty" } } diff --git a/csharp/ql/src/semmle/code/csharp/Stmt.qll b/csharp/ql/src/semmle/code/csharp/Stmt.qll index 6b64abe88aac..8fadbc1267b4 100644 --- a/csharp/ql/src/semmle/code/csharp/Stmt.qll +++ b/csharp/ql/src/semmle/code/csharp/Stmt.qll @@ -4,12 +4,13 @@ * All statements have the common base class `Stmt`. */ -import Location import Element +import Location import Member import exprs.Expr private import semmle.code.csharp.Enclosing::Internal private import semmle.code.csharp.frameworks.System +private import TypeRef /** * A statement. @@ -44,7 +45,7 @@ class Stmt extends ControlFlowElement, @stmt { /** * A block statement, for example * - * ``` + * ```csharp * { * ... * } @@ -76,12 +77,14 @@ class BlockStmt extends Stmt, @block_stmt { } override string toString() { result = "{...}" } + + override string getAPrimaryQlClass() { result = "BlockStmt" } } /** * An expression statement, for example `M1()` on line 5 * - * ``` + * ```csharp * class C { * int M1() { ... } * @@ -96,6 +99,8 @@ class ExprStmt extends Stmt, @expr_stmt { Expr getExpr() { result.getParent() = this } override string toString() { result = "...;" } + + override string getAPrimaryQlClass() { result = "ExprStmt" } } /** @@ -111,7 +116,7 @@ class SelectionStmt extends Stmt, @cond_stmt { /** * An `if` statement, for example * - * ``` + * ```csharp * if (x==0) { * ... * } else { @@ -131,12 +136,14 @@ class IfStmt extends SelectionStmt, @if_stmt { Stmt getElse() { result = this.getChild(2) } override string toString() { result = "if (...) ..." } + + override string getAPrimaryQlClass() { result = "IfStmt" } } /** * A `switch` statement, for example * - * ``` + * ```csharp * switch (instruction) { * ... * } @@ -152,7 +159,7 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt { * * Example: * - * ``` + * ```csharp * switch (x) { * case "abc": // i = 0 * return 0; @@ -180,12 +187,14 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt { override string toString() { result = "switch (...) {...}" } + override string getAPrimaryQlClass() { result = "SwitchStmt" } + /** * Gets the `i`th statement in the body of this `switch` statement. * * Example: * - * ``` + * ```csharp * switch (x) { * case "abc": // i = 0 * return 0; @@ -268,7 +277,7 @@ class CaseStmt extends Case, @case_stmt { * Gets the condition on this case, if any. For example, the type case on line 3 * has no condition, and the type case on line 4 has condition `s.Length > 0`, in * - * ``` + * ```csharp * switch(p) * { * case int i: @@ -284,13 +293,15 @@ class CaseStmt extends Case, @case_stmt { SwitchStmt getSwitchStmt() { result.getACase() = this } override string toString() { result = "case ...:" } + + override string getAPrimaryQlClass() { result = "CaseStmt" } } /** * A constant case of a `switch` statement, for example `case OpCode.Nop:` * on line 2 in * - * ``` + * ```csharp * switch (instruction) { * case OpCode.Nop: ... * default: ... @@ -305,13 +316,15 @@ class ConstCase extends CaseStmt, LabeledStmt { override string getLabel() { result = p.getValue() } override string toString() { result = CaseStmt.super.toString() } + + override string getAPrimaryQlClass() { result = "ConstCase" } } /** * A default case of a `switch` statement, for example `default:` on * line 3 in * - * ``` + * ```csharp * switch (instruction) { * case OpCode.Nop: ... * default: ... @@ -324,6 +337,8 @@ class DefaultCase extends CaseStmt, LabeledStmt { override string getLabel() { result = "default" } override string toString() { result = "default:" } + + override string getAPrimaryQlClass() { result = "DefaultCase" } } /** @@ -344,7 +359,7 @@ class LoopStmt extends Stmt, @loop_stmt { /** * A `while` statement, for example * - * ``` + * ```csharp * while (remaining > 0) { * ... * } @@ -354,12 +369,14 @@ class WhileStmt extends LoopStmt, @while_stmt { override Expr getCondition() { result.getParent() = this } override string toString() { result = "while (...) ..." } + + override string getAPrimaryQlClass() { result = "WhileStmt" } } /** * A `do`-`while` statement, for example * - * ``` + * ```csharp * do { * ... * } @@ -370,12 +387,14 @@ class DoStmt extends LoopStmt, @do_stmt { override Expr getCondition() { result.getParent() = this } override string toString() { result = "do ... while (...);" } + + override string getAPrimaryQlClass() { result = "DoStmt" } } /** * A `for` loop, for example * - * ``` + * ```csharp * for (int i = 0; i < 10; i++) { * ... * } @@ -387,7 +406,7 @@ class ForStmt extends LoopStmt, @for_stmt { * * For example, `i = 0` in * - * ``` + * ```csharp * for (int i = 0; i < 10; i++) { * ... * } @@ -401,7 +420,7 @@ class ForStmt extends LoopStmt, @for_stmt { * * For example, the second (`n = 1`) initializer is `j = 10` in * - * ``` + * ```csharp * for (int i = 0, j = 10; i < j; i++) { * ... * } @@ -418,7 +437,7 @@ class ForStmt extends LoopStmt, @for_stmt { * * For example, `i++` in * - * ``` + * ```csharp * for (int i = 0; i < 10; i++) { * ... * } @@ -431,7 +450,7 @@ class ForStmt extends LoopStmt, @for_stmt { * * For example, the second (`n = 1`) update expression is `j--` in * - * ``` + * ```csharp * for (int i = 0, j = 10; i < j; i++, j--) { * ... * } @@ -440,12 +459,14 @@ class ForStmt extends LoopStmt, @for_stmt { Expr getUpdate(int n) { exists(int i | result = this.getChild(i) and n = i - 1 and i >= 1) } override string toString() { result = "for (...;...;...) ..." } + + override string getAPrimaryQlClass() { result = "ForStmt" } } /** * A `foreach` loop, for example * - * ``` + * ```csharp * foreach (var item in items) { * ... * } @@ -457,7 +478,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `item` in * - * ``` + * ```csharp * foreach (var item in items) { * ... * } @@ -470,7 +491,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `var item` in * - * ``` + * ```csharp * foreach (var item in items) { * ... * } @@ -483,7 +504,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `int a` is the 0th local variable declaration in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -499,7 +520,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * Gets the local variable declaration tuple of this `foreach` loop, if any. * For example, `(int a, int b)` in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -512,7 +533,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `a` is the 0th local variable in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -525,7 +546,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `a` and `b` in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -538,7 +559,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `int a` and `int b` in * - * ``` + * ```csharp * foreach ((int a, int b) in items) { * ... * } @@ -553,7 +574,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * * For example, `items` in * - * ``` + * ```csharp * foreach (var item in items) { * ... * } @@ -562,6 +583,8 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { Expr getIterableExpr() { result = this.getChild(1) } override string toString() { result = "foreach (... ... in ...) ..." } + + override string getAPrimaryQlClass() { result = "ForeachStmt" } } /** @@ -576,7 +599,7 @@ class JumpStmt extends Stmt, @jump_stmt { } /** * A `break` statement, for example line 4 in * - * ``` + * ```csharp * while (true) { * ... * if (done) @@ -586,12 +609,14 @@ class JumpStmt extends Stmt, @jump_stmt { } */ class BreakStmt extends JumpStmt, @break_stmt { override string toString() { result = "break;" } + + override string getAPrimaryQlClass() { result = "BreakStmt" } } /** * A `continue` statement, for example line 4 in * - * ``` + * ```csharp * while (true) { * ... * if (!done) @@ -602,6 +627,8 @@ class BreakStmt extends JumpStmt, @break_stmt { */ class ContinueStmt extends JumpStmt, @continue_stmt { override string toString() { result = "continue;" } + + override string getAPrimaryQlClass() { result = "ContinueStmt" } } /** @@ -618,7 +645,7 @@ class GotoStmt extends JumpStmt, @goto_any_stmt { /** * A `goto` statement that jumps to a labeled statement, for example line 4 in * - * ``` + * ```csharp * while (true) { * ... * if (done) @@ -637,6 +664,8 @@ class GotoLabelStmt extends GotoStmt, @goto_stmt { result.getEnclosingCallable() = getEnclosingCallable() and result.getLabel() = getLabel() } + + override string getAPrimaryQlClass() { result = "GotoLabelStmt" } } /** @@ -644,7 +673,7 @@ class GotoLabelStmt extends GotoStmt, @goto_stmt { * * For example, line 5 in * - * ``` + * ```csharp * switch (x) { * case 0 : * return 1; @@ -662,6 +691,8 @@ class GotoCaseStmt extends GotoStmt, @goto_case_stmt { override string getLabel() { result = getExpr().getValue() } override string toString() { result = "goto case ...;" } + + override string getAPrimaryQlClass() { result = "GotoCaseStmt" } } /** @@ -669,7 +700,7 @@ class GotoCaseStmt extends GotoStmt, @goto_case_stmt { * * For example, line 5 in * - * ``` + * ```csharp * switch (x) { * case 0 : * return 1; @@ -684,12 +715,14 @@ class GotoDefaultStmt extends GotoStmt, @goto_default_stmt { override string toString() { result = "goto default;" } override string getLabel() { result = "default" } + + override string getAPrimaryQlClass() { result = "GotoDefaultStmt" } } /** * A `throw` statement, for example line 3 in * - * ``` + * ```csharp * void M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -714,6 +747,8 @@ class ThrowStmt extends JumpStmt, ThrowElement, @throw_stmt { result = mid.getParent() ) } + + override string getAPrimaryQlClass() { result = "ThrowStmt" } } /** @@ -727,7 +762,7 @@ class ExceptionClass extends Class { /** * A `return` statement, for example line 2 in * - * ``` + * ```csharp * int M() { * return 0; * } @@ -738,6 +773,8 @@ class ReturnStmt extends JumpStmt, @return_stmt { Expr getExpr() { result.getParent() = this } override string toString() { result = "return ...;" } + + override string getAPrimaryQlClass() { result = "ReturnStmt" } } /** @@ -754,7 +791,7 @@ class YieldStmt extends JumpStmt, @yield_stmt { /** * A `yield break` statement, for example line 6 in * - * ``` + * ```csharp * IEnumerable DownFrom(int i) { * while (true) { * if (i > 0) @@ -769,12 +806,14 @@ class YieldBreakStmt extends YieldStmt { YieldBreakStmt() { not exists(this.getExpr()) } override string toString() { result = "yield break;" } + + override string getAPrimaryQlClass() { result = "YieldBreakStmt" } } /** * A `yield return` statement, for example line 4 in * - * ``` + * ```csharp * IEnumerable DownFrom(int i) { * while (true) { * if (i > 0) @@ -789,12 +828,14 @@ class YieldReturnStmt extends YieldStmt { YieldReturnStmt() { exists(this.getExpr()) } override string toString() { result = "yield return ...;" } + + override string getAPrimaryQlClass() { result = "YieldReturnStmt" } } /** * A `try` statement, for example * - * ``` + * ```csharp * try { * ... * } @@ -824,6 +865,8 @@ class TryStmt extends Stmt, @try_stmt { override string toString() { result = "try {...} ..." } + override string getAPrimaryQlClass() { result = "TryStmt" } + /** Gets the `catch` clause that handles an exception of type `ex`, if any. */ CatchClause getAnExceptionHandler(ExceptionClass ex) { result = clauseHandlesException(ex, 0) } @@ -900,7 +943,7 @@ class CatchClause extends Stmt, @catch { * Gets the type of the exception caught. For example, the type of the exception * caught on line 4 is `System.IO.IOException` in * - * ``` + * ```csharp * try { * ... * } @@ -915,7 +958,7 @@ class CatchClause extends Stmt, @catch { * Gets the `catch` filter clause, if any. For example, the filter expression * of the catch clause on line 4 is `ex.HResult == 1` in * - * ``` + * ```csharp * try { * ... * } @@ -944,7 +987,7 @@ class CatchClause extends Stmt, @catch { * * For example, the `catch` clause on line 4 in * - * ``` + * ```csharp * try { * ... * } @@ -965,6 +1008,8 @@ class SpecificCatchClause extends CatchClause { LocalVariableDeclExpr getVariableDeclExpr() { result.getParent() = this } override string toString() { result = "catch (...) {...}" } + + override string getAPrimaryQlClass() { result = "SpecificCatchClause" } } /** @@ -972,7 +1017,7 @@ class SpecificCatchClause extends CatchClause { * * For example, the `catch` clause on line 4 in * - * ``` + * ```csharp * try { * ... * } @@ -985,12 +1030,14 @@ class GeneralCatchClause extends CatchClause { GeneralCatchClause() { catch_type(this, _, 2) } override string toString() { result = "catch {...}" } + + override string getAPrimaryQlClass() { result = "GeneralCatchClause" } } /** * A `checked` statement, for example * - * ``` + * ```csharp * checked { * int i = 2147483647; * i++; @@ -1002,12 +1049,14 @@ class CheckedStmt extends Stmt, @checked_stmt { BlockStmt getBlock() { result.getParent() = this } override string toString() { result = "checked {...}" } + + override string getAPrimaryQlClass() { result = "CheckedStmt" } } /** * An `unchecked` statement, for example * - * ``` + * ```csharp * unchecked { * int i = 2147483647; * i++; @@ -1019,12 +1068,14 @@ class UncheckedStmt extends Stmt, @unchecked_stmt { BlockStmt getBlock() { result.getParent() = this } override string toString() { result = "unchecked {...}" } + + override string getAPrimaryQlClass() { result = "UncheckedStmt" } } /** * A `lock` statement, for example * - * ``` + * ```csharp * lock (mutex) { * ... * } @@ -1056,6 +1107,8 @@ class LockStmt extends Stmt, @lock_stmt { /** Gets the type `T` if this statement is of the form `lock(typeof(T)) { ... }`. */ Type getLockTypeObject() { result = getExpr().(TypeofExpr).getTypeAccess().getTarget() } + + override string getAPrimaryQlClass() { result = "LockStmt" } } /** @@ -1074,7 +1127,7 @@ class UsingStmt extends Stmt, @using_stmt { * expression assigned to a variable, for example `File.Open("settings.xml")` * in * - * ``` + * ```csharp * using (FileStream f = File.Open("settings.xml")) { * ... * } @@ -1083,7 +1136,7 @@ class UsingStmt extends Stmt, @using_stmt { * or an expression directly used, for example `File.Open("settings.xml")` * in * - * ``` + * ```csharp * using (File.Open("settings.xml")) { * ... * } @@ -1095,7 +1148,7 @@ class UsingStmt extends Stmt, @using_stmt { /** * A `using` block statement, for example * - * ``` + * ```csharp * using (FileStream f = File.Open("settings.xml")) { * ... * } @@ -1115,7 +1168,7 @@ class UsingBlockStmt extends UsingStmt, @using_block_stmt { * Gets the expression directly used by this `using` statement, if any. For * example, `f` on line 2 in * - * ``` + * ```csharp * var f = File.Open("settings.xml"); * using (f) { * ... @@ -1134,12 +1187,14 @@ class UsingBlockStmt extends UsingStmt, @using_block_stmt { Stmt getBody() { result.getParent() = this } override string toString() { result = "using (...) {...}" } + + override string getAPrimaryQlClass() { result = "UsingBlockStmt" } } /** * A local declaration statement, for example line 2 in * - * ``` + * ```csharp * void M() { * string x = null, y = ""; * } @@ -1150,7 +1205,7 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt { * Gets a local variable declaration, for example `x = null` and * `y = ""` in * - * ``` + * ```csharp * void M() { * string x = null, y = ""; * } @@ -1162,7 +1217,7 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt { * Gets the `n`th local variable declaration. For example, the second * (`n = 1`) declaration is `y = ""` in * - * ``` + * ```csharp * void M() { * string x = null, y = ""; * } @@ -1171,12 +1226,14 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt { LocalVariableDeclExpr getVariableDeclExpr(int n) { result = this.getChild(n) } override string toString() { result = "... ...;" } + + override string getAPrimaryQlClass() { result = "LocalVariableDeclStmt" } } /** * A local constant declaration statement, for example line 2 in * - * ``` + * ```csharp * void M() { * const int x = 1, y = 2; * } @@ -1186,7 +1243,7 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt { /** * Gets a local constant declaration, for example `x = 1` and `y = 2` in * - * ``` + * ```csharp * void M() { * const int x = 1, y = 2; * } @@ -1198,7 +1255,7 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt { * Gets the `n`th local constant declaration. For example, the second * (`n = 1`) declaration is `y = 2` in * - * ``` + * ```csharp * void M() { * const int x = 1, y = 2; * } @@ -1207,12 +1264,14 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt { override LocalConstantDeclExpr getVariableDeclExpr(int n) { result = this.getChild(n) } override string toString() { result = "const ... ...;" } + + override string getAPrimaryQlClass() { result = "LocalConstantDeclStmt" } } /** * A `using` declaration statement, for example * - * ``` + * ```csharp * using FileStream f = File.Open("settings.xml"); * ``` */ @@ -1228,12 +1287,14 @@ class UsingDeclStmt extends LocalVariableDeclStmt, UsingStmt, @using_decl_stmt { } override Expr getAnExpr() { result = this.getAVariableDeclExpr().getInitializer() } + + override string getAPrimaryQlClass() { result = "UsingDeclStmt" } } /** * An empty statement, for example line 2 in * - * ``` + * ```csharp * while (true) do { * ; * } @@ -1241,12 +1302,14 @@ class UsingDeclStmt extends LocalVariableDeclStmt, UsingStmt, @using_decl_stmt { */ class EmptyStmt extends Stmt, @empty_stmt { override string toString() { result = ";" } + + override string getAPrimaryQlClass() { result = "EmptyStmt" } } /** * An `unsafe` statement, for example * - * ``` + * ```csharp * unsafe { * var data = new int[10]; * fixed (int* p = data) { @@ -1260,12 +1323,14 @@ class UnsafeStmt extends Stmt, @unsafe_stmt { BlockStmt getBlock() { result.getParent() = this } override string toString() { result = "unsafe {...}" } + + override string getAPrimaryQlClass() { result = "UnsafeStmt" } } /** * A `fixed` statement, for example lines 3--5 in * - * ``` + * ```csharp * unsafe { * var data = new int[10]; * fixed (int* p = data) { @@ -1291,12 +1356,14 @@ class FixedStmt extends Stmt, @fixed_stmt { Stmt getBody() { result.getParent() = this } override string toString() { result = "fixed(...) { ... }" } + + override string getAPrimaryQlClass() { result = "FixedStmt" } } /** * A label statement, for example line 7 in * - * ``` + * ```csharp * while (true) { * if (done) * goto exit; @@ -1306,7 +1373,9 @@ class FixedStmt extends Stmt, @fixed_stmt { * exit: ... * ``` */ -class LabelStmt extends LabeledStmt, @label_stmt { } +class LabelStmt extends LabeledStmt, @label_stmt { + override string getAPrimaryQlClass() { result = "LabelStmt" } +} /** * A labeled statement. @@ -1319,7 +1388,7 @@ class LabeledStmt extends Stmt, @labeled_stmt { * * For example, the `return` statement in * - * ``` + * ```csharp * exit: * return MetadataToken.Zero; * ``` @@ -1341,7 +1410,7 @@ class LabeledStmt extends Stmt, @labeled_stmt { * A statement defining a local function. For example, * the statement on lines 2--4 in * - * ``` + * ```csharp * int Choose(int n, int m) { * int Fac(int x) { * return x > 1 ? x * Fac(x - 1) : 1; @@ -1356,4 +1425,6 @@ class LocalFunctionStmt extends Stmt, @local_function_stmt { LocalFunction getLocalFunction() { local_function_stmts(this, result) } override string toString() { result = getLocalFunction().getName() + "(...)" } + + override string getAPrimaryQlClass() { result = "LocalFunctionStmt" } } diff --git a/csharp/ql/src/semmle/code/csharp/Type.qll b/csharp/ql/src/semmle/code/csharp/Type.qll index fef53c086415..f3cadbd1237d 100644 --- a/csharp/ql/src/semmle/code/csharp/Type.qll +++ b/csharp/ql/src/semmle/code/csharp/Type.qll @@ -1,14 +1,15 @@ /** Provides classes for types. */ -import Location -import Namespace import Callable -import Property import Event import Generics +import Location +import Namespace +import Property private import Conversion -private import semmle.code.csharp.metrics.Coupling private import dotnet +private import semmle.code.csharp.metrics.Coupling +private import TypeRef /** * A type. @@ -45,6 +46,9 @@ class Type extends DotNet::Type, Member, TypeContainer, @type { predicate isValueType() { none() } } +pragma[nomagic] +private predicate isObjectClass(Class c) { c instanceof ObjectType } + /** * A value or reference type. * @@ -96,7 +100,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ * In the following example, only the class `C2` has a parent namespace declaration * returned by `getParentNamespaceDeclaration`. * - * ``` + * ```csharp * class C1 { ... } * * namespace N { @@ -111,7 +115,15 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ } /** Gets the immediate base class of this class, if any. */ - Class getBaseClass() { extend(this, getTypeRef(result)) } + final Class getBaseClass() { + extend(this, getTypeRef(result)) + or + not extend(this, _) and + not isObjectClass(this) and + not this instanceof DynamicType and + not this instanceof NullType and + isObjectClass(result) + } /** Gets an immediate base interface of this type, if any. */ Interface getABaseInterface() { implement(this, getTypeRef(result)) } @@ -140,7 +152,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ * * For example, `C` has the methods `A.M1()`, `B.M3()`, and `C.M4()` in * - * ``` + * ```csharp * class A { * public void M1() { } * private void M2() { } @@ -165,7 +177,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ * For example, `C` has the callables `A.get_P1`, `A.set_P1`, `A.M2()`, `B.get_P2`, * `B.set_P2`, and `C.M3()` in * - * ``` + * ```csharp * class A { * public int P1 { get; set; } * public virtual int P2 { get; set; } @@ -194,7 +206,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ * * For example, `C` has the members `A.P1`, `A.M2()`, `B.P2`, and `C.M3()` in * - * ``` + * ```csharp * class A { * public int P1 { get; set; } * public virtual int P2 { get; set; } @@ -368,7 +380,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ /** * A nested type, for example `class B` in * - * ``` + * ```csharp * class A { * class B { * ... @@ -608,7 +620,7 @@ class DecimalType extends SimpleType, @decimal_type { /** * An `enum`. For example * - * ``` + * ```csharp * enum Parity { * Even, * Odd @@ -622,7 +634,7 @@ class Enum extends ValueType, @enum_type { * * For example, the underlying type of `Parity` is `int` in * - * ``` + * ```csharp * enum Parity : int { * Even, * Odd @@ -635,11 +647,12 @@ class Enum extends ValueType, @enum_type { * Gets an `enum` constant declared in this `enum`, for example `Even` * and `Odd` in * - * ``` + * ```csharp * enum Parity : int { * Even, * Odd * } + * ``` */ EnumConstant getAnEnumConstant() { result.getDeclaringEnum() = this } @@ -647,12 +660,14 @@ class Enum extends ValueType, @enum_type { EnumConstant getEnumConstant(string value) { result = this.getAnEnumConstant() and result.getValue() = value } + + override string getAPrimaryQlClass() { result = "Enum" } } /** * A `struct`, for example * - * ``` + * ```csharp * struct S { * ... * } @@ -664,6 +679,8 @@ class Struct extends ValueType, @struct_type { /** Holds if this `struct` has a `readonly` modifier. */ predicate isReadonly() { hasModifier("readonly") } + + override string getAPrimaryQlClass() { result = "Struct" } } /** @@ -706,20 +723,22 @@ private predicate isNonOverridden(Member m) { not m.(Virtualizable).isOverridden /** * A `class`, for example * - * ``` + * ```csharp * class C { * ... * } * ``` */ -class Class extends RefType, @class_type { } +class Class extends RefType, @class_type { + override string getAPrimaryQlClass() { result = "Class" } +} /** * A class generated by the compiler from an anonymous object creation. * * For example, the class with fields `X` and `Y` in * - * ``` + * ```csharp * new { X = 0, Y = 0 }; * ``` */ @@ -748,18 +767,20 @@ class StringType extends Class { /** * An `interface`, for example * - * ``` + * ```csharp * interface I { * ... * } * ``` */ -class Interface extends RefType, @interface_type { } +class Interface extends RefType, @interface_type { + override string getAPrimaryQlClass() { result = "Interface" } +} /** * A `delegate` type, for example * - * ``` + * ```csharp * delegate int D(int p); * ``` */ @@ -769,6 +790,8 @@ class DelegateType extends RefType, Parameterizable, @delegate_type { /** Gets the annotated return type of this delegate. */ AnnotatedType getAnnotatedReturnType() { result.appliesTo(this) } + + override string getAPrimaryQlClass() { result = "DelegateType" } } /** @@ -793,6 +816,8 @@ class NullableType extends ValueType, DotNet::ConstructedGeneric, @nullable_type override Location getALocation() { result = getUnderlyingType().getALocation() } override Type getTypeArgument(int p) { p = 0 and result = getUnderlyingType() } + + override string getAPrimaryQlClass() { result = "NullableType" } } /** @@ -874,6 +899,8 @@ class PointerType extends DotNet::PointerType, Type, @pointer_type { override Location getALocation() { result = getReferentType().getALocation() } override string toString() { result = DotNet::PointerType.super.toString() } + + override string getAPrimaryQlClass() { result = "PointerType" } } /** @@ -881,6 +908,8 @@ class PointerType extends DotNet::PointerType, Type, @pointer_type { */ class DynamicType extends RefType, @dynamic_type { override string toStringWithTypes() { result = "dynamic" } + + override string getAPrimaryQlClass() { result = "DynamicType" } } /** @@ -888,6 +917,8 @@ class DynamicType extends RefType, @dynamic_type { */ class ArglistType extends Type, @arglist_type { override string toStringWithTypes() { result = "__arglist" } + + override string getAPrimaryQlClass() { result = "ArglistType" } } /** @@ -901,7 +932,7 @@ class UnknownType extends Type, @unknown_type { } */ class TupleType extends ValueType, @tuple_type { /** Gets the underlying type of this tuple, which is of type `System.ValueTuple`. */ - ConstructedStruct getUnderlyingType() { tuple_underlying_type(this, getTypeRef(result)) } + Struct getUnderlyingType() { tuple_underlying_type(this, getTypeRef(result)) } /** * Gets the `n`th element of this tuple, indexed from 0. @@ -959,7 +990,7 @@ class TypeMention extends @type_mention { * Gets the element to which this type mention belongs, if any. * For example, `IEnumerable` belongs to parameter `p` in * - * ``` + * ```csharp * void M(IEnumerable p) { } * ``` */ @@ -969,7 +1000,7 @@ class TypeMention extends @type_mention { * Gets the parent of this type mention, if any. * For example, the parent of `int` is `IEnumerable` in * - * ``` + * ```csharp * void M(IEnumerable p) { * ... * } @@ -983,15 +1014,3 @@ class TypeMention extends @type_mention { /** Gets the location of this type mention. */ Location getLocation() { type_mention_location(this, result) } } - -/** - * INTERNAL: Do not use. - * Gets a type reference for a given type `type`. - * This is used for extensionals that can be supplied - * as either type references or types. - */ -@type_or_ref getTypeRef(@type type) { - result = type - or - typeref_type(result, type) -} diff --git a/csharp/ql/src/semmle/code/csharp/TypeRef.qll b/csharp/ql/src/semmle/code/csharp/TypeRef.qll new file mode 100644 index 000000000000..1cdf74cf48ae --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/TypeRef.qll @@ -0,0 +1,29 @@ +/** + * INTERNAL: Do not use. + * + * Provides support for type-references. + */ + +import csharp + +/** A typeref is a reference to a type in some assembly. */ +private class TypeRef extends @typeref { + string getName() { typerefs(this, result) } + + string toString() { result = this.getName() } + + Type getReferencedType() { typeref_type(this, result) } +} + +/** + * INTERNAL: Do not use. + * + * Gets a type reference for a given type `type`. + * This is used for extensionals that can be supplied + * as either type references or types. + */ +@type_or_ref getTypeRef(Type type) { + result = type + or + result.(TypeRef).getReferencedType() = type +} diff --git a/csharp/ql/src/semmle/code/csharp/Unification.qll b/csharp/ql/src/semmle/code/csharp/Unification.qll index 48256a59a29c..4c057ad8c030 100644 --- a/csharp/ql/src/semmle/code/csharp/Unification.qll +++ b/csharp/ql/src/semmle/code/csharp/Unification.qll @@ -213,6 +213,10 @@ module Gvn { ) } + predicate isFullyConstructed() { + this.getKind().getNumberOfTypeParameters() - 1 = this.length() + } + GvnType getArg(int i) { exists(GvnType head, ConstructedGvnTypeList tail | this = TConstructedGvnTypeCons(head, tail) @@ -224,47 +228,72 @@ module Gvn { ) } + private GenericType getConstructedGenericDeclaringTypeAt(int i) { + i = 0 and + result = this.getKind().getConstructedSourceDeclaration() + or + result = this.getConstructedGenericDeclaringTypeAt(i - 1).getGenericDeclaringType() + } + + private predicate isDeclaringTypeAt(int i) { + exists(this.getConstructedGenericDeclaringTypeAt(i - 1)) + } + /** - * Gets a textual representation of this constructed type, restricted - * to the prefix `t` of the underlying source declaration type. - * - * The `toString()` calculation needs to be split up into prefixes, in - * order to apply the type arguments correctly. For example, a source - * declaration type `A<>.B.C<,>` applied to types `int, string, bool` - * needs to be printed as `A.B.C`. + * Gets the `j`th `toString()` part of the `i`th nested component of this + * constructed type, if any. The nested components are sorted in reverse + * order, while the individual parts are sorted in normal order. */ language[monotonicAggregates] - private string toStringConstructed(GenericType t) { - t = this.getKind().getConstructedSourceDeclaration().getGenericDeclaringType*() and - exists(int offset, int children, string name, string nameArgs | - offset = t.getNumberOfDeclaringArguments() and - children = t.getNumberOfArgumentsSelf() and - name = getNameNested(t) and - if children = 0 - then nameArgs = name - else - exists(string offsetArgs | - offsetArgs = - concat(int i | - i in [offset .. offset + children - 1] - | - this.getArg(i).toString(), "," order by i - ) and - nameArgs = name.prefix(name.length() - children - 1) + "<" + offsetArgs + ">" + private string toStringConstructedPart(int i, int j) { + this.isFullyConstructed() and + exists(GenericType t | + t = this.getConstructedGenericDeclaringTypeAt(i) and + exists(int offset, int children, string name | + offset = t.getNumberOfDeclaringArguments() and + children = t.getNumberOfArgumentsSelf() and + name = getNameNested(t) and + if children = 0 + then + j = 0 and result = name + or + this.isDeclaringTypeAt(i) and j = 1 and result = "." + else ( + j = 0 and result = name.prefix(name.length() - children - 1) + "<" + or + j in [1 .. 2 * children - 1] and + if j % 2 = 0 + then result = "," + else result = this.getArg((j + 1) / 2 + offset - 1).toString() + or + j = 2 * children and + result = ">" + or + this.isDeclaringTypeAt(i) and + j = 2 * children + 1 and + result = "." ) - | - offset = 0 and result = nameArgs - or - result = this.toStringConstructed(t.getGenericDeclaringType()) + "." + nameArgs + ) ) } language[monotonicAggregates] string toString() { + this.isFullyConstructed() and exists(CompoundTypeKind k | k = this.getKind() | result = k.toStringBuiltin(this.getArg(0).toString()) or - result = this.toStringConstructed(k.getConstructedSourceDeclaration()) + result = + strictconcat(int i, int j, int offset | + exists(GenericType t, int children | + t = this.getConstructedGenericDeclaringTypeAt(i) and + children = t.getNumberOfArgumentsSelf() and + (if this.isDeclaringTypeAt(i) then offset = 1 else offset = 0) and + if children = 0 then j in [0 .. offset] else j in [0 .. 2 * children + offset] + ) + | + this.toStringConstructedPart(i, j) order by i desc, j + ) ) } @@ -482,7 +511,7 @@ module Gvn { newtype TGvnType = TLeafGvnType(LeafType t) or TTypeParameterGvnType() or - TConstructedGvnType(ConstructedGvnTypeList l) + TConstructedGvnType(ConstructedGvnTypeList l) { l.isFullyConstructed() } cached newtype TConstructedGvnTypeList = @@ -685,7 +714,7 @@ module Unification { private import Cached /** - * Holds if types `t1` and `t2` are unifiable. That is, is it possible to replace + * Holds if types `t1` and `t2` are unifiable. That is, it is possible to replace * all type parameters in `t1` and `t2` with some (other) types to make the two * substituted terms equal. * @@ -722,7 +751,7 @@ module Unification { } /** - * Holds if type `t1` subsumes type `t2`. That is, is it possible to replace all + * Holds if type `t1` subsumes type `t2`. That is, it is possible to replace all * type parameters in `t1` with some (other) types to make the two types equal. * * The same limitations that apply to the predicate `unifiable()` apply to this diff --git a/csharp/ql/src/semmle/code/csharp/Using.qll b/csharp/ql/src/semmle/code/csharp/Using.qll index 9b1362cf9dc9..f26958a54585 100644 --- a/csharp/ql/src/semmle/code/csharp/Using.qll +++ b/csharp/ql/src/semmle/code/csharp/Using.qll @@ -5,6 +5,7 @@ */ import Element +private import TypeRef /** * A `using` directive. Either a namespace `using` directive @@ -17,7 +18,7 @@ class UsingDirective extends Element, @using_directive { * * Example: * - * ``` + * ```csharp * using System; * * namespace N { @@ -48,6 +49,8 @@ class UsingNamespaceDirective extends UsingDirective, @using_namespace_directive Namespace getImportedNamespace() { using_namespace_directives(this, result) } override string toString() { result = "using ...;" } + + override string getAPrimaryQlClass() { result = "UsingNamespaceDirective" } } /** @@ -61,4 +64,6 @@ class UsingStaticDirective extends UsingDirective, @using_static_directive { ValueOrRefType getTarget() { using_static_directives(this, getTypeRef(result)) } override string toString() { result = "using static ...;" } + + override string getAPrimaryQlClass() { result = "UsingStaticDirective" } } diff --git a/csharp/ql/src/semmle/code/csharp/Variable.qll b/csharp/ql/src/semmle/code/csharp/Variable.qll index 0d1d462f8888..522bcaf71c94 100644 --- a/csharp/ql/src/semmle/code/csharp/Variable.qll +++ b/csharp/ql/src/semmle/code/csharp/Variable.qll @@ -3,12 +3,13 @@ * constants. */ -import Element +import Assignable import Callable +import Element import Type -import Assignable private import dotnet private import semmle.code.csharp.ExprOrStmtParent +private import TypeRef /** * A variable. Either a variable with local scope (`LocalScopeVariable`) or a field (`Field`). @@ -36,7 +37,7 @@ class LocalScopeVariable extends Variable, @local_scope_variable { * Holds if this variable is captured by a nested callable. For example, * `v` is captured by the nested lambda expression in * - * ``` + * ```csharp * void M() { * var v = "captured"; * Action a = () => { @@ -51,7 +52,7 @@ class LocalScopeVariable extends Variable, @local_scope_variable { * Gets a callable that captures this variable, if any. For example, * `v` is captured by the nested lambda expression in * - * ``` + * ```csharp * void M() { * var v = "captured"; * Action a = () => { @@ -77,7 +78,7 @@ class LocalScopeVariable extends Variable, @local_scope_variable { * A parameter of a parameterizable declaration (callable, delegate, or indexer). * For example, `p` in * - * ``` + * ```csharp * void M(int p) { * ... * } @@ -89,7 +90,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Gets the position of this parameter. For example, the position of `x` is * 0 and the position of `y` is 1 in * - * ``` + * ```csharp * void M(int x, int y) { * ... * } @@ -103,7 +104,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is a normal value parameter. For example, `p` * is a value parameter in * - * ``` + * ```csharp * void M(int p) { * ... * } @@ -115,7 +116,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is a reference parameter. For example, `p` * is a reference parameter in * - * ``` + * ```csharp * void M(ref int p) { * ... * } @@ -127,7 +128,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is an output parameter. For example, `p` * is an output parameter in * - * ``` + * ```csharp * void M(out int p) { * ... * } @@ -139,7 +140,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is a value type that is passed in by reference. * For example, `p` is an input parameter in * - * ``` + * ```csharp * void M(in int p) { * ... * } @@ -154,7 +155,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * Holds if this parameter is a parameter array. For example, `args` * is a parameter array in * - * ``` + * ```csharp * void M(params string[] args) { * ... * } @@ -167,7 +168,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * For example, `list` is the first parameter of the extension method * `Count` in * - * ``` + * ```csharp * static int Count(this IEnumerable list) { * ... * } @@ -194,11 +195,13 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top override string toString() { result = this.getName() } + override string getAPrimaryQlClass() { result = "Parameter" } + /** * Gets the default value of this parameter, if any. For example, the * default value of `numberOfTries` is `3` in * - * ``` + * ```csharp * void Connect(int numberOfTries = 3) { * ... * } @@ -220,7 +223,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * * Example: * - * ``` + * ```csharp * class C { * void M(int x, int y = 2, int z = 3) { } * @@ -249,7 +252,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * special `value` parameter. For example, the `value` parameter of * `set_ReadOnly` in * - * ``` + * ```csharp * public bool ReadOnly { * get { * return flags.HasValue(Attribute.ReadOnly); @@ -272,7 +275,7 @@ class ImplicitAccessorParameter extends Parameter { * A local variable, declared within the scope of a callable. For example, * the variables `total` and `s` in * - * ``` + * ```csharp * void M(string[] ss) { * int total = 0; * ... @@ -290,7 +293,7 @@ class LocalVariable extends LocalScopeVariable, @local_variable { * For example, the initializer of `total` is `0`, and `s` has no * initializer, in * - * ``` + * ```csharp * void M(string[] ss) { * int total = 0; * ... @@ -305,7 +308,7 @@ class LocalVariable extends LocalScopeVariable, @local_variable { * Holds if this variable is implicitly typed. For example, the variable * `s` is implicitly type, and the variable `total` is not, in * - * ``` + * ```csharp * void M(string[] ss) { * int total = 0; * ... @@ -338,7 +341,7 @@ class LocalVariable extends LocalScopeVariable, @local_variable { * A local constant, modeled as a special kind of local variable. For example, * the local constant `maxTries` in * - * ``` + * ```csharp * void M() { * const int maxTries = 10; * ... @@ -356,7 +359,7 @@ class LocalConstant extends LocalVariable, @local_constant { /** * A field. For example, the fields `x` and `y` in * - * ``` + * ```csharp * struct Coord { * public int x, y; * } @@ -368,7 +371,7 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent * Gets the initial value of this field, if any. For example, the initial * value of `F` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int F = 20; * } @@ -380,7 +383,7 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent * Holds if this field has an initial value. For example, the initial * value of `F` on line 2 is `20` in * - * ``` + * ```csharp * class C { * public int F = 20; * } @@ -407,13 +410,15 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent override Location getALocation() { field_location(this, result) } override string toString() { result = Variable.super.toString() } + + override string getAPrimaryQlClass() { result = "Field" } } /** * A member constant, modeled a special kind of field. For example, * the constant `Separator` in * - * ``` + * ```csharp * class Path { * const char Separator = `\\`; * ... @@ -428,7 +433,7 @@ class MemberConstant extends Field, @constant { /** * An `enum` member constant. For example, `ReadOnly` and `Shared` in * - * ``` + * ```csharp * enum Attribute { * ReadOnly = 1, * Shared = 2 @@ -445,7 +450,7 @@ class EnumConstant extends MemberConstant { * Gets the underlying integral type of this `enum` constant. For example, * the underlying type of `Attribute` is `byte` in * - * ``` + * ```csharp * enum Attribute : byte { * ReadOnly = 1, * Shared = 2 @@ -460,7 +465,7 @@ class EnumConstant extends MemberConstant { * In this example, `ReadOnly` has an explicit value but * `Shared` does not have an explicit value. * - * ``` + * ```csharp * enum Attribute { * ReadOnly = 1, * Shared diff --git a/csharp/ql/src/semmle/code/csharp/commons/Assertions.qll b/csharp/ql/src/semmle/code/csharp/commons/Assertions.qll index a51ed63e49b0..bf29195547f2 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/Assertions.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/Assertions.qll @@ -52,7 +52,7 @@ class Assertion extends MethodCall { * Moreover, this assertion corresponds to multiple control flow nodes, * which is why * - * ``` + * ```ql * exists(BasicBlock bb | * bb.getANode() = this.getAControlFlowNode() | * bb.immediatelyDominates(succ) @@ -62,7 +62,7 @@ class Assertion extends MethodCall { * does not work. */ pragma[nomagic] - private predicate immediatelyDominatesBlockSplit(BasicBlock succ) { + deprecated private predicate immediatelyDominatesBlockSplit(BasicBlock succ) { // Only calculate dominance by explicit recursion for split nodes; // all other nodes can use regular CFG dominance this instanceof ControlFlow::Internal::SplitControlFlowElement and @@ -78,11 +78,11 @@ class Assertion extends MethodCall { } pragma[noinline] - private predicate strictlyDominatesJoinBlockPredecessor(JoinBlock jb, int i) { + deprecated private predicate strictlyDominatesJoinBlockPredecessor(JoinBlock jb, int i) { this.strictlyDominatesSplit(jb.getJoinBlockPredecessor(i)) } - private predicate strictlyDominatesJoinBlockSplit(JoinBlock jb, int i) { + deprecated private predicate strictlyDominatesJoinBlockSplit(JoinBlock jb, int i) { i = -1 and this.strictlyDominatesJoinBlockPredecessor(jb, _) or @@ -95,12 +95,12 @@ class Assertion extends MethodCall { } pragma[nomagic] - private predicate strictlyDominatesSplit(BasicBlock bb) { + deprecated private predicate strictlyDominatesSplit(BasicBlock bb) { this.immediatelyDominatesBlockSplit(bb) or // Equivalent with // - // ``` + // ```ql // exists(JoinBlockPredecessor pred | pred = bb.getAPredecessor() | // this.strictlyDominatesSplit(pred) // ) and @@ -121,6 +121,8 @@ class Assertion extends MethodCall { } /** + * DEPRECATED: Use `getExpr().controlsBlock()` instead. + * * Holds if this assertion strictly dominates basic block `bb`. That is, `bb` * can only be reached from the callable entry point by going via *some* basic * block containing this element. @@ -130,7 +132,7 @@ class Assertion extends MethodCall { * in that it takes control flow splitting into account. */ pragma[nomagic] - predicate strictlyDominates(BasicBlock bb) { + deprecated predicate strictlyDominates(BasicBlock bb) { this.strictlyDominatesSplit(bb) or this.getAControlFlowNode().getBasicBlock().strictlyDominates(bb) @@ -288,7 +290,7 @@ class ForwarderAssertMethod extends AssertMethod { ForwarderAssertMethod() { p = this.getAParameter() and strictcount(AssignableDefinition def | def.getTarget() = p) = 1 and - forex(ControlFlowElement body | body = this.getABody() | + forex(ControlFlowElement body | body = this.getBody() | bodyAsserts(this, body, a) and a.getExpr() = p.getAnAccess() ) @@ -306,7 +308,7 @@ class ForwarderAssertMethod extends AssertMethod { pragma[noinline] private predicate bodyAsserts(Callable c, ControlFlowElement body, Assertion a) { - c.getABody() = body and + c.getBody() = body and body = getAnAssertingElement(a) } diff --git a/csharp/ql/src/semmle/code/csharp/commons/Collections.qll b/csharp/ql/src/semmle/code/csharp/commons/Collections.qll index 412b33a703b0..df39b7fa96b6 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/Collections.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/Collections.qll @@ -143,8 +143,8 @@ private predicate readonlyAccess(Access a) { // A read-only method call exists(MethodCall mc | mc.getQualifier() = a | mc.getTarget().hasName(readonlyMethodName())) or - // Any property access - a = any(PropertyAccess pa).getQualifier() + // Any property read + a = any(PropertyRead pr).getQualifier() or // An element read a = any(ElementRead er).getQualifier() diff --git a/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll b/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll index 4a485ba767e7..ea7d250abb07 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll @@ -2,7 +2,7 @@ * Provides classes for capturing various ways of performing comparison tests. */ -import csharp +private import csharp private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.system.Collections private import semmle.code.csharp.frameworks.system.collections.Generic @@ -348,11 +348,12 @@ class CompareMethodCallComparisonTest extends ComparisonTest, TCompareCall { } * A comparison test using a user-defined comparison operator, for example * `this == other` on line 3 in * - * ``` + * ```csharp * public class C { * public static bool operator ==(C lhs, C rhs) => true; * public bool Is(C other) => this == other; * } + * ``` */ class OperatorCallComparisonTest extends ComparisonTest, TComparisonOperatorCall { } diff --git a/csharp/ql/src/semmle/code/csharp/commons/Constants.qll b/csharp/ql/src/semmle/code/csharp/commons/Constants.qll index 7815890b51a3..0e01f7f7ab76 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/Constants.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/Constants.qll @@ -23,7 +23,7 @@ predicate isConstantCondition(Expr e, boolean b) { * Holds if comparison operation `co` is constant with the Boolean value `b`. * For example, the comparison `x > x` is constantly `false` in * - * ``` + * ```csharp * int MaxWrong(int x, int y) => x > x ? x : y; * ``` */ diff --git a/csharp/ql/src/semmle/code/csharp/commons/Strings.qll b/csharp/ql/src/semmle/code/csharp/commons/Strings.qll index a222c74281b3..4e007d61737e 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/Strings.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/Strings.qll @@ -12,7 +12,7 @@ private import semmle.code.csharp.frameworks.system.Text * invocation will take place, unless the expression is already a string. * For example, `o` and `o.ToString()` on lines 2 and 3, respectively, in * - * ``` + * ```csharp * void Hello(object o) { * Console.WriteLine("Hello, " + o); * Console.WriteLine("Hello, " + o.ToString()); diff --git a/csharp/ql/src/semmle/code/csharp/commons/TargetFramework.qll b/csharp/ql/src/semmle/code/csharp/commons/TargetFramework.qll index c6532eebcb3e..83de1b9d2949 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/TargetFramework.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/TargetFramework.qll @@ -6,7 +6,7 @@ import csharp * An attribute of type `System.Runtime.Versioning.TargetFrameworkAttribute`, * specifying the target framework of an assembly. For example * - * ``` + * ```csharp * [assembly: TargetFramework(".NETFramework,Version=v4.6.1")] * ``` */ @@ -20,7 +20,7 @@ class TargetFrameworkAttribute extends Attribute { /** * Gets the framework name of this attribute. For example, the framework name of - * ``` + * ```csharp * [assembly: TargetFramework(".NETFramework,Version=v4.6.1")] * ``` * is `".NETFramework,Version=v4.6.1"`. @@ -33,7 +33,7 @@ class TargetFrameworkAttribute extends Attribute { /** * Gets the framework type of this attribute. For example, the framework type of - * ``` + * ```csharp * [assembly: TargetFramework(".NETFramework,Version=v4.6.1")] * ``` * is `".NETFramework"`. Other framework types include `".NETStandard"` and `".NETCoreApp"`. @@ -42,7 +42,7 @@ class TargetFrameworkAttribute extends Attribute { /** * Gets the framework version of this attribute. For example, the framework version of - * ``` + * ```csharp * [assembly: TargetFramework(".NETFramework,Version=v4.6.1")] * ``` * is `"4.6.1"`. Note that you can use the `Version` class to compare versions, for example diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll index 03f98b36d32d..9dc2c30eb8eb 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -34,7 +34,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (x < 0) * x = -x; * ``` @@ -52,7 +52,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (!(x >= 0)) * x = -x; * ``` @@ -89,7 +89,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -112,7 +112,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -134,7 +134,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -161,7 +161,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * if (x < 0) { * x = -x; * if (x > 10) @@ -195,7 +195,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -219,7 +219,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * try { * return s.Length; @@ -244,7 +244,7 @@ class BasicBlock extends TBasicBlockStart { * * Example: * - * ``` + * ```csharp * int M(string s) { * try { * return s.Length; @@ -447,7 +447,7 @@ class ConditionBlock extends BasicBlock { * all predecessors of `this.getATrueSuccessor()` are either `this` or dominated by `this.getATrueSuccessor()`. * * For example, in the following C# snippet: - * ``` + * ```csharp * if (x) * controlled; * false_successor; @@ -455,7 +455,7 @@ class ConditionBlock extends BasicBlock { * ``` * `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`) * or dominated by itself. Whereas in the following code: - * ``` + * ```csharp * if (x) * while (controlled) * also_controlled; diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll index a8ba5f858933..7f0a3666b298 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll @@ -118,7 +118,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { * Moreover, this control flow element corresponds to multiple control flow nodes, * which is why * - * ``` + * ```ql * exists(ConditionBlock cb | * cb.getLastNode() = this.getAControlFlowNode() | * cb.immediatelyControls(succ, s) @@ -126,43 +126,51 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { * ``` * * does not work. + * + * `cb` records all of the possible condition blocks for this control flow element + * that a path from the callable entry point to `succ` may go through. */ pragma[nomagic] - private predicate immediatelyControlsBlockSplit(BasicBlock succ, ConditionalSuccessor s) { - exists(ConditionBlock cb | this.immediatelyControlsBlockSplit0(cb, succ, s) | - forall(BasicBlock pred, SuccessorType t | - this.immediatelyControlsBlockSplit1(cb, succ, s, pred, t) - | - this.immediatelyControlsBlockSplit2(cb, succ, s, pred, t) - ) + private predicate immediatelyControlsBlockSplit( + BasicBlock succ, ConditionalSuccessor s, ConditionBlock cb + ) { + this.immediatelyControlsBlockSplit0(cb, succ, s) and + forall(BasicBlock pred, SuccessorType t | + this.immediatelyControlsBlockSplit1(cb, succ, s, pred, t) + | + this.immediatelyControlsBlockSplit2(cb, succ, s, pred, t) ) } pragma[noinline] - private predicate controlsJoinBlockPredecessor(JoinBlock controlled, ConditionalSuccessor s, int i) { - this.controlsBlockSplit(controlled.getJoinBlockPredecessor(i), s) + private predicate controlsJoinBlockPredecessor( + JoinBlock controlled, ConditionalSuccessor s, int i, ConditionBlock cb + ) { + this.controlsBlockSplit(controlled.getJoinBlockPredecessor(i), s, cb) } private predicate controlsJoinBlockSplit(JoinBlock controlled, ConditionalSuccessor s, int i) { i = -1 and - this.controlsJoinBlockPredecessor(controlled, s, _) + this.controlsJoinBlockPredecessor(controlled, s, _, _) or this.controlsJoinBlockSplit(controlled, s, i - 1) and ( - this.controlsJoinBlockPredecessor(controlled, s, i) + this.controlsJoinBlockPredecessor(controlled, s, i, _) or controlled.dominates(controlled.getJoinBlockPredecessor(i)) ) } cached - private predicate controlsBlockSplit(BasicBlock controlled, ConditionalSuccessor s) { + private predicate controlsBlockSplit( + BasicBlock controlled, ConditionalSuccessor s, ConditionBlock cb + ) { Stages::GuardsStage::forceCachingInSameStage() and - this.immediatelyControlsBlockSplit(controlled, s) + this.immediatelyControlsBlockSplit(controlled, s, cb) or // Equivalent with // - // ``` + // ```ql // exists(JoinBlockPredecessor pred | pred = controlled.getAPredecessor() | // this.controlsBlockSplit(pred, s) // ) and @@ -178,10 +186,11 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { last = max(int i | exists(controlled.(JoinBlock).getJoinBlockPredecessor(i))) | this.controlsJoinBlockSplit(controlled, s, last) - ) + ) and + this.controlsJoinBlockPredecessor(controlled, s, _, cb) or not controlled instanceof JoinBlock and - this.controlsBlockSplit(controlled.getAPredecessor(), s) + this.controlsBlockSplit(controlled.getAPredecessor(), s, cb) } /** @@ -192,7 +201,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { * * This predicate is different from * - * ``` + * ```ql * exists(ConditionBlock cb | * cb.getLastNode() = this.getAControlFlowNode() | * cb.controls(controlled, s) @@ -200,23 +209,32 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { * ``` * * as control flow splitting is taken into account. + * + * `cb` records all of the possible condition blocks for this control flow element + * that a path from the callable entry point to `controlled` may go through. */ - predicate controlsBlock(BasicBlock controlled, ConditionalSuccessor s) { - this.controlsBlockSplit(controlled, s) + predicate controlsBlock(BasicBlock controlled, ConditionalSuccessor s, ConditionBlock cb) { + this.controlsBlockSplit(controlled, s, cb) or - exists(ConditionBlock cb | cb.getLastNode() = this.getAControlFlowNode() | - cb.controls(controlled, s) - ) + cb.getLastNode() = this.getAControlFlowNode() and + cb.controls(controlled, s) + } + + /** DEPRECATED: Use `controlsBlock/3` instead. */ + deprecated predicate controlsBlock(BasicBlock controlled, ConditionalSuccessor s) { + this.controlsBlock(controlled, s, _) } /** + * DEPRECATED. + * * Holds if control flow element `controlled` is controlled by this control flow * element with conditional value `s`. That is, `controlled` can only be reached * from the callable entry point by going via the `s` edge out of this element. * * This predicate is different from * - * ``` + * ```ql * exists(ConditionBlock cb | * cb.getLastNode() = this.getAControlFlowNode() | * cb.controls(controlled.getAControlFlowNode().getBasicBlock(), s) @@ -227,7 +245,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { */ // potentially very large predicate, so must be inlined pragma[inline] - predicate controlsElement(ControlFlowElement controlled, ConditionalSuccessor s) { + deprecated predicate controlsElement(ControlFlowElement controlled, ConditionalSuccessor s) { forex(BasicBlock bb | bb = controlled.getAControlFlowNode().getBasicBlock() | this.controlsBlock(bb, s) ) diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll index fdf6f986554d..5ba6486fd7a1 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll @@ -48,7 +48,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * int M(string s) * { * if (s == null) @@ -80,7 +80,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * int M(string s) * { * if (s == null) @@ -113,7 +113,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * int M(string s) * { * try @@ -151,7 +151,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * int M(string s) * { * try @@ -201,7 +201,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * if (x < 0) * x = -x; * ``` @@ -221,7 +221,7 @@ module ControlFlow { * * Example: * - * ``` + * ```csharp * if (!(x >= 0)) * x = -x; * ``` @@ -318,7 +318,7 @@ module ControlFlow { class BooleanSplit = BooleanSplitting::BooleanSplitImpl; - class LoopUnrollingSplit = LoopUnrollingSplitting::LoopUnrollingSplitImpl; + class LoopSplit = LoopSplitting::LoopSplitImpl; } class BasicBlock = BBs::BasicBlock; @@ -517,7 +517,6 @@ module ControlFlow { e = any(QualifiableExpr qe | not qe instanceof ExtensionMethodCall and - not qe.isConditional() and result = qe.getChild(i) ) or @@ -557,7 +556,7 @@ module ControlFlow { this.hasQualifier() or // Member initializers like - // ``` + // ```csharp // new Dictionary() { [0] = "Zero", [1] = "One", [2] = "Two" } // ``` // need special treatment, because the the accesses `[0]`, `[1]`, and `[2]` @@ -582,7 +581,7 @@ module ControlFlow { * that the accessor is called *after* the assigned value has been evaluated. * In the example above, this means we want a CFG that looks like * - * ``` + * ```csharp * x -> 0 -> set_Prop -> x.Prop = 0 * ``` */ @@ -656,17 +655,16 @@ module ControlFlow { cfe = any(AssignOperationWithExpandedAssignment a | result = first(a.getExpandedAssignment())) or - cfe = any(ConditionallyQualifiedExpr cqe | result = first(cqe.getChildExpr(-1))) + cfe = any(ConditionallyQualifiedExpr cqe | result = first(getExprChildElement(cqe, 0))) or cfe = any(ArrayCreation ac | - if ac.isImplicitlySized() - then - // No length argument: element itself - result = ac - else - // First element of first length argument - result = first(ac.getLengthArgument(0)) + // First element of first length argument + result = first(ac.getLengthArgument(0)) + or + // No length argument: element itself + not exists(ac.getLengthArgument(0)) and + result = ac ) or cfe = @@ -882,7 +880,7 @@ module ControlFlow { c = getValidSelfCompletion(result) or // Qualifier exits with a `null` completion - result = cqe.getChildExpr(-1) and + result = getExprChildElement(cqe, 0) and c = TRec(TLastRecSpecificCompletion(any(NullnessCompletion nc | nc.isNull()))) ) or @@ -1454,16 +1452,16 @@ module ControlFlow { ) or exists(ConditionallyQualifiedExpr parent, int i | - cfe = last(parent.getChildExpr(i), c) and + cfe = last(getExprChildElement(parent, i), c) and c instanceof NormalCompletion and - not c.(NullnessCompletion).isNull() + if i = 0 then c.(NullnessCompletion).isNonNull() else any() | // Post-order: flow from last element of last child to element itself - i = max(int j | exists(parent.getChildExpr(j))) and + i = max(int j | exists(getExprChildElement(parent, j))) and result = parent or // Standard left-to-right evaluation - result = first(parent.getChildExpr(i + 1)) + result = first(getExprChildElement(parent, i + 1)) ) or // Post-order: flow from last element of thrown expression to expression itself diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll index 14438ff14185..3bd57b848e55 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll @@ -31,6 +31,25 @@ class Guard extends Expr { predicate controlsNode(ControlFlow::Nodes::ElementNode cfn, AccessOrCallExpr sub, AbstractValue v) { isGuardedByNode(cfn, this, sub, v) } + + /** + * Holds if basic block `bb` is guarded by this expression having value `v`. + */ + predicate controlsBasicBlock(BasicBlock bb, AbstractValue v) { + Internal::guardControls(this, _, bb, v) + } + + /** + * Holds if this guard is an equality test between `e1` and `e2`. If the test is + * negated, that is `!=`, then `polarity` is false, otherwise `polarity` is + * true. + */ + predicate isEquality(Expr e1, Expr e2, boolean polarity) { + exists(BooleanValue v | + this = Internal::getAnEqualityCheck(e1, v, e2) and + polarity = v.getValue() + ) + } } /** An abstract value. */ @@ -62,7 +81,7 @@ abstract class AbstractValue extends TAbstractValue { * * Such values only propagate through adjacent reads, for example, in * - * ``` + * ```csharp * int M() * { * var x = new string[]{ "a", "b", "c" }.ToList(); @@ -212,7 +231,12 @@ module AbstractValues { c.isValidFor(cfe) and foreachEmptiness(fs, cfe) and e = fs.getIterableExpr() - ) + ) and + // Only when taking the non-empty successor do we know that the original iterator + // expression was non-empty. When taking the empty successor, we may have already + // iterated through the `foreach` loop zero or more times, hence the iterator + // expression can be both empty and non-empty + this.isNonEmpty() } override EmptyCollectionValue getDualValue() { @@ -350,7 +374,7 @@ class DereferenceableExpr extends Expr { * * For example, if the case statement `case string s` matches in * - * ``` + * ```csharp * switch (o) * { * case string s: @@ -562,7 +586,7 @@ class AccessOrCallExpr extends Expr { * * Examples: * - * ``` + * ```csharp * x.Foo.Bar(); // SSA qualifier: SSA definition for `x.Foo` * x.Bar(); // SSA qualifier: SSA definition for `x` * x.Foo().Bar(); // SSA qualifier: SSA definition for `x` @@ -588,14 +612,11 @@ private Ssa::Definition getAnSsaQualifier(Expr e, ControlFlow::Node cfn) { } private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Node cfn) { - ( - result = def.getAReadAtNode(cfn) - or - result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() and - result.getAControlFlowNode() = cfn and - cfn.getBasicBlock() = def.getBasicBlock() - ) and + result = def.getAReadAtNode(cfn) and not def instanceof Ssa::ImplicitUntrackedDefinition + or + result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() and + cfn = def.getControlFlowNode() } /** @@ -607,7 +628,7 @@ private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Nod * * For example, the property call `x.Field.Property` on line 3 is guarded in * - * ``` + * ```csharp * string M(C x) { * if (x.Field.Property != null) * return x.Field.Property.ToString(); @@ -621,7 +642,7 @@ private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Nod * guard, whereas the null-guard on `stack.Pop()` on line 4 is not (invoking * `Pop()` twice on a stack does not yield the same result): * - * ``` + * ```csharp * string M(Stack stack) { * if (stack == null) * return ""; @@ -686,7 +707,7 @@ class GuardedExpr extends AccessOrCallExpr { * into account. That is, one control flow node belonging to an expression may * be guarded, while another split need not be guarded: * - * ``` + * ```csharp * if (b) * if (x == null) * return; @@ -736,7 +757,7 @@ class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode { * is, one data flow node belonging to an expression may be guarded, while another * split need not be guarded: * - * ``` + * ```csharp * if (b) * if (x == null) * return; @@ -876,6 +897,8 @@ module Internal { not e.(QualifiableExpr).isConditional() or e instanceof SuppressNullableWarningExpr + or + e.stripCasts().getType() = any(ValueType t | not t instanceof NullableType) } /** Holds if expression `e2` is a non-`null` value whenever `e1` is. */ @@ -936,44 +959,6 @@ module Internal { e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand()) } - /** Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. */ - private predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) { - exists(ControlFlowElement cfe, ConditionalSuccessor s, AbstractValue v0, Guard g0 | - cfe.controlsBlock(bb, s) - | - v0.branch(cfe, s, g0) and - impliesSteps(g0, v0, g, v) - ) - } - - /** - * Holds if control flow node `cfn` only is reached when guard `g` evaluates to `v`, - * because of an assertion. - */ - private predicate guardAssertionControlsNode(Guard g, ControlFlow::Node cfn, AbstractValue v) { - exists(Assertion a, Guard g0, AbstractValue v0 | - asserts(a, g0, v0) and - impliesSteps(g0, v0, g, v) - | - a.strictlyDominates(cfn.getBasicBlock()) - or - exists(BasicBlock bb, int i, int j | bb.getNode(i) = a.getAControlFlowNode() | - bb.getNode(j) = cfn and - j > i - ) - ) - } - - /** - * Holds if control flow element `cfe` only is reached when guard `g` evaluates to `v`, - * because of an assertion. - */ - private predicate guardAssertionControlsElement(Guard g, ControlFlowElement cfe, AbstractValue v) { - forex(ControlFlow::Node cfn | cfn = cfe.getAControlFlowNode() | - guardAssertionControlsNode(g, cfn, v) - ) - } - /** Same as `this.getAChildExpr*()`, but avoids `fastTC`. */ private Expr getAChildExprStar(Guard g) { result = g @@ -981,26 +966,6 @@ module Internal { result = getAChildExprStar(g).getAChildExpr() } - /** - * Holds if assertion `a` directly asserts that expression `e` evaluates to value `v`. - */ - predicate asserts(Assertion a, Expr e, AbstractValue v) { - e = a.getExpr() and - ( - a.getAssertMethod() instanceof AssertTrueMethod and - v.(BooleanValue).getValue() = true - or - a.getAssertMethod() instanceof AssertFalseMethod and - v.(BooleanValue).getValue() = false - or - a.getAssertMethod() instanceof AssertNullMethod and - v.(NullValue).isNull() - or - a.getAssertMethod() instanceof AssertNonNullMethod and - v.(NullValue).isNonNull() - ) - } - private Expr stripConditionalExpr(Expr e) { e = any(ConditionalExpr ce | @@ -1262,7 +1227,7 @@ module Internal { * * For example, if the case statement `case ""` matches in * - * ``` + * ```csharp * switch (o) * { * case "": @@ -1284,24 +1249,6 @@ module Internal { ) } - /** - * Gets an expression that tests whether expression `e1` is equal to - * expression `e2`. - * - * If the returned expression has abstract value `v`, then expression `e1` is - * guaranteed to be equal to `e2`, and if the returned expression has abstract - * value `v.getDualValue()`, then this expression is guaranteed to be - * non-equal to `e`. - * - * For example, if the expression `x != ""` evaluates to `false` then the - * expression `x` is guaranteed to be equal to `""`. - */ - Expr getAnEqualityCheck(Expr e1, AbstractValue v, Expr e2) { - result = getABooleanEqualityCheck(e1, v, e2) - or - result = getAMatchingEqualityCheck(e1, v, e2) - } - private Expr getAnEqualityCheckVal(Expr e, AbstractValue v, AbstractValue vExpr) { result = getAnEqualityCheck(e, v, vExpr.getAnExpr()) } @@ -1448,8 +1395,6 @@ module Internal { or val.branch(_, _, e) or - asserts(_, e, val) - or e instanceof CollectionExpr and val = TEmptyCollectionValue(_) ) and @@ -1457,6 +1402,29 @@ module Internal { not e = any(LocalVariableDeclStmt s).getAVariableDeclExpr() } + /** + * Gets an expression that tests whether expression `e1` is equal to + * expression `e2`. + * + * If the returned expression has abstract value `v`, then expression `e1` is + * guaranteed to be equal to `e2`, and if the returned expression has abstract + * value `v.getDualValue()`, then this expression is guaranteed to be + * non-equal to `e`. + * + * For example, if the expression `x != ""` evaluates to `false` then the + * expression `x` is guaranteed to be equal to `""`. + */ + cached + Expr getAnEqualityCheck(Expr e1, AbstractValue v, Expr e2) { + result = getABooleanEqualityCheck(e1, v, e2) + or + result = getABooleanEqualityCheck(e2, v, e1) + or + result = getAMatchingEqualityCheck(e1, v, e2) + or + result = getAMatchingEqualityCheck(e2, v, e1) + } + cached predicate isCustomNullCheck(Call call, Expr arg, BooleanValue v, boolean isNull) { exists(Callable callable, Parameter p | @@ -1739,11 +1707,7 @@ module Internal { pragma[noinline] private predicate candidateAux(AccessOrCallExpr e, Declaration target, BasicBlock bb) { target = e.getTarget() and - exists(Guard g | e = getAChildExprStar(g) | - guardControls(g, bb, _) - or - guardAssertionControlsNode(g, bb.getANode(), _) - ) + exists(Guard g | e = getAChildExprStar(g) | guardControls(g, _, bb, _)) } } @@ -1751,27 +1715,52 @@ module Internal { private module Cached { private import semmle.code.csharp.Caching + /** + * Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. + * + * `cb` records all of the possible condition blocks for `g` that a path from the + * callable entry point to `bb` may go through. + */ + cached + predicate guardControls(Guard g, ConditionBlock cb, BasicBlock bb, AbstractValue v) { + exists(AbstractValue v0, Guard g0 | + impliesSteps(g0, v0, g, v) and + exists(ControlFlowElement cfe, ConditionalSuccessor cs | + v0.branch(cfe, cs, g0) and cfe.controlsBlock(bb, cs, cb) + ) + ) + } + pragma[noinline] - private predicate isGuardedByNode0( - ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, - AbstractValue v + private predicate nodeIsGuardedBySameSubExpr0( + ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ConditionBlock cb, + AccessOrCallExpr sub, AbstractValue v ) { Stages::GuardsStage::forceCachingInSameStage() and - cfn = guarded.getAControlFlowNode() and - guardControls(g, cfn.getBasicBlock(), v) and + guardedCfn = guarded.getAControlFlowNode() and + guardControls(g, cb, guardedCfn.getBasicBlock(), v) and exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded)) } pragma[noinline] - private predicate isGuardedByExpr1( - AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v + private predicate nodeIsGuardedBySameSubExpr( + ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ConditionBlock cb, + AccessOrCallExpr sub, AbstractValue v ) { - forex(ControlFlow::Node cfn | cfn = guarded.getAControlFlowNode() | - isGuardedByNode0(cfn, guarded, g, sub, v) + nodeIsGuardedBySameSubExpr0(guardedCfn, guarded, g, cb, sub, v) and + sub = getAChildExprStar(g) + } + + pragma[noinline] + private predicate nodeIsGuardedBySameSubExprSsaDef( + ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, ControlFlow::Node subCfn, + AccessOrCallExpr sub, AbstractValue v, Ssa::Definition def + ) { + exists(ConditionBlock cb | + nodeIsGuardedBySameSubExpr(cfn, guarded, g, cb, sub, v) and + subCfn.getBasicBlock().dominates(cb) and + def = sub.getAnSsaQualifier(subCfn) ) - or - guardAssertionControlsElement(g, guarded, v) and - exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded)) } private predicate adjacentReadPairSameVarUniquePredecessor( @@ -1785,47 +1774,45 @@ module Internal { ) } + pragma[noinline] + private predicate isGuardedByExpr0( + AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v + ) { + forex(ControlFlow::Node cfn | cfn = guarded.getAControlFlowNode() | + nodeIsGuardedBySameSubExpr(cfn, guarded, g, _, sub, v) + ) + } + cached predicate isGuardedByExpr( AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { - isGuardedByExpr1(guarded, g, sub, v) and - sub = getAChildExprStar(g) and - forall(Ssa::Definition def, ControlFlow::Node subCfn | def = sub.getAnSsaQualifier(subCfn) | - exists(ControlFlow::Node defCfn | def = guarded.getAnSsaQualifier(defCfn) | + isGuardedByExpr0(guarded, g, sub, v) and + forall(ControlFlow::Node subCfn, Ssa::Definition def | + nodeIsGuardedBySameSubExprSsaDef(_, guarded, g, subCfn, sub, v, def) + | + exists(ControlFlow::Node guardedCfn | + def = guarded.getAnSsaQualifier(guardedCfn) and if v.isReferentialProperty() - then adjacentReadPairSameVarUniquePredecessor(def, subCfn, defCfn) + then adjacentReadPairSameVarUniquePredecessor(def, subCfn, guardedCfn) else any() ) ) } - pragma[noinline] - private predicate isGuardedByNode1( - ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v - ) { - isGuardedByNode0(guarded, _, g, sub, v) - or - guardAssertionControlsNode(g, guarded, v) and - exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded.getElement())) - } - - pragma[noinline] - private predicate isGuardedByNode2(ControlFlow::Nodes::ElementNode guarded, Ssa::Definition def) { - isGuardedByNode1(guarded, _, _, _) and - exists(BasicBlock bb | bb = guarded.getBasicBlock() | - def = guarded.getElement().(AccessOrCallExpr).getAnSsaQualifier(bb.getANode()) - ) - } - cached predicate isGuardedByNode( ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { - isGuardedByNode1(guarded, g, sub, v) and - sub = getAChildExprStar(g) and - forall(Ssa::Definition def, ControlFlow::Node subCfn | def = sub.getAnSsaQualifier(subCfn) | - isGuardedByNode2(guarded, def) and + nodeIsGuardedBySameSubExpr(guarded, _, g, _, sub, v) and + forall(ControlFlow::Node subCfn, Ssa::Definition def | + nodeIsGuardedBySameSubExprSsaDef(guarded, _, g, subCfn, sub, v, def) + | + def = + guarded + .getElement() + .(AccessOrCallExpr) + .getAnSsaQualifier(guarded.getBasicBlock().getANode()) and if v.isReferentialProperty() then adjacentReadPairSameVarUniquePredecessor(def, subCfn, guarded) else any() diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll index 36aa4e926e06..a1c77b78eeda 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll @@ -20,6 +20,7 @@ */ import csharp +private import semmle.code.csharp.commons.Assertions private import semmle.code.csharp.commons.Constants private import semmle.code.csharp.frameworks.System private import NonReturning @@ -98,6 +99,13 @@ class Completion extends TCompletion { cfe instanceof ThrowElement and this = TThrowCompletion(cfe.(ThrowElement).getThrownExceptionType()) or + exists(AssertMethod m | assertion(cfe, m, _) | + this = TThrowCompletion(m.getExceptionClass()) + or + not exists(m.getExceptionClass()) and + this = TExitCompletion() + ) + or completionIsValidForStmt(cfe, this) or mustHaveBooleanCompletion(cfe) and @@ -382,6 +390,11 @@ private predicate invalidCastCandidate(CastExpr ce) { ce.getType() = ce.getExpr().getType().(ValueOrRefType).getASubType+() } +private predicate assertion(Assertion a, AssertMethod am, Expr e) { + e = a.getExpr() and + am = a.getAssertMethod() +} + /** * Holds if a normal completion of `e` must be a Boolean completion. */ @@ -409,6 +422,9 @@ private predicate inBooleanContext(Expr e, boolean isBooleanCompletionForParent) or exists(SpecificCatchClause scc | scc.getFilterClause() = e | isBooleanCompletionForParent = false) or + assertion(_, [any(AssertTrueMethod m).(AssertMethod), any(AssertFalseMethod m)], e) and + isBooleanCompletionForParent = false + or exists(LogicalNotExpr lne | lne.getAnOperand() = e | inBooleanContext(lne, _) and isBooleanCompletionForParent = true @@ -479,6 +495,9 @@ private predicate inNullnessContext(Expr e, boolean isNullnessCompletionForParen isNullnessCompletionForParent = false ) or + assertion(_, [any(AssertNullMethod m).(AssertMethod), any(AssertNonNullMethod m)], e) and + isNullnessCompletionForParent = false + or exists(ConditionalExpr ce | inNullnessContext(ce, _) | (e = ce.getThen() or e = ce.getElse()) and isNullnessCompletionForParent = true @@ -635,7 +654,7 @@ class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion { * * Example: * - * ``` + * ```csharp * while (...) { * ... * break; @@ -656,7 +675,7 @@ class BreakNormalCompletion extends NormalCompletion, TBreakNormalCompletion { /** * A nested completion. For example, in * - * ``` + * ```csharp * void M(bool b) * { * try diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/NonReturning.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/NonReturning.qll index 9cead96e1206..1fc91f2cc049 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/NonReturning.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/NonReturning.qll @@ -55,10 +55,13 @@ private class ThrowingCall extends NonReturningCall { override ThrowCompletion getACompletion() { result = c } } +/** Holds if accessor `a` has an auto-implementation. */ +private predicate hasAccessorAutoImplementation(Accessor a) { not a.hasBody() } + abstract private class NonReturningCallable extends Callable { NonReturningCallable() { not exists(ReturnStmt ret | ret.getEnclosingCallable() = this) and - not hasAccessorAutoImplementation(this, _) and + not hasAccessorAutoImplementation(this) and not exists(Virtualizable v | v.isOverridableOrImplementable() | v = this or v = this.(Accessor).getDeclaration() @@ -80,7 +83,7 @@ private class DirectlyExitingCallable extends ExitingCallable { private class IndirectlyExitingCallable extends ExitingCallable { IndirectlyExitingCallable() { - forex(ControlFlowElement body | body = this.getABody() | body = getAnExitingElement()) + forex(ControlFlowElement body | body = this.getBody() | body = getAnExitingElement()) } } @@ -104,11 +107,11 @@ private Stmt getAnExitingStmt() { private class ThrowingCallable extends NonReturningCallable { ThrowingCallable() { - forex(ControlFlowElement body | body = this.getABody() | body = getAThrowingElement(_)) + forex(ControlFlowElement body | body = this.getBody() | body = getAThrowingElement(_)) } /** Gets a valid completion for a call to this throwing callable. */ - ThrowCompletion getACallCompletion() { this.getABody() = getAThrowingElement(result) } + ThrowCompletion getACallCompletion() { this.getBody() = getAThrowingElement(result) } } private predicate directlyThrows(ThrowElement te, ThrowCompletion c) { diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll index b2c9398a9b1a..f71ea9dba362 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll @@ -89,10 +89,14 @@ class PreBasicBlock extends ControlFlowElement { private predicate dominatesPredecessor(PreBasicBlock df) { this.dominates(df.getAPredecessor()) } } +private Completion getConditionalCompletion(ConditionalCompletion cc) { + result.getInnerCompletion() = cc +} + class ConditionBlock extends PreBasicBlock { ConditionBlock() { strictcount(Completion c | - c.getInnerCompletion() instanceof ConditionalCompletion and + c = getConditionalCompletion(_) and ( exists(succ(this.getLastElement(), c)) or @@ -102,9 +106,20 @@ class ConditionBlock extends PreBasicBlock { } private predicate immediatelyControls(PreBasicBlock succ, ConditionalCompletion cc) { - succ = succ(this.getLastElement(), any(Completion c | c.getInnerCompletion() = cc)) and - forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != this | - succ.dominates(pred) + exists(ControlFlowElement last, Completion c | + last = this.getLastElement() and + c = getConditionalCompletion(cc) and + succ = succ(last, c) and + // In the pre-CFG, we need to account for case where one predecessor node has + // two edges to the same successor node. Assertion expressions are examples of + // such nodes. + not exists(Completion other | + succ = succ(last, other) and + other != c + ) and + forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != this | + succ.dominates(pred) + ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll index 5a78344a3912..c56f3db362ab 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll @@ -27,14 +27,16 @@ private module Cached { cached newtype TSplitKind = TInitializerSplitKind() or + TAssertionSplitKind() or TFinallySplitKind(int nestLevel) { nestLevel = FinallySplitting::nestLevel(_) } or TExceptionHandlerSplitKind() or TBooleanSplitKind(BooleanSplitting::BooleanSplitSubKind kind) { kind.startsSplit(_) } or - TLoopUnrollingSplitKind(LoopUnrollingSplitting::UnrollableLoopStmt loop) + TLoopSplitKind(LoopSplitting::AnalyzableLoopStmt loop) cached newtype TSplit = TInitializerSplit(Constructor c) { InitializerSplitting::constructorInitializes(c, _) } or + TAssertionSplit(AssertionSplitting::Assertion a, boolean success) { success = [true, false] } or TFinallySplit(FinallySplitting::FinallySplitType type, int nestLevel) { nestLevel = FinallySplitting::nestLevel(_) } or @@ -43,7 +45,7 @@ private module Cached { kind.startsSplit(_) and (branch = true or branch = false) } or - TLoopUnrollingSplit(LoopUnrollingSplitting::UnrollableLoopStmt loop) + TLoopSplit(LoopSplitting::AnalyzableLoopStmt loop) cached newtype TSplits = @@ -278,7 +280,7 @@ module InitializerSplitting { * A split for non-static member initializers belonging to a given non-static * constructor. For example, in * - * ``` + * ```csharp * class C * { * int Field1 = 0; @@ -301,7 +303,7 @@ module InitializerSplitting { * on the two constructors. This is in order to generate CFGs for the two * constructors that mimic * - * ``` + * ```csharp * public C() * { * Field1 = 0; @@ -312,7 +314,7 @@ module InitializerSplitting { * * and * - * ``` + * ```csharp * public C() * { * Field1 = 0; @@ -385,6 +387,135 @@ module InitializerSplitting { } } +module AssertionSplitting { + import semmle.code.csharp.commons.Assertions + private import semmle.code.csharp.ExprOrStmtParent + + private ControlFlowElement getAnAssertionDescendant(Assertion a) { + result = a + or + result = getAnAssertionDescendant(a).getAChild() + } + + /** + * A split for assertions. For example, in + * + * ```csharp + * void M(int i) + * { + * Debug.Assert(i >= 0); + * System.Console.WriteLine("i is positive") + * } + * ``` + * + * we record whether `i >= 0` evaluates to `true` or `false`, and restrict the + * edges out of the assertion accordingly. + */ + class AssertionSplitImpl extends SplitImpl, TAssertionSplit { + Assertion a; + boolean success; + + AssertionSplitImpl() { this = TAssertionSplit(a, success) } + + /** Gets the assertion. */ + Assertion getAssertion() { result = a } + + /** Holds if this split represents a successful assertion. */ + predicate isSuccess() { success = true } + + override string toString() { + success = true and result = "assertion success" + or + success = false and result = "assertion failure" + } + } + + private class AssertionSplitKind extends SplitKind, TAssertionSplitKind { + override int getListOrder() { result = InitializerSplitting::getNextListOrder() } + + override predicate isEnabled(ControlFlowElement cfe) { this.appliesTo(cfe) } + + override string toString() { result = "Assertion" } + } + + int getNextListOrder() { result = InitializerSplitting::getNextListOrder() + 1 } + + private class AssertionSplitInternal extends SplitInternal, AssertionSplitImpl { + override AssertionSplitKind getKind() { any() } + + override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + exists(AssertMethod m | + pred = last(a.getExpr(), c) and + succ = succ(pred, c) and + this.getAssertion() = a and + m = a.getAssertMethod() + | + m instanceof AssertTrueMethod and + ( + c instanceof TrueCompletion and success = true + or + c instanceof FalseCompletion and success = false + ) + or + m instanceof AssertFalseMethod and + ( + c instanceof TrueCompletion and success = false + or + c instanceof FalseCompletion and success = true + ) + or + m instanceof AssertNullMethod and + ( + c.(NullnessCompletion).isNull() and success = true + or + c.(NullnessCompletion).isNonNull() and success = false + ) + or + m instanceof AssertNonNullMethod and + ( + c.(NullnessCompletion).isNull() and success = false + or + c.(NullnessCompletion).isNonNull() and success = true + ) + ) + } + + override predicate hasEntry(Callable c, ControlFlowElement succ) { none() } + + override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + this.appliesTo(pred) and + pred = a and + succ = succ(pred, c) and + ( + success = true and + c instanceof NormalCompletion + or + success = false and + not c instanceof NormalCompletion + ) + } + + override Callable hasExit(ControlFlowElement pred, Completion c) { + this.appliesTo(pred) and + pred = a and + result = succExit(pred, c) and + ( + success = true and + c instanceof NormalCompletion + or + success = false and + not c instanceof NormalCompletion + ) + } + + override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + this.appliesTo(pred) and + succ = succ(pred, c) and + succ = getAnAssertionDescendant(a) + } + } +} + pragma[noinline] private ControlFlowElement getAChild(ControlFlowElement cfe, Callable c) { result = cfe.getAChild() and @@ -467,7 +598,7 @@ module FinallySplitting { * A split for elements belonging to a `finally` block, which determines how to * continue execution after leaving the `finally` block. For example, in * - * ``` + * ```csharp * try * { * if (!M()) @@ -509,11 +640,11 @@ module FinallySplitting { } private int getListOrder(FinallySplitKind kind) { - result = InitializerSplitting::getNextListOrder() + kind.getNestLevel() + result = AssertionSplitting::getNextListOrder() + kind.getNestLevel() } int getNextListOrder() { - result = max(int i | i = getListOrder(_) + 1 or i = InitializerSplitting::getNextListOrder()) + result = max(int i | i = getListOrder(_) + 1 or i = AssertionSplitting::getNextListOrder()) } private class FinallySplitKind extends SplitKind, TFinallySplitKind { @@ -599,7 +730,7 @@ module FinallySplitting { // If this split is normal, and an outer split can exit based on a inherited // completion, we need to exit this split as well. For example, in // - // ``` + // ```csharp // bool done; // try // { @@ -677,7 +808,7 @@ module ExceptionHandlerSplitting { * A split for elements belonging to a `catch` clause, which determines the type of * exception to handle. For example, in * - * ``` + * ```csharp * try * { * if (M() > 0) @@ -698,11 +829,11 @@ module ExceptionHandlerSplitting { * ``` * * all control flow nodes in - * ``` + * ```csharp * catch (ArgumentException e) * ``` * and - * ``` + * ```csharp * catch (ArithmeticException e) when (e.Message != null) * ``` * have two splits: one representing the `try` block throwing an `ArgumentException`, @@ -853,7 +984,7 @@ module BooleanSplitting { * * For example, in * - * ``` + * ```csharp * var b = GetB(); * if (b) * Console.WriteLine("b is true"); @@ -892,7 +1023,7 @@ module BooleanSplitting { * * For example, in * - * ``` + * ```csharp * var b = GetB(); * if (b) * Console.WriteLine("b is true"); @@ -969,7 +1100,7 @@ module BooleanSplitting { * A split for elements that can reach a condition where this split determines * the Boolean value that the condition evaluates to. For example, in * - * ``` + * ```csharp * if (b) * Console.WriteLine("b is true"); * if (!b) @@ -1109,7 +1240,7 @@ module BooleanSplitting { } } -module LoopUnrollingSplitting { +module LoopSplitting { private import semmle.code.csharp.controlflow.Guards as Guards private import PreBasicBlocks private import PreSsa @@ -1125,53 +1256,80 @@ module LoopUnrollingSplitting { } /** - * A loop where the body is guaranteed to be executed at least once, and - * can therefore be unrolled in the control flow graph. + * A loop where the body is guaranteed to be executed at least once, and hence + * can be unrolled in the control flow graph, or where the body is guaranteed + * to never be executed, and hence can be removed from the control flow graph. */ - abstract class UnrollableLoopStmt extends LoopStmt { - /** Holds if the step `pred --c--> succ` should start loop unrolling. */ - abstract predicate startUnroll(ControlFlowElement pred, ControlFlowElement succ, Completion c); + abstract class AnalyzableLoopStmt extends LoopStmt { + /** Holds if the step `pred --c--> succ` should start the split. */ + abstract predicate start(ControlFlowElement pred, ControlFlowElement succ, Completion c); - /** Holds if the step `pred --c--> succ` should stop loop unrolling. */ - abstract predicate stopUnroll(ControlFlowElement pred, ControlFlowElement succ, Completion c); + /** Holds if the step `pred --c--> succ` should stop the split. */ + abstract predicate stop(ControlFlowElement pred, ControlFlowElement succ, Completion c); /** - * Holds if any step `pred --c--> _` should be pruned from the unrolled loop - * (the loop condition evaluating to `false`). + * Holds if any step `pred --c--> _` should be pruned from the control flow graph. */ abstract predicate pruneLoopCondition(ControlFlowElement pred, ConditionalCompletion c); + + /** + * Holds if the body is guaranteed to be executed at least once. If not, the + * body is guaranteed to never be executed. + */ + abstract predicate isUnroll(); } - private class UnrollableForeachStmt extends UnrollableLoopStmt, ForeachStmt { - UnrollableForeachStmt() { - exists(Guards::AbstractValues::EmptyCollectionValue v | v.isNonEmpty() | - emptinessGuarded(_, this.getIterableExpr(), v) - or - this.getIterableExpr() = v.getAnExpr() - ) + private class AnalyzableForeachStmt extends AnalyzableLoopStmt, ForeachStmt { + Guards::AbstractValues::EmptyCollectionValue v; + + AnalyzableForeachStmt() { + /* + * We use `unique` to avoid degenerate cases like + * ```csharp + * if (xs.Length == 0) + * return; + * if (xs.Length > 0) + * return; + * foreach (var x in xs) + * .... + * ``` + * where the iterator expression `xs` is guarded by both an emptiness check + * and a non-emptiness check. + */ + + v = + unique(Guards::AbstractValues::EmptyCollectionValue v0 | + emptinessGuarded(_, this.getIterableExpr(), v0) + or + this.getIterableExpr() = v0.getAnExpr() + | + v0 + ) } - override predicate startUnroll(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate start(ControlFlowElement pred, ControlFlowElement succ, Completion c) { pred = last(this.getIterableExpr(), c) and succ = this } - override predicate stopUnroll(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate stop(ControlFlowElement pred, ControlFlowElement succ, Completion c) { pred = this and succ = succ(pred, c) } override predicate pruneLoopCondition(ControlFlowElement pred, ConditionalCompletion c) { pred = this and - c.(EmptinessCompletion).isEmpty() + c = any(EmptinessCompletion ec | if v.isEmpty() then not ec.isEmpty() else ec.isEmpty()) } + + override predicate isUnroll() { v.isNonEmpty() } } /** - * A split for loops where the body is guaranteed to be executed at least once, and - * can therefore be unrolled in the control flow graph. For example, in + * A split for loops where the body is guaranteed to be executed at least once, or + * guaranteed to never be executed. For example, in * - * ``` + * ```csharp * void M(string[] args) * { * if (args.Length == 0) @@ -1184,21 +1342,23 @@ module LoopUnrollingSplitting { * the `foreach` loop is guaranteed to be executed at least once, as a result of the * `args.Length == 0` check. */ - class LoopUnrollingSplitImpl extends SplitImpl, TLoopUnrollingSplit { - UnrollableLoopStmt loop; + class LoopSplitImpl extends SplitImpl, TLoopSplit { + AnalyzableLoopStmt loop; - LoopUnrollingSplitImpl() { this = TLoopUnrollingSplit(loop) } + LoopSplitImpl() { this = TLoopSplit(loop) } override string toString() { - result = "unroll (line " + loop.getLocation().getStartLine() + ")" + if loop.isUnroll() + then result = "unroll (line " + loop.getLocation().getStartLine() + ")" + else result = "skip (line " + loop.getLocation().getStartLine() + ")" } } - private int getListOrder(UnrollableLoopStmt loop) { + private int getListOrder(AnalyzableLoopStmt loop) { exists(Callable c, int r | c = loop.getEnclosingCallable() | result = r + BooleanSplitting::getNextListOrder() - 1 and loop = - rank[r](UnrollableLoopStmt loop0 | + rank[r](AnalyzableLoopStmt loop0 | loop0.getEnclosingCallable() = c | loop0 order by loop0.getLocation().getStartLine(), loop0.getLocation().getStartColumn() @@ -1210,21 +1370,21 @@ module LoopUnrollingSplitting { result = max(int i | i = getListOrder(_) + 1 or i = BooleanSplitting::getNextListOrder()) } - private class LoopUnrollingSplitKind extends SplitKind, TLoopUnrollingSplitKind { - private UnrollableLoopStmt loop; + private class LoopSplitKind extends SplitKind, TLoopSplitKind { + private AnalyzableLoopStmt loop; - LoopUnrollingSplitKind() { this = TLoopUnrollingSplitKind(loop) } + LoopSplitKind() { this = TLoopSplitKind(loop) } override int getListOrder() { result = getListOrder(loop) } override string toString() { result = "Unroll" } } - private class LoopUnrollingSplitInternal extends SplitInternal, LoopUnrollingSplitImpl { - override LoopUnrollingSplitKind getKind() { result = TLoopUnrollingSplitKind(loop) } + private class LoopUnrollingSplitInternal extends SplitInternal, LoopSplitImpl { + override LoopSplitKind getKind() { result = TLoopSplitKind(loop) } override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - loop.startUnroll(pred, succ, c) + loop.start(pred, succ, c) } override predicate hasEntry(Callable pred, ControlFlowElement succ) { none() } @@ -1241,7 +1401,7 @@ module LoopUnrollingSplitting { override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { this.appliesToPredecessor(pred, c) and - loop.stopUnroll(pred, succ, c) + loop.stop(pred, succ, c) } override Callable hasExit(ControlFlowElement pred, Completion c) { @@ -1252,7 +1412,7 @@ module LoopUnrollingSplitting { override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { this.appliesToPredecessor(pred, c) and succ = succ(pred, c) and - not loop.stopUnroll(pred, succ, c) + not loop.stop(pred, succ, c) } } } @@ -1338,7 +1498,7 @@ predicate succExitSplits(ControlFlowElement pred, Splits predSplits, Callable su * * For the successor relation * - * ``` + * ```ql * succSplits(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c) * ``` * diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll index 3f265236fb41..8e34ac19a0d0 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/SuccessorType.qll @@ -59,7 +59,7 @@ module SuccessorTypes { * * For example, this program fragment: * - * ``` + * ```csharp * if (x < 0) * return 0; * else @@ -95,7 +95,7 @@ module SuccessorTypes { * * For example, this program fragment: * - * ``` + * ```csharp * int? M(string s) => s?.Length; * ``` * @@ -134,7 +134,7 @@ module SuccessorTypes { * * For example, this program fragment: * - * ``` + * ```csharp * switch (x) { * case 0 : * return 0; @@ -181,7 +181,7 @@ module SuccessorTypes { * * For example, this program fragment: * - * ``` + * ```csharp * foreach (var arg in args) * { * yield return arg; @@ -228,7 +228,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * void M() * { * return; @@ -249,7 +249,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(int x) * { * while (true) @@ -277,7 +277,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(int x) * { * while (true) { @@ -302,7 +302,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(int x) * { * while (true) @@ -333,7 +333,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(string s) * { * if (s == null) @@ -361,7 +361,7 @@ module SuccessorTypes { * * Example: * - * ``` + * ```csharp * int M(string s) * { * if (s == null) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll b/csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll new file mode 100644 index 000000000000..b129203db706 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll @@ -0,0 +1,78 @@ +/** + * Provides classes for representing abstract bounds for use in, for example, range analysis. + */ + +private import internal.rangeanalysis.BoundSpecific + +private newtype TBound = + TBoundZero() or + TBoundSsa(SsaVariable v) { v.getSourceVariable().getType() instanceof IntegralType } or + TBoundExpr(Expr e) { + interestingExprBound(e) and + not exists(SsaVariable v | e = v.getAUse()) + } + +/** + * A bound that may be inferred for an expression plus/minus an integer delta. + */ +abstract class Bound extends TBound { + /** Gets a textual representation of this bound. */ + abstract string toString(); + + /** Gets an expression that equals this bound plus `delta`. */ + abstract Expr getExpr(int delta); + + /** Gets an expression that equals this bound. */ + Expr getExpr() { result = getExpr(0) } + + /** + * Holds if this element is at the specified location. + * The location spans column `sc` of line `sl` to + * column `ec` of line `el` in file `path`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 + } +} + +/** + * The bound that corresponds to the integer 0. This is used to represent all + * integer bounds as bounds are always accompanied by an added integer delta. + */ +class ZeroBound extends Bound, TBoundZero { + override string toString() { result = "0" } + + override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta } +} + +/** + * A bound corresponding to the value of an SSA variable. + */ +class SsaBound extends Bound, TBoundSsa { + /** Gets the SSA variable that equals this bound. */ + SsaVariable getSsa() { this = TBoundSsa(result) } + + override string toString() { result = getSsa().toString() } + + override Expr getExpr(int delta) { result = getSsa().getAUse() and delta = 0 } + + override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + getSsa().getLocation().hasLocationInfo(path, sl, sc, el, ec) + } +} + +/** + * A bound that corresponds to the value of a specific expression that might be + * interesting, but isn't otherwise represented by the value of an SSA variable. + */ +class ExprBound extends Bound, TBoundExpr { + override string toString() { result = getExpr().toString() } + + override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 } + + override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + getExpr().getLocation().hasLocationInfo(path, sl, sc, el, ec) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll b/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll index c36bcc0dc8a7..b0a6d9d98edc 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll @@ -11,10 +11,7 @@ cached private newtype TCallContext = TEmptyCallContext() or TArgNonDelegateCallContext(Expr arg) { exists(DispatchCall dc | arg = dc.getArgument(_)) } or - TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) } or - TDelegateToLibraryCallableArgCallContext(DelegateArgumentToLibraryCallable arg, int i) { - exists(arg.getDelegateType().getParameter(i)) - } + TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) } /** * A call context. @@ -79,31 +76,3 @@ class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateC override Location getLocation() { result = dc.getArgument(arg).getLocation() } } - -/** - * An argument of a call to a delegate supplied to a library callable, - * identified by the delegate argument itself. - * - * For example, in `x.Select(y => y)` the call to the supplied delegate - * that happens inside the library callable `Select` is not available - * in the database, so the delegate argument `y => y` is used to - * represent the call. - */ -class DelegateArgumentToLibraryCallableArgumentContext extends ArgumentCallContext, - TDelegateToLibraryCallableArgCallContext { - Expr delegate; - int arg; - - DelegateArgumentToLibraryCallableArgumentContext() { - this = TDelegateToLibraryCallableArgCallContext(delegate, arg) - } - - override predicate isArgument(Expr call, int i) { - call = delegate and - i = arg - } - - override string toString() { result = "argument " + arg + " of " + delegate.toString() } - - override Location getLocation() { result = delegate.getLocation() } -} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll index 1f08f81548a7..c1c10b9bf49d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll @@ -3,7 +3,6 @@ */ import csharp -private import semmle.code.csharp.frameworks.WCF private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.system.Collections private import semmle.code.csharp.frameworks.system.collections.Generic @@ -12,28 +11,66 @@ private import semmle.code.csharp.frameworks.system.io.Compression private import semmle.code.csharp.frameworks.system.linq.Expressions private import semmle.code.csharp.frameworks.system.Net private import semmle.code.csharp.frameworks.system.Text +private import semmle.code.csharp.frameworks.system.runtime.CompilerServices private import semmle.code.csharp.frameworks.system.threading.Tasks private import semmle.code.csharp.frameworks.system.Web private import semmle.code.csharp.frameworks.system.web.ui.WebControls private import semmle.code.csharp.frameworks.system.Xml +private import semmle.code.csharp.dataflow.internal.DataFlowPrivate private import semmle.code.csharp.dataflow.internal.DataFlowPublic private import semmle.code.csharp.dataflow.internal.DelegateDataFlow +// import `LibraryTypeDataFlow` definitions from other files to avoid potential reevaluation +private import semmle.code.csharp.frameworks.EntityFramework +private import semmle.code.csharp.frameworks.JsonNET private newtype TAccessPath = TNilAccessPath() or - TAccessPathConsNil(Content c) + TConsAccessPath(Content head, AccessPath tail) { + tail = TNilAccessPath() + or + exists(LibraryTypeDataFlow ltdf | + ltdf.requiresAccessPath(head, tail) and + tail.length() < accessPathLimit() + ) + or + tail = AccessPath::singleton(_) and + head instanceof ElementContent + or + tail = AccessPath::element() + } -/** An access path of length 0 or 1. */ +/** An access path. */ class AccessPath extends TAccessPath { /** Gets the head of this access path, if any. */ - Content getHead() { this = TAccessPathConsNil(result) } + Content getHead() { this = TConsAccessPath(result, _) } + + /** Gets the tail of this access path, if any. */ + AccessPath getTail() { this = TConsAccessPath(_, result) } + + /** Gets the length of this access path. */ + int length() { + this = TNilAccessPath() and result = 0 + or + result = 1 + this.getTail().length() + } + + /** Gets the access path obtained by dropping the first `i` elements, if any. */ + AccessPath drop(int i) { + i = 0 and result = this + or + result = this.getTail().drop(i - 1) + } /** Holds if this access path contains content `c`. */ - predicate contains(Content c) { this = TAccessPathConsNil(c) } + predicate contains(Content c) { c = this.drop(_).getHead() } /** Gets a textual representation of this access path. */ string toString() { - result = this.getHead().toString() + exists(Content head, AccessPath tail | + head = this.getHead() and + tail = this.getTail() and + if tail.length() = 0 then result = head.toString() else result = head + ", " + tail + ) or this = TNilAccessPath() and result = "" @@ -45,10 +82,27 @@ module AccessPath { /** Gets the empty access path. */ AccessPath empty() { result = TNilAccessPath() } + /** Gets a singleton access path containing `c`. */ + AccessPath singleton(Content c) { result = TConsAccessPath(c, TNilAccessPath()) } + + /** Gets the access path obtained by concatenating `head` onto `tail`. */ + AccessPath cons(Content head, AccessPath tail) { result = TConsAccessPath(head, tail) } + + /** Gets the singleton "element content" access path. */ + AccessPath element() { result = singleton(any(ElementContent c)) } + /** Gets a singleton property access path. */ AccessPath property(Property p) { - result = TAccessPathConsNil(any(PropertyContent c | c.getProperty() = p.getSourceDeclaration())) + result = singleton(any(PropertyContent c | c.getProperty() = p.getSourceDeclaration())) } + + /** Gets a singleton field access path. */ + AccessPath field(Field f) { + result = singleton(any(FieldContent c | c.getField() = f.getSourceDeclaration())) + } + + /** Gets an access path representing a property inside a collection. */ + AccessPath properties(Property p) { result = TConsAccessPath(any(ElementContent c), property(p)) } } /** An unbound callable. */ @@ -61,25 +115,9 @@ class SourceDeclarationMethod extends SourceDeclarationCallable, Method { } private newtype TCallableFlowSource = TCallableFlowSourceQualifier() or - TCallableFlowSourceArg(int i) { hasArgumentPosition(_, i) } or + TCallableFlowSourceArg(int i) { i = any(Parameter p).getPosition() } or TCallableFlowSourceDelegateArg(int i) { hasDelegateArgumentPosition(_, i) } -private predicate hasArgumentPosition(SourceDeclarationCallable callable, int position) { - exists(int arity | - if callable.getAParameter().isParams() - then - arity = - max(Call call | - call.getTarget().getSourceDeclaration() = callable - | - call.getNumberOfArguments() - ) - else arity = callable.getNumberOfParameters() - | - position in [0 .. arity - 1] - ) -} - private predicate hasDelegateArgumentPosition(SourceDeclarationCallable c, int i) { exists(DelegateType dt | dt = c.getParameter(i).getType().(SystemLinqExpressions::DelegateExtType).getDelegateType() @@ -161,12 +199,6 @@ class CallableFlowSink extends TCallableFlowSink { /** Gets the sink of flow for call `c`, if any. */ Expr getSink(Call c) { none() } - - /** - * Gets the type of the sink for call `c`. Unlike `getSink()`, this is defined - * for all flow sink specifications. - */ - Type getSinkType(Call c) { result = this.getSink(c).getType() } } /** A flow sink specification: (method call) qualifier. */ @@ -207,14 +239,22 @@ class CallableFlowSinkArg extends CallableFlowSink, TCallableFlowSinkArg { // The uses of the `i`th argument are the actual sinks none() } +} - override Type getSinkType(Call c) { result = this.getArgument(c).getType() } +private predicate isCollectionType(ValueOrRefType t) { + t.getABaseType*() instanceof SystemCollectionsIEnumerableInterface and + not t instanceof StringType } /** Gets the flow source for argument `i` of callable `callable`. */ -private CallableFlowSourceArg getFlowSourceArg(SourceDeclarationCallable callable, int i) { +private CallableFlowSourceArg getFlowSourceArg( + SourceDeclarationCallable callable, int i, AccessPath ap +) { i = result.getArgumentIndex() and - hasArgumentPosition(callable, i) + exists(Parameter p | + p = callable.getParameter(i) and + if isCollectionType(p.getType()) then ap = AccessPath::element() else ap = AccessPath::empty() + ) } /** Gets the flow source for argument `i` of delegate `callable`. */ @@ -256,16 +296,6 @@ class CallableFlowSinkDelegateArg extends CallableFlowSink, TCallableFlowSinkDel // The uses of the `j`th parameter are the actual sinks none() } - - override Type getSinkType(Call c) { - result = - c - .getArgument(delegateIndex) - .(DelegateArgumentToLibraryCallable) - .getDelegateType() - .getParameter(parameterIndex) - .getType() - } } /** A specification of data flow for a library (non-source code) type. */ @@ -291,13 +321,33 @@ abstract class LibraryTypeDataFlow extends Type { * Holds if data may flow from `source` to `sink` when calling callable `c`. * * `sourceAp` describes the contents of `source` that flows to `sink` - * (if any), and `sinkContent` describes the contents of `sink` that it + * (if any), and `sinkAp` describes the contents of `sink` that it * flows to (if any). */ pragma[nomagic] predicate callableFlow( CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, - SourceDeclarationCallable c + SourceDeclarationCallable c, boolean preservesValue + ) { + none() + } + + /** + * Holds if the access path obtained by concatenating `head` onto `tail` is + * needed for a summary specified by `callableFlow()`. + * + * This predicate is needed for QL technical reasons only (the IPA type used + * to represent access paths needs to be bounded). + */ + predicate requiresAccessPath(Content head, AccessPath tail) { none() } + + /** + * Holds if values stored inside `content` are cleared on objects passed as + * arguments of type `source` to calls that target `callable`. + */ + pragma[nomagic] + predicate clearsContent( + CallableFlowSource source, Content content, SourceDeclarationCallable callable ) { none() } @@ -392,8 +442,7 @@ class SystemUriFlow extends LibraryTypeDataFlow, SystemUriClass { private predicate methodFlow( CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m ) { - m.getDeclaringType() = getABaseType*() and - m = any(SystemObjectClass c).getToStringMethod().getAnOverrider*() and + m = this.getAMethod("ToString") and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() } @@ -441,41 +490,48 @@ class SystemIOStringReaderFlow extends LibraryTypeDataFlow, SystemIOStringReader /** Data flow for `System.String`. */ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { - constructorFlow(source, sink, c) and preservesValue = false + constructorFlow(source, sourceAp, sink, sinkAp, c) and + preservesValue = false or - methodFlow(source, sink, c, preservesValue) + methodFlow(source, sourceAp, sink, sinkAp, c, preservesValue) } - private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) { + private predicate constructorFlow( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + Constructor c + ) { c = getAMember() and c.getParameter(0).getType().(ArrayType).getElementType() instanceof CharType and source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() } private predicate methodFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationMethod m, boolean preservesValue ) { - m = getAMethod() and - ( - m = any(SystemObjectClass c).getToStringMethod().getAnOverrider*() and - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - preservesValue = true - ) + m = this.getAMethod("ToString") and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = true or m = getSplitMethod() and - ( - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - preservesValue = false - ) + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() and + preservesValue = false or m = getReplaceMethod() and + sourceAp = AccessPath::empty() and + sinkAp = AccessPath::empty() and ( source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() and @@ -487,20 +543,22 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { ) or m = getSubstringMethod() and - ( - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - preservesValue = false - ) + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = false or m = getCloneMethod() and - ( - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - preservesValue = true - ) + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = true or m = getInsertMethod() and + sourceAp = AccessPath::empty() and + sinkAp = AccessPath::empty() and ( source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() and @@ -512,55 +570,54 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { ) or m = getNormalizeMethod() and - ( - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - preservesValue = false - ) + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = false or m = getRemoveMethod() and - ( - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - preservesValue = false - ) + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = false or m = getAMethod() and - ( - m - .getName() - .regexpMatch("((ToLower|ToUpper)(Invariant)?)|(Trim(Start|End)?)|(Pad(Left|Right))") and - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - preservesValue = false - ) + m.getName().regexpMatch("((ToLower|ToUpper)(Invariant)?)|(Trim(Start|End)?)|(Pad(Left|Right))") and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = false or m = getConcatMethod() and - ( - source = getFlowSourceArg(m, _) and + exists(int i | + source = getFlowSourceArg(m, i, sourceAp) and sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and preservesValue = false ) or m = getCopyMethod() and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() and - preservesValue = true - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = true or m = getJoinMethod() and - ( - source = getFlowSourceArg(m, _) and - sink = TCallableFlowSinkReturn() and - preservesValue = false - ) + source = getFlowSourceArg(m, [0, 1], sourceAp) and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = false or m = getFormatMethod() and exists(int i | (m.getParameter(0).getType() instanceof SystemIFormatProviderInterface implies i != 0) and - source = getFlowSourceArg(m, i) and + source = getFlowSourceArg(m, i, sourceAp) and sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and preservesValue = false ) } @@ -569,53 +626,72 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { /** Data flow for `System.Text.StringBuilder`. */ class SystemTextStringBuilderFlow extends LibraryTypeDataFlow, SystemTextStringBuilderClass { override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { ( - constructorFlow(source, sink, c) + constructorFlow(source, sourceAp, sink, sinkAp, c) and + preservesValue = true or - methodFlow(source, sink, c) - ) and - preservesValue = false + methodFlow(source, sourceAp, sink, sinkAp, c, preservesValue) + ) } - private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) { + private predicate constructorFlow( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + Constructor c + ) { c = getAMember() and c.getParameter(0).getType() instanceof StringType and source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() } private predicate methodFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationMethod m, boolean preservesValue ) { - m.getDeclaringType() = getABaseType*() and - ( - m = any(SystemObjectClass c).getToStringMethod().getAnOverrider*() and + exists(string name | m = this.getAMethod(name) | + name = "ToString" and source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() - ) - or - m = getAMethod() and - exists(int i, Type t | - m.getName().regexpMatch("Append(Format|Line)?") and - t = m.getParameter(i).getType() and - source = getFlowSourceArg(m, i) and - sink = TCallableFlowSinkQualifier() - | - t instanceof StringType or - t instanceof ObjectType + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + preservesValue = false + or + exists(int i, Type t | + name.regexpMatch("Append(Format|Line)?") and + t = m.getParameter(i).getType() and + source = TCallableFlowSourceArg(i) and + sourceAp = AccessPath::empty() and + sink = [TCallableFlowSinkQualifier().(TCallableFlowSink), TCallableFlowSinkReturn()] and + sinkAp = AccessPath::element() and + preservesValue = true + | + t instanceof StringType or + t instanceof ObjectType + ) ) } + + override predicate clearsContent( + CallableFlowSource source, Content content, SourceDeclarationCallable callable + ) { + source = TCallableFlowSourceQualifier() and + callable = this.getAMethod("Clear") and + content instanceof ElementContent + } } /** Data flow for `System.Lazy<>`. */ class SystemLazyFlow extends LibraryTypeDataFlow, SystemLazyClass { override predicate callableFlow( CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, - SourceDeclarationCallable c + SourceDeclarationCallable c, boolean preservesValue ) { + preservesValue = true and exists(SystemFuncDelegateType t, int i | t.getNumberOfTypeParameters() = 1 | c.(Constructor).getDeclaringType() = this and c.getParameter(i).getType().getSourceDeclaration() = t and @@ -624,53 +700,137 @@ class SystemLazyFlow extends LibraryTypeDataFlow, SystemLazyClass { sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::property(this.getValueProperty()) ) + or + preservesValue = false and + c = this.getValueProperty().getGetter() and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() } } -/** - * Data flow for `System.Collections.IEnumerable`, `System.Collections.Generic.IEnumerable<>`, - * and their sub types (for example `System.Collections.Generic.List<>`). - */ -class IEnumerableFlow extends LibraryTypeDataFlow { - IEnumerableFlow() { - exists(RefType t | t = this.(RefType).getABaseType*() | - t instanceof SystemCollectionsIEnumerableInterface - or - t instanceof SystemCollectionsGenericIEnumerableTInterface - or - t.(ConstructedInterface).getUnboundGeneric() instanceof - SystemCollectionsGenericIEnumerableTInterface - ) +/** Data flow for `System.Nullable<>`. */ +class SystemNullableFlow extends LibraryTypeDataFlow, SystemNullableStruct { + override predicate callableFlow( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue + ) { + preservesValue = true and + c.(Constructor).getDeclaringType() = this and + source = getFlowSourceArg(c, 0, sourceAp) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(this.getValueProperty()) + or + preservesValue = true and + c = this.getAGetValueOrDefaultMethod() and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::property(this.getValueProperty()) and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() + or + preservesValue = false and + c = this.getHasValueProperty().getGetter() and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::property(this.getValueProperty()) and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() + or + preservesValue = true and + c = this.getAGetValueOrDefaultMethod() and + source = getFlowSourceArg(c, 0, _) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() + or + preservesValue = false and + c = this.getValueProperty().getGetter() and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() } +} + +/** Data flow for `System.Collections.IEnumerable` (and sub types). */ +class IEnumerableFlow extends LibraryTypeDataFlow, RefType { + IEnumerableFlow() { this.getABaseType*() instanceof SystemCollectionsIEnumerableInterface } override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { + preservesValue = true and ( - methodFlow(source, sink, c) + methodFlowLINQExtensions(source, sourceAp, sink, sinkAp, c) or - exists(Property p | - propertyFlow(p) and + c = this.getFind() and + sourceAp = AccessPath::element() and + sinkAp = AccessPath::empty() and + if c.(Method).isStatic() + then + source = TCallableFlowSourceArg(0) and + ( + sink = TCallableFlowSinkReturn() or + sink = getDelegateFlowSinkArg(c, 1, 0) + ) + else ( source = TCallableFlowSourceQualifier() and + ( + sink = TCallableFlowSinkReturn() or + sink = getDelegateFlowSinkArg(c, 0, 0) + ) + ) + or + exists(string name, int arity | + arity = c.getNumberOfParameters() and + c = this.getAMethod(name) + | + name = "Add" and + arity = 1 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::empty() and + sink instanceof CallableFlowSinkQualifier and + sinkAp = AccessPath::element() + or + name = "AddRange" and + arity = 1 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkQualifier() and + sinkAp = AccessPath::element() + or + exists(Property current | + name = "GetEnumerator" and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(current) and + current = c.getReturnType().(ValueOrRefType).getProperty("Current") + ) + or + name = "Repeat" and + c.(Method).isStatic() and + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::empty() and sink = TCallableFlowSinkReturn() and - c = p.getGetter() + sinkAp = AccessPath::element() + or + name = "Reverse" and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) - ) and - preservesValue = false - } - - private predicate methodFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m - ) { - methodFlowLINQ(source, sink, m) - or - methodFlowSpecific(source, sink, m) + ) } - /** Flow for LINQ methods. */ - private predicate methodFlowLINQ( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m + /** Flow for LINQ extension methods. */ + private predicate methodFlowLINQExtensions( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationMethod m ) { m.(ExtensionMethod).getExtendedType().getSourceDeclaration() = this and exists(string name, int arity | name = m.getName() and arity = m.getNumberOfParameters() | @@ -679,192 +839,239 @@ class IEnumerableFlow extends LibraryTypeDataFlow { arity = 2 and ( source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 1) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 1) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceDelegateArg(1) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() ) or arity = 3 and ( source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 1) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 1) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceArg(1) and - sink = getDelegateFlowSinkArg(m, 2, 0) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceDelegateArg(2) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() ) or arity = 4 and ( source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 1) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 1) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceArg(1) and - sink = getDelegateFlowSinkArg(m, 2, 0) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceDelegateArg(2) and - sink = getDelegateFlowSinkArg(m, 3, 0) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, 3, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceDelegateArg(3) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() ) ) or name = "All" and - ( - arity = 2 and - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or name = "Any" and - ( - arity = 2 and - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or name = "AsEnumerable" and - ( - arity = 1 and - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + arity = 1 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or name = "AsQueryable" and arity = 1 and source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or name = "Average" and - ( - arity = 2 and - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or name = "Cast" and - ( - arity = 1 and - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + arity = 1 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or name = "Concat" and + arity = 2 and ( - arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - or - source = TCallableFlowSourceArg(1) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() + or + source = TCallableFlowSourceArg(1) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) or name.regexpMatch("(Long)?Count") and - ( - arity = 2 and - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or name = "DefaultIfEmpty" and ( arity in [1 .. 2] and source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() or arity = 2 and source = TCallableFlowSourceArg(1) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() ) or name = "Distinct" and - ( - arity in [1 .. 2] and - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + arity in [1 .. 2] and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or name.regexpMatch("ElementAt(OrDefault)?") and - ( - arity = 2 and - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() or name = "Except" and - ( - arity in [2 .. 3] and - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + arity in [2 .. 3] and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() or name.regexpMatch("(First|Single)(OrDefault)?") and ( arity in [1 .. 2] and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() or arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() ) or name = "GroupBy" and ( + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() + or arity = 3 and ( source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or m.getParameter(2).getType().(ConstructedDelegateType).getNumberOfTypeArguments() = 2 and source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() or m.getParameter(2).getType().(ConstructedDelegateType).getNumberOfTypeArguments() = 3 and source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 1) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, 2, 1) and + sinkAp = AccessPath::empty() or m.getParameter(2).getType().(ConstructedDelegateType).getNumberOfTypeArguments() = 3 and source = getDelegateFlowSourceArg(m, 1) and - sink = getDelegateFlowSinkArg(m, 2, 0) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() or not m.getParameter(2).getType().getSourceDeclaration() instanceof SystemCollectionsGenericIEqualityComparerTInterface and source = getDelegateFlowSourceArg(m, 2) and - sink = TCallableFlowSinkReturn() - or - m.getParameter(2).getType().getSourceDeclaration() instanceof - SystemCollectionsGenericIEqualityComparerTInterface and - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) or arity in [4 .. 5] and ( source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() or source = getDelegateFlowSourceArg(m, 1) and - sink = getDelegateFlowSinkArg(m, 3, 0) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() or source = getDelegateFlowSourceArg(m, 2) and - sink = getDelegateFlowSinkArg(m, 3, 1) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, 3, 1) and + sinkAp = AccessPath::element() or source = getDelegateFlowSourceArg(m, 3) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) ) or @@ -873,19 +1080,29 @@ class IEnumerableFlow extends LibraryTypeDataFlow { arity in [5 .. 6] and ( source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 4, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 4, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceArg(1) and - sink = getDelegateFlowSinkArg(m, 3, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 3, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceArg(1) and - sink = getDelegateFlowSinkArg(m, 4, 1) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 4, 1) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceDelegateArg(4) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) ) or @@ -894,285 +1111,426 @@ class IEnumerableFlow extends LibraryTypeDataFlow { arity in [2 .. 3] and ( source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or source = TCallableFlowSourceArg(1) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) ) or name.regexpMatch("Last(OrDefault)?") and ( arity in [1 .. 2] and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() or arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() ) or name.regexpMatch("Max|Min|Sum") and ( arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() ) or name = "OfType" and - ( - arity = 1 and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) - ) + arity = 1 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or name.regexpMatch("OrderBy(Descending)?") and + arity in [2 .. 3] and ( - arity in [2 .. 3] and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - or - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) - ) - or - name = "Repeat" and - ( - arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() + or + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() ) or name = "Reverse" and - ( - arity = 1 and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) - ) + arity = 1 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or name.regexpMatch("Select(Many)?") and + arity = 2 and ( - arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - or - source = TCallableFlowSourceDelegateArg(1) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() + or + source = TCallableFlowSourceDelegateArg(1) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) or name = "SelectMany" and + arity = 3 and ( - arity = 3 and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - or - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 0) - or - source = TCallableFlowSourceDelegateArg(1) and - sink = getDelegateFlowSinkArg(m, 2, 1) - or - source = TCallableFlowSourceDelegateArg(2) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() + or + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() + or + source = TCallableFlowSourceDelegateArg(1) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 1) and + sinkAp = AccessPath::empty() + or + source = TCallableFlowSourceDelegateArg(2) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) or name.regexpMatch("(Skip|Take)(While)?") and - ( - arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) - ) + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or name.regexpMatch("(Skip|Take)While") and - ( - arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - ) - ) + arity = 2 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or name.regexpMatch("ThenBy(Descending)?") and + arity in [2 .. 3] and ( - arity in [2 .. 3] and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - or - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() + or + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) or name.regexpMatch("To(Array|List)") and - ( - arity = 1 and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) - ) + arity = 1 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or name.regexpMatch("To(Dictionary|Lookup)") and ( arity in [2 .. 3] and ( source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() and not m.getParameter(2).getType() instanceof DelegateType ) or arity in [3 .. 4] and ( source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() or source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 0) + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() or source = getDelegateFlowSourceArg(m, 2) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) ) or name = "Union" and + arity in [2 .. 3] and ( - arity in [2 .. 3] and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - or - source = TCallableFlowSourceArg(1) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() + or + source = TCallableFlowSourceArg(1) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) or name = "Where" and + arity = 2 and ( - arity = 2 and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 1, 0) - or - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 1, 0) and + sinkAp = AccessPath::empty() + or + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) or name = "Zip" and + arity = 3 and ( - arity = 3 and - ( - source = TCallableFlowSourceArg(0) and - sink = getDelegateFlowSinkArg(m, 2, 0) - or - source = TCallableFlowSourceArg(1) and - sink = getDelegateFlowSinkArg(m, 2, 1) - or - source = getDelegateFlowSourceArg(m, 2) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 0) and + sinkAp = AccessPath::empty() + or + source = TCallableFlowSourceArg(1) and + sourceAp = AccessPath::element() and + sink = getDelegateFlowSinkArg(m, 2, 1) and + sinkAp = AccessPath::empty() + or + source = getDelegateFlowSourceArg(m, 2) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) ) } - /** Flow for specific enumerables (e.g., `List` and `Stack`). */ - private predicate methodFlowSpecific( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m + private SourceDeclarationMethod getFind() { + exists(string name | + name = result.getName() and + result.getDeclaringType() = this.getABaseType*() + | + name.regexpMatch("Find(All|Last)?") + ) + } + + override predicate clearsContent( + CallableFlowSource source, Content content, SourceDeclarationCallable callable ) { - m = getFind() and - if m.isStatic() - then - source = TCallableFlowSourceArg(0) and - ( - sink = TCallableFlowSinkReturn() or - sink = getDelegateFlowSinkArg(m, 1, 0) - ) - else ( - source = TCallableFlowSourceQualifier() and - ( - sink = TCallableFlowSinkReturn() or - sink = getDelegateFlowSinkArg(m, 0, 0) - ) + source = TCallableFlowSourceQualifier() and + callable = this.getAMethod("Clear") and + content instanceof ElementContent + } +} + +/** Data flow for `System.Collections.[Generic.]ICollection` (and sub types). */ +class ICollectionFlow extends LibraryTypeDataFlow, RefType { + ICollectionFlow() { + exists(Interface i | i = this.getABaseType*().getSourceDeclaration() | + i instanceof SystemCollectionsICollectionInterface + or + i instanceof SystemCollectionsGenericICollectionInterface ) - or + } + + override predicate callableFlow( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue + ) { + preservesValue = true and exists(string name, int arity | - name = m.getName() and - arity = m.getNumberOfParameters() and - m.getDeclaringType() = this.(RefType).getABaseType*() + name = c.getName() and + arity = c.getNumberOfParameters() and + c = this.getAMethod() | - name = "FixedSize" and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + name = "CopyTo" and + arity = 2 and + source instanceof CallableFlowSourceQualifier and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkArg(0) and + sinkAp = AccessPath::element() or - name - .regexpMatch("GetByIndex|Peek|Pop|AsReadOnly|Clone|GetRange|MemberwiseClone|Reverse|GetEnumerator|GetValueList") and - ( - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() - ) + name.regexpMatch("AsReadOnly|Clone") and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() or - name.regexpMatch("Add(Range)?") and - ( - arity = 1 and - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkQualifier() - ) + name.regexpMatch("Peek|Pop") and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() or - name = "Add" and - ( - arity = 2 and - source = TCallableFlowSourceArg(1) and - sink = TCallableFlowSinkQualifier() - ) + name = "InsertRange" and + arity = 2 and + source = TCallableFlowSourceArg(1) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkQualifier() and + sinkAp = AccessPath::element() + ) + } +} + +/** Data flow for `System.Collections.[Generic.]IList` (and sub types). */ +class IListFlow extends LibraryTypeDataFlow, RefType { + IListFlow() { + exists(Interface i | i = this.getABaseType*().getSourceDeclaration() | + i instanceof SystemCollectionsIListInterface or - name.regexpMatch("Insert(Range)?") and - ( - not this instanceof StringType and + i instanceof SystemCollectionsGenericIListInterface + ) + } + + override predicate callableFlow( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue + ) { + preservesValue = true and + ( + exists(string name, int arity | + name = c.getName() and + arity = c.getNumberOfParameters() and + c = this.getAMethod() + | + name = "Insert" and arity = 2 and source = TCallableFlowSourceArg(1) and - sink = TCallableFlowSinkQualifier() + sourceAp = AccessPath::empty() and + sink instanceof CallableFlowSinkQualifier and + sinkAp = AccessPath::element() + or + name.regexpMatch("FixedSize|GetRange") and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::element() ) + or + c = this.getAnIndexer().getSetter() and + source = TCallableFlowSourceArg(1) and + sourceAp = AccessPath::empty() and + sink instanceof CallableFlowSinkQualifier and + sinkAp = AccessPath::element() + or + c = this.getAnIndexer().getGetter() and + source instanceof CallableFlowSourceQualifier and + sourceAp = AccessPath::element() and + sink instanceof CallableFlowSinkReturn and + sinkAp = AccessPath::empty() ) } +} - private SourceDeclarationMethod getFind() { - exists(string name | - name = result.getName() and - result.getDeclaringType() = this.(RefType).getABaseType*() - | - name.regexpMatch("Find(All|Last)?") +/** Data flow for `System.Collections.[Generic.]IDictionary` (and sub types). */ +class IDictionaryFlow extends LibraryTypeDataFlow, RefType { + IDictionaryFlow() { + exists(Interface i | i = this.getABaseType*().getSourceDeclaration() | + i instanceof SystemCollectionsIDictionaryInterface + or + i instanceof SystemCollectionsGenericIDictionaryInterface ) } - private predicate propertyFlow(Property p) { - this.(RefType).getABaseType*() = p.getDeclaringType() and - p.hasName("Values") + override predicate callableFlow( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue + ) { + preservesValue = true and + exists(SystemCollectionsGenericKeyValuePairStruct kvp | + exists(int i | + c = this.getAConstructor() and + source = TCallableFlowSourceArg(i) and + sourceAp = sinkAp and + c.getParameter(i).getType().(ValueOrRefType).getABaseType*() instanceof + SystemCollectionsIEnumerableInterface and + sink instanceof CallableFlowSinkReturn + | + sinkAp = AccessPath::properties(kvp.getKeyProperty()) + or + sinkAp = AccessPath::properties(kvp.getValueProperty()) + ) + or + c = this.getProperty("Keys").getGetter() and + source instanceof CallableFlowSourceQualifier and + sourceAp = AccessPath::properties(kvp.getKeyProperty()) and + sink instanceof CallableFlowSinkReturn and + sinkAp = AccessPath::element() + or + ( + c = this.getProperty("Values").getGetter() + or + c = this.getAMethod("GetValueList") + ) and + source instanceof CallableFlowSourceQualifier and + sourceAp = AccessPath::properties(kvp.getValueProperty()) and + sink instanceof CallableFlowSinkReturn and + sinkAp = AccessPath::element() + or + ( + c = this.getAMethod("Add") and + c.getNumberOfParameters() = 2 + or + c = this.getAnIndexer().getSetter() + ) and + ( + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::empty() and + sink instanceof CallableFlowSinkQualifier and + sinkAp = AccessPath::properties(kvp.getKeyProperty()) + or + source = TCallableFlowSourceArg(1) and + sourceAp = AccessPath::empty() and + sink instanceof CallableFlowSinkQualifier and + sinkAp = AccessPath::properties(kvp.getValueProperty()) + ) + or + exists(Property p | + c = this.getAMethod("Add") and + c.getNumberOfParameters() = 1 and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::property(p) and + sink instanceof CallableFlowSinkQualifier and + sinkAp = AccessPath::properties(p) and + p = kvp.getAProperty() + ) + or + ( + c = this.getAnIndexer().getGetter() + or + c = this.getAMethod("GetByIndex") + ) and + source instanceof CallableFlowSourceQualifier and + sourceAp = AccessPath::properties(kvp.getValueProperty()) and + sink instanceof CallableFlowSinkReturn and + sinkAp = AccessPath::empty() + ) } } @@ -1195,33 +1553,6 @@ class SystemConvertFlow extends LibraryTypeDataFlow, SystemConvertClass { } } -/** - * Data flow for WCF data contracts. - * - * Flow is defined from a WCF data contract object to any of its data member - * properties. This flow model only makes sense from a taint-tracking perspective - * (a tainted data contract object implies tainted data members). - */ -class DataContractFlow extends LibraryTypeDataFlow, DataContractClass { - override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue - ) { - exists(Property p | - propertyFlow(p) and - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - c = p.getGetter() - ) and - preservesValue = false - } - - private predicate propertyFlow(Property p) { - p.getDeclaringType() = this and - p.getAnAttribute() instanceof DataMemberAttribute - } -} - /** Data flow for `System.Web.HttpCookie`. */ class SystemWebHttpCookieFlow extends LibraryTypeDataFlow, SystemWebHttpCookie { override predicate callableFlow( @@ -1301,97 +1632,39 @@ class SystemWebUIWebControlsTextBoxFlow extends LibraryTypeDataFlow, private predicate propertyFlow(Property p) { p = getTextProperty() } } -/** - * Data flow for `System.Collections.Generic.KeyValuePair`. - * - * Flow is only considered for the value (not the key). - */ -class SystemCollectionsGenericKeyValuePairStructFlow extends LibraryTypeDataFlow { - SystemCollectionsGenericKeyValuePairStructFlow() { - this instanceof SystemCollectionsGenericKeyValuePairStruct - } - +/** Data flow for `System.Collections.Generic.KeyValuePair`. */ +class SystemCollectionsGenericKeyValuePairStructFlow extends LibraryTypeDataFlow, + SystemCollectionsGenericKeyValuePairStruct { override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { - ( - constructorFlow(source, sink, c) + preservesValue = true and + exists(int i | + c.(Constructor).getDeclaringType() = this and + source = TCallableFlowSourceArg(i) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() + | + i = 0 and sinkAp = AccessPath::property(this.getKeyProperty()) or - exists(Property p | - propertyFlow(p) and - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - c = p.getGetter() - ) - ) and - preservesValue = true - } - - private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) { - c.getDeclaringType() = this and - source = getFlowSourceArg(c, 1) and - sink = TCallableFlowSinkReturn() - } - - private predicate propertyFlow(Property p) { - p = this.(SystemCollectionsGenericKeyValuePairStruct).getValueProperty() - } -} - -/** Data flow for `System.Collections.Generic.IEnumerator`. */ -class SystemCollectionsGenericIEnumeratorInterfaceFlow extends LibraryTypeDataFlow { - SystemCollectionsGenericIEnumeratorInterfaceFlow() { - this instanceof SystemCollectionsGenericIEnumeratorInterface - } - - override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue - ) { - exists(Property p | - propertyFlow(p) and - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - c = p.getGetter() - ) and - preservesValue = true - } - - private predicate propertyFlow(Property p) { - p = this.(SystemCollectionsGenericIEnumeratorInterface).getCurrentProperty() - } -} - -/** Data flow for `System.Collections.IEnumerator`. */ -class SystemCollectionsIEnumeratorInterfaceFlow extends LibraryTypeDataFlow, - SystemCollectionsIEnumeratorInterface { - override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue - ) { - exists(Property p | - propertyFlow(p) and - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - c = p.getGetter() - ) and - preservesValue = true + i = 1 and sinkAp = AccessPath::property(this.getValueProperty()) + ) } - - private predicate propertyFlow(Property p) { p = getCurrentProperty() } } /** Data flow for `System.Threading.Tasks.Task`. */ class SystemThreadingTasksTaskFlow extends LibraryTypeDataFlow, SystemThreadingTasksTaskClass { override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { ( - constructorFlow(source, sink, c) + constructorFlow(source, sink, c) and + sourceAp = AccessPath::empty() and + sinkAp = AccessPath::empty() or - methodFlow(source, sink, c) + methodFlow(source, sourceAp, sink, sinkAp, c) ) and preservesValue = true } @@ -1410,11 +1683,13 @@ class SystemThreadingTasksTaskFlow extends LibraryTypeDataFlow, SystemThreadingT } private predicate methodFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationMethod m ) { m.getDeclaringType() = this and ( m.hasName("ContinueWith") and + sourceAp = AccessPath::empty() and ( // flow from supplied state to supplied delegate exists(ConstructedDelegateType delegate, int i, int j, int k | @@ -1426,7 +1701,8 @@ class SystemThreadingTasksTaskFlow extends LibraryTypeDataFlow, SystemThreadingT ) and delegate.getTypeArgument(k) instanceof ObjectType and source = TCallableFlowSourceArg(i) and - sink = getDelegateFlowSinkArg(m, j, k) + sink = getDelegateFlowSinkArg(m, j, k) and + sinkAp = AccessPath::empty() ) or // flow out of supplied function @@ -1434,66 +1710,74 @@ class SystemThreadingTasksTaskFlow extends LibraryTypeDataFlow, SystemThreadingT m.getParameter(i).getType() = func and func.getUnboundGeneric() instanceof SystemFuncDelegateType and source = getDelegateFlowSourceArg(m, i) and - sink = TCallableFlowSinkReturn() + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty()) ) ) or m.hasName("FromResult") and - ( - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() - ) + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty()) or m.hasName("Run") and - ( - m.getReturnType() = any(SystemThreadingTasksTaskTClass c).getAConstructedGeneric() and - m.(UnboundGenericMethod).getNumberOfTypeParameters() = 1 and - source = TCallableFlowSourceDelegateArg(0) and - sink = TCallableFlowSinkReturn() - ) + m.getReturnType() = any(SystemThreadingTasksTaskTClass c).getAConstructedGeneric() and + m.(UnboundGenericMethod).getNumberOfTypeParameters() = 1 and + source = TCallableFlowSourceDelegateArg(0) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty()) or m.getName().regexpMatch("WhenAll|WhenAny") and - ( - m.getReturnType() = any(SystemThreadingTasksTaskTClass c).getAConstructedGeneric() and - m.(UnboundGenericMethod).getNumberOfTypeParameters() = 1 and - source = getFlowSourceArg(m, _) and - sink = TCallableFlowSinkReturn() - ) + m.getReturnType() = any(SystemThreadingTasksTaskTClass c).getAConstructedGeneric() and + m.(UnboundGenericMethod).getNumberOfTypeParameters() = 1 and + source = getFlowSourceArg(m, _, _) and + sourceAp = AccessPath::properties(any(SystemThreadingTasksTaskTClass c).getResultProperty()) and + sink = TCallableFlowSinkReturn() and + sinkAp = + AccessPath::cons(any(PropertyContent c | + c.getProperty() = any(SystemThreadingTasksTaskTClass tc).getResultProperty() + ), AccessPath::element()) ) } } /** Data flow for `System.Threading.Tasks.Task<>`. */ -class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow { - SystemThreadingTasksTaskTFlow() { this instanceof SystemThreadingTasksTaskTClass } - +class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow, SystemThreadingTasksTaskTClass { override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { ( - constructorFlow(source, sink, c) - or - methodFlow(source, sink, c) + constructorFlow(source, sourceAp, sink, sinkAp, c) or - exists(Property p | - propertyFlow(p) and - source = TCallableFlowSourceQualifier() and - sink = TCallableFlowSinkReturn() and - c = p.getGetter() - ) + methodFlow(source, sourceAp, sink, sinkAp, c) ) and preservesValue = true + or + exists(Property p | + p = this.(SystemThreadingTasksTaskTClass).getResultProperty() and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and + c = p.getGetter() and + preservesValue = false + ) } - private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) { + private predicate constructorFlow( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + Constructor c + ) { // flow from supplied function into constructed Task c.getDeclaringType() = this and - ( - c.getParameter(0).getType() = any(SystemFuncDelegateType t).getAConstructedGeneric() and - source = TCallableFlowSourceDelegateArg(0) and - sink = TCallableFlowSinkReturn() - ) + c.getParameter(0).getType() = any(SystemFuncDelegateType t).getAConstructedGeneric() and + source = TCallableFlowSourceDelegateArg(0) and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(this.(SystemThreadingTasksTaskTClass).getResultProperty()) or // flow from supplied state to supplied delegate c.getDeclaringType() = this and @@ -1503,12 +1787,15 @@ class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow { func.getUnboundGeneric().(SystemFuncDelegateType).getNumberOfTypeParameters() = 2 and func.getTypeArgument(0) instanceof ObjectType and source = TCallableFlowSourceArg(1) and - sink = getDelegateFlowSinkArg(c, 0, 0) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(c, 0, 0) and + sinkAp = AccessPath::empty() ) } private predicate methodFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationMethod m ) { m.getDeclaringType() = this and m.hasName("ContinueWith") and @@ -1525,13 +1812,17 @@ class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow { delegate.getTypeArgument(j) instanceof ObjectType and m.getParameter(k).getType() instanceof ObjectType and source = TCallableFlowSourceArg(k) and - sink = getDelegateFlowSinkArg(m, i, j) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, i, j) and + sinkAp = AccessPath::empty() ) or // flow from this task to supplied delegate delegate.getTypeArgument(j) = this and source = TCallableFlowSourceQualifier() and - sink = getDelegateFlowSinkArg(m, i, j) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, i, j) and + sinkAp = AccessPath::empty() ) or // flow out of supplied function @@ -1539,13 +1830,19 @@ class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow { m.getParameter(i).getType() = func and func.getUnboundGeneric() instanceof SystemFuncDelegateType and source = getDelegateFlowSourceArg(m, i) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(this.(SystemThreadingTasksTaskTClass).getResultProperty()) ) ) - } - - private predicate propertyFlow(Property p) { - p = this.(SystemThreadingTasksTaskTClass).getResultProperty() + or + m = this.getGetAwaiterMethod() and + source = TCallableFlowSourceQualifier() and + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = + AccessPath::field(any(SystemRuntimeCompilerServicesTaskAwaiterStruct s) + .getUnderlyingTaskField()) } } @@ -1557,15 +1854,16 @@ class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow { } override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { - methodFlow(source, sink, c) and + methodFlow(source, sourceAp, sink, sinkAp, c) and preservesValue = true } private predicate methodFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationMethod m ) { m.getDeclaringType() = this and ( @@ -1582,7 +1880,9 @@ class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow { delegate.getUnboundGeneric() instanceof SystemFuncDelegateType ) and source = TCallableFlowSourceArg(i) and - sink = getDelegateFlowSinkArg(m, j, k) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, j, k) and + sinkAp = AccessPath::empty() ) or // flow out of supplied function @@ -1590,7 +1890,9 @@ class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow { m.getParameter(i).getType() = func and func.getUnboundGeneric() instanceof SystemFuncDelegateType and source = getDelegateFlowSourceArg(m, i) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty()) ) ) or @@ -1606,7 +1908,9 @@ class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow { ) and delegate.getTypeArgument(k) instanceof ObjectType and source = TCallableFlowSourceArg(i) and - sink = getDelegateFlowSinkArg(m, j, k) + sourceAp = AccessPath::empty() and + sink = getDelegateFlowSinkArg(m, j, k) and + sinkAp = AccessPath::empty() ) or // flow out of supplied function @@ -1614,23 +1918,58 @@ class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow { m.getParameter(i).getType() = func and func.getUnboundGeneric() instanceof SystemFuncDelegateType and source = getDelegateFlowSourceArg(m, i) and - sink = TCallableFlowSinkReturn() + sourceAp = AccessPath::empty() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::property(any(SystemThreadingTasksTaskTClass c).getResultProperty()) ) ) ) } } +/** Data flow for `System.Runtime.CompilerServices.TaskAwaiter<>`. */ +class SystemRuntimeCompilerServicesTaskAwaiterFlow extends LibraryTypeDataFlow, + SystemRuntimeCompilerServicesTaskAwaiterStruct { + override predicate callableFlow( + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue + ) { + preservesValue = true and + c = this.getGetResultMethod() and + source = TCallableFlowSourceQualifier() and + sourceAp = + AccessPath::cons(any(FieldContent fc | fc.getField() = this.getUnderlyingTaskField()), + AccessPath::property(any(SystemThreadingTasksTaskTClass t).getResultProperty())) and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() + } + + override predicate requiresAccessPath(Content head, AccessPath tail) { + head.(FieldContent).getField() = this.getUnderlyingTaskField() and + tail = AccessPath::property(any(SystemThreadingTasksTaskTClass t).getResultProperty()) + } +} + /** Data flow for `System.Text.Encoding`. */ library class SystemTextEncodingFlow extends LibraryTypeDataFlow, SystemTextEncodingClass { override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { - (c = getGetBytesMethod() or c = getGetStringMethod() or c = getGetCharsMethod()) and - source = TCallableFlowSourceArg(0) and - sink = TCallableFlowSinkReturn() and - preservesValue = false + preservesValue = false and + c = this.getAMethod() and + exists(Method m | m.getAnOverrider*().getSourceDeclaration() = c | + m = getGetBytesMethod() and + source = getFlowSourceArg(m, 0, sourceAp) and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() + or + m = [getGetStringMethod(), getGetCharsMethod()] and + source = TCallableFlowSourceArg(0) and + sourceAp = AccessPath::element() and + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() + ) } } @@ -1766,12 +2105,13 @@ class SystemXmlXmlNamedNodeMapFlow extends LibraryTypeDataFlow, SystemXmlXmlName /** Data flow for `System.IO.Path`. */ class SystemIOPathFlow extends LibraryTypeDataFlow, SystemIOPathClass { override predicate callableFlow( - CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, - boolean preservesValue + CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, + SourceDeclarationCallable c, boolean preservesValue ) { c = getAMethod("Combine") and - source = getFlowSourceArg(c, _) and + source = getFlowSourceArg(c, _, sourceAp) and sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and preservesValue = false or exists(Parameter p | @@ -1779,8 +2119,9 @@ class SystemIOPathFlow extends LibraryTypeDataFlow, SystemIOPathClass { c.getName().matches("Get%") and p = c.getAParameter() and p.hasName("path") and - source = getFlowSourceArg(c, p.getPosition()) and + source = getFlowSourceArg(c, p.getPosition(), sourceAp) and sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() and preservesValue = false ) } @@ -1837,26 +2178,21 @@ class SystemNetWebUtilityFlow extends LibraryTypeDataFlow, SystemNetWebUtility { } /** - * The `StringValues` class used in many .NET Core libraries. Requires special `LibraryTypeDataFlow` flow. + * Custom flow through `StringValues` library class. */ -class StringValues extends Struct { - StringValues() { this.hasQualifiedName("Microsoft.Extensions.Primitives", "StringValues") } -} +class StringValuesFlow extends LibraryTypeDataFlow, Struct { + StringValuesFlow() { this.hasQualifiedName("Microsoft.Extensions.Primitives", "StringValues") } -/** - * Custom flow through StringValues.StringValues library class - */ -class StringValuesFlow extends LibraryTypeDataFlow, StringValues { override predicate callableFlow( CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, boolean preservesValue ) { - c = any(Callable ca | this = ca.getDeclaringType()) and + c.getDeclaringType() = this and ( - source = any(CallableFlowSourceArg a) or - source = any(CallableFlowSourceQualifier q) + source instanceof CallableFlowSourceArg or + source instanceof CallableFlowSourceQualifier ) and - sink = any(CallableFlowSinkReturn r) and + sink instanceof CallableFlowSinkReturn and preservesValue = false } } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/ModulusAnalysis.qll b/csharp/ql/src/semmle/code/csharp/dataflow/ModulusAnalysis.qll new file mode 100644 index 000000000000..2b3c3402415d --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/ModulusAnalysis.qll @@ -0,0 +1,302 @@ +/** + * Provides inferences of the form: `e` equals `b + v` modulo `m` where `e` is + * an expression, `b` is a `Bound` (typically zero or the value of an SSA + * variable), and `v` is an integer in the range `[0 .. m-1]`. + */ + +private import internal.rangeanalysis.ModulusAnalysisSpecific::Private +private import Bound +private import internal.rangeanalysis.SsaReadPositionCommon + +/** + * Holds if `e + delta` equals `v` at `pos`. + */ +private predicate valueFlowStepSsa(SsaVariable v, SsaReadPosition pos, Expr e, int delta) { + ssaUpdateStep(v, e, delta) and pos.hasReadOfVar(v) + or + exists(Guard guard, boolean testIsTrue | + pos.hasReadOfVar(v) and + guard = eqFlowCond(v, e, delta, true, testIsTrue) and + guardDirectlyControlsSsaRead(guard, pos, testIsTrue) + ) +} + +/** + * Holds if `add` is the addition of `larg` and `rarg`, neither of which are + * `ConstantIntegerExpr`s. + */ +private predicate nonConstAddition(Expr add, Expr larg, Expr rarg) { + exists(AddExpr a | a = add | + larg = a.getLhs() and + rarg = a.getRhs() + ) and + not larg instanceof ConstantIntegerExpr and + not rarg instanceof ConstantIntegerExpr +} + +/** + * Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not + * a `ConstantIntegerExpr`. + */ +private predicate nonConstSubtraction(Expr sub, Expr larg, Expr rarg) { + exists(SubExpr s | s = sub | + larg = s.getLhs() and + rarg = s.getRhs() + ) and + not rarg instanceof ConstantIntegerExpr +} + +/** Gets an expression that is the remainder modulo `mod` of `arg`. */ +private Expr modExpr(Expr arg, int mod) { + exists(RemExpr rem | + result = rem and + arg = rem.getLeftOperand() and + rem.getRightOperand().(ConstantIntegerExpr).getIntValue() = mod and + mod >= 2 + ) + or + exists(ConstantIntegerExpr c | + mod = 2.pow([1 .. 30]) and + c.getIntValue() = mod - 1 and + result.(BitwiseAndExpr).hasOperands(arg, c) + ) +} + +/** + * Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on + * its `testIsTrue` branch. + */ +private Guard moduloCheck(SsaVariable v, int val, int mod, boolean testIsTrue) { + exists(Expr rem, ConstantIntegerExpr c, int r, boolean polarity | + result.isEquality(rem, c, polarity) and + c.getIntValue() = r and + rem = modExpr(v.getAUse(), mod) and + ( + testIsTrue = polarity and val = r + or + testIsTrue = polarity.booleanNot() and + mod = 2 and + val = 1 - r and + (r = 0 or r = 1) + ) + ) +} + +/** + * Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`. + */ +private predicate moduloGuardedRead(SsaVariable v, SsaReadPosition pos, int val, int mod) { + exists(Guard guard, boolean testIsTrue | + pos.hasReadOfVar(v) and + guard = moduloCheck(v, val, mod, testIsTrue) and + guardControlsSsaRead(guard, pos, testIsTrue) + ) +} + +/** Holds if `factor` is a power of 2 that divides `mask`. */ +bindingset[mask] +private predicate andmaskFactor(int mask, int factor) { + mask % factor = 0 and + factor = 2.pow([1 .. 30]) +} + +/** Holds if `e` is evenly divisible by `factor`. */ +private predicate evenlyDivisibleExpr(Expr e, int factor) { + exists(ConstantIntegerExpr c, int k | k = c.getIntValue() | + e.(MulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2 + or + e.(LShiftExpr).getRhs() = c and factor = 2.pow(k) and k > 0 + or + e.(BitwiseAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f)) + ) +} + +/** + * Holds if `inp` is an input to `phi` along `edge` and this input has index `r` + * in an arbitrary 1-based numbering of the input edges to `phi`. + */ +private predicate rankedPhiInput( + SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge, int r +) { + edge.phiInput(phi, inp) and + edge = + rank[r](SsaReadPositionPhiInputEdge e | e.phiInput(phi, _) | e order by getId(e.getOrigBlock())) +} + +/** + * Holds if `rix` is the number of input edges to `phi`. + */ +private predicate maxPhiInputRank(SsaPhiNode phi, int rix) { + rix = max(int r | rankedPhiInput(phi, _, _, r)) +} + +/** + * Gets the remainder of `val` modulo `mod`. + * + * For `mod = 0` the result equals `val` and for `mod > 1` the result is within + * the range `[0 .. mod-1]`. + */ +bindingset[val, mod] +private int remainder(int val, int mod) { + mod = 0 and result = val + or + mod > 1 and result = ((val % mod) + mod) % mod +} + +/** + * Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`. + */ +private predicate phiSelfModulus( + SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge, int mod +) { + exists(SsaBound phibound, int v, int m | + edge.phiInput(phi, inp) and + phibound.getSsa() = phi and + ssaModulus(inp, edge, phibound, v, m) and + mod = m.gcd(v) and + mod != 1 + ) +} + +/** + * Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`. + */ +private predicate phiModulusInit(SsaPhiNode phi, Bound b, int val, int mod) { + exists(SsaVariable inp, SsaReadPositionPhiInputEdge edge | + edge.phiInput(phi, inp) and + ssaModulus(inp, edge, b, val, mod) + ) +} + +/** + * Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`. + */ +private predicate phiModulusRankStep(SsaPhiNode phi, Bound b, int val, int mod, int rix) { + rix = 0 and + phiModulusInit(phi, b, val, mod) + or + exists(SsaVariable inp, SsaReadPositionPhiInputEdge edge, int v1, int m1 | + mod != 1 and + val = remainder(v1, mod) + | + exists(int v2, int m2 | + rankedPhiInput(phi, inp, edge, rix) and + phiModulusRankStep(phi, b, v1, m1, rix - 1) and + ssaModulus(inp, edge, b, v2, m2) and + mod = m1.gcd(m2).gcd(v1 - v2) + ) + or + exists(int m2 | + rankedPhiInput(phi, inp, edge, rix) and + phiModulusRankStep(phi, b, v1, m1, rix - 1) and + phiSelfModulus(phi, inp, edge, m2) and + mod = m1.gcd(m2) + ) + ) +} + +/** + * Holds if `phi` is equal to `b + val` modulo `mod`. + */ +private predicate phiModulus(SsaPhiNode phi, Bound b, int val, int mod) { + exists(int r | + maxPhiInputRank(phi, r) and + phiModulusRankStep(phi, b, val, mod, r) + ) +} + +/** + * Holds if `v` at `pos` is equal to `b + val` modulo `mod`. + */ +private predicate ssaModulus(SsaVariable v, SsaReadPosition pos, Bound b, int val, int mod) { + phiModulus(v, b, val, mod) and pos.hasReadOfVar(v) + or + b.(SsaBound).getSsa() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0 + or + exists(Expr e, int val0, int delta | + exprModulus(e, b, val0, mod) and + valueFlowStepSsa(v, pos, e, delta) and + val = remainder(val0 + delta, mod) + ) + or + moduloGuardedRead(v, pos, val, mod) and b instanceof ZeroBound +} + +/** + * Holds if `e` is equal to `b + val` modulo `mod`. + * + * There are two cases for the modulus: + * - `mod = 0`: The equality `e = b + val` is an ordinary equality. + * - `mod > 1`: `val` lies within the range `[0 .. mod-1]`. + */ +cached +predicate exprModulus(Expr e, Bound b, int val, int mod) { + e = b.getExpr(val) and mod = 0 + or + evenlyDivisibleExpr(e, mod) and val = 0 and b instanceof ZeroBound + or + exists(SsaVariable v, SsaReadPositionBlock bb | + ssaModulus(v, bb, b, val, mod) and + e = v.getAUse() and + getABasicBlockExpr(bb.getBlock()) = e + ) + or + exists(Expr mid, int val0, int delta | + exprModulus(mid, b, val0, mod) and + valueFlowStep(e, mid, delta) and + val = remainder(val0 + delta, mod) + ) + or + exists(ConditionalExpr cond, int v1, int v2, int m1, int m2 | + cond = e and + condExprBranchModulus(cond, true, b, v1, m1) and + condExprBranchModulus(cond, false, b, v2, m2) and + mod = m1.gcd(m2).gcd(v1 - v2) and + mod != 1 and + val = remainder(v1, mod) + ) + or + exists(Bound b1, Bound b2, int v1, int v2, int m1, int m2 | + addModulus(e, true, b1, v1, m1) and + addModulus(e, false, b2, v2, m2) and + mod = m1.gcd(m2) and + mod != 1 and + val = remainder(v1 + v2, mod) + | + b = b1 and b2 instanceof ZeroBound + or + b = b2 and b1 instanceof ZeroBound + ) + or + exists(int v1, int v2, int m1, int m2 | + subModulus(e, true, b, v1, m1) and + subModulus(e, false, any(ZeroBound zb), v2, m2) and + mod = m1.gcd(m2) and + mod != 1 and + val = remainder(v1 - v2, mod) + ) +} + +private predicate condExprBranchModulus( + ConditionalExpr cond, boolean branch, Bound b, int val, int mod +) { + exprModulus(cond.getTrueExpr(), b, val, mod) and branch = true + or + exprModulus(cond.getFalseExpr(), b, val, mod) and branch = false +} + +private predicate addModulus(Expr add, boolean isLeft, Bound b, int val, int mod) { + exists(Expr larg, Expr rarg | nonConstAddition(add, larg, rarg) | + exprModulus(larg, b, val, mod) and isLeft = true + or + exprModulus(rarg, b, val, mod) and isLeft = false + ) +} + +private predicate subModulus(Expr sub, boolean isLeft, Bound b, int val, int mod) { + exists(Expr larg, Expr rarg | nonConstSubtraction(sub, larg, rarg) | + exprModulus(larg, b, val, mod) and isLeft = true + or + exprModulus(rarg, b, val, mod) and isLeft = false + ) +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll index 1750a297299b..f76b3414a867 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll @@ -5,7 +5,7 @@ * a `null` pointer exception (`NullReferenceException`) may be thrown. * Example: * - * ``` + * ```csharp * void M(string s) { * if (s != null) { * ... @@ -21,10 +21,8 @@ import csharp private import ControlFlow private import internal.CallableReturns private import semmle.code.csharp.commons.Assertions -private import semmle.code.csharp.commons.ComparisonTest private import semmle.code.csharp.controlflow.Guards as G private import semmle.code.csharp.controlflow.Guards::AbstractValues -private import semmle.code.csharp.dataflow.SSA private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.Test @@ -138,30 +136,6 @@ private predicate exprImpliesSsaDef( ) } -/** - * Holds if the `i`th node of basic block `bb` ensures that SSA definition - * `def` is not `null` in any subsequent uses. - */ -private predicate ensureNotNullAt(BasicBlock bb, int i, Ssa::Definition def) { - exists(Expr e, G::AbstractValue v, NullValue nv | - G::Internal::asserts(bb.getNode(i).getElement(), e, v) - | - exprImpliesSsaDef(e, v, def, nv) and - nv.isNonNull() - ) -} - -/** - * Holds if the `i`th node of basic block `bb` is a dereference `d` of SSA - * definition `def`, and `def` may potentially be `null`. - */ -private predicate potentialNullDereferenceAt( - BasicBlock bb, int i, Ssa::Definition def, Dereference d -) { - dereferenceAt(bb, i, def, d) and - not exists(int j | ensureNotNullAt(bb, j, def) | j < i) -} - /** * Gets an element that tests whether a given SSA definition, `def`, is * `null` or not. @@ -197,42 +171,44 @@ private predicate isNullDefaultArgument(Ssa::ExplicitDefinition def, AlwaysNullE /** Holds if `def` is an SSA definition that may be `null`. */ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason) { - // A variable compared to `null` might be `null` - exists(G::DereferenceableExpr de | de = def.getARead() | - reason = de.getANullCheck(_, true) and - msg = "as suggested by $@ null check" and - not de = any(Ssa::PseudoDefinition pdef).getARead() and - strictcount(Element e | e = any(Ssa::Definition def0 | de = def0.getARead()).getElement()) = 1 and - not nonNullDef(def) and - // Don't use a check as reason if there is a `null` assignment - // or argument - not def.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof MaybeNullExpr and - not isMaybeNullArgument(def, _) - ) - or - // A parameter might be `null` if there is a `null` argument somewhere - isMaybeNullArgument(def, reason) and + not nonNullDef(def) and ( - if reason instanceof AlwaysNullExpr - then msg = "because of $@ null argument" - else msg = "because of $@ potential null argument" - ) - or - isNullDefaultArgument(def, reason) and msg = "because the parameter has a null default value" - or - // If the source of a variable is `null` then the variable may be `null` - exists(AssignableDefinition adef | adef = def.(Ssa::ExplicitDefinition).getADefinition() | - adef.getSource() instanceof MaybeNullExpr and - reason = adef.getExpr() and - msg = "because of $@ assignment" - ) - or - // A variable of nullable type may be null - exists(Dereference d | dereferenceAt(_, _, def, d) | - d.hasNullableType() and - not def instanceof Ssa::PseudoDefinition and - reason = def.getSourceVariable().getAssignable() and - msg = "because it has a nullable type" + // A variable compared to `null` might be `null` + exists(G::DereferenceableExpr de | de = def.getARead() | + reason = de.getANullCheck(_, true) and + msg = "as suggested by $@ null check" and + not de = any(Ssa::PseudoDefinition pdef).getARead() and + strictcount(Element e | e = any(Ssa::Definition def0 | de = def0.getARead()).getElement()) = 1 and + // Don't use a check as reason if there is a `null` assignment + // or argument + not def.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof MaybeNullExpr and + not isMaybeNullArgument(def, _) + ) + or + // A parameter might be `null` if there is a `null` argument somewhere + isMaybeNullArgument(def, reason) and + ( + if reason instanceof AlwaysNullExpr + then msg = "because of $@ null argument" + else msg = "because of $@ potential null argument" + ) + or + isNullDefaultArgument(def, reason) and msg = "because the parameter has a null default value" + or + // If the source of a variable is `null` then the variable may be `null` + exists(AssignableDefinition adef | adef = def.(Ssa::ExplicitDefinition).getADefinition() | + adef.getSource() instanceof MaybeNullExpr and + reason = adef.getExpr() and + msg = "because of $@ assignment" + ) + or + // A variable of nullable type may be null + exists(Dereference d | dereferenceAt(_, _, def, d) | + d.hasNullableType() and + not def instanceof Ssa::PseudoDefinition and + reason = def.getSourceVariable().getAssignable() and + msg = "because it has a nullable type" + ) ) } @@ -269,7 +245,6 @@ private predicate defNullImpliesStep( bb2 = def.getBasicBlock() ) ) and - not ensureNotNullAt(bb1, _, def1) and not exists(SuccessorTypes::ConditionalSuccessor s, NullValue nv | bb1.getLastNode() = getANullCheck(def1, s, nv).getAControlFlowNode() | @@ -296,7 +271,7 @@ private predicate defMaybeNullInBlock(Ssa::Definition def, BasicBlock bb) { * dereference. */ private predicate nullDerefCandidateVariable(Ssa::SourceVariable v) { - exists(Ssa::Definition def, BasicBlock bb | potentialNullDereferenceAt(bb, _, def, _) | + exists(Ssa::Definition def, BasicBlock bb | dereferenceAt(bb, _, def, _) | defMaybeNullInBlock(def, bb) and v = def.getSourceVariable() ) @@ -328,7 +303,7 @@ private newtype TPathNode = succNullArgument(_, def, bb) } or TSinkPathNode(Ssa::Definition def, BasicBlock bb, int i, Dereference d) { - potentialNullDereferenceAt(bb, i, def, d) and + dereferenceAt(bb, i, def, d) and ( succStep(_, def, bb) or diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll index 7a6b58961477..928682a761ef 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll @@ -171,7 +171,7 @@ module Ssa { * callable. A pseudo read is inserted to make assignments to `out`/`ref` variables * live, for example line 1 in * - * ``` + * ```csharp * void M(out int i) { * i = 0; * } @@ -189,7 +189,7 @@ module Ssa { * A pseudo read is inserted to make assignments to the `ref` variable live, for example * line 2 in * - * ``` + * ```csharp * void M() { * ref int i = ref GetRef(); * i = 0; @@ -612,7 +612,7 @@ module Ssa { * For example, if `bb` is a basic block with a phi node for `v` (considered * to be at index -1), reads `v` at node 2, and defines it at node 5, we have: * - * ``` + * ```ql * ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node * ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2 * ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5 @@ -706,9 +706,9 @@ module Ssa { /** * Holds if `def` is accessed in basic block `bb1` (either a read or a write), - * `bb2` is a transitive successor of `bb1`, and `def` is *maybe* read in `bb2` - * or one of its transitive successors, but not in any block on the path between - * `bb1` and `bb2`. + * `bb2` is a transitive successor of `bb1`, `def` is live at the end of `bb1`, + * and the underlying variable for `def` is neither read nor written in any block + * on the path between `bb1` and `bb2`. */ private predicate varBlockReaches(TrackedDefinition def, BasicBlock bb1, BasicBlock bb2) { varOccursInBlock(def, bb1, _) and @@ -893,7 +893,7 @@ module Ssa { * of the field or property. For example, there is an implicit update of * `this.Field` on line 7 in * - * ``` + * ```csharp * int Field; * * void SetField(int i) { Field = i; } @@ -1018,7 +1018,7 @@ module Ssa { private predicate intraInstanceCallEdge(Callable c1, InstanceCallable c2) { exists(Call c | c.getEnclosingCallable() = c1 and - c2 = getARuntimeTarget(c) and + c2 = getARuntimeTarget(c, _) and c.(QualifiableExpr).targetIsLocalInstance() ) } @@ -1034,15 +1034,28 @@ module Ssa { * the SSA library and `Call.getARuntimeTarget()` mutually recursive), and * * (3) indirect calls to delegates via calls to library callables are included. + * + * The Boolean `libraryDelegateCall` indicates whether `c` is a call to a library + * method and the result is a delegate passed to `c`. For example, in + * + * ```csharp + * Lazy M1() + * { + * return new Lazy(M2); + * } + * ``` + * + * the constructor call `new Lazy(M2)` includes `M2` as a target. */ - Callable getARuntimeTarget(Call c) { + Callable getARuntimeTarget(Call c, boolean libraryDelegateCall) { // Non-delegate call: use dispatch library exists(DispatchCall dc | dc.getCall() = c | - result = dc.getADynamicTarget().getSourceDeclaration() + result = dc.getADynamicTarget().getSourceDeclaration() and + libraryDelegateCall = false ) or // Delegate call: use simple analysis - result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c) + result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c, libraryDelegateCall) } private module SimpleDelegateAnalysis { @@ -1055,12 +1068,14 @@ module Ssa { * Either `c` is a delegate call and `e` is the qualifier, or `c` is a call to * a library callable and `e` is a delegate argument. */ - private predicate delegateCall(Call c, Expr e) { - c = any(DelegateCall dc | e = dc.getDelegateExpr()) + private predicate delegateCall(Call c, Expr e, boolean libraryDelegateCall) { + c = any(DelegateCall dc | e = dc.getDelegateExpr()) and + libraryDelegateCall = false or c.getTarget().fromLibrary() and e = c.getAnArgument() and - e.getType() instanceof SystemLinqExpressions::DelegateExtType + e.getType() instanceof SystemLinqExpressions::DelegateExtType and + libraryDelegateCall = true } /** Holds if expression `e` is a delegate creation for callable `c` of type `t`. */ @@ -1090,7 +1105,7 @@ module Ssa { callable = call.getTarget() or callable = call.getTarget().(Method).getAnOverrider+() or callable = call.getTarget().(Method).getAnUltimateImplementor() or - callable = getARuntimeDelegateTarget(call) + callable = getARuntimeDelegateTarget(call, false) ) or pred = succ.(DelegateCreation).getArgument() @@ -1110,7 +1125,7 @@ module Ssa { } private predicate reachesDelegateCall(Expr e) { - delegateCall(_, e) + delegateCall(_, e, _) or exists(Expr mid | reachesDelegateCall(mid) | delegateFlowStep(e, mid)) } @@ -1128,12 +1143,14 @@ module Ssa { } /** Gets a run-time target for the delegate call `c`. */ - Callable getARuntimeDelegateTarget(Call c) { delegateCall(c, delegateCallSource(result)) } + Callable getARuntimeDelegateTarget(Call c, boolean libraryDelegateCall) { + delegateCall(c, delegateCallSource(result), libraryDelegateCall) + } } /** Holds if `(c1,c2)` is an edge in the call graph. */ predicate callEdge(Callable c1, Callable c2) { - exists(Call c | c.getEnclosingCallable() = c1 | getARuntimeTarget(c) = c2) + exists(Call c | c.getEnclosingCallable() = c1 and c2 = getARuntimeTarget(c, _)) } /** @@ -1179,7 +1196,7 @@ module Ssa { pragma[noinline] predicate callAt(BasicBlock bb, int i, Call call) { bb.getNode(i) = call.getAControlFlowNode() and - getARuntimeTarget(call).hasBody() + getARuntimeTarget(call, _).hasBody() } /** @@ -1201,7 +1218,7 @@ module Ssa { private predicate pruneFromLeft(Callable c) { exists(Call call, TrackedFieldOrProp f | updateCandidate(_, _, f, call) and - c = getARuntimeTarget(call) and + c = getARuntimeTarget(call, _) and generalSetter(_, f.getAssignable(), _) ) or @@ -1238,7 +1255,7 @@ module Ssa { updateCandidate(_, _, tfp, call) and fp = tfp.getAssignable() and generalSetter(_, fp, _) and - c1 = getARuntimeTarget(call) + c1 = getARuntimeTarget(call, _) } pragma[noinline] @@ -1279,7 +1296,7 @@ module Ssa { Call call, TrackedFieldOrProp tfp, Callable setter ) { updateCandidate(_, _, tfp, call) and - setsOwnFieldOrPropTransitive(getARuntimeTarget(call), tfp.getAssignable(), setter) + setsOwnFieldOrPropTransitive(getARuntimeTarget(call, _), tfp.getAssignable(), setter) } private predicate updatesNamedFieldOrPropPossiblyLive( @@ -1359,7 +1376,7 @@ module Ssa { * site that conceivably could reach an update of the captured variable. * For example, there is an implicit update of `v` on line 4 in * - * ``` + * ```csharp * int M() { * int i = 0; * Action a = () => { i = 1; }; @@ -1430,7 +1447,14 @@ module Ssa { ) { possiblyLiveAtAllNodes(bb, v) and callAt(bb, i, call) and - relevantDefinition(_, v.getAssignable(), _) + exists(Assignable a | + a = v.getAssignable() and + relevantDefinition(_, a, _) and + not exists(AssignableDefinitions::OutRefDefinition def | + def.getCall() = call and + def.getTarget() = a + ) + ) } /** @@ -1440,7 +1464,7 @@ module Ssa { private predicate pruneFromLeft(Callable c) { exists(Call call, CapturedWrittenLocalScopeSourceVariable v | updateCandidate(_, _, v, call) and - c = getARuntimeTarget(call) and + c = getARuntimeTarget(call, _) and relevantDefinition(_, v.getAssignable(), _) ) or @@ -1478,12 +1502,12 @@ module Ssa { pragma[noinline] private predicate updatesCapturedVariablePrefix( Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable c, - CapturedWrittenLocalScopeVariable captured + CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall ) { updateCandidate(_, _, v, call) and captured = v.getAssignable() and relevantDefinitionProj(_, captured) and - c = getARuntimeTarget(call) + c = getARuntimeTarget(call, libraryDelegateCall) } /** @@ -1498,11 +1522,13 @@ module Ssa { Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable writer, boolean additionalCalls ) { - exists(PrunedCallable c, CapturedWrittenLocalScopeVariable captured | - updatesCapturedVariablePrefix(call, v, c, captured) and + exists( + PrunedCallable c, CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall + | + updatesCapturedVariablePrefix(call, v, c, captured, libraryDelegateCall) and relevantDefinitionProj(writer, captured) and ( - c = writer and additionalCalls = false + c = writer and additionalCalls = libraryDelegateCall or callEdgePrunedPlus(c, writer) and additionalCalls = true ) @@ -1559,7 +1585,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * void M() { * int i = 0; * void M2() { @@ -1660,7 +1686,7 @@ module Ssa { private predicate pruneFromLeft(Callable c) { exists(Call call, CapturedReadLocalScopeSourceVariable v | implicitReadCandidate(_, _, call.getAControlFlowNode(), v) and - c = getARuntimeTarget(call) + c = getARuntimeTarget(call, _) ) or exists(Callable mid | pruneFromLeft(mid) | callEdge(mid, c)) @@ -1695,12 +1721,12 @@ module Ssa { pragma[noinline] private predicate readsCapturedVariablePrefix( ControlFlow::Node call, CapturedReadLocalScopeSourceVariable v, PrunedCallable c, - CapturedReadLocalScopeVariable captured + CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall ) { implicitReadCandidate(_, _, call, v) and captured = v.getAssignable() and capturerReads(_, captured) and - c = getARuntimeTarget(call.getElement()) + c = getARuntimeTarget(call.getElement(), libraryDelegateCall) } /** @@ -1715,11 +1741,13 @@ module Ssa { ControlFlow::Nodes::ElementNode call, CapturedReadLocalScopeSourceVariable v, Callable reader, boolean additionalCalls ) { - exists(PrunedCallable c, CapturedReadLocalScopeVariable captured | - readsCapturedVariablePrefix(call, v, c, captured) and + exists( + PrunedCallable c, CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall + | + readsCapturedVariablePrefix(call, v, c, captured, libraryDelegateCall) and capturerReads(reader, captured) and ( - c = reader and additionalCalls = false + c = reader and additionalCalls = libraryDelegateCall or callEdgePrunedPlus(c, reader) and additionalCalls = true ) @@ -1736,7 +1764,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * class C { * void M1() { * int i = 0; @@ -1758,7 +1786,6 @@ module Ssa { def.getSourceVariable().getAssignable() = lsv | lsv = v.getAssignable() and - adef = def.getAPossibleDefinition() and bb.getNode(i) = adef.getAControlFlowNode() and updatesCapturedVariable(def.getCall(), _, adef, additionalCalls) ) @@ -1775,7 +1802,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * class C { * void M1() { * int i = 0; @@ -1845,7 +1872,7 @@ module Ssa { ( exists(ReadKind rk | liveAfterWrite(bb, i, v, rk) | // A `ref` assignment such as - // ``` + // ```csharp // ref int i = ref GetRef(); // ``` // is dead when there are no reads of or writes to `i`. @@ -2005,7 +2032,7 @@ module Ssa { * can be reached from this SSA definition without passing through any * other SSA definitions. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2034,7 +2061,7 @@ module Ssa { * control flow node `cfn` that can be reached from this SSA definition * without passing through any other SSA definitions. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2066,7 +2093,7 @@ module Ssa { * can be reached from this SSA definition without passing through any * other SSA definition or read. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2102,7 +2129,7 @@ module Ssa { * control flow node `cfn` that can be reached from this SSA definition * without passing through any other SSA definition or read. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2142,7 +2169,7 @@ module Ssa { * another SSA definition for the source variable, without passing through * any other read. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2172,7 +2199,7 @@ module Ssa { * enclosing callable, or another SSA definition for the source variable, * without passing through any other read. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2203,7 +2230,7 @@ module Ssa { * Gets a definition that ultimately defines this SSA definition and is * not itself a pseudo node. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2322,7 +2349,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * class C { * void M1() { * int i = 0; @@ -2349,7 +2376,7 @@ module Ssa { * * Example: * - * ``` + * ```csharp * class C { * void M1() { * int i = 0; @@ -2510,7 +2537,7 @@ module Ssa { /** * Gets an input of this phi node. Example: * - * ``` + * ```csharp * int Field; * * void SetField(int i) { @@ -2537,6 +2564,13 @@ module Ssa { ) } + /** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */ + predicate hasInputFromBlock(Definition inp, BasicBlock bb) { + this.getAnInput() = inp and + this.getBasicBlock().getAPredecessor() = bb and + inp.isLiveAtEndOfBlock(bb) + } + override string toString() { result = getToStringPrefix(this) + "SSA phi(" + getSourceVariable() + ")" } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll new file mode 100644 index 000000000000..3742f87de251 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll @@ -0,0 +1,9 @@ +/** + * Provides sign analysis to determine whether expression are always positive + * or negative. + * + * The analysis is implemented as an abstract interpretation over the + * three-valued domain `{negative, zero, positive}`. + */ + +import semmle.code.csharp.dataflow.internal.rangeanalysis.SignAnalysisCommon diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/flowsources/PublicCallableParameter.qll b/csharp/ql/src/semmle/code/csharp/dataflow/flowsources/PublicCallableParameter.qll index a9e27915fe3a..e24a793174ad 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/flowsources/PublicCallableParameter.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/flowsources/PublicCallableParameter.qll @@ -7,7 +7,7 @@ import csharp /** * A parameter of a public callable, for example `p` in * - * ``` + * ```csharp * public void M(int p) { * ... * } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll index 2b09436f27d1..9c2cbabcd618 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll @@ -44,7 +44,7 @@ private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, bo * * For example, in * - * ``` + * ```csharp * if (b) * .... * var x = "foo"; diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index fdf0480c8ef3..08d099961d00 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -3,18 +3,24 @@ private import cil private import dotnet private import DataFlowPrivate private import DelegateDataFlow +private import semmle.code.csharp.dataflow.LibraryTypeDataFlow private import semmle.code.csharp.dispatch.Dispatch private import semmle.code.csharp.frameworks.system.Collections private import semmle.code.csharp.frameworks.system.collections.Generic /** - * Gets a source declaration of callable `c` that has a body. + * Gets a source declaration of callable `c` that has a body or has + * a flow summary. + * * If the callable has both CIL and source code, return only the source * code version. */ DotNet::Callable getCallableForDataFlow(DotNet::Callable c) { - result.hasBody() and exists(DotNet::Callable sourceDecl | sourceDecl = c.getSourceDeclaration() | + result = sourceDecl and + Summaries::summary(result, _, _, _, _, _) + or + result.hasBody() and if sourceDecl.getFile().fromSource() then // C# callable with C# implementation in the database @@ -67,7 +73,6 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c cached private module Cached { - private import CallContext private import semmle.code.csharp.Caching cached @@ -84,7 +89,8 @@ private module Cached { exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) | v = def.getSourceVariable().getAssignable() ) - } + } or + TQualifierReturnKind() cached newtype TDataFlowCall = @@ -94,87 +100,79 @@ private module Cached { TExplicitDelegateCall(ControlFlow::Nodes::ElementNode cfn, DelegateCall dc) { cfn.getElement() = dc } or - TImplicitDelegateCall(ControlFlow::Nodes::ElementNode cfn, DelegateArgumentToLibraryCallable arg) { - cfn.getElement() = arg - } or TTransitiveCapturedCall(ControlFlow::Nodes::ElementNode cfn, Callable target) { transitiveCapturedCallTarget(cfn, target) } or TCilCall(CIL::Call call) { // No need to include calls that are compiled from source not call.getImplementation().getMethod().compiledFromSource() + } or + TSummaryDelegateCall(SourceDeclarationCallable c, int pos) { + exists(CallableFlowSourceDelegateArg source | + Summaries::summary(c, source, _, _, _, _) and + pos = source.getArgumentIndex() + ) + or + exists(CallableFlowSinkDelegateArg sink | + Summaries::summary(c, _, _, sink, _, _) and + pos = sink.getDelegateIndex() + ) } /** Gets a viable run-time target for the call `call`. */ cached - DataFlowCallable viableImpl(DataFlowCall call) { result = call.getARuntimeTarget() } + DataFlowCallable viableCallable(DataFlowCall call) { result = call.getARuntimeTarget() } +} + +import Cached + +private module DispatchImpl { + private import CallContext /** * Gets a viable run-time target for the delegate call `call`, requiring * call context `cc`. */ - private DotNet::Callable viableDelegateCallable(DataFlowCall call, CallContext cc) { + private DataFlowCallable viableDelegateCallable(DataFlowCall call, CallContext cc) { result = call.(DelegateDataFlowCall).getARuntimeTarget(cc) } /** - * Holds if the call context `ctx` reduces the set of viable run-time - * targets of call `call` in `c`. + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. This is the case if the + * call is a delegate call, or if the qualifier accesses a parameter of + * the enclosing callable `c` (including the implicit `this` parameter). */ - cached - predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { - c = viableImpl(ctx) and + predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) { c = call.getEnclosingCallable() and - exists(CallContext cc | exists(viableDelegateCallable(call, cc)) | - not cc instanceof EmptyCallContext - ) - } - - private DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) | - cc.isArgument(ctx.getExpr(), _) + ( + exists(CallContext cc | exists(viableDelegateCallable(call, cc)) | + not cc instanceof EmptyCallContext + ) + or + call.(NonDelegateDataFlowCall).getDispatchCall().mayBenefitFromCallContext() ) } /** - * Gets a viable run-time target for the call `call` in the context - * `ctx`. This is restricted to those call nodes for which a context - * might make a difference. - */ - cached - DotNet::Callable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - reducedViableImplInCallContext(call, _, ctx) - } - - /** - * Holds if flow returning from callable `c` to call `call` might return - * further and if this path restricts the set of call sites that can be - * returned to. + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. */ - cached - predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { - exists(int tgts, int ctxtgts | - c = viableImpl(call) and - ctxtgts = strictcount(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableImpl(ctx) = call.getEnclosingCallable()) and - ctxtgts < tgts + DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) | + cc.isArgument(ctx.getExpr(), _) ) - } - - /** - * Gets a viable run-time target for the call `call` in the context `ctx`. - * This is restricted to those call nodes and results for which the return - * flow from the result to `call` restricts the possible context `ctx`. - */ - cached - DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { - result = viableImplInCallContext(call, ctx) and - reducedViableImplInReturn(result, call) + or + result = + call + .(NonDelegateDataFlowCall) + .getDispatchCall() + .getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall()) + .getSourceDeclaration() } } -import Cached +import DispatchImpl /** * Gets a node that can read the value returned from `call` with return kind @@ -182,8 +180,6 @@ import Cached */ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } -predicate viableCallable = viableImpl/1; - /** * A return kind. A return kind describes how a value can be returned * from a callable. @@ -238,6 +234,11 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind override string toString() { result = "captured " + v } } +/** A value returned through the qualifier of a call. */ +class QualifierReturnKind extends ReturnKind, TQualifierReturnKind { + override string toString() { result = "qualifier" } +} + class DataFlowCallable = DotNet::Callable; /** A call relevant for data flow. */ @@ -278,7 +279,10 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall { NonDelegateDataFlowCall() { this = TNonDelegateCall(cfn, dc) } - override DotNet::Callable getARuntimeTarget() { + /** Gets the underlying call. */ + DispatchCall getDispatchCall() { result = dc } + + override DataFlowCallable getARuntimeTarget() { result = getCallableForDataFlow(dc.getADynamicTarget()) } @@ -296,9 +300,9 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall { /** A delegate call relevant for data flow. */ abstract class DelegateDataFlowCall extends DataFlowCall { /** Gets a viable run-time target of this call requiring call context `cc`. */ - abstract DotNet::Callable getARuntimeTarget(CallContext::CallContext cc); + abstract DataFlowCallable getARuntimeTarget(CallContext::CallContext cc); - override DotNet::Callable getARuntimeTarget() { result = this.getARuntimeTarget(_) } + override DataFlowCallable getARuntimeTarget() { result = this.getARuntimeTarget(_) } } /** An explicit delegate call relevant for data flow. */ @@ -308,8 +312,8 @@ class ExplicitDelegateDataFlowCall extends DelegateDataFlowCall, TExplicitDelega ExplicitDelegateDataFlowCall() { this = TExplicitDelegateCall(cfn, dc) } - override DotNet::Callable getARuntimeTarget(CallContext::CallContext cc) { - result = dc.getARuntimeTarget(cc) + override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) { + result = getCallableForDataFlow(dc.getARuntimeTarget(cc)) } override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn } @@ -323,50 +327,6 @@ class ExplicitDelegateDataFlowCall extends DelegateDataFlowCall, TExplicitDelega override Location getLocation() { result = cfn.getLocation() } } -/** - * An implicit delegate call in a call to a library method. For example, we add - * an implicit call to `M` in `new Lazy(M)` (although, technically, the delegate - * would first be called when accessing the `Value` property). - */ -class ImplicitDelegateDataFlowCall extends DelegateDataFlowCall, TImplicitDelegateCall { - private ControlFlow::Nodes::ElementNode cfn; - private DelegateArgumentToLibraryCallable arg; - - ImplicitDelegateDataFlowCall() { this = TImplicitDelegateCall(cfn, arg) } - - /** - * Holds if the underlying delegate argument is the `i`th argument of the - * call `c` targeting a library callable. For example, `M` is the `0`th - * argument of `new Lazy(M)`. - */ - predicate isArgumentOf(NonDelegateDataFlowCall c, int i) { - exists(ImplicitDelegateOutNode out | out.getControlFlowNode() = cfn | out.isArgumentOf(c, i)) - } - - /** Gets the number of parameters of the supplied delegate. */ - int getNumberOfDelegateParameters() { result = arg.getDelegateType().getNumberOfParameters() } - - /** Gets the type of the `i`th parameter of the supplied delegate. */ - Type getDelegateParameterType(int i) { result = arg.getDelegateType().getParameter(i).getType() } - - /** Gets the return type of the supplied delegate. */ - Type getDelegateReturnType() { result = arg.getDelegateType().getReturnType() } - - override DotNet::Callable getARuntimeTarget(CallContext::CallContext cc) { - result = arg.getARuntimeTarget(cc) - } - - override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn } - - override ImplicitDelegateOutNode getNode() { result.getControlFlowNode() = cfn } - - override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() } - - override string toString() { result = "[implicit call] " + cfn.toString() } - - override Location getLocation() { result = cfn.getLocation() } -} - /** * A call that can reach a callable, using one or more additional calls, which * reads or updates a captured variable. We model such a chain of calls as just @@ -397,7 +357,7 @@ class CilDataFlowCall extends DataFlowCall, TCilCall { CilDataFlowCall() { this = TCilCall(call) } - override DotNet::Callable getARuntimeTarget() { + override DataFlowCallable getARuntimeTarget() { // There is no dispatch library for CIL, so do not consider overrides for now result = getCallableForDataFlow(call.getTarget()) } @@ -412,3 +372,34 @@ class CilDataFlowCall extends DataFlowCall, TCilCall { override Location getLocation() { result = call.getLocation() } } + +/** + * A delegate call inside a callable with a flow summary. + * + * For example, in `ints.Select(i => i + 1)` there is a call to the delegate at + * parameter position `1` (counting the qualifier as the `0`th argument) inside + * the method `Select`. + */ +class SummaryDelegateCall extends DelegateDataFlowCall, TSummaryDelegateCall { + private SourceDeclarationCallable c; + private int pos; + + SummaryDelegateCall() { this = TSummaryDelegateCall(c, pos) } + + override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) { + exists(SummaryDelegateParameterSink p | + p = TSummaryParameterNode(c, pos) and + result = p.getARuntimeTarget(cc) + ) + } + + override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() } + + override DataFlow::Node getNode() { none() } + + override Callable getEnclosingCallable() { result = c } + + override string toString() { result = "[summary] delegate call, parameter " + pos + " of " + c } + + override Location getLocation() { result = c.getLocation() } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index f876c04d6c66..8bc3d75ff86e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public * a subclass whose characteristic predicate is a unique singleton string. * For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends DataFlow::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) exists(Node mid | useFieldFlow(config) and nodeCandFwd1(mid, fromArg, config) and - store(mid, _, node) and + store(mid, _, node, _) and not outBarrier(mid, config) ) or // read - exists(Content f | - nodeCandFwd1Read(f, node, fromArg, config) and - nodeCandFwd1IsStored(f, config) and + exists(Content c | + nodeCandFwd1Read(c, node, fromArg, config) and + nodeCandFwd1IsStored(c, config) and not inBarrier(node, config) ) or @@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) } pragma[nomagic] -private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) { +private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) { exists(Node mid | nodeCandFwd1(mid, fromArg, config) and - read(mid, f, node) + read(mid, c, node) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`. */ pragma[nomagic] -private predicate nodeCandFwd1IsStored(Content f, Configuration config) { - exists(Node mid, Node node | +private predicate nodeCandFwd1IsStored(Content c, Configuration config) { + exists(Node mid, Node node, TypedContent tc | not fullBarrier(node, config) and useFieldFlow(config) and nodeCandFwd1(mid, config) and - store(mid, f, node) + store(mid, tc, node, _) and + c = tc.getContent() ) } @@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) ) or // store - exists(Content f | - nodeCand1Store(f, node, toReturn, config) and - nodeCand1IsRead(f, config) + exists(Content c | + nodeCand1Store(c, node, toReturn, config) and + nodeCand1IsRead(c, config) ) or // read - exists(Node mid, Content f | - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + exists(Node mid, Content c | + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, toReturn, config) ) or @@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config) } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand1`. */ pragma[nomagic] -private predicate nodeCand1IsRead(Content f, Configuration config) { +private predicate nodeCand1IsRead(Content c, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd1(node, unbind(config)) and - read(node, f, mid) and - nodeCandFwd1IsStored(f, unbind(config)) and + read(node, c, mid) and + nodeCandFwd1IsStored(c, unbind(config)) and nodeCand1(mid, _, config) ) } pragma[nomagic] -private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) { - exists(Node mid | +private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) { + exists(Node mid, TypedContent tc | nodeCand1(mid, toReturn, config) and - nodeCandFwd1IsStored(f, unbind(config)) and - store(node, f, mid) + nodeCandFwd1IsStored(c, unbind(config)) and + store(node, tc, mid, _) and + c = tc.getContent() ) } /** - * Holds if `f` is the target of both a read and a store in the flow covered + * Holds if `c` is the target of both a read and a store in the flow covered * by `nodeCand1`. */ -private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) { - nodeCand1IsRead(f, conf) and - nodeCand1Store(f, _, _, conf) +private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) { + nodeCand1IsRead(c, conf) and + nodeCand1Store(c, _, _, conf) } pragma[nomagic] @@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c } pragma[nomagic] -private predicate store(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and - nodeCand1(n2, unbind(config)) and - store(n1, f, n2) +private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) { + exists(TypedContent tc | + nodeCand1IsReadAndStored(c, config) and + nodeCand1(n2, unbind(config)) and + store(n1, tc, n2, _) and + c = tc.getContent() + ) } pragma[nomagic] -private predicate read(Node n1, Content f, Node n2, Configuration config) { - nodeCand1IsReadAndStored(f, config) and +private predicate read(Node n1, Content c, Node n2, Configuration config) { + nodeCand1IsReadAndStored(c, config) and nodeCand1(n2, unbind(config)) and - read(n1, f, n2) + read(n1, c, n2) } pragma[noinline] @@ -748,16 +753,16 @@ private predicate nodeCandFwd2( ) or // store - exists(Node mid, Content f | + exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, _, config) and - store(mid, f, node, config) and + storeCand1(mid, _, node, config) and stored = true ) or // read - exists(Content f | - nodeCandFwd2Read(f, node, fromArg, argStored, config) and - nodeCandFwd2IsStored(f, stored, config) + exists(Content c | + nodeCandFwd2Read(c, node, fromArg, argStored, config) and + nodeCandFwd2IsStored(c, stored, config) ) or // flow into a callable @@ -781,25 +786,25 @@ private predicate nodeCandFwd2( } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`. */ pragma[noinline] -private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) { +private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCand1(node, unbind(config)) and nodeCandFwd2(mid, _, _, stored, config) and - store(mid, f, node, config) + storeCand1(mid, c, node, config) ) } pragma[nomagic] private predicate nodeCandFwd2Read( - Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config + Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config ) { exists(Node mid | nodeCandFwd2(mid, fromArg, argStored, true, config) and - read(mid, f, node, config) + read(mid, c, node, config) ) } @@ -896,15 +901,15 @@ private predicate nodeCand2( ) or // store - exists(Content f | - nodeCand2Store(f, node, toReturn, returnRead, read, config) and - nodeCand2IsRead(f, read, config) + exists(Content c | + nodeCand2Store(c, node, toReturn, returnRead, read, config) and + nodeCand2IsRead(c, read, config) ) or // read - exists(Node mid, Content f, boolean read0 | - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and + exists(Node mid, Content c, boolean read0 | + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and nodeCand2(mid, toReturn, returnRead, read0, config) and read = true ) @@ -930,51 +935,51 @@ private predicate nodeCand2( } /** - * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a read in the flow covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) { +private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) { exists(Node mid, Node node | useFieldFlow(config) and nodeCandFwd2(node, _, _, true, unbind(config)) and - read(node, f, mid, config) and - nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and + read(node, c, mid, config) and + nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and nodeCand2(mid, _, _, read, config) ) } pragma[nomagic] private predicate nodeCand2Store( - Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, + Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored, Configuration config ) { exists(Node mid | - store(node, f, mid, config) and + storeCand1(node, c, mid, config) and nodeCand2(mid, toReturn, returnRead, true, config) and nodeCandFwd2(node, _, _, stored, unbind(config)) ) } /** - * Holds if `f` is the target of a store in the flow covered by `nodeCand2`. + * Holds if `c` is the target of a store in the flow covered by `nodeCand2`. */ pragma[nomagic] -private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) { +private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) { exists(Node node | - nodeCand2Store(f, node, _, _, stored, conf) and + nodeCand2Store(c, node, _, _, stored, conf) and nodeCand2(node, _, _, stored, conf) ) } /** - * Holds if `f` is the target of both a store and a read in the path graph + * Holds if `c` is the target of both a store and a read in the path graph * covered by `nodeCand2`. */ pragma[noinline] -private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) { +private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) { exists(boolean apNonEmpty | - nodeCand2IsStored(f, apNonEmpty, conf) and - nodeCand2IsRead(f, apNonEmpty, conf) + nodeCand2IsStored(c, apNonEmpty, conf) and + nodeCand2IsRead(c, apNonEmpty, conf) ) } @@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2( } private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends Node { + FlowCheckNode() { + this instanceof CastNode or + clearsContent(this, _) + } + } + /** * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Node node, Configuration config) { + predicate localFlowEntry(Node node, Configuration config) { nodeCand2(node, config) and ( config.isSource(node) or @@ -1058,9 +1074,9 @@ private module LocalFlowBigStep { additionalJumpStep(_, node, config) or node instanceof ParameterNode or node instanceof OutNodeExt or - store(_, _, node) or + store(_, _, node, _) or read(_, _, node) or - node instanceof CastNode + node instanceof FlowCheckNode ) } @@ -1074,11 +1090,11 @@ private module LocalFlowBigStep { additionalJumpStep(node, next, config) or flowIntoCallNodeCand1(_, node, next, config) or flowOutOfCallNodeCand1(_, node, next, config) or - store(node, _, next) or + store(node, _, next, _) or read(node, _, next) ) or - node instanceof CastNode + node instanceof FlowCheckNode or config.isSink(node) } @@ -1108,11 +1124,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getErasedNodeTypeBound(node1) + t = getNodeType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getErasedNodeTypeBound(node2) + t = getNodeType(node2) ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and @@ -1122,16 +1138,16 @@ private module LocalFlowBigStep { exists(Node mid | localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and localFlowStepNodeCand1(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and nodeCand2(node2, unbind(config)) ) or exists(Node mid | localFlowStepPlus(node1, mid, _, _, config, cc) and additionalLocalFlowStepNodeCand2(mid, node2, config) and - not mid instanceof CastNode and + not mid instanceof FlowCheckNode and preservesValue = false and - t = getErasedNodeTypeBound(node2) and + t = getNodeType(node2) and nodeCand2(node2, unbind(config)) ) ) @@ -1154,19 +1170,21 @@ private module LocalFlowBigStep { private import LocalFlowBigStep pragma[nomagic] -private predicate readCand2(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2, config) and +private predicate readCand2(Node node1, Content c, Node node2, Configuration config) { + read(node1, c, node2, config) and nodeCand2(node1, _, _, true, unbind(config)) and nodeCand2(node2, config) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(c, unbind(config)) } pragma[nomagic] -private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2, config) and +private predicate storeCand2( + Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config +) { + store(node1, tc, node2, contentType) and nodeCand2(node1, config) and nodeCand2(node2, _, _, true, unbind(config)) and - nodeCand2IsReadAndStored(f, unbind(config)) + nodeCand2IsReadAndStored(tc.getContent(), unbind(config)) } /** @@ -1183,9 +1201,8 @@ private predicate flowCandFwd( Configuration config ) { flowCandFwd0(node, fromArg, argApf, apf, config) and - if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), apf.getType()) - else any() + not apf.isClearedAt(node) and + if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any() } pragma[nomagic] @@ -1197,7 +1214,7 @@ private predicate flowCandFwd0( config.isSource(node) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) or exists(Node mid | flowCandFwd(mid, fromArg, argApf, apf, config) and @@ -1223,21 +1240,22 @@ private predicate flowCandFwd0( additionalJumpStep(mid, node, config) and fromArg = false and argApf = TAccessPathFrontNone() and - apf = TFrontNil(getErasedNodeTypeBound(node)) + apf = TFrontNil(getNodeType(node)) ) or // store - exists(Node mid, Content f | - flowCandFwd(mid, fromArg, argApf, _, config) and - storeCand2(mid, f, node, config) and + exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType | + flowCandFwd(mid, fromArg, argApf, apf0, config) and + storeCand2(mid, tc, node, contentType, config) and nodeCand2(node, _, _, true, unbind(config)) and - apf.headUsesContent(f) + apf.headUsesContent(tc) and + compatibleTypes(apf0.getType(), contentType) ) or // read - exists(Content f | - flowCandFwdRead(f, node, fromArg, argApf, config) and - flowCandFwdConsCand(f, apf, config) and + exists(TypedContent tc | + flowCandFwdRead(tc, node, fromArg, argApf, config) and + flowCandFwdConsCand(tc, apf, config) and nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config)) ) or @@ -1261,24 +1279,30 @@ private predicate flowCandFwd0( } pragma[nomagic] -private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) { - exists(Node mid, Node n | +private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n, DataFlowType contentType | flowCandFwd(mid, _, _, apf, config) and - storeCand2(mid, f, n, config) and + storeCand2(mid, tc, n, contentType, config) and nodeCand2(n, _, _, true, unbind(config)) and - compatibleTypes(apf.getType(), f.getType()) + compatibleTypes(apf.getType(), contentType) ) } +pragma[nomagic] +private predicate flowCandFwdRead0( + Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf, + AccessPathFrontHead apf, Configuration config +) { + flowCandFwd(node1, fromArg, argApf, apf, config) and + readCand2(node1, c, node2, config) and + apf.headUsesContent(tc) +} + pragma[nomagic] private predicate flowCandFwdRead( - Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config + TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowCandFwd(mid, fromArg, argApf, apf0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) - ) + flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config) } pragma[nomagic] @@ -1385,17 +1409,15 @@ private predicate flowCand0( ) or // store - exists(Content f, AccessPathFrontHead apf0 | - flowCandStore(node, f, toReturn, returnApf, apf0, config) and - apf0.headUsesContent(f) and - flowCandConsCand(f, apf, config) + exists(TypedContent tc | + flowCandStore(node, tc, apf, toReturn, returnApf, config) and + flowCandConsCand(tc, apf, config) ) or // read - exists(Content f, AccessPathFront apf0 | - flowCandRead(node, f, toReturn, returnApf, apf0, config) and - flowCandFwdConsCand(f, apf0, config) and - apf.headUsesContent(f) + exists(TypedContent tc, AccessPathFront apf0 | + flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and + flowCandFwdConsCand(tc, apf0, config) ) or // flow into a callable @@ -1417,36 +1439,40 @@ private predicate flowCand0( else returnApf = TAccessPathFrontNone() } +pragma[nomagic] +private predicate readCandFwd( + Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config +) { + flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config) +} + pragma[nomagic] private predicate flowCandRead( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config ) { exists(Node mid | - readCand2(node, f, mid, config) and + readCandFwd(node, tc, apf, mid, config) and flowCand(mid, toReturn, returnApf, apf0, config) ) } pragma[nomagic] private predicate flowCandStore( - Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0, - Configuration config + Node node, TypedContent tc, AccessPathFront apf, boolean toReturn, + AccessPathFrontOption returnApf, Configuration config ) { exists(Node mid | - storeCand2(node, f, mid, config) and - flowCand(mid, toReturn, returnApf, apf0, config) + flowCandFwd(node, _, _, apf, config) and + storeCand2(node, tc, mid, _, unbind(config)) and + flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config)) ) } pragma[nomagic] -private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) { - flowCandFwdConsCand(f, apf, config) and - exists(Node n, AccessPathFrontHead apf0 | - flowCandFwd(n, _, _, apf0, config) and - apf0.headUsesContent(f) and - flowCandRead(n, f, _, _, apf, config) - ) +private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) { + flowCandFwdConsCand(tc, apf, config) and + flowCandRead(_, tc, _, _, _, apf, config) } pragma[nomagic] @@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or - TConsCons(Content f1, Content f2, int len) { - flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()] + TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); - abstract Content getHead(); + abstract TypedContent getHead(); abstract int len(); @@ -1522,20 +1548,18 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(Content head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } - override Content getHead() { none() } + override TypedContent getHead() { none() } override int len() { result = 0 } @@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(Content head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { - private Content f; +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(f, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. - result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t)) + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) } - override Content getHead() { result = f } + override TypedContent getHead() { result = tc } override int len() { result = 1 } - override DataFlowType getType() { result = f.getContainerType() } + override DataFlowType getType() { result = tc.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f) } + override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { - private Content f1; - private Content f2; +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(f1, f2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 - then result = "[" + f1.toString() + ", " + f2.toString() + "]" - else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" } - override Content getHead() { result = f1 } + override TypedContent getHead() { result = tc1 } override int len() { result = len } - override DataFlowType getType() { result = f1.getContainerType() } + override DataFlowType getType() { result = tc1.getContainerType() } - override AccessPathFront getFront() { result = TFrontHead(f1) } + override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(Content head, AccessPath tail) { - head = f1 and + override AccessPathApprox pop(TypedContent head) { + head = tc1 and ( - tail = TConsCons(f2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(f2, _) + result = TConsNil(tc2, _) ) } } -/** Gets the access path obtained by popping `f` from `ap`, if any. */ -private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) } +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } -/** Gets the access path obtained by pushing `f` onto `ap`. */ -private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) } +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `apa` from a source + * in the configuration `config`. * - * The Boolean `fromArg` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, fromArg, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( - exists(Node mid | - flowFwd(mid, fromArg, argAp, apf, ap, config) and - localFlowBigStep(mid, node, true, _, config, _) + exists(Node mid, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and + localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(mid, fromArg, argAp, _, nil, config) and - localFlowBigStep(mid, node, false, apf, config, _) and - apf = ap.(AccessPathNil).getFront() + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and + localFlowBigStep(mid, node, false, apf, config, localCC) and + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and - fromArg = false and - argAp = TAccessPathNone() and - ap = TNil(getErasedNodeTypeBound(node)) and - apf = ap.(AccessPathNil).getFront() + cc instanceof CallContextAny and + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(Content f, AccessPath ap0 | - flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and - ap = push(f, ap0) - ) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read - exists(Content f | - flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and - flowFwdConsCand(f, apf, ap, config) + exists(TypedContent tc | + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, _, apf, ap, config) and - fromArg = true and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | - flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and - fromArg = false + exists(DataFlowCallable c | + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, fromArg, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } +pragma[nomagic] +private predicate flowFwdLocalEntry( + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config +) { + flowFwd(node, cc, argApa, apf, apa, config) and + localFlowEntry(node, config) and + localCC = getLocalCallContext(cc, node.getEnclosingCallable()) +} + pragma[nomagic] private predicate flowFwdStore( - Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - flowFwdStore1(mid, f, node, apf0, apf, config) + flowFwd(mid, cc, argApa, apf0, apa0, config) and + flowFwdStore0(mid, tc, node, apf0, apf, config) ) } pragma[nomagic] -private predicate flowFwdStore0( - Node mid, Content f, Node node, AccessPathFront apf0, Configuration config +private predicate storeCand( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf, + Configuration config ) { - storeCand2(mid, f, node, config) and - flowCand(mid, _, _, apf0, config) + storeCand2(mid, tc, node, _, config) and + flowCand(mid, _, _, apf0, config) and + apf.headUsesContent(tc) } pragma[noinline] -private predicate flowFwdStore1( - Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf, +private predicate flowFwdStore0( + Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf, Configuration config ) { - flowFwdStore0(mid, f, node, apf0, config) and - flowCandConsCand(f, apf0, config) and - apf.headUsesContent(f) and + storeCand(mid, tc, node, apf0, apf, config) and + flowCandConsCand(tc, apf0, config) and flowCand(node, _, _, apf, unbind(config)) } +pragma[nomagic] +private predicate flowFwdRead0( + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config +) { + flowFwd(node1, cc, argApa, apf0, apa0, config) and + readCandFwd(node1, tc, apf0, node2, config) +} + pragma[nomagic] private predicate flowFwdRead( - Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp, - Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { - exists(Node mid, AccessPathFrontHead apf0 | - flowFwd(mid, fromArg, argAp, apf0, ap0, config) and - readCand2(mid, f, node, config) and - apf0.headUsesContent(f) and - flowCand(node, _, _, _, unbind(config)) + exists(Node mid, TypedContent tc | + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and + flowCand(node, _, _, apf, unbind(config)) and + flowCandConsCand(tc, apf, unbind(config)) ) } pragma[nomagic] private predicate flowFwdConsCand( - Content f, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and - flowFwdStore1(n, f, _, apf, _, config) + flowFwd(n, _, _, apf, apa, config) and + flowFwdStore0(n, tc, _, apf, _, config) ) } pragma[nomagic] private predicate flowFwdIn( - DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | - flowFwd(arg, fromArg, argAp, apf, ap, config) and + exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and - flowCand(p, _, _, _, unbind(config)) + c = p.getEnclosingCallable() and + c = resolveCall(call, outercc) and + flowCand(p, _, _, _, unbind(config)) and + if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( - DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, - AccessPath ap, Configuration config + DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, fromArg, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and - flowCand(node, _, _, _, unbind(config)) + innerc = ret.getEnclosingCallable() and + flowCand(node, _, _, _, unbind(config)) and + ( + resolveReturn(innercc, innerc, call) + or + innercc.(CallContextCall).matchesCall(call) + ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `apa` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store - exists(Content f | - flowStore(f, node, toReturn, returnAp, ap, config) and - flowConsCand(f, ap, config) + exists(TypedContent tc | + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, true, TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - storeCand2(node1, f, node2, config) and - flowFwdStore(node2, f, ap, _, _, _, config) and - ap0 = push(f, ap) + storeCand2(node1, tc, node2, _, config) and + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, f, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { - readCand2(node1, f, node2, config) and - flowFwdRead(node2, f, ap, _, _, config) and - ap0 = pop(f, ap) and - flowFwdConsCand(f, _, ap0, unbind(config)) + exists(AccessPathFrontHead apf | + readCandFwd(node1, tc, apf, node2, config) and + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) + ) } pragma[nomagic] -private predicate flowConsCand(Content f, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, f, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -1969,12 +2028,13 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { - exists(ReturnNodeExt ret | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, true, TAccessPathSome(_), _, ap, config) + exists(ReturnNodeExt ret, CallContextCall ccc | + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and + ccc.matchesCall(call) ) } @@ -1985,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... @@ -2041,13 +2107,13 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2059,12 +2125,99 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { result = "[" + this.toStringImpl() } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2098,14 +2251,31 @@ class PathNode extends TPathNode { /** Gets the associated configuration. */ Configuration getConfiguration() { none() } + private predicate isHidden() { + nodeIsHidden(this.getNode()) and + not this.isSource() and + not this instanceof PathNodeSink + } + + private PathNode getASuccessorIfHidden() { + this.isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + /** Gets a successor of this node, if any. */ - PathNode getASuccessor() { none() } + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } /** Holds if this node is a source. */ predicate isSource() { none() } } abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + private string ppAp() { this instanceof PathNodeSink and result = "" or @@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { result.getConfiguration() = unbind(this.getConfiguration()) } - override PathNodeImpl getASuccessor() { + override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node result = getSuccMid() or @@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { override Configuration getConfiguration() { result = config } - override PathNode getASuccessor() { none() } + override PathNode getASuccessorImpl() { none() } override predicate isSource() { config.isSource(node) } } @@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TNil(getErasedNodeTypeBound(node)) + ap = TAccessPathNil(getNodeType(node)) or - exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or - exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() @@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt } pragma[nomagic] -private predicate readCand(Node node1, Content f, Node node2, Configuration config) { - read(node1, f, node2) and +private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) { + readCandFwd(node1, tc, _, node2, config) and flow(node2, config) } pragma[nomagic] -private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { +private predicate pathReadStep( + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc +) { ap0 = mid.getAp() and - readCand(mid.getNode(), f, node, mid.getConfiguration()) and + readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } pragma[nomagic] -private predicate storeCand(Node node1, Content f, Node node2, Configuration config) { - store(node1, f, node2) and +private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) { + storeCand2(node1, tc, node2, _, config) and flow(node2, config) } pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - storeCand(mid.getNode(), f, node, mid.getConfiguration()) and + storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and - ap = mid.getAp() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2321,10 +2494,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2333,10 +2506,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2345,22 +2517,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = ap.getApprox() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, AccessPath ap ) { - pathIntoArg(mid, i, outercc, call, ap) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + exists(AccessPathApprox apa | + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + ) } /** @@ -2403,7 +2578,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2419,11 +2596,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } @@ -2521,10 +2699,10 @@ private module FlowExploration { private newtype TPartialAccessPath = TPartialNil(DataFlowType t) or - TPartialCons(Content f, int len) { len in [1 .. 5] } + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } /** - * Conceptually a list of `Content`s followed by a `Type`, but only the first + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first * element of the list and its length are tracked. If data flows from a source to * a given node with a given `AccessPath`, this indicates the sequence of * dereference operations needed to get from the value in the node to the @@ -2533,7 +2711,7 @@ private module FlowExploration { private class PartialAccessPath extends TPartialAccessPath { abstract string toString(); - Content getHead() { this = TPartialCons(result, _) } + TypedContent getHead() { this = TPartialCons(result, _) } int len() { this = TPartialNil(_) and result = 0 @@ -2544,7 +2722,7 @@ private module FlowExploration { DataFlowType getType() { this = TPartialNil(result) or - exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType()) + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) } abstract AccessPathFront getFront(); @@ -2562,15 +2740,15 @@ private module FlowExploration { private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { override string toString() { - exists(Content f, int len | this = TPartialCons(f, len) | + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | if len = 1 - then result = "[" + f.toString() + "]" - else result = "[" + f.toString() + ", ... (" + len.toString() + ")]" + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" ) } override AccessPathFront getFront() { - exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f)) + exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc)) } } @@ -2591,7 +2769,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -2608,7 +2786,7 @@ private module FlowExploration { partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and if node instanceof CastingNode - then compatibleTypes(getErasedNodeTypeBound(node), ap.getType()) + then compatibleTypes(getNodeType(node), ap.getType()) else any() ) } @@ -2721,7 +2899,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() ) or @@ -2737,7 +2915,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getErasedNodeTypeBound(node)) and + ap = TPartialNil(getNodeType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -2746,11 +2924,12 @@ private module FlowExploration { sc2 = mid.getSummaryCtx2() and config = mid.getConfiguration() or - exists(PartialAccessPath ap0, Content f | - partialPathReadStep(mid, ap0, f, node, cc, config) and + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and - apConsFwd(ap, f, ap0, config) + apConsFwd(ap, tc, ap0, config) and + compatibleTypes(ap.getType(), getNodeType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -2769,35 +2948,42 @@ private module FlowExploration { pragma[inline] private predicate partialPathStoreStep( - PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2 + PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node, + PartialAccessPath ap2 ) { - ap1 = mid.getAp() and - store(mid.getNode(), f, node) and - ap2.getHead() = f and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypes(ap1.getType(), f.getType()) + exists(Node midNode, DataFlowType contentType | + midNode = mid.getNode() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) } pragma[nomagic] private predicate apConsFwd( - PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config ) { exists(PartialPathNodePriv mid | - partialPathStoreStep(mid, ap1, f, _, ap2) and + partialPathStoreStep(mid, ap1, tc, _, ap2) and config = mid.getConfiguration() ) } pragma[nomagic] private predicate partialPathReadStep( - PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc, + PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc, Configuration config ) { - ap = mid.getAp() and - readStep(mid.getNode(), f, node) and - ap.getHead() = f and - config = mid.getConfiguration() and - cc = mid.getCallContext() + exists(Node midNode | + midNode = mid.getNode() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node) and + ap.getHead() = tc and + config = mid.getConfiguration() and + cc = mid.getCallContext() + ) } private predicate partialPathOutOfCallable0( @@ -2806,7 +2992,7 @@ private module FlowExploration { ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and - not innercc instanceof CallContextCall and + innercc instanceof CallContextNoCall and ap = mid.getAp() and config = mid.getConfiguration() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index 852f54974e24..892250f44bb8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -22,7 +22,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p)) + compatibleTypes(getNodeType(arg), getNodeType(p)) ) } @@ -147,174 +147,140 @@ private module Cached { } } - private module LocalFlowBigStep { - private predicate localFlowEntry(Node n) { - Cand::cand(_, n) and - ( - n instanceof ParameterNode or - n instanceof OutNode or - readStep(_, _, n) or - n instanceof CastNode - ) - } - - private predicate localFlowExit(Node n) { - Cand::cand(_, n) and - ( - n instanceof ArgumentNode - or - n instanceof ReturnNode - or - readStep(n, _, _) - or - n instanceof CastNode - or - n = - any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun)) - .getPreUpdateNode() - ) - } - - pragma[nomagic] - private predicate localFlowStepPlus(Node node1, Node node2) { - localFlowEntry(node1) and - simpleLocalFlowStep(node1, node2) and - node1 != node2 - or - exists(Node mid | - localFlowStepPlus(node1, mid) and - simpleLocalFlowStep(mid, node2) and - not mid instanceof CastNode - ) - } - - pragma[nomagic] - predicate localFlowBigStep(Node node1, Node node2) { - localFlowStepPlus(node1, node2) and - localFlowExit(node2) - } - } - /** * The final flow-through calculation: * - * - Input access paths are abstracted with a `ContentOption` parameter - * that represents the head of the access path. `TContentNone()` means that - * the access path is unrestricted. + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. * - Types are checked using the `compatibleTypes()` relation. */ private module Final { /** * Holds if `p` can flow to `node` in the same callable using only - * value-preserving steps, not taking call contexts into account. + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `p` that can flow to `node` - * (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) { - parameterValueFlow0(p, node, contentIn) and + predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) + read = TReadStepTypesNone() and + compatibleTypes(getNodeType(p), getNodeType(node)) or // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) - ) + compatibleTypes(read.getContentType(), getNodeType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) { + private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and - contentIn = TContentNone() + read = TReadStepTypesNone() or // local flow exists(Node mid | - parameterValueFlow(p, mid, contentIn) and - LocalFlowBigStep::localFlowBigStep(mid, node) + parameterValueFlow(p, mid, read) and + simpleLocalFlowStep(mid, node) ) or // read - exists(Node mid, Content f | - parameterValueFlow(p, mid, TContentNone()) and - readStep(mid, f, node) and - contentIn.getContent() = f and + exists(Node mid | + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType()) + compatibleTypes(getNodeType(p), read.getContainerType()) ) or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ) { // flow through: no prior read exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, TContentNone()) and - argumentValueFlowsThrough(arg, contentIn, node) + parameterValueFlowArg(p, arg, mustBeNone) and + argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method exists(ArgumentNode arg | - parameterValueFlowArg(p, arg, contentIn) and - argumentValueFlowsThrough(arg, TContentNone(), node) + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ContentOption contentIn + ParameterNode p, ArgumentNode arg, ReadStepTypesOption read ) { - parameterValueFlow(p, arg, contentIn) and + parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn + DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | - parameterValueFlowReturn(param, kind, contentIn) + parameterValueFlowReturn(param, kind, read) ) } /** - * Holds if `arg` flows to `out` through a call using only value-preserving steps, - * not taking call contexts into account. + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. * - * `contentIn` describes the content of `arg` that can flow to `out` (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) { + predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | - argumentValueFlowsThrough0(call, arg, kind, contentIn) and + argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through - contentIn = TContentNone() and - compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) + read = TReadStepTypesNone() and + compatibleTypes(getNodeType(arg), getNodeType(out)) or // getter - exists(Content fIn | - contentIn.getContent() = fIn and - compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and - compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) - ) + compatibleTypes(getNodeType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeType(out)) ) } + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and a single read step, not taking call + * contexts into account, thus representing a getter-step. + */ + predicate getterStep(ArgumentNode arg, Content c, Node out) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + /** * Holds if `p` can flow to a return node of kind `kind` in the same - * callable using only value-preserving steps. + * callable using only value-preserving steps and possibly a single read + * step. * - * `contentIn` describes the content of `p` that can flow to the return - * node (if any). + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ContentOption contentIn + ParameterNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | - parameterValueFlow(p, ret, contentIn) and + parameterValueFlow(p, ret, read) and kind = ret.getKind() ) } @@ -323,37 +289,107 @@ private module Cached { import Final } + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, c) and + c = viableCallable(ctx) and + ctxtgts = count(viableImplInCallContext(call, ctx)) and + tgts = strictcount(viableCallable(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContext(call, _) and + c = viableCallable(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + /** * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ cached predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { - parameterValueFlow(p, n.getPreUpdateNode(), TContentNone()) + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } - /** - * Holds if data can flow from `node1` to `node2` via a direct assignment to - * `f`. - * - * This includes reverse steps through reads when the result of the read has - * been stored into, in order to handle cases like `x.f1.f2 = y`. - */ - cached - predicate store(Node node1, Content f, Node node2) { - storeStep(node1, f, node2) and readStep(_, f, _) + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + storeStep(node1, c, node2) and + readStep(_, c, _) and + contentType = getNodeType(node1) and + containerType = getNodeType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and n2 = node2.(PostUpdateNode).getPreUpdateNode() | - argumentValueFlowsThrough(n2, TContentSome(f), n1) + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, f, n1) + readStep(n2, c, n1) and + contentType = getNodeType(n1) and + containerType = getNodeType(n2) ) } - import FlowThrough + /** + * Holds if data can flow from `node1` to `node2` via a direct assignment to + * `f`. + * + * This includes reverse steps through reads when the result of the read has + * been stored into, in order to handle cases like `x.f1.f2 = y`. + */ + cached + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) + } /** * Holds if the call context `call` either improves virtual dispatch in @@ -397,10 +433,13 @@ private module Cached { TBooleanNone() or TBooleanSome(boolean b) { b = true or b = false } + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } + cached newtype TAccessPathFront = TFrontNil(DataFlowType t) or - TFrontHead(Content f) + TFrontHead(TypedContent tc) cached newtype TAccessPathFrontOption = @@ -415,25 +454,38 @@ class CastingNode extends Node { CastingNode() { this instanceof ParameterNode or this instanceof CastNode or - this instanceof OutNodeExt + this instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + readStep(_, _, this) } } -newtype TContentOption = - TContentNone() or - TContentSome(Content f) +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + readStep(n1, c, n2) and + container = getNodeType(n1) and + content = getNodeType(n2) +} -private class ContentOption extends TContentOption { - Content getContent() { this = TContentSome(result) } +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) + } - predicate hasContent() { exists(this.getContent()) } +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } - string toString() { - result = this.getContent().toString() - or - not this.hasContent() and - result = "" - } + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } } /** @@ -460,13 +512,19 @@ abstract class CallContext extends TCallContext { abstract predicate relevantFor(DataFlowCallable callable); } -class CallContextAny extends CallContext, TAnyCallContext { +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { override string toString() { result = "CcAny" } override predicate relevantFor(DataFlowCallable callable) { any() } } -abstract class CallContextCall extends CallContext { } +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} class CallContextSpecificCall extends CallContextCall, TSpecificCall { override string toString() { @@ -477,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { recordDataFlowCallSite(getCall(), callable) } + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + DataFlowCall getCall() { this = TSpecificCall(result) } } @@ -486,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override predicate relevantFor(DataFlowCallable callable) { exists(ParameterNode p | p.getEnclosingCallable() = callable) } + + override predicate matchesCall(DataFlowCall call) { any() } } -class CallContextReturn extends CallContext, TReturn { +class CallContextReturn extends CallContextNoCall, TReturn { override string toString() { exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") } @@ -678,9 +740,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallable(call) and cc instanceof CallContextReturn } -pragma[noinline] -DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } - predicate read = readStep/3; /** An optional Boolean value. */ @@ -692,6 +751,23 @@ class BooleanOption extends TBooleanOption { } } +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } +} + /** * The front of an access path. This is either a head or a nil. */ @@ -702,25 +778,36 @@ abstract class AccessPathFront extends TAccessPathFront { abstract boolean toBoolNonEmpty(); - predicate headUsesContent(Content f) { this = TFrontHead(f) } + predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) } + + predicate isClearedAt(Node n) { + exists(TypedContent tc | + this.headUsesContent(tc) and + clearsContent(n, tc.getContent()) + ) + } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { - override string toString() { - exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t)) - } + private DataFlowType t; + + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } - override DataFlowType getType() { this = TFrontNil(result) } + override DataFlowType getType() { result = t } override boolean toBoolNonEmpty() { result = false } } class AccessPathFrontHead extends AccessPathFront, TFrontHead { - override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) } + private TypedContent tc; - override DataFlowType getType() { - exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) - } + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } override boolean toBoolNonEmpty() { result = true } } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll index 0dc3b8eff450..4e1cd281488f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll @@ -37,21 +37,12 @@ module Consistency { ) } - query predicate uniqueTypeBound(Node n, string msg) { + query predicate uniqueType(Node n, string msg) { exists(int c | n instanceof RelevantNode and - c = count(n.getTypeBound()) and + c = count(getNodeType(n)) and c != 1 and - msg = "Node should have one type bound but has " + c + "." - ) - } - - query predicate uniqueTypeRepr(Node n, string msg) { - exists(int c | - n instanceof RelevantNode and - c = count(getErasedRepr(n.getTypeBound())) and - c != 1 and - msg = "Node should have one type representation but has " + c + "." + msg = "Node should have one type but has " + c + "." ) } @@ -104,7 +95,7 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } - private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { t = typeRepr() and @@ -132,8 +123,18 @@ module Consistency { n.getEnclosingCallable() != call.getEnclosingCallable() } + // This predicate helps the compiler forget that in some languages + // it is impossible for a result of `getPreUpdateNode` to be an + // instance of `PostUpdateNode`. + private Node getPre(PostUpdateNode n) { + result = n.getPreUpdateNode() + or + none() + } + query predicate postIsNotPre(PostUpdateNode n, string msg) { - n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node." + getPre(n) = n and + msg = "PostUpdateNode should not equal its pre-update node." } query predicate postHasUniquePre(PostUpdateNode n, string msg) { @@ -161,15 +162,14 @@ module Consistency { msg = "Origin of readStep is missing a PostUpdateNode." } - query predicate storeIsPostUpdate(Node n, string msg) { - storeStep(_, _, n) and - not n instanceof PostUpdateNode and - msg = "Store targets should be PostUpdateNodes." - } - query predicate argHasPostUpdate(ArgumentNode n, string msg) { not hasPost(n) and not isImmutableOrUnobservable(n) and msg = "ArgumentNode is missing PostUpdateNode." } + + query predicate postWithInFlow(PostUpdateNode n, string msg) { + simpleLocalFlowStep(_, n) and + msg = "PostUpdateNode should not be the target of local flow." + } } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index f9bac316d706..bae73a6609d9 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -15,6 +15,57 @@ private import semmle.code.csharp.dataflow.LibraryTypeDataFlow private import semmle.code.csharp.dispatch.Dispatch private import semmle.code.csharp.frameworks.EntityFramework private import semmle.code.csharp.frameworks.NHibernate +private import semmle.code.csharp.frameworks.system.Collections +private import semmle.code.csharp.frameworks.system.threading.Tasks +private import semmle.code.csharp.frameworks.system.linq.Expressions + +abstract class NodeImpl extends Node { + /** Do not call: use `getEnclosingCallable()` instead. */ + abstract DataFlowCallable getEnclosingCallableImpl(); + + /** Do not call: use `getType()` instead. */ + abstract DotNet::Type getTypeImpl(); + + /** Gets the type of this node used for type pruning. */ + cached + Gvn::GvnType getDataFlowType() { + Stages::DataFlowStage::forceCachingInSameStage() and + exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) | + t0 = getCSharpType(this.getType()) + or + not exists(getCSharpType(this.getType())) and + t0 instanceof ObjectType + ) + } + + /** Do not call: use `getControlFlowNode()` instead. */ + abstract ControlFlow::Node getControlFlowNodeImpl(); + + /** Do not call: use `getLocation()` instead. */ + abstract Location getLocationImpl(); + + /** Do not call: use `toString()` instead. */ + abstract string toStringImpl(); +} + +private class ExprNodeImpl extends ExprNode, NodeImpl { + override DataFlowCallable getEnclosingCallableImpl() { + result = this.getExpr().getEnclosingCallable() + } + + override DotNet::Type getTypeImpl() { result = this.getExpr().getType() } + + override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { this = TExprNode(result) } + + override Location getLocationImpl() { result = this.getExpr().getLocation() } + + override string toStringImpl() { + result = this.getControlFlowNode().toString() + or + this = TCilExprNode(_) and + result = "CIL expression" + } +} /** Calculation of the relative order in which `this` references are read. */ private module ThisFlow { @@ -73,7 +124,7 @@ private module ThisFlow { /** Provides predicates related to local data flow. */ module LocalFlow { - class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration { + private class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration { LocalExprStepConfiguration() { this = "LocalExprStepConfiguration" } override predicate candidate( @@ -105,10 +156,6 @@ module LocalFlow { scope = e2 and isSuccessor = true or - e1 = e2.(AwaitExpr).getExpr() and - scope = e2 and - isSuccessor = true - or // An `=` expression, where the result of the expression is used e2 = any(AssignExpr ae | @@ -117,6 +164,18 @@ module LocalFlow { ) and scope = e2 and isSuccessor = true + or + e1 = e2.(ObjectCreation).getInitializer() and + scope = e2 and + isSuccessor = false + or + e1 = e2.(ArrayCreation).getInitializer() and + scope = e2 and + isSuccessor = false + or + e1 = e2.(SwitchExpr).getACase().getBody() and + scope = e2 and + isSuccessor = false ) } @@ -151,7 +210,7 @@ module LocalFlow { result = node.asExpr() } - predicate localFlowStepCil(Node nodeFrom, Node nodeTo) { + private predicate localFlowStepCil(Node nodeFrom, Node nodeTo) { asCilDataFlowNode(nodeFrom).getALocalFlowSucc(asCilDataFlowNode(nodeTo), any(CIL::Untainted t)) } @@ -241,6 +300,62 @@ module LocalFlow { nodeTo = TImplicitCapturedArgumentNode(call, def.getSourceVariable().getAssignable()) ) } + + predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { + exists(Ssa::Definition def | + localSsaFlowStep(def, nodeFrom, nodeTo) and + not usesInstanceField(def) + ) + or + any(LocalExprStepConfiguration x).hasNodePath(nodeFrom, nodeTo) + or + ThisFlow::adjacentThisRefs(nodeFrom, nodeTo) + or + ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo) + or + localFlowStepCil(nodeFrom, nodeTo) + } + + /** + * Holds if node `n` should not be included in the exposed local data/taint + * flow relations. This is the case for nodes that are only relevant for + * inter-procedurality or field-sensitivity. + */ + predicate excludeFromExposedRelations(Node n) { + n instanceof Summaries::SummaryNodeImpl or + n instanceof ImplicitCapturedArgumentNode + } +} + +pragma[noinline] +private Expr getImplicitArgument(Call c, int pos) { + result = c.getArgument(pos) and + not exists(result.getExplicitArgumentName()) +} + +pragma[nomagic] +private Expr getExplicitArgument(Call c, string name) { + result = c.getAnArgument() and + result.getExplicitArgumentName() = name +} + +/** + * Holds if `arg` is a `params` argument of `c`, for parameter `p`, and `arg` will + * be wrapped in an array by the C# compiler. + */ +private predicate isParamsArg(Call c, Expr arg, Parameter p) { + exists(Callable target, int numArgs | + target = c.getTarget() and + p = target.getAParameter() and + p.isParams() and + numArgs = c.getNumberOfArguments() and + arg = + [getImplicitArgument(c, [p.getPosition() .. numArgs - 1]), getExplicitArgument(c, p.getName())] + | + numArgs > target.getNumberOfParameters() + or + not arg.getType().isImplicitlyConvertibleTo(p.getType()) + ) } /** An argument of a C# call (including qualifier arguments). */ @@ -251,7 +366,8 @@ private class Argument extends Expr { Argument() { call = any(DispatchCall dc | - this = dc.getArgument(arg) + this = dc.getArgument(arg) and + not isParamsArg(_, this, _) or this = dc.getQualifier() and arg = -1 and not dc.getAStaticTarget().(Modifiable).isStatic() ).getCall() @@ -269,43 +385,39 @@ private class Argument extends Expr { /** * Holds if `e` is an assignment of `src` to field or property `c` of `q`. + * + * `postUpdate` indicates whether the store targets a post-update node. */ -private predicate fieldOrPropertyAssign(Expr e, Content c, Expr src, Expr q) { - exists(FieldOrPropertyAccess fa, FieldOrProperty f, AssignableDefinition def | - def.getTargetAccess() = fa and - f = fa.getTarget() and +private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, boolean postUpdate) { + exists(FieldOrProperty f | c = f.getContent() and - src = def.getSource() and - q = fa.getQualifier() and - e = def.getExpr() - | - f.isFieldLike() and - f instanceof InstanceFieldOrProperty - or - exists(AccessPath ap | - LibraryFlow::libraryFlow(_, _, ap, _, _, _) and - ap.contains(f.getContent()) + ( + f.isFieldLike() and + f instanceof InstanceFieldOrProperty + or + exists(AccessPath ap | + Summaries::summary(_, _, ap, _, _, _) and + ap.contains(f.getContent()) + ) ) - ) -} - -/** - * Holds if `oc` has an object initializer that assigns `src` to field or - * property `c`. - */ -private predicate fieldOrPropertyInit(ObjectCreation oc, Content c, Expr src) { - exists(MemberInitializer mi, FieldOrProperty f | - mi = oc.getInitializer().(ObjectInitializer).getAMemberInitializer() and - f = mi.getInitializedMember() and - c = f.getContent() and - src = mi.getRValue() | - f.isFieldLike() and - f instanceof InstanceFieldOrProperty + // Direct assignment, `q.f = src` + exists(FieldOrPropertyAccess fa, AssignableDefinition def | + def.getTargetAccess() = fa and + f = fa.getTarget() and + src = def.getSource() and + q = fa.getQualifier() and + e = def.getExpr() and + postUpdate = true + ) or - exists(AccessPath ap | - LibraryFlow::libraryFlow(_, _, ap, _, _, _) and - ap.contains(f.getContent()) + // Object initializer, `new C() { f = src }` + exists(MemberInitializer mi | + e = q and + mi = q.(ObjectInitializer).getAMemberInitializer() and + f = mi.getInitializedMember() and + src = mi.getRValue() and + postUpdate = false ) ) } @@ -329,7 +441,7 @@ private predicate fieldOrPropertyRead(Expr e1, Content c, FieldOrPropertyRead e2 ret = e2.getTarget() or exists(AccessPath ap, Property target | - LibraryFlow::libraryFlow(_, _, _, _, ap, _) and + Summaries::summary(_, _, _, _, ap, _) and ap.contains(ret.getContent()) and target.getGetter() = e2.(PropertyCall).getARuntimeTarget() and overridesOrImplementsSourceDecl(target, ret) @@ -337,20 +449,64 @@ private predicate fieldOrPropertyRead(Expr e1, Content c, FieldOrPropertyRead e2 ) } -Type getCSharpType(DotNet::Type t) { +/** + * Holds if `e` is an expression that adds `src` to array `a`. + * + * `postUpdate` indicates whether the store targets a post-update node. + */ +private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) { + // Direct assignment, `a[i] = src` + exists(AssignableDefinition def | + a = def.getTargetAccess().(ArrayWrite).getQualifier() and + src = def.getSource() and + e = def.getExpr() and + postUpdate = true + ) + or + // Array initializer, `new [] { src }` + src = a.(ArrayInitializer).getAnElement() and + e = a and + postUpdate = false + or + // Member initalizer, `new C { Array = { [i] = src } }` + exists(MemberInitializer mi | + mi = a.(ObjectInitializer).getAMemberInitializer() and + mi.getLValue() instanceof ArrayAccess and + mi.getRValue() = src and + e = a and + postUpdate = false + ) +} + +/** + * Holds if `e2` is an expression that reads an array element from + * from expresion `e1`. + */ +private predicate arrayRead(Expr e1, ArrayRead e2) { e1 = e2.getQualifier() } + +private Type getCSharpType(DotNet::Type t) { result = t or result.matchesHandle(t) } +/** A GVN type that is either a `DataFlowType` or unifiable with a `DataFlowType`. */ +private class DataFlowTypeOrUnifiable extends Gvn::GvnType { + pragma[nomagic] + DataFlowTypeOrUnifiable() { + this instanceof DataFlowType or + Gvn::unifiable(any(DataFlowType t), this) + } +} + pragma[noinline] -private TypeParameter getATypeParameterSubType(DataFlowType t) { +private TypeParameter getATypeParameterSubType(DataFlowTypeOrUnifiable t) { not t instanceof Gvn::TypeParameterGvnType and exists(Type t0 | t = Gvn::getGlobalValueNumber(t0) | implicitConversionRestricted(result, t0)) } pragma[noinline] -private DataFlowType getANonTypeParameterSubType(DataFlowType t) { +private Gvn::GvnType getANonTypeParameterSubType(DataFlowTypeOrUnifiable t) { not t instanceof Gvn::TypeParameterGvnType and not result instanceof Gvn::TypeParameterGvnType and exists(Type t1, Type t2 | @@ -363,6 +519,8 @@ private DataFlowType getANonTypeParameterSubType(DataFlowType t) { /** A collection of cached types and predicates to be evaluated in the same stage. */ cached private module Cached { + private import Summaries + cached newtype TNode = TExprNode(ControlFlow::Nodes::ElementNode cfn) { @@ -380,21 +538,10 @@ private module Cached { v = def.getSourceVariable().getAssignable() ) } or - TImplicitDelegateOutNode( - ControlFlow::Nodes::ElementNode cfn, ControlFlow::Nodes::ElementNode call - ) { - cfn.getElement() instanceof DelegateArgumentToLibraryCallable and - any(DelegateArgumentConfiguration x).hasExprPath(_, cfn, _, call) - } or - TImplicitDelegateArgumentNode(ControlFlow::Nodes::ElementNode cfn, int i, int j) { - exists(Call call, CallableFlowSinkDelegateArg sink | - LibraryFlow::libraryFlow(call, _, _, sink, _, _) and - i = sink.getDelegateIndex() and - j = sink.getDelegateParameterIndex() and - call.getArgument(i).getAControlFlowNode() = cfn - ) - } or TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof ObjectCreation } or + TObjectInitializerNode(ControlFlow::Nodes::ElementNode cfn) { + cfn.getElement().(ObjectCreation).hasInitializer() + } or TExprPostUpdateNode(ControlFlow::Nodes::ElementNode cfn) { exists(Argument a, Type t | a = cfn.getElement() and @@ -406,7 +553,9 @@ private module Cached { t = any(TypeParameter tp | not tp.isValueType()) ) or - fieldOrPropertyAssign(_, _, _, cfn.getElement()) + fieldOrPropertyStore(_, _, _, cfn.getElement(), true) + or + arrayStore(_, _, cfn.getElement(), true) or exists(TExprPostUpdateNode upd, FieldOrPropertyAccess fla | upd = TExprPostUpdateNode(fla.getAControlFlowNode()) @@ -414,54 +563,87 @@ private module Cached { cfn.getElement() = fla.getQualifier() ) } or - TLibraryCodeNode( - ControlFlow::Node callCfn, CallableFlowSource source, AccessPath sourceAp, - CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue + TSummaryParameterNode(SourceDeclarationCallable c, int i) { + exists(CallableFlowSource source | Summaries::summary(c, source, _, _, _, _) | + source instanceof CallableFlowSourceQualifier and i = -1 + or + i = source.(CallableFlowSourceArg).getArgumentIndex() + or + i = source.(CallableFlowSourceDelegateArg).getArgumentIndex() + ) + or + exists(CallableFlowSink sink | Summaries::summary(c, _, _, sink, _, _) | + i = sink.(CallableFlowSinkDelegateArg).getDelegateIndex() + ) + } or + TSummaryInternalNode( + SourceDeclarationCallable c, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue, + SummaryInternalNodeState state ) { - LibraryFlow::libraryFlow(callCfn.getElement(), source, sourceAp, sink, sinkAp, preservesValue) + Summaries::summary(c, source, sourceAp, sink, sinkAp, preservesValue) and + ( + state = TSummaryInternalNodeAfterReadState(sourceAp.drop(_)) + or + state = TSummaryInternalNodeBeforeStoreState(sinkAp.drop(_)) + ) + } or + TSummaryReturnNode(SourceDeclarationCallable c, ReturnKind rk) { + exists(CallableFlowSink sink | + Summaries::summary(c, _, _, sink, _, _) and + rk = Summaries::toReturnKind(sink) + ) + } or + TSummaryDelegateOutNode(SourceDeclarationCallable c, int pos) { + exists(CallableFlowSourceDelegateArg source | + Summaries::summary(c, source, _, _, _, _) and + pos = source.getArgumentIndex() + ) + } or + TSummaryDelegateArgumentNode(SourceDeclarationCallable c, int delegateIndex, int parameterIndex) { + exists(CallableFlowSinkDelegateArg sink | + Summaries::summary(c, _, _, sink, _, _) and + delegateIndex = sink.getDelegateIndex() and + parameterIndex = sink.getDelegateParameterIndex() + ) + } or + TParamsArgumentNode(ControlFlow::Node callCfn) { + callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode() } /** * This is the local flow predicate that is used as a building block in global - * data flow. It is a strict subset of the `localFlowStep` predicate, as it - * excludes SSA flow through instance fields. + * data flow. It excludes SSA flow through instance fields, as flow through fields + * is handled by the global data-flow library, but includes various other steps + * that are only relevant for global flow. */ cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { - exists(Ssa::Definition def | - LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and - not LocalFlow::usesInstanceField(def) - ) - or - any(LocalFlow::LocalExprStepConfiguration x).hasNodePath(nodeFrom, nodeTo) - or - ThisFlow::adjacentThisRefs(nodeFrom, nodeTo) - or - ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo) + LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) or - LocalFlow::localFlowStepCil(nodeFrom, nodeTo) + Summaries::summaryLocalStep(nodeFrom, nodeTo, true) or - exists(LibraryCodeNode n | n.preservesValue() | - n = nodeTo and - nodeFrom = n.getPredecessor(AccessPath::empty()) - or - n = nodeFrom and - nodeTo = n.getSuccessor(AccessPath::empty()) - ) + nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) } /** - * This is the extension of the predicate `simpleLocalFlowStep` that is exposed - * as the `localFlowStep` predicate. It includes SSA flow through instance fields. + * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. */ cached - predicate extendedLocalFlowStep(Node nodeFrom, Node nodeTo) { + predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { + LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) + or exists(Ssa::Definition def | LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and LocalFlow::usesInstanceField(def) ) + or + // Simple flow through library code is included in the exposed local + // step relation, even though flow is technically inter-procedural + Summaries::summaryThroughStep(nodeFrom, nodeTo, true) } /** @@ -477,7 +659,8 @@ private module Cached { cached newtype TContent = TFieldContent(Field f) { f = f.getSourceDeclaration() } or - TPropertyContent(Property p) { p = p.getSourceDeclaration() } + TPropertyContent(Property p) { p = p.getSourceDeclaration() } or + TElementContent() /** * Holds if data can flow from `node1` to `node2` via an assignment to @@ -485,17 +668,34 @@ private module Cached { */ cached predicate storeStepImpl(Node node1, Content c, Node node2) { - exists(StoreStepConfiguration x, ExprNode preNode2 | - preNode2 = node2.(PostUpdateNode).getPreUpdateNode() and - x.hasNodePath(node1, preNode2) and - fieldOrPropertyAssign(_, c, node1.asExpr(), preNode2.getExpr()) + exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate | + x.hasNodePath(node1, node) and + if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2 + | + fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate) + or + arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent ) or - exists(StoreStepConfiguration x | x.hasNodePath(node1, node2) | - fieldOrPropertyInit(node2.(ObjectCreationNode).getExpr(), c, node1.asExpr()) + exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn | + x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and + node2 = TParamsArgumentNode(callCfn) and + isParamsArg(_, arg, _) and + c instanceof ElementContent ) or - node2 = node1.(LibraryCodeNode).getSuccessor(any(AccessPath ap | ap.getHead() = c)) + exists(Expr e | + e = node1.asExpr() and + node2.(YieldReturnNode).getYieldReturnStmt().getExpr() = e and + c instanceof ElementContent + ) + or + summaryStoreStep(node1, c, node2) + } + + pragma[nomagic] + private PropertyContent getResultContent() { + result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty() } /** @@ -506,9 +706,42 @@ private module Cached { exists(ReadStepConfiguration x | x.hasNodePath(node1, node2) and fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr()) + or + x.hasNodePath(node1, node2) and + arrayRead(node1.asExpr(), node2.asExpr()) and + c instanceof ElementContent + or + exists(ForeachStmt fs, Ssa::ExplicitDefinition def | + x + .hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(), + def.getControlFlowNode()) and + node2.(SsaDefinitionNode).getDefinition() = def and + c instanceof ElementContent + ) + or + x.hasNodePath(node1, node2) and + node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and + c = getResultContent() ) or - node1 = node2.(LibraryCodeNode).getPredecessor(any(AccessPath ap | ap.getHead() = c)) + summaryReadStep(node1, c, node2) + } + + /** + * Holds if values stored inside content `c` are cleared at node `n`. For example, + * any value stored inside `f` is cleared at the pre-update node associated with `x` + * in `x.f = newValue`. + */ + cached + predicate clearsContent(Node n, Content c) { + fieldOrPropertyStore(_, c, _, n.asExpr(), true) + or + fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) + or + summaryStoreStep(n, c, _) and + not c instanceof ElementContent + or + summaryClearsContent(n, c) } /** @@ -523,16 +756,12 @@ private module Cached { viableConstantBooleanParamArg(paramNode, bs.getValue().booleanNot(), call) and paramNode.getDefinition() = param and param.getARead() = guard and - guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs) + guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs, _) ) } - /** - * Holds if GVNs `t1` and `t2` may have a common sub type. Neither `t1` nor - * `t2` are allowed to be type parameters. - */ - cached - predicate commonSubType(DataFlowType t1, DataFlowType t2) { + pragma[nomagic] + private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, DataFlowType t2) { not t1 instanceof Gvn::TypeParameterGvnType and t1 = t2 or @@ -541,19 +770,75 @@ private module Cached { getANonTypeParameterSubType(t1) = getANonTypeParameterSubType(t2) } + /** + * Holds if GVNs `t1` and `t2` may have a common sub type. Neither `t1` nor + * `t2` are allowed to be type parameters. + */ + cached + predicate commonSubType(DataFlowType t1, DataFlowType t2) { commonSubTypeGeneral(t1, t2) } + cached predicate commonSubTypeUnifiableLeft(DataFlowType t1, DataFlowType t2) { - exists(DataFlowType t | + exists(Gvn::GvnType t | Gvn::unifiable(t1, t) and - commonSubType(t, t2) + commonSubTypeGeneral(t, t2) + ) + } + + cached + predicate outRefReturnNode(Ssa::ExplicitDefinition def, OutRefReturnKind kind) { + exists(Parameter p | + def.isLiveOutRefParameterDefinition(p) and + kind.getPosition() = p.getPosition() + | + p.isOut() and kind instanceof OutReturnKind + or + p.isRef() and kind instanceof RefReturnKind + ) + } + + cached + predicate qualifierOutNode(DataFlowCall call, Node n) { + n.(ExprPostUpdateNode).getPreUpdateNode().(ExplicitArgumentNode).argumentOf(call, -1) + or + any(ObjectOrCollectionInitializerConfiguration x) + .hasExprPath(_, n.(ExprNode).getControlFlowNode(), _, call.getControlFlowNode()) + } + + cached + predicate castNode(Node n) { + n.asExpr() instanceof Cast + or + n.(AssignableDefinitionNode).getDefinition() instanceof AssignableDefinitions::PatternDefinition + } + + /** Holds if `n` should be hidden from path explanations. */ + cached + predicate nodeIsHidden(Node n) { + exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | + def instanceof Ssa::PseudoDefinition + or + def instanceof Ssa::ImplicitEntryDefinition + or + def instanceof Ssa::ImplicitCallDefinition ) + or + n instanceof YieldReturnNode + or + n instanceof ImplicitCapturedArgumentNode + or + n instanceof MallocNode + or + n instanceof Summaries::SummaryNodeImpl + or + n instanceof ParamsArgumentNode } } import Cached /** An SSA definition, viewed as a node in a data flow graph. */ -class SsaDefinitionNode extends Node, TSsaDefinitionNode { +class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode { Ssa::Definition def; SsaDefinitionNode() { this = TSsaDefinitionNode(def) } @@ -561,19 +846,23 @@ class SsaDefinitionNode extends Node, TSsaDefinitionNode { /** Gets the underlying SSA definition. */ Ssa::Definition getDefinition() { result = def } - override Callable getEnclosingCallable() { result = def.getEnclosingCallable() } + override Callable getEnclosingCallableImpl() { result = def.getEnclosingCallable() } + + override Type getTypeImpl() { result = def.getSourceVariable().getType() } - override Type getType() { result = def.getSourceVariable().getType() } + override ControlFlow::Node getControlFlowNodeImpl() { result = def.getControlFlowNode() } - override Location getLocation() { result = def.getLocation() } + override Location getLocationImpl() { result = def.getLocation() } - override string toString() { + override string toStringImpl() { not explicitParameterNode(this, _) and result = def.toString() } } private module ParameterNodes { + abstract private class ParameterNodeImpl extends ParameterNode, NodeImpl { } + /** * Holds if definition node `node` is an entry definition for parameter `p`. */ @@ -585,11 +874,12 @@ private module ParameterNodes { * The value of an explicit parameter at function entry, viewed as a node in a data * flow graph. */ - class ExplicitParameterNode extends ParameterNode { + class ExplicitParameterNode extends ParameterNodeImpl { private DotNet::Parameter parameter; ExplicitParameterNode() { - explicitParameterNode(this, parameter) or + explicitParameterNode(this, parameter) + or this = TCilParameterNode(parameter) } @@ -597,17 +887,19 @@ private module ParameterNodes { override predicate isParameterOf(DataFlowCallable c, int i) { c.getParameter(i) = parameter } - override DotNet::Callable getEnclosingCallable() { result = parameter.getCallable() } + override DataFlowCallable getEnclosingCallableImpl() { result = parameter.getCallable() } - override DotNet::Type getType() { result = parameter.getType() } + override DotNet::Type getTypeImpl() { result = parameter.getType() } - override Location getLocation() { result = parameter.getLocation() } + override ControlFlow::Node getControlFlowNodeImpl() { none() } - override string toString() { result = parameter.toString() } + override Location getLocationImpl() { result = parameter.getLocation() } + + override string toStringImpl() { result = parameter.toString() } } /** An implicit instance (`this`) parameter. */ - class InstanceParameterNode extends ParameterNode, TInstanceParameterNode { + class InstanceParameterNode extends ParameterNodeImpl, TInstanceParameterNode { private Callable callable; InstanceParameterNode() { this = TInstanceParameterNode(callable) } @@ -617,13 +909,15 @@ private module ParameterNodes { override predicate isParameterOf(DataFlowCallable c, int pos) { callable = c and pos = -1 } - override Callable getEnclosingCallable() { result = callable } + override Callable getEnclosingCallableImpl() { result = callable } + + override Type getTypeImpl() { result = callable.getDeclaringType() } - override Type getType() { result = callable.getDeclaringType() } + override ControlFlow::Node getControlFlowNodeImpl() { none() } - override Location getLocation() { result = callable.getLocation() } + override Location getLocationImpl() { result = callable.getLocation() } - override string toString() { result = "this" } + override string toStringImpl() { result = "this" } } module ImplicitCapturedParameterNodeImpl { @@ -692,11 +986,52 @@ private module ParameterNodes { c = this.getEnclosingCallable() } } + + /** A parameter node for a callable with a flow summary. */ + class SummaryParameterNode extends ParameterNodeImpl, Summaries::SummaryNodeImpl, + TSummaryParameterNode { + private SourceDeclarationCallable sdc; + private int i; + + SummaryParameterNode() { this = TSummaryParameterNode(sdc, i) } + + override Parameter getParameter() { result = sdc.getParameter(i) } + + override predicate isParameterOf(DataFlowCallable c, int pos) { + c = sdc and + pos = i + } + + override Callable getEnclosingCallableImpl() { result = sdc } + + override Type getTypeImpl() { + result = sdc.getParameter(i).getType() + or + i = -1 and + result = sdc.getDeclaringType() + } + + override ControlFlow::Node getControlFlowNodeImpl() { none() } + + override Location getLocationImpl() { + result = sdc.getParameter(i).getLocation() + or + i = -1 and + result = sdc.getLocation() + } + + override string toStringImpl() { + result = "[summary] " + sdc.getParameter(i) + or + i = -1 and + result = "[summary] this" + } + } } import ParameterNodes -/** A data flow node that represents a call argument. */ +/** A data-flow node that represents a call argument. */ abstract class ArgumentNode extends Node { /** Holds if this argument occurs at the given position in the given call. */ cached @@ -707,21 +1042,6 @@ abstract class ArgumentNode extends Node { } private module ArgumentNodes { - class DelegateArgumentConfiguration extends ControlFlowReachabilityConfiguration { - DelegateArgumentConfiguration() { this = "DelegateArgumentConfiguration" } - - override predicate candidate( - Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor - ) { - exists(DelegateArgumentToLibraryCallable arg | e1 = arg.getCall().getAnArgument() | - e2 = arg.getCall() and - scope = e2 and - exactScope = false and - isSuccessor = true - ) - } - } - private class ArgumentConfiguration extends ControlFlowReachabilityConfiguration { ArgumentConfiguration() { this = "ArgumentConfiguration" } @@ -735,8 +1055,8 @@ private module ArgumentNodes { } } - /** A data flow node that represents an explicit call argument. */ - private class ExplicitArgumentNode extends ArgumentNode { + /** A data-flow node that represents an explicit call argument. */ + class ExplicitArgumentNode extends ArgumentNode { ExplicitArgumentNode() { this.asExpr() instanceof Argument or @@ -776,7 +1096,7 @@ private module ArgumentNodes { * } } * ``` */ - class ImplicitCapturedArgumentNode extends ArgumentNode, TImplicitCapturedArgumentNode { + class ImplicitCapturedArgumentNode extends ArgumentNode, NodeImpl, TImplicitCapturedArgumentNode { private LocalScopeVariable v; private ControlFlow::Nodes::ElementNode cfn; @@ -795,39 +1115,30 @@ private module ArgumentNodes { override predicate argumentOf(DataFlowCall call, int pos) { exists(ImplicitCapturedParameterNode p, boolean additionalCalls | this.flowsInto(p, additionalCalls) and - p.isParameterOf(call.getARuntimeTarget(), pos) - | + p.isParameterOf(call.getARuntimeTarget(), pos) and + call.getControlFlowNode() = cfn and if call instanceof TransitiveCapturedDataFlowCall - then additionalCalls = true and call.getControlFlowNode() = cfn - else ( - additionalCalls = false and - ( - call.getControlFlowNode() = cfn - or - exists(DataFlowCall parent | - call.(ImplicitDelegateDataFlowCall).isArgumentOf(parent, _) - | - parent.getControlFlowNode() = cfn - ) - ) - ) + then additionalCalls = true + else additionalCalls = false ) } - override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() } + override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } + + override Type getTypeImpl() { result = v.getType() } - override Type getType() { result = v.getType() } + override ControlFlow::Node getControlFlowNodeImpl() { none() } - override Location getLocation() { result = cfn.getLocation() } + override Location getLocationImpl() { result = cfn.getLocation() } - override string toString() { result = "[implicit argument] " + v } + override string toStringImpl() { result = "[implicit argument] " + v } } /** * A node that corresponds to the value of an object creation (`new C()`) before * the constructor has run. */ - class MallocNode extends ArgumentNode, TMallocNode { + class MallocNode extends ArgumentNode, NodeImpl, TMallocNode { private ControlFlow::Nodes::ElementNode cfn; MallocNode() { this = TMallocNode(cfn) } @@ -837,58 +1148,104 @@ private module ArgumentNodes { pos = -1 } - override ControlFlow::Node getControlFlowNode() { result = cfn } + override ControlFlow::Node getControlFlowNodeImpl() { result = cfn } - override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() } + override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } - override Type getType() { result = cfn.getElement().(Expr).getType() } + override Type getTypeImpl() { result = cfn.getElement().(Expr).getType() } - override Location getLocation() { result = cfn.getLocation() } + override Location getLocationImpl() { result = cfn.getLocation() } - override string toString() { result = "malloc" } + override string toStringImpl() { result = "malloc" } } /** - * A data flow node that represents an implicit argument of an implicit delegate - * call in library code. For example, in + * A data-flow node that represents the implicit array creation in a call to a + * callable with a `params` parameter. For example, there is an implicit array + * creation `new [] { "a", "b", "c" }` in * * ```csharp - * x.Select(Foo); + * void Foo(params string[] args) { ... } + * Foo("a", "b", "c"); * ``` * - * `x` is an implicit argument of the implicit call to `Foo`. + * Note that array creations are not inserted when there is only one argument, + * and that argument is itself a compatible array, for example + * `Foo(new[] { "a", "b", "c" })`. */ - class ImplicitDelegateArgumentNode extends ArgumentNode, TImplicitDelegateArgumentNode { - private ControlFlow::Node cfn; - private int delegateIndex; - private int parameterIndex; + class ParamsArgumentNode extends ArgumentNode, NodeImpl, TParamsArgumentNode { + private ControlFlow::Node callCfn; - ImplicitDelegateArgumentNode() { - this = TImplicitDelegateArgumentNode(cfn, delegateIndex, parameterIndex) - } + ParamsArgumentNode() { this = TParamsArgumentNode(callCfn) } - private ImplicitDelegateDataFlowCall getDelegateCall() { result.getControlFlowNode() = cfn } + private Parameter getParameter() { + callCfn = any(Call c | isParamsArg(c, _, result)).getAControlFlowNode() + } override predicate argumentOf(DataFlowCall call, int pos) { - call = this.getDelegateCall() and - pos = parameterIndex + callCfn = call.getControlFlowNode() and + pos = this.getParameter().getPosition() } - override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() } + override Callable getEnclosingCallableImpl() { result = callCfn.getEnclosingCallable() } + + override Type getTypeImpl() { result = this.getParameter().getType() } + + override ControlFlow::Node getControlFlowNodeImpl() { none() } + + override Location getLocationImpl() { result = callCfn.getLocation() } + + override string toStringImpl() { result = "[implicit array creation] " + callCfn } + } + + /** + * An argument node inside a callable with a flow summary, where the argument is + * passed to a supplied delegate. For example, in `ints.Select(Foo)` there is a + * node that represents the argument of the call to `Foo` inside `Select`. + */ + class SummaryDelegateArgumentNode extends ArgumentNode, Summaries::SummaryNodeImpl, + TSummaryDelegateArgumentNode { + private SourceDeclarationCallable c; + private int delegateIndex; + private int parameterIndex; + + SummaryDelegateArgumentNode() { + this = TSummaryDelegateArgumentNode(c, delegateIndex, parameterIndex) + } - override Type getType() { - result = this.getDelegateCall().getDelegateParameterType(parameterIndex) + override DataFlowCallable getEnclosingCallableImpl() { result = c } + + override DotNet::Type getTypeImpl() { + result = + c + .getParameter(delegateIndex) + .getType() + .(SystemLinqExpressions::DelegateExtType) + .getDelegateType() + .getParameter(parameterIndex) + .getType() } - override Location getLocation() { result = cfn.getLocation() } + override ControlFlow::Node getControlFlowNodeImpl() { none() } + + override Location getLocationImpl() { result = c.getLocation() } - override string toString() { result = "[implicit argument " + parameterIndex + "] " + cfn } + override string toStringImpl() { + result = + "[summary] argument " + parameterIndex + " of delegate call, parameter " + parameterIndex + + " of " + c + } + + override predicate argumentOf(DataFlowCall call, int pos) { + call = TSummaryDelegateCall(c, delegateIndex) and + pos = parameterIndex + } } } import ArgumentNodes -/** A data flow node that represents a value returned by a callable. */ +/** A data-flow node that represents a value returned by a callable. */ abstract class ReturnNode extends Node { /** Gets the kind of this return node. */ abstract ReturnKind getKind(); @@ -896,7 +1253,7 @@ abstract class ReturnNode extends Node { private module ReturnNodes { /** - * A data flow node that represents an expression returned by a callable, + * A data-flow node that represents an expression returned by a callable, * either using a (`yield`) `return` statement or an expression body (`=>`). */ class ExprReturnNode extends ReturnNode, ExprNode { @@ -914,22 +1271,13 @@ private module ReturnNodes { } /** - * A data flow node that represents an assignment to an `out` or a `ref` + * A data-flow node that represents an assignment to an `out` or a `ref` * parameter. */ class OutRefReturnNode extends ReturnNode, SsaDefinitionNode { OutRefReturnKind kind; - OutRefReturnNode() { - exists(Parameter p | - this.getDefinition().(Ssa::ExplicitDefinition).isLiveOutRefParameterDefinition(p) and - kind.getPosition() = p.getPosition() - | - p.isOut() and kind instanceof OutReturnKind - or - p.isRef() and kind instanceof RefReturnKind - ) - } + OutRefReturnNode() { outRefReturnNode(this.getDefinition(), kind) } override ReturnKind getKind() { result = kind } } @@ -939,7 +1287,7 @@ private module ReturnNodes { * `yield return`s as stores into collections, i.e., there is flow from `e` * to `yield return e [e]`. */ - class YieldReturnNode extends ReturnNode, PostUpdateNode, TYieldReturnNode { + class YieldReturnNode extends ReturnNode, NodeImpl, TYieldReturnNode { private ControlFlow::Nodes::ElementNode cfn; private YieldReturnStmt yrs; @@ -949,15 +1297,15 @@ private module ReturnNodes { override YieldReturnKind getKind() { any() } - override ExprNode getPreUpdateNode() { result.getControlFlowNode() = cfn } + override Callable getEnclosingCallableImpl() { result = yrs.getEnclosingCallable() } - override Callable getEnclosingCallable() { result = yrs.getEnclosingCallable() } + override Type getTypeImpl() { result = yrs.getEnclosingCallable().getReturnType() } - override Type getType() { result = yrs.getEnclosingCallable().getReturnType() } + override ControlFlow::Node getControlFlowNodeImpl() { result = cfn } - override Location getLocation() { result = yrs.getLocation() } + override Location getLocationImpl() { result = yrs.getLocation() } - override string toString() { result = yrs.toString() } + override string toStringImpl() { result = yrs.toString() } } /** @@ -998,11 +1346,39 @@ private module ReturnNodes { result.getVariable() = edef.getSourceVariable().getAssignable() } } + + /** A return node for a callable with a flow summary. */ + class SummaryReturnNode extends ReturnNode, Summaries::SummaryNodeImpl, TSummaryReturnNode { + private SourceDeclarationCallable sdc; + private ReturnKind rk; + + SummaryReturnNode() { this = TSummaryReturnNode(sdc, rk) } + + override Callable getEnclosingCallableImpl() { result = sdc } + + override DotNet::Type getTypeImpl() { + rk instanceof NormalReturnKind and + result in [sdc.getReturnType(), sdc.(Constructor).getDeclaringType()] + or + rk instanceof QualifierReturnKind and + result = sdc.getDeclaringType() + or + result = sdc.getParameter(rk.(OutRefReturnKind).getPosition()).getType() + } + + override ControlFlow::Node getControlFlowNodeImpl() { none() } + + override Location getLocationImpl() { result = sdc.getLocation() } + + override string toStringImpl() { result = "[summary] return of kind " + rk + " inside " + sdc } + + override ReturnKind getKind() { result = rk } + } } import ReturnNodes -/** A data flow node that represents the output of a call. */ +/** A data-flow node that represents the output of a call. */ abstract class OutNode extends Node { /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ cached @@ -1034,7 +1410,7 @@ private module OutNodes { } /** - * A data flow node that reads a value returned directly by a callable, + * A data-flow node that reads a value returned directly by a callable, * either via a C# call or a CIL call. */ class ExprOutNode extends OutNode, ExprNode { @@ -1060,8 +1436,44 @@ private module OutNodes { } } + class ObjectOrCollectionInitializerConfiguration extends ControlFlowReachabilityConfiguration { + ObjectOrCollectionInitializerConfiguration() { + this = "ObjectOrCollectionInitializerConfiguration" + } + + override predicate candidate( + Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor + ) { + exactScope = false and + scope = e1 and + isSuccessor = false and + ( + // E.g. `new Dictionary{ {0, "a"}, {1, "b"} }` + e1.(CollectionInitializer).getAnElementInitializer() = e2 + or + // E.g. `new Dictionary() { [0] = "a", [1] = "b" }` + e1.(ObjectInitializer).getAMemberInitializer().getLValue() = e2 + ) + } + } + + /** + * A data-flow node that contains a value returned by a callable, by writing + * to the qualifier of the call. + */ + private class QualifierOutNode extends OutNode, Node { + private DataFlowCall call; + + QualifierOutNode() { qualifierOutNode(call, this) } + + override DataFlowCall getCall(ReturnKind kind) { + result = call and + kind instanceof QualifierReturnKind + } + } + /** - * A data flow node that reads a value returned implicitly by a callable + * A data-flow node that reads a value returned implicitly by a callable * using a captured variable. */ class CapturedOutNode extends OutNode, SsaDefinitionNode { @@ -1074,10 +1486,8 @@ private module OutNodes { | additionalCalls = false and call = csharpCall(_, cfn) or - additionalCalls = false and - call.(ImplicitDelegateDataFlowCall).isArgumentOf(csharpCall(_, cfn), _) - or - additionalCalls = true and call = TTransitiveCapturedCall(cfn, n.getEnclosingCallable()) + additionalCalls = true and + call = TTransitiveCapturedCall(cfn, n.getEnclosingCallable()) ) } @@ -1089,7 +1499,7 @@ private module OutNodes { } /** - * A data flow node that reads a value returned by a callable using an + * A data-flow node that reads a value returned by a callable using an * `out` or `ref` parameter. */ class ParamOutNode extends OutNode, AssignableDefinitionNode { @@ -1108,331 +1518,357 @@ private module OutNodes { } /** - * A data flow node that represents the output of an implicit delegate call, - * in a call to a library method. For example, the output from the implicit - * call to `M` in `new Lazy(M)`. + * An output node inside a callable with a flow summary, where the output is the + * result of calling a supplied delegate. For example, in `ints.Select(Foo)` there + * is a node that represents the output of calling `Foo` inside `Select`. */ - class ImplicitDelegateOutNode extends OutNode, TImplicitDelegateOutNode { - private ControlFlow::Nodes::ElementNode cfn; - private ControlFlow::Nodes::ElementNode call; - - ImplicitDelegateOutNode() { this = TImplicitDelegateOutNode(cfn, call) } - - /** - * Holds if the underlying delegate argument is the `i`th argument of the - * call `c` targeting a library callable. For example, `M` is the `0`th - * argument of `new Lazy(M)`. - */ - predicate isArgumentOf(DataFlowCall c, int i) { - c.getControlFlowNode() = call and - call.getElement().(Call).getArgument(i) = cfn.getElement() + private class SummaryDelegateOutNode extends OutNode, Summaries::SummaryNodeImpl, + TSummaryDelegateOutNode { + private SourceDeclarationCallable c; + private int pos; + + SummaryDelegateOutNode() { this = TSummaryDelegateOutNode(c, pos) } + + override Callable getEnclosingCallableImpl() { result = c } + + override DotNet::Type getTypeImpl() { + result = + c + .getParameter(pos) + .getType() + .(SystemLinqExpressions::DelegateExtType) + .getDelegateType() + .getReturnType() } - override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn } + override ControlFlow::Node getControlFlowNodeImpl() { none() } - override ImplicitDelegateDataFlowCall getCall(ReturnKind kind) { - result.getNode() = this and - ( - kind instanceof NormalReturnKind and - not result.getDelegateReturnType() instanceof VoidType - or - kind instanceof YieldReturnKind and - result.getDelegateReturnType() instanceof YieldReturnType - ) - } + override Location getLocationImpl() { result = c.getLocation() } - override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() } - - override Type getType() { - exists(ImplicitDelegateDataFlowCall c | c.getNode() = this | - result = c.getDelegateReturnType() - ) + override string toStringImpl() { + result = "[summary] output from delegate call, parameter " + pos + " of " + c + "]" } - override Location getLocation() { result = cfn.getLocation() } - - override string toString() { result = "[output] " + cfn } + override SummaryDelegateCall getCall(ReturnKind kind) { + result = TSummaryDelegateCall(c, pos) and + kind instanceof NormalReturnKind + } } } import OutNodes /** - * Provides predicates related to flow through library code, based on - * the flow summaries in `LibraryTypeDataFlow.qll`. + * Provides predicates for interpreting flow summaries defined in + * `LibraryTypeDataFlow.qll`. */ -module LibraryFlow { - pragma[nomagic] - private ValueOrRefType getPreciseSourceProperty0( - Call call, CallableFlowSource source, AccessPath sourceAp, Property p - ) { - exists(LibraryTypeDataFlow ltdf, Property p0 | - ltdf.callableFlow(source, sourceAp, _, _, call.getTarget().getSourceDeclaration()) and - sourceAp = AccessPath::property(p0) and - overridesOrImplementsSourceDecl(p, p0) and - result = source.getSourceType(call) - ) - } +module Summaries { + /** A data-flow node used to interpret a flow summary. */ + abstract class SummaryNodeImpl extends NodeImpl { } /** - * Gets a precise source property for source `source` and access path `sourceAp`, - * in the context of `call`. For example, in - * - * ```csharp - * var list = new List(); - * var count = list.Count(); - * ``` + * Holds if data can flow from a node of kind `source` to a node of kind `sink`, + * using a call to a callable with a flow summary. * - * the step from `list` to `list.Count()`, which may be modeled as a read of - * the `Count` property from `ICollection`, can be strengthened to be a - * read of the `Count` property from `List`. + * `sourceAp` describes the contents of the source node that flows to the sink + * (if any), and `sinkAp` describes the contents of the sink that it flows to + * (if any). */ pragma[nomagic] - private Property getPreciseSourceProperty( - Call call, CallableFlowSource source, AccessPath sourceAp + predicate summary( + SourceDeclarationCallable c, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue ) { - getPreciseSourceProperty0(call, source, sourceAp, result).hasMember(result) + any(LibraryTypeDataFlow ltdf).callableFlow(source, sink, c, preservesValue) and + sourceAp = AccessPath::empty() and + sinkAp = AccessPath::empty() + or + any(LibraryTypeDataFlow ltdf).callableFlow(source, sourceAp, sink, sinkAp, c, preservesValue) } - pragma[nomagic] - private ValueOrRefType getPreciseSinkProperty0( - Call call, CallableFlowSink sink, AccessPath sinkAp, Property p - ) { - exists(LibraryTypeDataFlow ltdf, Property p0 | - ltdf.callableFlow(_, _, sink, sinkAp, call.getTarget().getSourceDeclaration()) and - sinkAp = AccessPath::property(p0) and - overridesOrImplementsSourceDecl(p, p0) and - result = sink.getSinkType(call) - ) + /** Gets the return kind that matches `sink`, if any. */ + ReturnKind toReturnKind(CallableFlowSink sink) { + sink instanceof CallableFlowSinkQualifier and result instanceof QualifierReturnKind + or + sink instanceof CallableFlowSinkReturn and result instanceof NormalReturnKind + or + sink.(CallableFlowSinkArg).getArgumentIndex() = result.(OutRefReturnKind).getPosition() } + newtype TSummaryInternalNodeState = + TSummaryInternalNodeAfterReadState(AccessPath ap) { ap.length() > 0 } or + TSummaryInternalNodeBeforeStoreState(AccessPath ap) { ap.length() > 0 } + /** - * Gets a precise sink property for sink `sink` and access path `sinkAp`, - * in the context of `call`. For example, in + * A state used to break up (complex) flow summaries for library code into atomic + * flow steps. For a flow summary with source access path `sourceAp` and sink + * access path `sinkAp`, the following states are used: * - * ```csharp - * var list = new List(); - * list.Add("taint"); - * var enumerator = list.getEnumerator(); - * ``` + * - `TSummaryInternalNodeAfterReadState(AccessPath ap)`: this state represents + * that the head of `ap` has been read from, where `ap` is a suffix of + * `sourceAp`. + * - `TSummaryInternalNodeBeforeStoreState(AccessPath ap)`: this state represents + * that the head of `ap` is to be stored into next, where `ap` is a suffix of + * `sinkAp`. * - * the step from `list` to `list.getEnumerator()`, which may be modeled as a - * read of a collection element followed by a store into the `Current` - * property, can be strengthened to be a store into the `Current` property - * from `List.Enumerator`, rather than the generic `Current` property - * from `IEnumerator`. + * The state machine for flow summaries has no branching, hence from the entry + * state there is a unique path to the exit state. */ - pragma[nomagic] - private Property getPreciseSinkProperty(Call call, CallableFlowSink sink, AccessPath sinkAp) { - getPreciseSinkProperty0(call, sink, sinkAp, result).hasMember(result) + class SummaryInternalNodeState extends TSummaryInternalNodeState { + string toString() { + exists(AccessPath ap | + this = TSummaryInternalNodeAfterReadState(ap) and + result = "after read: " + ap + ) + or + exists(AccessPath ap | + this = TSummaryInternalNodeBeforeStoreState(ap) and + result = "before store: " + ap + ) + } + + /** Holds if this state represents the state after the last read. */ + predicate isLastReadState() { + this = TSummaryInternalNodeAfterReadState(AccessPath::singleton(_)) + } + + /** Holds if this state represents the state before the first store. */ + predicate isFirstStoreState() { + this = TSummaryInternalNodeBeforeStoreState(AccessPath::singleton(_)) + } + } + + private NodeImpl getSourceNode(SourceDeclarationCallable c, CallableFlowSource source) { + exists(int i | result = TSummaryParameterNode(c, i) | + source instanceof CallableFlowSourceQualifier and i = -1 + or + i = source.(CallableFlowSourceArg).getArgumentIndex() + ) + or + result = TSummaryDelegateOutNode(c, source.(CallableFlowSourceDelegateArg).getArgumentIndex()) + } + + private NodeImpl getSinkNode(SourceDeclarationCallable c, CallableFlowSink sink) { + result = TSummaryReturnNode(c, toReturnKind(sink)) + or + sink = + any(CallableFlowSinkDelegateArg s | + result = + TSummaryDelegateArgumentNode(c, s.getDelegateIndex(), s.getDelegateParameterIndex()) + ) } /** - * Holds if data can flow from a node of kind `source` to a node of kind `sink`, - * using a call to a library callable. - * - * `sourceAp` describes the contents of the source node that flows to the sink - * (if any), and `sinkAp` describes the contents of the sink that it flows to - * (if any). - * - * `preservesValue = false` implies that both `sourceAp` and `sinkAp` are empty. + * Holds if there is a local step from `pred` to `succ`, which is synthesized + * from a flow summary. */ - pragma[nomagic] - predicate libraryFlow( - Call call, CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, - AccessPath sinkAp, boolean preservesValue - ) { - exists(LibraryTypeDataFlow ltdf, SourceDeclarationCallable c | - c = call.getTarget().getSourceDeclaration() + predicate summaryLocalStep(Node pred, Node succ, boolean preservesValue) { + exists( + SourceDeclarationCallable c, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp + | + pred = getSourceNode(c, source) | - ltdf.callableFlow(source, sink, c, preservesValue) and + // Simple flow summary without reads or stores sourceAp = AccessPath::empty() and - sinkAp = AccessPath::empty() + sinkAp = AccessPath::empty() and + summary(c, source, sourceAp, sink, sinkAp, preservesValue) and + succ = getSinkNode(c, sink) or - preservesValue = true and - exists(AccessPath sourceAp0, AccessPath sinkAp0 | - ltdf.callableFlow(source, sourceAp0, sink, sinkAp0, c) and - ( - not sourceAp0 = AccessPath::property(_) and - sourceAp = sourceAp0 - or - exists(Property p | - overridesOrImplementsSourceDecl(p, - getPreciseSourceProperty(call, source, sourceAp0).getSourceDeclaration()) and - sourceAp = AccessPath::property(p) - ) - ) and - ( - not sinkAp0 = AccessPath::property(_) and - sinkAp = sinkAp0 - or - sinkAp = AccessPath::property(getPreciseSinkProperty(call, sink, sinkAp0)) - ) + // Flow summary with stores but no reads + exists(SummaryInternalNodeState succState | + sourceAp = AccessPath::empty() and + succState.isFirstStoreState() and + succ = TSummaryInternalNode(c, source, sourceAp, sink, sinkAp, preservesValue, succState) ) ) + or + // Exit step after last read (no stores) + exists( + SourceDeclarationCallable c, SummaryInternalNodeState predState, CallableFlowSink sink, + AccessPath sinkAp + | + sinkAp = AccessPath::empty() and + predState.isLastReadState() and + pred = TSummaryInternalNode(c, _, _, sink, sinkAp, preservesValue, predState) and + succ = getSinkNode(c, sink) + ) + or + // Internal step for complex flow summaries with both reads and writes + exists( + SourceDeclarationCallable c, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, SummaryInternalNodeState predState, + SummaryInternalNodeState succState + | + predState.isLastReadState() and + pred = TSummaryInternalNode(c, source, sourceAp, sink, sinkAp, preservesValue, predState) and + succState.isFirstStoreState() and + succ = TSummaryInternalNode(c, source, sourceAp, sink, sinkAp, preservesValue, succState) + ) } - class LibrarySourceConfiguration extends ControlFlowReachabilityConfiguration { - LibrarySourceConfiguration() { this = "LibrarySourceConfiguration" } - - override predicate candidate( - Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor - ) { - exists(CallableFlowSource source | - libraryFlow(e2, source, _, _, _, _) and - e1 = source.getSource(e2) and - scope = e2 and - exactScope = false and - isSuccessor = true + /** + * Holds if data can flow from `pred` to `succ` via an assignment to + * content `c`, using a flow summary. + */ + predicate summaryStoreStep(Node pred, Content c, Node succ) { + exists( + SourceDeclarationCallable sdc, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue, + SummaryInternalNodeState predState, AccessPath predAp + | + predState = TSummaryInternalNodeBeforeStoreState(predAp) and + pred = TSummaryInternalNode(sdc, source, sourceAp, sink, sinkAp, preservesValue, predState) and + c = predAp.getHead() + | + // More stores needed + exists(SummaryInternalNodeState succState | + succState = + TSummaryInternalNodeBeforeStoreState(any(AccessPath succAp | succAp.getTail() = predAp)) and + succ = TSummaryInternalNode(sdc, source, sourceAp, sink, sinkAp, preservesValue, succState) ) - } + or + // Last store + predAp = sinkAp and + succ = getSinkNode(sdc, sink) + ) } - class LibrarySinkConfiguration extends ControlFlowReachabilityConfiguration { - LibrarySinkConfiguration() { this = "LibrarySinkConfiguration" } - - override predicate candidate( - Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor - ) { - exists(CallableFlowSink sink | - libraryFlow(e1, _, _, sink, _, _) and - e2 = sink.getSink(e1) and - scope = e1 and - exactScope = false and - isSuccessor = false - ) - } - - override predicate candidateDef( - Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope, - boolean isSuccessor - ) { - exists(CallableFlowSinkArg sink | - libraryFlow(e, _, _, sink, _, _) and - scope = e and - exactScope = false and - isSuccessor = true and - def.getTargetAccess() = sink.getArgument(e) and - def instanceof AssignableDefinitions::OutRefDefinition + /** + * Holds if data can flow from `pred` to `succ` via a read of content `c`, + * using library code. + */ + predicate summaryReadStep(Node pred, Content c, Node succ) { + exists( + SourceDeclarationCallable sdc, CallableFlowSource source, AccessPath sourceAp, + CallableFlowSink sink, AccessPath sinkAp, boolean preservesValue, + SummaryInternalNodeState succState, AccessPath succAp + | + succState = TSummaryInternalNodeAfterReadState(succAp) and + succ = TSummaryInternalNode(sdc, source, sourceAp, sink, sinkAp, preservesValue, succState) and + c = succAp.getHead() + | + // First read + succAp = sourceAp and + pred = getSourceNode(sdc, source) + or + // Subsequent reads + exists(SummaryInternalNodeState predState, AccessPath predAp | + predState = TSummaryInternalNodeAfterReadState(predAp) and + predAp.getTail() = succAp and + pred = TSummaryInternalNode(sdc, source, sourceAp, sink, sinkAp, preservesValue, predState) ) - } + ) } -} -/** A data-flow node used to model flow through library code. */ -class LibraryCodeNode extends Node, TLibraryCodeNode { - private ControlFlow::Node callCfn; - private CallableFlowSource source; - private AccessPath sourceAp; - private CallableFlowSink sink; - private AccessPath sinkAp; - private boolean preservesValue; + pragma[nomagic] + private SummaryParameterNode summaryArgParam(ArgumentNode arg, ReturnKind rk, OutNode out) { + exists(DataFlowCall call, int pos, SourceDeclarationCallable sdc | + arg.argumentOf(call, pos) and + call.getARuntimeTarget() = sdc and + result = TSummaryParameterNode(sdc, pos) and + call = out.getCall(rk) + ) + } - LibraryCodeNode() { - this = TLibraryCodeNode(callCfn, source, sourceAp, sink, sinkAp, preservesValue) + /** + * Holds if `arg` flows to `out` using a simple flow summary, that is, a flow + * summary without delegates, reads, and stores. + */ + predicate summaryThroughStep(ArgumentNode arg, OutNode out, boolean preservesValue) { + exists(ReturnKind rk | + summaryLocalStep(summaryArgParam(arg, rk, out), TSummaryReturnNode(_, rk), preservesValue) + ) } - /** Holds if this node is part of a value-preserving library step. */ - predicate preservesValue() { preservesValue = true } + /** + * Holds if there is a (taint+)store of `arg` into content `c` of `out` using a + * flow summary. + */ + predicate summarySetterStep(ArgumentNode arg, Content c, OutNode out) { + exists(ReturnKind rk, Node mid | + summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and + summaryStoreStep(mid, c, TSummaryReturnNode(_, rk)) + ) + } /** - * Gets the predecessor of this library-code node. The head of `ap` describes - * the content that is read from when entering this node (if any). + * Holds if there is a read(+taint) of `c` from `arg` to `out` using a + * flow summary. */ - Node getPredecessor(AccessPath ap) { - ap = sourceAp and - ( - // The source is either an argument or a qualifier, for example - // `s` in `int.Parse(s)` - exists(LibraryFlow::LibrarySourceConfiguration x, Call call | - callCfn = call.getAControlFlowNode() and - x.hasExprPath(source.getSource(call), result.(ExprNode).getControlFlowNode(), _, callCfn) - ) - or - // The source is the output of a supplied delegate argument, for - // example the output of `Foo` in `new Lazy(Foo)` - exists(DataFlowCall call, int pos | - pos = source.(CallableFlowSourceDelegateArg).getArgumentIndex() and - result.(ImplicitDelegateOutNode).isArgumentOf(call, pos) and - callCfn = call.getControlFlowNode() - ) + predicate summaryGetterStep(ArgumentNode arg, Content c, OutNode out) { + exists(ReturnKind rk, Node mid | + summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and + summaryLocalStep(mid, TSummaryReturnNode(_, rk), _) ) } /** - * Gets the successor of this library-code node. The head of `ap` describes - * the content that is stored into when leaving this node (if any). + * Holds if values stored inside content `c` are cleared at node `n`, as a result + * of calling a library method. */ - Node getSuccessor(AccessPath ap) { - ap = sinkAp and - ( - exists(LibraryFlow::LibrarySinkConfiguration x, Call call, ExprNode e | - callCfn = call.getAControlFlowNode() and - x.hasExprPath(_, callCfn, sink.getSink(call), e.getControlFlowNode()) - | - // The sink is an ordinary return value, for example `int.Parse(s)` - sink instanceof CallableFlowSinkReturn and - result = e - or - // The sink is a qualifier, for example `list` in `list.Add(x)` - sink instanceof CallableFlowSinkQualifier and - if sinkAp = AccessPath::empty() - then result = e - else result.(ExprPostUpdateNode).getPreUpdateNode() = e - ) + predicate summaryClearsContent(Node n, Content c) { + exists(LibraryTypeDataFlow ltdf, CallableFlowSource source, Call call | + ltdf.clearsContent(source, c, call.getTarget().getSourceDeclaration()) and + n.asExpr() = source.getSource(call) + ) + } + + /** Gets the type of content `c`. */ + pragma[noinline] + private Gvn::GvnType getContentType(Content c) { + exists(Type t | result = Gvn::getGlobalValueNumber(t) | + t = c.(FieldContent).getField().getType() or - // The sink is an `out`/`ref` argument, for example `out i` in - // `int.TryParse(s, out i)` - exists(LibraryFlow::LibrarySinkConfiguration x, OutRefReturnKind k | - result = - any(ParamOutNode out | - out.getCall(k).getControlFlowNode() = callCfn and - sink.(CallableFlowSinkArg).getArgumentIndex() = k.getPosition() and - x.hasDefPath(_, callCfn, out.getDefinition(), _) - ) - ) + t = c.(PropertyContent).getProperty().getType() or - // The sink is a parameter of a supplied delegate argument, for example - // the parameter of `Foo` in `list.Select(Foo)`. - // - // This is implemented using a node that represents the implicit argument - // (`ImplicitDelegateArgumentNode`) of the implicit call - // (`ImplicitDelegateDataFlowCall`) to `Foo`. - exists( - DataFlowCall call, ImplicitDelegateDataFlowCall dcall, int delegateIndex, int parameterIndex - | - sink = - any(CallableFlowSinkDelegateArg s | - delegateIndex = s.getDelegateIndex() and - parameterIndex = s.getDelegateParameterIndex() - ) and - result = TImplicitDelegateArgumentNode(dcall.getControlFlowNode(), _, parameterIndex) and - dcall.isArgumentOf(call, delegateIndex) and - callCfn = call.getControlFlowNode() - ) + c instanceof ElementContent and + t instanceof ObjectType // we don't know what the actual element type is ) } - override Callable getEnclosingCallable() { result = callCfn.getEnclosingCallable() } + /** A data-flow node used to model flow summaries. */ + private class SummaryInternalNode extends SummaryNodeImpl, TSummaryInternalNode { + private SourceDeclarationCallable c; + private CallableFlowSource source; + private AccessPath sourceAp; + private CallableFlowSink sink; + private AccessPath sinkAp; + private boolean preservesValue; + private SummaryInternalNodeState state; - override DataFlowType getTypeBound() { - preservesValue = true and - sourceAp = AccessPath::empty() and - result = this.getPredecessor(_).getTypeBound() - or - result = sourceAp.getHead().getType() - or - preservesValue = false and - result = this.getSuccessor(_).getTypeBound() - } + SummaryInternalNode() { + this = TSummaryInternalNode(c, source, sourceAp, sink, sinkAp, preservesValue, state) + } + + override DataFlowCallable getEnclosingCallableImpl() { result = c } + + override Gvn::GvnType getDataFlowType() { + exists(AccessPath ap | + state = TSummaryInternalNodeAfterReadState(ap) and + if sinkAp.length() = 0 and state.isLastReadState() and preservesValue = true + then result = getSinkNode(c, sink).getDataFlowType() + else result = getContentType(ap.getHead()) + or + state = TSummaryInternalNodeBeforeStoreState(ap) and + if sourceAp.length() = 0 and state.isFirstStoreState() and preservesValue = true + then result = getSourceNode(c, source).getDataFlowType() + else result = getContentType(ap.getHead()) + ) + } + + override DotNet::Type getTypeImpl() { none() } - override Location getLocation() { result = callCfn.getLocation() } + override ControlFlow::Node getControlFlowNodeImpl() { none() } - override string toString() { result = "[library code] " + callCfn } + override Location getLocationImpl() { result = c.getLocation() } + + override string toStringImpl() { result = "[summary] " + state + " in " + c } + } } /** A field or a property. */ -private class FieldOrProperty extends Assignable, Modifiable { +class FieldOrProperty extends Assignable, Modifiable { FieldOrProperty() { this instanceof Field or @@ -1516,12 +1952,14 @@ private class StoreStepConfiguration extends ControlFlowReachabilityConfiguratio Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor ) { exactScope = false and - isSuccessor = false and - fieldOrPropertyAssign(scope, _, e1, e2) + fieldOrPropertyStore(scope, _, e1, e2, isSuccessor.booleanNot()) + or + exactScope = false and + arrayStore(scope, e1, e2, isSuccessor.booleanNot()) or exactScope = false and - isSuccessor = false and - fieldOrPropertyInit(e2, _, e1) and + isSuccessor = true and + isParamsArg(e2, e1, _) and scope = e2 } } @@ -1538,12 +1976,60 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration isSuccessor = true and fieldOrPropertyRead(e1, _, e2) and scope = e2 + or + exactScope = false and + isSuccessor = true and + arrayRead(e1, e2) and + scope = e2 + or + exactScope = false and + e1 = e2.(AwaitExpr).getExpr() and + scope = e2 and + isSuccessor = true + } + + override predicate candidateDef( + Expr e, AssignableDefinition defTo, ControlFlowElement scope, boolean exactScope, + boolean isSuccessor + ) { + exists(ForeachStmt fs | + e = fs.getIterableExpr() and + defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() = + fs.getVariableDeclExpr() and + isSuccessor = true + | + scope = fs and + exactScope = true + or + scope = fs.getIterableExpr() and + exactScope = false + or + scope = fs.getVariableDeclExpr() and + exactScope = false + ) } } predicate readStep = readStepImpl/3; -/** Gets a string representation of a type returned by `getErasedRepr`. */ +/** + * An entity used to represent the type of data-flow node. Two nodes will have + * the same `DataFlowType` when the underlying `Type`s are structurally equal + * modulo type parameters and identity conversions. + * + * For example, `Func` and `Func` are mapped to the same + * `DataFlowType`, while `Func` and `Func` are not, because + * `string` is not a type parameter. + */ +class DataFlowType extends Gvn::GvnType { + pragma[nomagic] + DataFlowType() { this = any(NodeImpl n).getDataFlowType() } +} + +/** Gets the type of `n` used for type pruning. */ +DataFlowType getNodeType(NodeImpl n) { result = n.getDataFlowType() } + +/** Gets a string representation of a `DataFlowType`. */ string ppReprType(DataFlowType t) { result = t.toString() } private class DataFlowNullType extends DataFlowType { @@ -1595,25 +2081,68 @@ abstract class PostUpdateNode extends Node { private module PostUpdateNodes { class ObjectCreationNode extends PostUpdateNode, ExprNode, TExprNode { - ObjectCreationNode() { exists(ObjectCreation oc | this = TExprNode(oc.getAControlFlowNode())) } + private ObjectCreation oc; + + ObjectCreationNode() { this = TExprNode(oc.getAControlFlowNode()) } + + override Node getPreUpdateNode() { + exists(ControlFlow::Nodes::ElementNode cfn | this = TExprNode(cfn) | + result.(ObjectInitializerNode).getControlFlowNode() = cfn + or + not oc.hasInitializer() and + result.(MallocNode).getControlFlowNode() = cfn + ) + } + } + + /** + * A node that represents the value of a newly created object after the object + * has been created, but before the object initializer has been executed. + * + * Such a node acts as both a post-update node for the `MallocNode`, as well as + * a pre-update node for the `ObjectCreationNode`. + */ + class ObjectInitializerNode extends PostUpdateNode, NodeImpl, TObjectInitializerNode { + private ObjectCreation oc; + private ControlFlow::Nodes::ElementNode cfn; + + ObjectInitializerNode() { + this = TObjectInitializerNode(cfn) and + cfn = oc.getAControlFlowNode() + } + + /** Gets the initializer to which this initializer node belongs. */ + ObjectOrCollectionInitializer getInitializer() { result = oc.getInitializer() } + + override MallocNode getPreUpdateNode() { result.getControlFlowNode() = cfn } + + override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } - override MallocNode getPreUpdateNode() { this = TExprNode(result.getControlFlowNode()) } + override DotNet::Type getTypeImpl() { result = oc.getType() } + + override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { result = cfn } + + override Location getLocationImpl() { result = cfn.getLocation() } + + override string toStringImpl() { result = "[pre-initializer] " + cfn } } - class ExprPostUpdateNode extends PostUpdateNode, TExprPostUpdateNode { + class ExprPostUpdateNode extends PostUpdateNode, NodeImpl, TExprPostUpdateNode { private ControlFlow::Nodes::ElementNode cfn; ExprPostUpdateNode() { this = TExprPostUpdateNode(cfn) } override ExprNode getPreUpdateNode() { cfn = result.getControlFlowNode() } - override Callable getEnclosingCallable() { result = cfn.getEnclosingCallable() } + override Callable getEnclosingCallableImpl() { result = cfn.getEnclosingCallable() } + + override Type getTypeImpl() { result = cfn.getElement().(Expr).getType() } - override Type getType() { result = cfn.getElement().(Expr).getType() } + override ControlFlow::Node getControlFlowNodeImpl() { none() } - override Location getLocation() { result = cfn.getLocation() } + override Location getLocationImpl() { result = cfn.getLocation() } - override string toString() { result = "[post] " + cfn.toString() } + override string toStringImpl() { result = "[post] " + cfn.toString() } } } @@ -1621,22 +2150,11 @@ private import PostUpdateNodes /** A node that performs a type cast. */ class CastNode extends Node { - CastNode() { - this.asExpr() instanceof Cast - or - this.(AssignableDefinitionNode).getDefinition() instanceof - AssignableDefinitions::PatternDefinition - or - readStep(_, _, this) - or - storeStep(this, _, _) - } + CastNode() { castNode(this) } } class DataFlowExpr = DotNet::Expr; -class DataFlowType = Gvn::GvnType; - /** Holds if `e` is an expression that always has the same Boolean value `val`. */ private predicate constantBooleanExpr(Expr e, boolean val) { e = any(AbstractValues::BooleanValue bv | val = bv.getValue()).getAnExpr() @@ -1666,7 +2184,7 @@ private predicate viableConstantBooleanParamArg( ) } -int accessPathLimit() { result = 3 } +int accessPathLimit() { result = 5 } /** * Holds if `n` does not require a `PostUpdateNode` as it either cannot be @@ -1676,6 +2194,3 @@ int accessPathLimit() { result = 3 } * This predicate is only used for consistency checks. */ predicate isImmutableOrUnobservable(Node n) { none() } - -pragma[inline] -DataFlowType getErasedRepr(DataFlowType t) { result = t } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll index 328db073d6e9..bf012e53283f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -39,35 +39,37 @@ class Node extends TNode { /** Gets the type of this node. */ cached - DotNet::Type getType() { none() } - - /** INTERNAL: Do not use. Gets an upper bound on the type of this node. */ - cached - DataFlowType getTypeBound() { - Stages::DataFlowStage::forceCachingInSameStage() and - exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) | - t0 = getCSharpType(this.getType()) - or - not exists(getCSharpType(this.getType())) and - t0 instanceof ObjectType - ) + final DotNet::Type getType() { + Stages::DataFlowStage::forceCachingInSameStage() and result = this.(NodeImpl).getTypeImpl() } /** Gets the enclosing callable of this node. */ cached - DataFlowCallable getEnclosingCallable() { none() } + final DataFlowCallable getEnclosingCallable() { + Stages::DataFlowStage::forceCachingInSameStage() and + result = unique(DataFlowCallable c | c = this.(NodeImpl).getEnclosingCallableImpl() | c) + } /** Gets the control flow node corresponding to this node, if any. */ cached - ControlFlow::Node getControlFlowNode() { none() } + final ControlFlow::Node getControlFlowNode() { + Stages::DataFlowStage::forceCachingInSameStage() and + result = unique(ControlFlow::Node n | n = this.(NodeImpl).getControlFlowNodeImpl() | n) + } /** Gets a textual representation of this node. */ cached - string toString() { none() } + final string toString() { + Stages::DataFlowStage::forceCachingInSameStage() and + result = this.(NodeImpl).toStringImpl() + } /** Gets the location of this node. */ cached - Location getLocation() { none() } + final Location getLocation() { + Stages::DataFlowStage::forceCachingInSameStage() and + result = this.(NodeImpl).getLocationImpl() + } /** * Holds if this element is at the specified location. @@ -108,31 +110,6 @@ class ExprNode extends Node { this = TExprNode(cfn) and result = cfn.getElement() } - - override DataFlowCallable getEnclosingCallable() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.getExpr().getEnclosingCallable() - } - - override ControlFlow::Nodes::ElementNode getControlFlowNode() { - Stages::DataFlowStage::forceCachingInSameStage() and this = TExprNode(result) - } - - override DotNet::Type getType() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getType() - } - - override Location getLocation() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getLocation() - } - - override string toString() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.getControlFlowNode().toString() - or - this = TCilExprNode(_) and - result = "CIL expression" - } } /** @@ -146,7 +123,8 @@ class ParameterNode extends Node { this.(SsaDefinitionNode).getDefinition() instanceof ImplicitCapturedParameterNodeImpl::SsaCapturedEntryDefinition or this = TInstanceParameterNode(_) or - this = TCilParameterNode(_) + this = TCilParameterNode(_) or + this = TSummaryParameterNode(_, _) } /** Gets the parameter corresponding to this node, if any. */ @@ -188,15 +166,7 @@ AssignableDefinitionNode assignableDefinitionNode(AssignableDefinition def) { result.getDefinition() = def } -/** - * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local - * (intra-procedural) step. - */ -predicate localFlowStep(Node nodeFrom, Node nodeTo) { - simpleLocalFlowStep(nodeFrom, nodeTo) - or - extendedLocalFlowStep(nodeFrom, nodeTo) -} +predicate localFlowStep = localFlowStepImpl/2; /** * Holds if data flows from `source` to `sink` in zero or more local @@ -242,7 +212,8 @@ class BarrierGuard extends Guard { } /** - * A reference contained in an object. This is either a field or a property. + * A reference contained in an object. This is either a field, a property, + * or an element in a collection. */ class Content extends TContent { /** Gets a textual representation of this content. */ @@ -252,10 +223,10 @@ class Content extends TContent { Location getLocation() { none() } /** Gets the type of the object containing this content. */ - DataFlowType getContainerType() { none() } + deprecated Gvn::GvnType getContainerType() { none() } /** Gets the type of this content. */ - DataFlowType getType() { none() } + deprecated Gvn::GvnType getType() { none() } } /** A reference to a field. */ @@ -271,11 +242,11 @@ class FieldContent extends Content, TFieldContent { override Location getLocation() { result = f.getLocation() } - override DataFlowType getContainerType() { + deprecated override Gvn::GvnType getContainerType() { result = Gvn::getGlobalValueNumber(f.getDeclaringType()) } - override DataFlowType getType() { result = Gvn::getGlobalValueNumber(f.getType()) } + deprecated override Gvn::GvnType getType() { result = Gvn::getGlobalValueNumber(f.getType()) } } /** A reference to a property. */ @@ -291,9 +262,16 @@ class PropertyContent extends Content, TPropertyContent { override Location getLocation() { result = p.getLocation() } - override DataFlowType getContainerType() { + deprecated override Gvn::GvnType getContainerType() { result = Gvn::getGlobalValueNumber(p.getDeclaringType()) } - override DataFlowType getType() { result = Gvn::getGlobalValueNumber(p.getType()) } + deprecated override Gvn::GvnType getType() { result = Gvn::getGlobalValueNumber(p.getType()) } +} + +/** A reference to an element in a collection. */ +class ElementContent extends Content, TElementContent { + override string toString() { result = "[]" } + + override Location getLocation() { result instanceof EmptyLocation } } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll index 56b2cdbccf56..243c6b83ef50 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DelegateDataFlow.qll @@ -30,13 +30,13 @@ private class DelegateFlowSource extends DataFlow::ExprNode { } /** A sink of flow for a delegate expression. */ -abstract private class DelegateFlowSink extends DataFlow::ExprNode { +abstract private class DelegateFlowSink extends DataFlow::Node { /** * Gets an actual run-time target of this delegate call in the given call * context, if any. The call context records the *last* call required to * resolve the target, if any. Example: * - * ``` + * ```csharp * public int M(Func f, string x) { * return f(x); * } @@ -60,7 +60,7 @@ abstract private class DelegateFlowSink extends DataFlow::ExprNode { * Note that only the *last* call required is taken into account, hence if * `M` above is redefined as follows: * - * ``` + * ```csharp * public int M(Func f, string x) { * return M2(f, x); * } @@ -92,7 +92,7 @@ abstract private class DelegateFlowSink extends DataFlow::ExprNode { } /** A delegate call expression. */ -library class DelegateCallExpr extends DelegateFlowSink { +class DelegateCallExpr extends DelegateFlowSink, DataFlow::ExprNode { DelegateCall dc; DelegateCallExpr() { this.getExpr() = dc.getDelegateExpr() } @@ -101,50 +101,15 @@ library class DelegateCallExpr extends DelegateFlowSink { DelegateCall getDelegateCall() { result = dc } } -/** A delegate expression that is passed as the argument to a library callable. */ -library class DelegateArgumentToLibraryCallable extends Expr { - DelegateType dt; - Call call; - - DelegateArgumentToLibraryCallable() { - exists(Callable callable, Parameter p | - this = call.getArgumentForParameter(p) and - callable = call.getTarget() and - callable.fromLibrary() and - dt = p.getType().(SystemLinqExpressions::DelegateExtType).getDelegateType() - ) - } - - /** Gets the call that this argument belongs to. */ - Call getCall() { result = call } - - /** Gets the index of this delegate argument in the call. */ - int getArgumentIndex() { this = this.getCall().getArgument(result) } - - /** Gets the delegate type of this argument. */ - DelegateType getDelegateType() { result = dt } - - /** - * Gets an actual run-time target of this delegate call in the given call - * context, if any. The call context records the *last* call required to - * resolve the target, if any. Example: - */ - Callable getARuntimeTarget(CallContext context) { - exists(DelegateArgumentToLibraryCallableSink sink | sink.getExpr() = this | - result = sink.getARuntimeTarget(context) - ) - } -} - -/** A delegate expression that is passed as the argument to a library callable. */ -private class DelegateArgumentToLibraryCallableSink extends DelegateFlowSink { - DelegateArgumentToLibraryCallableSink() { - this.getExpr() instanceof DelegateArgumentToLibraryCallable +/** A parameter of delegate type belonging to a callable with a flow summary. */ +class SummaryDelegateParameterSink extends DelegateFlowSink, SummaryParameterNode { + SummaryDelegateParameterSink() { + this.getType() instanceof SystemLinqExpressions::DelegateExtType } } /** A delegate expression that is added to an event. */ -library class AddEventSource extends DelegateFlowSink { +class AddEventSource extends DelegateFlowSink, DataFlow::ExprNode { AddEventExpr ae; AddEventSource() { this.getExpr() = ae.getRValue() } @@ -192,7 +157,13 @@ private predicate flowsFrom( or // Local flow exists(DataFlow::Node mid | flowsFrom(sink, mid, isReturned, lastCall) | - DataFlow::localFlowStep(node, mid) or + LocalFlow::localFlowStepCommon(node, mid) + or + exists(Ssa::Definition def | + LocalFlow::localSsaFlowStep(def, node, mid) and + LocalFlow::usesInstanceField(def) + ) + or node.asExpr() = mid.asExpr().(DelegateCreation).getArgument() ) or @@ -205,7 +176,7 @@ private predicate flowsFrom( ) or // Flow into a callable (non-delegate call) - exists(ExplicitParameterNode mid, CallContext prevLastCall, NonDelegateCall call, Parameter p | + exists(ParameterNode mid, CallContext prevLastCall, NonDelegateCall call, Parameter p | flowsFrom(sink, mid, isReturned, prevLastCall) and isReturned = false and p = mid.getParameter() and @@ -215,8 +186,7 @@ private predicate flowsFrom( or // Flow into a callable (delegate call) exists( - ExplicitParameterNode mid, CallContext prevLastCall, DelegateCall call, Callable c, Parameter p, - int i + ParameterNode mid, CallContext prevLastCall, DelegateCall call, Callable c, Parameter p, int i | flowsFrom(sink, mid, isReturned, prevLastCall) and isReturned = false and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/Steps.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/Steps.qll index f80a2036aeaa..9eb296b67873 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/Steps.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/Steps.qll @@ -78,7 +78,7 @@ module Steps { * assumption. For example, there is flow from `0` on line 3 to `i` on line * 8 and from `1` on line 4 to `i` on line 12 in * - * ``` + * ```csharp * public class C { * public void A() { * B(0); @@ -106,7 +106,7 @@ module Steps { * 8 (but not from `1` on line 4 to `i` on line 12 because `C` is virtual) * in * - * ``` + * ```csharp * public class C { * public void A() { * B(0); diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll index 93fe29c4619f..68284e965fa0 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll @@ -1,29 +1,25 @@ private import csharp private import TaintTrackingPublic +private import DataFlowImplCommon +private import semmle.code.csharp.Caching private import semmle.code.csharp.dataflow.internal.DataFlowPrivate private import semmle.code.csharp.dataflow.internal.ControlFlowReachability private import semmle.code.csharp.dataflow.LibraryTypeDataFlow private import semmle.code.csharp.dispatch.Dispatch private import semmle.code.csharp.commons.ComparisonTest -private import semmle.code.csharp.frameworks.JsonNET private import cil private import dotnet +// import `TaintedMember` definitions from other files to avoid potential reevaluation +private import semmle.code.csharp.frameworks.JsonNET +private import semmle.code.csharp.frameworks.WCF /** - * Holds if `node` should be a barrier in all global taint flow configurations + * Holds if `node` should be a sanitizer in all global taint flow configurations * but not in local taint. */ -predicate defaultTaintBarrier(DataFlow::Node node) { none() } +predicate defaultTaintSanitizer(DataFlow::Node node) { none() } -/** - * Holds if the additional step from `src` to `sink` should be included in all - * global taint flow configurations. - */ -predicate defaultAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { - localAdditionalTaintStep(pred, succ) - or - succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false) -} +deprecated predicate localAdditionalTaintStep = defaultAdditionalTaintStep/2; private CIL::DataFlowNode asCilDataFlowNode(DataFlow::Node node) { result = node.asParameter() or @@ -34,9 +30,6 @@ private predicate localTaintStepCil(DataFlow::Node nodeFrom, DataFlow::Node node asCilDataFlowNode(nodeFrom).getALocalFlowSucc(asCilDataFlowNode(nodeTo), any(CIL::Tainted t)) } -/** Gets the qualifier of element access `ea`. */ -private Expr getElementAccessQualifier(ElementAccess ea) { result = ea.getQualifier() } - private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityConfiguration { LocalTaintExprStepConfiguration() { this = "LocalTaintExprStepConfiguration" } @@ -45,28 +38,6 @@ private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityCon ) { exactScope = false and ( - // Taint from assigned value to element qualifier (`x[i] = 0`) - exists(AssignExpr ae | - e1 = ae.getRValue() and - e2.(AssignableRead) = getElementAccessQualifier+(ae.getLValue()) and - scope = ae and - isSuccessor = false - ) - or - // Taint from array initializer - e1 = e2.(ArrayCreation).getInitializer().getAnElement() and - scope = e2 and - isSuccessor = false - or - // Taint from object initializer - exists(ElementInitializer ei | - ei = e2.(ObjectCreation).getInitializer().(CollectionInitializer).getAnElementInitializer() and - e1 = ei.getArgument(ei.getNumberOfArguments() - 1) and // assume the last argument is the value (i.e., not a key) - scope = e2 and - isSuccessor = false - ) - or - // Taint from element qualifier e1 = e2.(ElementAccess).getQualifier() and scope = e2 and isSuccessor = true @@ -124,63 +95,84 @@ private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityCon scope = e2 and isSuccessor = true ) - ) - } - - override predicate candidateDef( - Expr e, AssignableDefinition defTo, ControlFlowElement scope, boolean exactScope, - boolean isSuccessor - ) { - // Taint from `foreach` expression - exists(ForeachStmt fs | - e = fs.getIterableExpr() and - defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() = - fs.getVariableDeclExpr() and - isSuccessor = true - | - scope = fs and - exactScope = true - or - scope = fs.getIterableExpr() and - exactScope = false or - scope = fs.getVariableDeclExpr() and - exactScope = false + e1 = e2.(AwaitExpr).getExpr() and + scope = e2 and + isSuccessor = true ) } } +private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + Stages::DataFlowStage::forceCachingInSameStage() and + any(LocalTaintExprStepConfiguration x).hasNodePath(nodeFrom, nodeTo) + or + localTaintStepCil(nodeFrom, nodeTo) +} + cached -module Cached { - private import semmle.code.csharp.Caching +private module Cached { + private import Summaries + /** + * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ cached - predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - Stages::DataFlowStage::forceCachingInSameStage() and - any(LocalTaintExprStepConfiguration x).hasNodePath(nodeFrom, nodeTo) + predicate localTaintStepImpl(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // Ordinary data flow + DataFlow::localFlowStep(nodeFrom, nodeTo) or - nodeFrom = nodeTo.(YieldReturnNode).getPreUpdateNode() + localTaintStepCommon(nodeFrom, nodeTo) or - localTaintStepCil(nodeFrom, nodeTo) - or - // Taint members - exists(Access access | - access = nodeTo.asExpr() and - access.getTarget() instanceof TaintedMember - | - access.(FieldRead).getQualifier() = nodeFrom.asExpr() + not LocalFlow::excludeFromExposedRelations(nodeFrom) and + not LocalFlow::excludeFromExposedRelations(nodeTo) and + ( + // Simple flow through library code is included in the exposed local + // step relation, even though flow is technically inter-procedural + summaryThroughStep(nodeFrom, nodeTo, false) or - access.(PropertyRead).getQualifier() = nodeFrom.asExpr() - ) - or - exists(LibraryCodeNode n | not n.preservesValue() | - n = nodeTo and - nodeFrom = n.getPredecessor(AccessPath::empty()) + // Taint collection by adding a tainted element + exists(DataFlow::ElementContent c | + storeStep(nodeFrom, c, nodeTo) + or + summarySetterStep(nodeFrom, c, nodeTo) + ) or - n = nodeFrom and - nodeTo = n.getSuccessor(AccessPath::empty()) + exists(DataFlow::Content c | + readStep(nodeFrom, c, nodeTo) + or + summaryGetterStep(nodeFrom, c, nodeTo) + | + // Taint members + c = any(TaintedMember m).(FieldOrProperty).getContent() + or + // Read from a tainted collection + c = TElementContent() + ) ) } + + /** + * Holds if the additional step from `nodeFrom` to `nodeTo` should be included + * in all global taint flow configurations. + */ + cached + predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + localTaintStepCommon(nodeFrom, nodeTo) + or + // Taint members + readStep(nodeFrom, any(TaintedMember m).(FieldOrProperty).getContent(), nodeTo) + or + // Although flow through collections is modelled precisely using stores/reads, we still + // allow flow out of a _tainted_ collection. This is needed in order to support taint- + // tracking configurations where the source is a collection + readStep(nodeFrom, TElementContent(), nodeTo) + or + summaryLocalStep(nodeFrom, nodeTo, false) + or + nodeTo = nodeFrom.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false) + } } import Cached diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPublic.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPublic.qll index eda33f2fcd90..6e4ba538a402 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPublic.qll @@ -18,13 +18,4 @@ predicate localExprTaint(Expr e1, Expr e2) { /** A member (property or field) that is tainted if its containing object is tainted. */ abstract class TaintedMember extends AssignableMember { } -/** - * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local - * (intra-procedural) step. - */ -predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - // Ordinary data flow - DataFlow::localFlowStep(nodeFrom, nodeTo) - or - localAdditionalTaintStep(nodeFrom, nodeTo) -} +predicate localTaintStep = localTaintStepImpl/2; diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll new file mode 100644 index 000000000000..edf437e919c4 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll @@ -0,0 +1,21 @@ +/** + * Provides C#-specific definitions for bounds. + */ + +private import csharp as CS +private import semmle.code.csharp.dataflow.SSA::Ssa as Ssa +private import semmle.code.csharp.dataflow.internal.rangeanalysis.ConstantUtils as CU + +class SsaVariable extends Ssa::Definition { + /** Gets a read of the source variable underlying this SSA definition. */ + Expr getAUse() { result = getARead() } +} + +class Expr = CS::Expr; + +class IntegralType = CS::IntegralType; + +class ConstantIntegerExpr = CU::ConstantIntegerExpr; + +/** Holds if `e` is a bound expression and it is not an SSA variable read. */ +predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.(CS::PropertyRead)) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll new file mode 100644 index 000000000000..08b64321a38a --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll @@ -0,0 +1,71 @@ +/** + * Provides classes and predicates to represent constant integer expressions. + */ + +private import csharp +private import Ssa + +/** + * Holds if property `p` matches `property` in `baseClass` or any overrides. + */ +predicate propertyOverrides(Property p, string baseClass, string property) { + exists(Property p2 | + p2.getSourceDeclaration().getDeclaringType().hasQualifiedName(baseClass) and + p2.hasName(property) + | + p.overridesOrImplementsOrEquals(p2) + ) +} + +/** + * Holds if `pa` is an access to the `Length` property of an array. + */ +predicate systemArrayLengthAccess(PropertyAccess pa) { + propertyOverrides(pa.getTarget(), "System.Array", "Length") +} + +/** + * Holds if expression `e` is either + * - a compile time constant with integer value `val`, or + * - a read of a compile time constant with integer value `val`, or + * - a read of the `Length` of an array with `val` lengths. + */ +private predicate constantIntegerExpr(Expr e, int val) { + e.getValue().toInt() = val + or + exists(ExplicitDefinition v, Expr src | + e = v.getARead() and + src = v.getADefinition().getSource() and + constantIntegerExpr(src, val) + ) + or + isArrayLengthAccess(e, val) +} + +private int getArrayLength(ArrayCreation arrCreation, int index) { + constantIntegerExpr(arrCreation.getLengthArgument(index), result) +} + +private int getArrayLengthRec(ArrayCreation arrCreation, int index) { + index = 0 and result = getArrayLength(arrCreation, 0) + or + index > 0 and + result = getArrayLength(arrCreation, index) * getArrayLengthRec(arrCreation, index - 1) +} + +private predicate isArrayLengthAccess(PropertyAccess pa, int length) { + systemArrayLengthAccess(pa) and + exists(ExplicitDefinition arr, ArrayCreation arrCreation | + getArrayLengthRec(arrCreation, arrCreation.getNumberOfLengthArguments() - 1) = length and + arrCreation = arr.getADefinition().getSource() and + pa.getQualifier() = arr.getARead() + ) +} + +/** An expression that always has the same integer value. */ +class ConstantIntegerExpr extends Expr { + ConstantIntegerExpr() { constantIntegerExpr(this, _) } + + /** Gets the integer value of this expression. */ + int getIntValue() { constantIntegerExpr(this, result) } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll new file mode 100644 index 000000000000..2cdbfbf65f4e --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll @@ -0,0 +1,110 @@ +module Private { + private import csharp as CS + private import ConstantUtils as CU + private import semmle.code.csharp.controlflow.Guards as G + private import semmle.code.csharp.dataflow.internal.rangeanalysis.RangeUtils as RU + private import SsaUtils as SU + private import SignAnalysisSpecific::Private as SA + + class BasicBlock = CS::Ssa::BasicBlock; + + class SsaVariable extends CS::Ssa::Definition { + CS::AssignableRead getAUse() { result = this.getARead() } + } + + class SsaPhiNode = CS::Ssa::PhiNode; + + class Expr = CS::Expr; + + class Guard = G::Guard; + + class ConstantIntegerExpr = CU::ConstantIntegerExpr; + + class ConditionalExpr extends CS::ConditionalExpr { + /** Gets the "then" expression of this conditional expression. */ + Expr getTrueExpr() { result = this.getThen() } + + /** Gets the "else" expression of this conditional expression. */ + Expr getFalseExpr() { result = this.getElse() } + } + + /** Represent an addition expression. */ + class AddExpr extends CS::AddExpr { + /** Gets the LHS operand of this add expression. */ + Expr getLhs() { result = this.getLeftOperand() } + + /** Gets the RHS operand of this add expression. */ + Expr getRhs() { result = this.getRightOperand() } + } + + /** Represent a subtraction expression. */ + class SubExpr extends CS::SubExpr { + /** Gets the LHS operand of this subtraction expression. */ + Expr getLhs() { result = this.getLeftOperand() } + + /** Gets the RHS operand of this subtraction expression. */ + Expr getRhs() { result = this.getRightOperand() } + } + + class RemExpr = CS::RemExpr; + + /** Represent a bitwise and or an assign-and expression. */ + class BitwiseAndExpr extends CS::Expr { + BitwiseAndExpr() { this instanceof CS::BitwiseAndExpr or this instanceof CS::AssignAndExpr } + + /** Gets an operand of this bitwise and operation. */ + Expr getAnOperand() { + result = this.(CS::BitwiseAndExpr).getAnOperand() or + result = this.(CS::AssignAndExpr).getRValue() or + result = this.(CS::AssignAndExpr).getLValue() + } + + /** Holds if this expression has operands `e1` and `e2`. */ + predicate hasOperands(Expr e1, Expr e2) { + this.getAnOperand() = e1 and + this.getAnOperand() = e2 and + e1 != e2 + } + } + + /** Represent a multiplication or an assign-mul expression. */ + class MulExpr extends CS::Expr { + MulExpr() { this instanceof CS::MulExpr or this instanceof CS::AssignMulExpr } + + /** Gets an operand of this multiplication. */ + Expr getAnOperand() { + result = this.(CS::MulExpr).getAnOperand() or + result = this.(CS::AssignMulExpr).getRValue() or + result = this.(CS::AssignMulExpr).getLValue() + } + } + + /** Represent a left shift or an assign-lshift expression. */ + class LShiftExpr extends CS::Expr { + LShiftExpr() { this instanceof CS::LShiftExpr or this instanceof CS::AssignLShiftExpr } + + /** Gets the RHS operand of this shift. */ + Expr getRhs() { + result = this.(CS::LShiftExpr).getRightOperand() or + result = this.(CS::AssignLShiftExpr).getRValue() + } + } + + predicate guardDirectlyControlsSsaRead = SA::guardControlsSsaRead/3; + + predicate guardControlsSsaRead = SA::guardControlsSsaRead/3; + + predicate valueFlowStep = RU::valueFlowStep/3; + + predicate eqFlowCond = RU::eqFlowCond/5; + + predicate ssaUpdateStep = RU::ssaUpdateStep/3; + + Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode().getElement() } + + private predicate id(CS::ControlFlowElement x, CS::ControlFlowElement y) { x = y } + + private predicate idOf(CS::ControlFlowElement x, int y) = equivalenceRelation(id/2)(x, y) + + int getId(BasicBlock bb) { idOf(bb.getFirstNode().getElement(), result) } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll new file mode 100644 index 000000000000..4abfb2d17793 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll @@ -0,0 +1,98 @@ +/** + * Provides predicates for range and modulus analysis. + */ + +private import csharp +private import Ssa +private import SsaUtils +private import ConstantUtils +private import SsaReadPositionCommon +private import semmle.code.csharp.controlflow.Guards as G + +private class BooleanValue = G::AbstractValues::BooleanValue; + +/** + * Holds if `v` is an `ExplicitDefinition` that equals `e + delta`. + */ +predicate ssaUpdateStep(ExplicitDefinition v, Expr e, int delta) { + v.getADefinition().getExpr().(Assignment).getRValue() = e and delta = 0 + or + v.getADefinition().getExpr().(PostIncrExpr).getOperand() = e and delta = 1 + or + v.getADefinition().getExpr().(PreIncrExpr).getOperand() = e and delta = 1 + or + v.getADefinition().getExpr().(PostDecrExpr).getOperand() = e and delta = -1 + or + v.getADefinition().getExpr().(PreDecrExpr).getOperand() = e and delta = -1 +} + +private G::Guard eqFlowCondAbs(Definition def, Expr e, int delta, boolean isEq, G::AbstractValue v) { + exists(boolean eqpolarity | + result.isEquality(ssaRead(def, delta), e, eqpolarity) and + eqpolarity.booleanXor(v.(BooleanValue).getValue()).booleanNot() = isEq + ) + or + exists(G::AbstractValue v0 | + G::Internal::impliesSteps(result, v, eqFlowCondAbs(def, e, delta, isEq, v0), v0) + ) +} + +/** + * Gets a condition that tests whether `def` equals `e + delta`. + * + * If the condition evaluates to `testIsTrue`: + * - `isEq = true` : `def == e + delta` + * - `isEq = false` : `def != e + delta` + */ +G::Guard eqFlowCond(Definition def, Expr e, int delta, boolean isEq, boolean testIsTrue) { + exists(BooleanValue v | + result = eqFlowCondAbs(def, e, delta, isEq, v) and + testIsTrue = v.getValue() + ) +} + +/** + * Holds if `e1 + delta` equals `e2`. + */ +predicate valueFlowStep(Expr e2, Expr e1, int delta) { + valueFlowStep(e2.(AssignOperation).getExpandedAssignment(), e1, delta) + or + e2.(AssignExpr).getRValue() = e1 and delta = 0 + or + e2.(UnaryPlusExpr).getOperand() = e1 and delta = 0 + or + e2.(PostIncrExpr).getOperand() = e1 and delta = 0 + or + e2.(PostDecrExpr).getOperand() = e1 and delta = 0 + or + e2.(PreIncrExpr).getOperand() = e1 and delta = 1 + or + e2.(PreDecrExpr).getOperand() = e1 and delta = -1 + or + exists(ConstantIntegerExpr x | + e2.(AddExpr).getAnOperand() = e1 and + e2.(AddExpr).getAnOperand() = x and + not e1 = x + | + x.getIntValue() = delta + ) + or + exists(ConstantIntegerExpr x | + exists(SubExpr sub | + e2 = sub and + sub.getLeftOperand() = e1 and + sub.getRightOperand() = x + ) + | + x.getIntValue() = -delta + ) +} + +/** + * Holds if `guard` controls the position `controlled` with the value `testIsTrue`. + */ +predicate guardControlsSsaRead(G::Guard guard, SsaReadPosition controlled, boolean testIsTrue) { + exists(BooleanValue b | b.getValue() = testIsTrue | + guard.controlsBasicBlock(controlled.(SsaReadPositionBlock).getBlock(), b) + ) +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll new file mode 100644 index 000000000000..b2058a271148 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll @@ -0,0 +1,280 @@ +newtype TSign = + TNeg() or + TZero() or + TPos() + +newtype TUnarySignOperation = + TNegOp() or + TIncOp() or + TDecOp() or + TBitNotOp() + +newtype TBinarySignOperation = + TAddOp() or + TSubOp() or + TMulOp() or + TDivOp() or + TRemOp() or + TBitAndOp() or + TBitOrOp() or + TBitXorOp() or + TLShiftOp() or + TRShiftOp() or + TURShiftOp() + +/** Class representing expression signs (+, -, 0). */ +class Sign extends TSign { + /** Gets the string representation of this sign. */ + string toString() { + result = "-" and this = TNeg() + or + result = "0" and this = TZero() + or + result = "+" and this = TPos() + } + + /** Gets a possible sign after incrementing an expression that has this sign. */ + Sign inc() { + this = TNeg() and result = TNeg() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TPos() + or + this = TPos() and result = TPos() + } + + /** Gets a possible sign after decrementing an expression that has this sign. */ + Sign dec() { result.inc() = this } + + /** Gets a possible sign after negating an expression that has this sign. */ + Sign neg() { + this = TNeg() and result = TPos() + or + this = TZero() and result = TZero() + or + this = TPos() and result = TNeg() + } + + /** + * Gets a possible sign after bitwise complementing an expression that has this + * sign. + */ + Sign bitnot() { + this = TNeg() and result = TPos() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TNeg() + or + this = TPos() and result = TNeg() + } + + /** + * Gets a possible sign after adding an expression with sign `s` to an expression + * that has this sign. + */ + Sign add(Sign s) { + this = TZero() and result = s + or + s = TZero() and result = this + or + this = s and this = result + or + this = TPos() and s = TNeg() + or + this = TNeg() and s = TPos() + } + + /** + * Gets a possible sign after subtracting an expression with sign `s` from an expression + * that has this sign. + */ + Sign sub(Sign s) { result = add(s.neg()) } + + /** + * Gets a possible sign after multiplying an expression with sign `s` to an expression + * that has this sign. + */ + Sign mul(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after integer dividing an expression that has this sign + * by an expression with sign `s`. + */ + Sign div(Sign s) { + result = TZero() and s = TNeg() // ex: 3 / -5 = 0 + or + result = TZero() and s = TPos() // ex: 3 / 5 = 0 + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after modulo dividing an expression that has this sign + * by an expression with sign `s`. + */ + Sign rem(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = this and s = TNeg() + or + result = this and s = TPos() + } + + /** + * Gets a possible sign after bitwise `and` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitand(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TZero() and this = TPos() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TNeg() + or + result = TPos() and this = TPos() and s = TPos() + } + + /** + * Gets a possible sign after bitwise `or` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitor(Sign s) { + result = TZero() and this = TZero() and s = TZero() + or + result = TNeg() and this = TNeg() + or + result = TNeg() and s = TNeg() + or + result = TPos() and this = TPos() and s = TZero() + or + result = TPos() and this = TZero() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + } + + /** + * Gets a possible sign after bitwise `xor` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitxor(Sign s) { + result = TZero() and this = s + or + result = this and s = TZero() + or + result = s and this = TZero() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after left shift of an expression that has this sign + * by an expression with sign `s`. + */ + Sign lshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + this != TZero() and s != TZero() + } + + /** + * Gets a possible sign after right shift of an expression that has this sign + * by an expression with sign `s`. + */ + Sign rshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result = TNeg() and this = TNeg() + or + result != TNeg() and this = TPos() and s != TZero() + } + + /** + * Gets a possible sign after unsigned right shift of an expression that has + * this sign by an expression with sign `s`. + */ + Sign urshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result != TZero() and this = TNeg() and s != TZero() + or + result != TNeg() and this = TPos() and s != TZero() + } + + /** Perform `op` on this sign. */ + Sign applyUnaryOp(TUnarySignOperation op) { + op = TIncOp() and result = inc() + or + op = TDecOp() and result = dec() + or + op = TNegOp() and result = neg() + or + op = TBitNotOp() and result = bitnot() + } + + /** Perform `op` on this sign and sign `s`. */ + Sign applyBinaryOp(Sign s, TBinarySignOperation op) { + op = TAddOp() and result = add(s) + or + op = TSubOp() and result = sub(s) + or + op = TMulOp() and result = mul(s) + or + op = TDivOp() and result = div(s) + or + op = TRemOp() and result = rem(s) + or + op = TBitAndOp() and result = bitand(s) + or + op = TBitOrOp() and result = bitor(s) + or + op = TBitXorOp() and result = bitxor(s) + or + op = TLShiftOp() and result = lshift(s) + or + op = TRShiftOp() and result = rshift(s) + or + op = TURShiftOp() and result = urshift(s) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll new file mode 100644 index 000000000000..d00a38bac737 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll @@ -0,0 +1,376 @@ +/** + * Provides sign analysis to determine whether expression are always positive + * or negative. + * + * The analysis is implemented as an abstract interpretation over the + * three-valued domain `{negative, zero, positive}`. + */ + +private import SignAnalysisSpecific::Private +private import SsaReadPositionCommon +private import Sign + +/** Gets the sign of `e` if this can be directly determined. */ +private Sign certainExprSign(Expr e) { + exists(int i | e.(ConstantIntegerExpr).getIntValue() = i | + i < 0 and result = TNeg() + or + i = 0 and result = TZero() + or + i > 0 and result = TPos() + ) + or + not exists(e.(ConstantIntegerExpr).getIntValue()) and + ( + exists(float f | f = getNonIntegerValue(e) | + f < 0 and result = TNeg() + or + f = 0 and result = TZero() + or + f > 0 and result = TPos() + ) + or + exists(string charlit | charlit = getCharValue(e) | + if charlit.regexpMatch("\\u0000") then result = TZero() else result = TPos() + ) + or + containerSizeAccess(e) and + (result = TPos() or result = TZero()) + or + positiveExpression(e) and result = TPos() + ) +} + +/** Holds if the sign of `e` is too complicated to determine. */ +private predicate unknownSign(Expr e) { + not exists(certainExprSign(e)) and + ( + exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt())) + or + exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat())) + or + exists(CastExpr cast, Type fromtyp | + cast = e and + fromtyp = cast.getExpr().getType() and + not fromtyp instanceof NumericOrCharType + ) + or + numericExprWithUnknownSign(e) + ) +} + +/** + * Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate lowerBound(Expr lowerbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) { + exists(boolean testIsTrue, ComparisonExpr comp | + pos.hasReadOfVar(v) and + guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and + not unknownSign(lowerbound) + | + testIsTrue = true and + comp.getLesserOperand() = lowerbound and + comp.getGreaterOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = true else isStrict = false) + or + testIsTrue = false and + comp.getGreaterOperand() = lowerbound and + comp.getLesserOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = false else isStrict = true) + ) +} + +/** + * Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate upperBound(Expr upperbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) { + exists(boolean testIsTrue, ComparisonExpr comp | + pos.hasReadOfVar(v) and + guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and + not unknownSign(upperbound) + | + testIsTrue = true and + comp.getGreaterOperand() = upperbound and + comp.getLesserOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = true else isStrict = false) + or + testIsTrue = false and + comp.getLesserOperand() = upperbound and + comp.getGreaterOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = false else isStrict = true) + ) +} + +/** + * Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is + * restricted to only include bounds for which we might determine a sign. The + * boolean `isEq` gives the polarity: + * - `isEq = true` : `v = eqbound` + * - `isEq = false` : `v != eqbound` + */ +private predicate eqBound(Expr eqbound, SsaVariable v, SsaReadPosition pos, boolean isEq) { + exists(Guard guard, boolean testIsTrue, boolean polarity | + pos.hasReadOfVar(v) and + guardControlsSsaRead(guard, pos, testIsTrue) and + guard.isEquality(eqbound, ssaRead(v, 0), polarity) and + isEq = polarity.booleanXor(testIsTrue).booleanNot() and + not unknownSign(eqbound) + ) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in + * order for `v` to be positive. + */ +private predicate posBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + upperBound(bound, v, pos, _) or + eqBound(bound, v, pos, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in + * order for `v` to be negative. + */ +private predicate negBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) or + eqBound(bound, v, pos, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` + * can be zero. + */ +private predicate zeroBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) or + upperBound(bound, v, pos, _) or + eqBound(bound, v, pos, _) +} + +/** Holds if `bound` allows `v` to be positive at `pos`. */ +private predicate posBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + posBound(bound, v, pos) and TPos() = exprSign(bound) +} + +/** Holds if `bound` allows `v` to be negative at `pos`. */ +private predicate negBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + negBound(bound, v, pos) and TNeg() = exprSign(bound) +} + +/** Holds if `bound` allows `v` to be zero at `pos`. */ +private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound) + or + lowerBound(bound, v, pos, false) and TZero() = exprSign(bound) + or + upperBound(bound, v, pos, _) and TPos() = exprSign(bound) + or + upperBound(bound, v, pos, false) and TZero() = exprSign(bound) + or + eqBound(bound, v, pos, true) and TZero() = exprSign(bound) + or + eqBound(bound, v, pos, false) and TZero() != exprSign(bound) +} + +/** + * Holds if there is a bound that might restrict whether `v` has the sign `s` + * at `pos`. + */ +private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) { + s = TPos() and posBound(_, v, pos) + or + s = TNeg() and negBound(_, v, pos) + or + s = TZero() and zeroBound(_, v, pos) +} + +/** + * Gets a possible sign of `v` at `pos` based on its definition, where the sign + * might be ruled out by a guard. + */ +pragma[noinline] +private Sign guardedSsaSign(SsaVariable v, SsaReadPosition pos) { + result = ssaDefSign(v) and + pos.hasReadOfVar(v) and + hasGuard(v, pos, result) +} + +/** + * Gets a possible sign of `v` at `pos` based on its definition, where no guard + * can rule it out. + */ +pragma[noinline] +private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) { + result = ssaDefSign(v) and + pos.hasReadOfVar(v) and + not hasGuard(v, pos, result) +} + +/** + * Gets a possible sign of `v` at read position `pos`, where a guard could have + * ruled out the sign but does not. + * This does not check that the definition of `v` also allows the sign. + */ +private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) { + result = TPos() and + forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos)) + or + result = TNeg() and + forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos)) + or + result = TZero() and + forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos)) +} + +/** Gets a possible sign for `v` at `pos`. */ +private Sign ssaSign(SsaVariable v, SsaReadPosition pos) { + result = unguardedSsaSign(v, pos) + or + result = guardedSsaSign(v, pos) and + result = guardedSsaSignOk(v, pos) +} + +/** Gets a possible sign for `v`. */ +pragma[nomagic] +private Sign ssaDefSign(SsaVariable v) { + result = explicitSsaDefSign(v) + or + result = implicitSsaDefSign(v) + or + exists(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge | + v = phi and + edge.phiInput(phi, inp) and + result = ssaSign(inp, edge) + ) +} + +/** Returns the sign of explicit SSA definition `v`. */ +private Sign explicitSsaDefSign(SsaVariable v) { + exists(VariableUpdate def | def = getExplicitSsaAssignment(v) | + result = exprSign(getExprFromSsaAssignment(def)) + or + anySign(result) and explicitSsaDefWithAnySign(def) + or + result = exprSign(getIncrementOperand(def)).inc() + or + result = exprSign(getDecrementOperand(def)).dec() + ) +} + +/** Returns the sign of implicit SSA definition `v`. */ +private Sign implicitSsaDefSign(SsaVariable v) { + result = fieldSign(getImplicitSsaDeclaration(v)) + or + anySign(result) and nonFieldImplicitSsaDefinition(v) +} + +/** Gets a possible sign for `f`. */ +private Sign fieldSign(Field f) { + if not fieldWithUnknownSign(f) + then + result = exprSign(getAssignedValueToField(f)) + or + fieldIncrementOperationOperand(f) and result = fieldSign(f).inc() + or + fieldDecrementOperationOperand(f) and result = fieldSign(f).dec() + or + result = specificFieldSign(f) + else anySign(result) +} + +/** Gets a possible sign for `e`. */ +cached +Sign exprSign(Expr e) { + exists(Sign s | + s = certainExprSign(e) + or + not exists(certainExprSign(e)) and + ( + anySign(s) and unknownSign(e) + or + exists(SsaVariable v | getARead(v) = e | + s = ssaSign(v, any(SsaReadPositionBlock bb | getAnExpression(bb) = e)) + or + not exists(SsaReadPositionBlock bb | getAnExpression(bb) = e) and + s = ssaDefSign(v) + ) + or + exists(VarAccess access | access = e | + not exists(SsaVariable v | getARead(v) = access) and + ( + s = fieldSign(getField(access.(FieldAccess))) + or + anySign(s) and not access instanceof FieldAccess + ) + ) + or + s = specificSubExprSign(e) + ) + | + if e.getType() instanceof UnsignedNumericType and s = TNeg() + then result = TPos() + else result = s + ) +} + +/** Gets a possible sign for `e` from the signs of its child nodes. */ +private Sign specificSubExprSign(Expr e) { + result = exprSign(getASubExprWithSameSign(e)) + or + exists(DivExpr div | div = e | + result = exprSign(div.getLeftOperand()) and + result != TZero() and + div.getRightOperand().(RealLiteral).getValue().toFloat() = 0 + ) + or + exists(UnaryOperation unary | unary = e | + result = exprSign(unary.getOperand()).applyUnaryOp(unary.getOp()) + ) + or + exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) | + result = s1.applyBinaryOp(s2, e.(BinaryOperation).getOp()) + ) +} + +pragma[noinline] +private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) { + lhs = binaryOpLhsSign(e) and + rhs = binaryOpRhsSign(e) +} + +private Sign binaryOpLhsSign(BinaryOperation e) { result = exprSign(e.getLeftOperand()) } + +private Sign binaryOpRhsSign(BinaryOperation e) { result = exprSign(e.getRightOperand()) } + +/** + * Dummy predicate that holds for any sign. This is added to improve readability + * of cases where the sign is unrestricted. + */ +predicate anySign(Sign s) { any() } + +/** Holds if `e` can be positive and cannot be negative. */ +predicate positive(Expr e) { + exprSign(e) = TPos() and + not exprSign(e) = TNeg() +} + +/** Holds if `e` can be negative and cannot be positive. */ +predicate negative(Expr e) { + exprSign(e) = TNeg() and + not exprSign(e) = TPos() +} + +/** Holds if `e` is strictly positive. */ +predicate strictlyPositive(Expr e) { + exprSign(e) = TPos() and + not exprSign(e) = TNeg() and + not exprSign(e) = TZero() +} + +/** Holds if `e` is strictly negative. */ +predicate strictlyNegative(Expr e) { + exprSign(e) = TNeg() and + not exprSign(e) = TPos() and + not exprSign(e) = TZero() +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll new file mode 100644 index 000000000000..782c01d15b7a --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -0,0 +1,339 @@ +/** + * Provides C#-specific definitions for use in sign analysis. + */ +module Private { + private import csharp as CS + private import SsaUtils as SU + private import ConstantUtils as CU + private import RangeUtils as RU + private import semmle.code.csharp.controlflow.Guards as G + private import Sign + import Impl + + class Guard = G::Guard; + + class ConstantIntegerExpr = CU::ConstantIntegerExpr; + + class SsaVariable = CS::Ssa::Definition; + + class SsaPhiNode = CS::Ssa::PhiNode; + + class VarAccess = CS::AssignableAccess; + + class FieldAccess = CS::FieldAccess; + + class CharacterLiteral = CS::CharLiteral; + + class IntegerLiteral = CS::IntegerLiteral; + + class LongLiteral = CS::LongLiteral; + + class CastExpr = CS::CastExpr; + + class Type = CS::Type; + + class Expr = CS::Expr; + + class VariableUpdate = CS::AssignableDefinition; + + class Field = CS::Field; + + class RealLiteral = CS::RealLiteral; + + class DivExpr = CS::DivExpr; + + /** Class to represent unary operation. */ + class UnaryOperation extends Expr { + UnaryOperation() { + this instanceof CS::PreIncrExpr or + this instanceof CS::PreDecrExpr or + this instanceof CS::UnaryMinusExpr or + this instanceof CS::ComplementExpr + } + + /** Returns the operand of this expression. */ + Expr getOperand() { + result = this.(CS::PreIncrExpr).getOperand() or + result = this.(CS::PreDecrExpr).getOperand() or + result = this.(CS::UnaryMinusExpr).getOperand() or + result = this.(CS::ComplementExpr).getOperand() + } + + /** Returns the operation representing this expression. */ + TUnarySignOperation getOp() { + this instanceof CS::PreIncrExpr and result = TIncOp() + or + this instanceof CS::PreDecrExpr and result = TDecOp() + or + this instanceof CS::UnaryMinusExpr and result = TNegOp() + or + this instanceof CS::ComplementExpr and result = TBitNotOp() + } + } + + /** Class to represent binary operation. */ + class BinaryOperation extends CS::BinaryOperation { + BinaryOperation() { + this instanceof CS::AddExpr or + this instanceof CS::SubExpr or + this instanceof CS::MulExpr or + this instanceof CS::DivExpr or + this instanceof CS::RemExpr or + this instanceof CS::BitwiseAndExpr or + this instanceof CS::BitwiseOrExpr or + this instanceof CS::BitwiseXorExpr or + this instanceof CS::LShiftExpr or + this instanceof CS::RShiftExpr + } + + /** Returns the operation representing this expression. */ + TBinarySignOperation getOp() { + this instanceof CS::AddExpr and result = TAddOp() + or + this instanceof CS::SubExpr and result = TSubOp() + or + this instanceof CS::MulExpr and result = TMulOp() + or + this instanceof CS::DivExpr and result = TDivOp() + or + this instanceof CS::RemExpr and result = TRemOp() + or + this instanceof CS::BitwiseAndExpr and result = TBitAndOp() + or + this instanceof CS::BitwiseOrExpr and result = TBitOrOp() + or + this instanceof CS::BitwiseXorExpr and result = TBitXorOp() + or + this instanceof CS::LShiftExpr and result = TLShiftOp() + or + this instanceof CS::RShiftExpr and result = TRShiftOp() + } + } + + predicate ssaRead = SU::ssaRead/2; + + predicate guardControlsSsaRead = RU::guardControlsSsaRead/3; +} + +private module Impl { + private import csharp + private import SsaUtils + private import ConstantUtils + private import semmle.code.csharp.controlflow.Guards + private import Linq.Helpers + private import Sign + private import SignAnalysisCommon + private import SsaReadPositionCommon + private import semmle.code.csharp.commons.ComparisonTest + + private class BooleanValue = AbstractValues::BooleanValue; + + /** Gets the character value of expression `e`. */ + string getCharValue(Expr e) { result = e.getValue() and e.getType() instanceof CharType } + + /** Gets the constant `float` value of non-`ConstantIntegerExpr` expressions. */ + float getNonIntegerValue(Expr e) { + exists(string s | + s = e.getValue() and + result = s.toFloat() and + not exists(s.toInt()) + ) + } + + /** + * Holds if `e` is an access to the size of a container (`string`, `Array`, + * `IEnumerable`, or `ICollection`). + */ + predicate containerSizeAccess(Expr e) { + exists(Property p | p = e.(PropertyAccess).getTarget() | + propertyOverrides(p, "System.Collections.Generic.IEnumerable<>", "Count") or + propertyOverrides(p, "System.Collections.ICollection", "Count") or + propertyOverrides(p, "System.String", "Length") or + propertyOverrides(p, "System.Array", "Length") + ) + or + e instanceof CountCall + } + + /** Holds if `e` is by definition strictly positive. */ + predicate positiveExpression(Expr e) { e instanceof SizeofExpr } + + abstract class NumericOrCharType extends Type { } + + class UnsignedNumericType extends NumericOrCharType { + UnsignedNumericType() { + this instanceof CharType + or + this instanceof UnsignedIntegralType + or + this instanceof PointerType + or + this instanceof Enum and this.(Enum).getUnderlyingType() instanceof UnsignedIntegralType + } + } + + class SignedNumericType extends NumericOrCharType { + SignedNumericType() { + this instanceof SignedIntegralType + or + this instanceof FloatingPointType + or + this instanceof DecimalType + or + this instanceof Enum and this.(Enum).getUnderlyingType() instanceof SignedIntegralType + } + } + + /** Returns the underlying variable update of the explicit SSA variable `v`. */ + AssignableDefinition getExplicitSsaAssignment(Ssa::ExplicitDefinition v) { + result = v.getADefinition() + } + + /** Returns the assignment of the variable update `def`. */ + Expr getExprFromSsaAssignment(AssignableDefinition def) { result = def.getSource() } + + /** Holds if `def` can have any sign. */ + predicate explicitSsaDefWithAnySign(AssignableDefinition def) { + not exists(def.getSource()) and + not def.getElement() instanceof MutatorOperation + } + + /** Returns the operand of the operation if `def` is a decrement. */ + Expr getDecrementOperand(AssignableDefinition def) { + result = def.getElement().(DecrementOperation).getOperand() + } + + /** Returns the operand of the operation if `def` is an increment. */ + Expr getIncrementOperand(AssignableDefinition def) { + result = def.getElement().(IncrementOperation).getOperand() + } + + /** Gets the variable underlying the implicit SSA variable `v`. */ + Declaration getImplicitSsaDeclaration(Ssa::ImplicitDefinition v) { + result = v.getSourceVariable().getAssignable() + } + + /** Holds if the variable underlying the implicit SSA variable `v` is not a field. */ + predicate nonFieldImplicitSsaDefinition(Ssa::ImplicitDefinition v) { + not getImplicitSsaDeclaration(v) instanceof Field + } + + /** Returned an expression that is assigned to `f`. */ + Expr getAssignedValueToField(Field f) { + result = f.getAnAssignedValue() or + result = any(AssignOperation a | a.getLValue() = f.getAnAccess()) + } + + /** Holds if `f` can have any sign. */ + predicate fieldWithUnknownSign(Field f) { not f.fromSource() or not f.isEffectivelyPrivate() } + + /** Holds if `f` is accessed in an increment operation. */ + predicate fieldIncrementOperationOperand(Field f) { + any(IncrementOperation inc).getOperand() = f.getAnAccess() + } + + /** Holds if `f` is accessed in a decrement operation. */ + predicate fieldDecrementOperationOperand(Field f) { + any(DecrementOperation dec).getOperand() = f.getAnAccess() + } + + /** Returns possible signs of `f` based on the declaration. */ + pragma[inline] + Sign specificFieldSign(Field f) { not exists(f.getInitializer()) and result = TZero() } + + /** + * Holds if `e` has type `NumericOrCharType`, but the sign of `e` is unknown. + */ + predicate numericExprWithUnknownSign(Expr e) { + e.getType() instanceof NumericOrCharType and + not e = getARead(_) and + not e instanceof FieldAccess and + not e instanceof TypeAccess and + // The expression types that are listed here are the ones handled in `specificSubExprSign`. + // Keep them in sync. + not e instanceof AssignExpr and + not e instanceof AssignOperation and + not e instanceof UnaryPlusExpr and + not e instanceof PostIncrExpr and + not e instanceof PostDecrExpr and + not e instanceof PreIncrExpr and + not e instanceof PreDecrExpr and + not e instanceof UnaryMinusExpr and + not e instanceof ComplementExpr and + not e instanceof AddExpr and + not e instanceof SubExpr and + not e instanceof MulExpr and + not e instanceof DivExpr and + not e instanceof RemExpr and + not e instanceof BitwiseAndExpr and + not e instanceof BitwiseOrExpr and + not e instanceof BitwiseXorExpr and + not e instanceof LShiftExpr and + not e instanceof RShiftExpr and + not e instanceof ConditionalExpr and + not e instanceof RefExpr and + not e instanceof LocalVariableDeclAndInitExpr and + not e instanceof SwitchCaseExpr and + not e instanceof CastExpr and + not e instanceof SwitchExpr and + not e instanceof NullCoalescingExpr + } + + /** Returns a sub expression of `e` for expression types where the sign depends on the child. */ + Expr getASubExprWithSameSign(Expr e) { + result = e.(AssignExpr).getRValue() or + result = e.(AssignOperation).getExpandedAssignment() or + result = e.(UnaryPlusExpr).getOperand() or + result = e.(PostIncrExpr).getOperand() or + result = e.(PostDecrExpr).getOperand() or + result = e.(ConditionalExpr).getAChild() or + result = e.(NullCoalescingExpr).getAChild() or + result = e.(SwitchExpr).getACase().getBody() or + result = e.(SwitchCaseExpr).getBody() or + result = e.(LocalVariableDeclAndInitExpr).getInitializer() or + result = e.(RefExpr).getExpr() or + result = e.(CastExpr).getExpr() + } + + Expr getARead(Ssa::Definition v) { result = v.getARead() } + + Field getField(FieldAccess fa) { result = fa.getTarget() } + + Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode().getElement() } + + Guard getComparisonGuard(ComparisonExpr ce) { result = ce.getExpr() } + + /** A relational comparison */ + class ComparisonExpr extends ComparisonTest { + private boolean strict; + + ComparisonExpr() { + this.getComparisonKind() = + any(ComparisonKind ck | + ck.isLessThan() and strict = true + or + ck.isLessThanEquals() and + strict = false + ) + } + + /** + * Gets the operand on the "greater" (or "greater-or-equal") side + * of this relational expression, that is, the side that is larger + * if the overall expression evaluates to `true`; for example on + * `x <= 20` this is the `20`, and on `y > 0` it is `y`. + */ + Expr getGreaterOperand() { result = this.getSecondArgument() } + + /** + * Gets the operand on the "lesser" (or "lesser-or-equal") side + * of this relational expression, that is, the side that is smaller + * if the overall expression evaluates to `true`; for example on + * `x <= 20` this is `x`, and on `y > 0` it is the `0`. + */ + Expr getLesserOperand() { result = this.getFirstArgument() } + + /** Holds if this comparison is strict, i.e. `<` or `>`. */ + predicate isStrict() { strict = true } + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll new file mode 100644 index 000000000000..558ecd1b88b3 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll @@ -0,0 +1,57 @@ +/** + * Provides classes for representing a position at which an SSA variable is read. + */ + +private import SsaReadPositionSpecific + +private newtype TSsaReadPosition = + TSsaReadPositionBlock(BasicBlock bb) { bb = getAReadBasicBlock(_) } or + TSsaReadPositionPhiInputEdge(BasicBlock bbOrig, BasicBlock bbPhi) { + exists(SsaPhiNode phi | phi.hasInputFromBlock(_, bbOrig) and bbPhi = phi.getBasicBlock()) + } + +/** + * A position at which an SSA variable is read. This includes both ordinary + * reads occurring in basic blocks and input to phi nodes occurring along an + * edge between two basic blocks. + */ +class SsaReadPosition extends TSsaReadPosition { + /** Holds if `v` is read at this position. */ + abstract predicate hasReadOfVar(SsaVariable v); + + /** Gets a textual representation of this SSA read position. */ + abstract string toString(); +} + +/** A basic block in which an SSA variable is read. */ +class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock { + /** Gets the basic block corresponding to this position. */ + BasicBlock getBlock() { this = TSsaReadPositionBlock(result) } + + override predicate hasReadOfVar(SsaVariable v) { getBlock() = getAReadBasicBlock(v) } + + override string toString() { result = "block" } +} + +/** + * An edge between two basic blocks where the latter block has an SSA phi + * definition. The edge therefore has a read of an SSA variable serving as the + * input to the phi node. + */ +class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiInputEdge { + /** Gets the source of the edge. */ + BasicBlock getOrigBlock() { this = TSsaReadPositionPhiInputEdge(result, _) } + + /** Gets the target of the edge. */ + BasicBlock getPhiBlock() { this = TSsaReadPositionPhiInputEdge(_, result) } + + override predicate hasReadOfVar(SsaVariable v) { this.phiInput(_, v) } + + /** Holds if `inp` is an input to `phi` along this edge. */ + predicate phiInput(SsaPhiNode phi, SsaVariable inp) { + phi.hasInputFromBlock(inp, getOrigBlock()) and + getPhiBlock() = phi.getBasicBlock() + } + + override string toString() { result = "edge" } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll new file mode 100644 index 000000000000..d7df9781b2ae --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll @@ -0,0 +1,16 @@ +/** + * Provides C#-specific definitions for use in the `SsaReadPosition`. + */ + +private import csharp + +class SsaVariable = Ssa::Definition; + +class SsaPhiNode = Ssa::PhiNode; + +class BasicBlock = Ssa::BasicBlock; + +/** Gets a basic block in which SSA variable `v` is read. */ +BasicBlock getAReadBasicBlock(SsaVariable v) { + result = v.getARead().getAControlFlowNode().getBasicBlock() +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll new file mode 100644 index 000000000000..7f1811c1f371 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll @@ -0,0 +1,42 @@ +/** + * Provides utility predicates to extend the core SSA functionality. + */ + +private import csharp +private import Ssa +private import ConstantUtils + +/** + * Gets an expression that equals `v - delta`. + */ +Expr ssaRead(Definition v, int delta) { + result = v.getARead() and delta = 0 + or + exists(AddExpr add, int d1, ConstantIntegerExpr c | + result = add and + delta = d1 - c.getIntValue() + | + add.getLeftOperand() = ssaRead(v, d1) and add.getRightOperand() = c + or + add.getRightOperand() = ssaRead(v, d1) and add.getLeftOperand() = c + ) + or + exists(SubExpr sub, int d1, ConstantIntegerExpr c | + result = sub and + sub.getLeftOperand() = ssaRead(v, d1) and + sub.getRightOperand() = c and + delta = d1 + c.getIntValue() + ) + or + v.(ExplicitDefinition).getADefinition().getExpr().(PreIncrExpr) = result and delta = 0 + or + v.(ExplicitDefinition).getADefinition().getExpr().(PreDecrExpr) = result and delta = 0 + or + v.(ExplicitDefinition).getADefinition().getExpr().(PostIncrExpr) = result and delta = 1 // x++ === ++x - 1 + or + v.(ExplicitDefinition).getADefinition().getExpr().(PostDecrExpr) = result and delta = -1 // x-- === --x + 1 + or + v.(ExplicitDefinition).getADefinition().getExpr().(Assignment) = result and delta = 0 + or + result.(AssignExpr).getRValue() = ssaRead(v, delta) +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll index 0f0607662e90..b509fad9cd20 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll @@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private * To create a configuration, extend this class with a subclass whose * characteristic predicate is a unique singleton string. For example, write * - * ``` + * ```ql * class MyAnalysisConfiguration extends TaintTracking::Configuration { * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } * // Override `isSource` and `isSink`. @@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private * Then, to query whether there is flow between some `source` and `sink`, * write * - * ``` + * ```ql * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) * ``` * @@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration { final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) or - defaultTaintBarrier(node) + defaultTaintSanitizer(node) } - /** Holds if data flow into `node` is prohibited. */ + /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } - /** Holds if data flow out of `node` is prohibited. */ + /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } - /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll index 3e74eb4ff45a..925ee3e97445 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll @@ -30,6 +30,26 @@ class DispatchCall extends Internal::TDispatchCall { /** Gets a dynamic (run-time) target of this call, if any. */ RuntimeCallable getADynamicTarget() { result = Internal::getADynamicTarget(this) } + + /** + * Holds if a call context may limit the set of viable source declaration + * run-time targets of this call. + * + * This is the case if the qualifier is either a `this` access or a parameter + * access, as the corresponding qualifier/argument in the call context may + * have a more precise type. + */ + predicate mayBenefitFromCallContext() { Internal::mayBenefitFromCallContext(this) } + + /** + * Gets a dynamic (run-time) target of this call in call context `ctx`, if any. + * + * This predicate is restricted to calls for which `mayBenefitFromCallContext()` + * holds. + */ + RuntimeCallable getADynamicTargetInCallContext(DispatchCall ctx) { + result = Internal::getADynamicTargetInCallContext(this, ctx) + } } /** Internal implementation details. */ @@ -40,6 +60,7 @@ private module Internal { private import semmle.code.csharp.dataflow.internal.Steps private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.system.Reflection + private import semmle.code.csharp.dataflow.internal.BaseSSA cached private module Cached { @@ -90,6 +111,16 @@ private module Internal { RuntimeCallable getADynamicTarget(DispatchCall dc) { result = dc.(DispatchCallImpl).getADynamicTarget() } + + cached + predicate mayBenefitFromCallContext(DispatchMethodOrAccessorCall dc) { + dc.mayBenefitFromCallContext(_, _) + } + + cached + RuntimeCallable getADynamicTargetInCallContext(DispatchMethodOrAccessorCall dc, DispatchCall ctx) { + result = dc.getADynamicTargetInCallContext(ctx) + } } import Cached @@ -190,6 +221,17 @@ private module Internal { abstract RuntimeCallable getADynamicTarget(); } + /** A non-constructed overridable callable. */ + private class NonConstructedOverridableCallable extends OverridableCallable { + NonConstructedOverridableCallable() { not this instanceof ConstructedMethod } + + OverridableCallable getAConstructingCallableOrSelf() { + result = this + or + result = this.(UnboundGenericMethod).getAConstructedGeneric() + } + } + pragma[noinline] private predicate hasOverrider(OverridableCallable oc, ValueOrRefType t) { exists(oc.getAnOverrider(t)) @@ -223,12 +265,13 @@ private module Internal { private predicate hasQualifierTypeOverridden0(ValueOrRefType t, DispatchMethodOrAccessorCall call) { hasOverrider(_, t) and ( - exists(Type t0 | t0 = getAPossibleType(call.getQualifier(), false) | - t = t0 - or - Unification::subsumes(t0, t) + exists(Type t0, Type t1 | + t0 = getAPossibleType(call.getQualifier(), false) and + t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()] + | + t = t1 or - t = t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType() + Unification::subsumes(t1, t) ) or constrainedTypeParameterQualifierTypeSubsumes(t, @@ -249,15 +292,257 @@ private module Internal { abstract private class DispatchMethodOrAccessorCall extends DispatchCallImpl { pragma[nomagic] - predicate hasQualifierTypeInherited(SourceDeclarationType t) { - t = getAPossibleType(this.getQualifier(), _).getSourceDeclaration() - } + predicate hasQualifierTypeInherited(Type t) { t = getAPossibleType(this.getQualifier(), _) } pragma[nomagic] predicate hasQualifierTypeOverridden(ValueOrRefType t, OverridableCallable c) { hasQualifierTypeOverridden0(t, this) and hasCallable(any(OverridableCallable oc | hasQualifierTypeOverridden1(oc, this)), t, c) } + + /** + * Holds if a call context may limit the set of viable source declaration + * run-time targets of this call. + * + * This is the case if the qualifier is either a `this` access or a parameter + * access, as the corresponding qualifier/argument in the call context may + * have a more precise type. + */ + predicate mayBenefitFromCallContext(Callable c, int i) { + 1 < strictcount(this.getADynamicTarget().getSourceDeclaration()) and + c = this.getCall().getEnclosingCallable().getSourceDeclaration() and + ( + exists(AssignableDefinitions::ImplicitParameterDefinition pdef, Parameter p | + this.getQualifier() = BaseSsa::getARead(pdef, p) and + p.getPosition() = i and + c.getAParameter() = p and + not p.isParams() + ) + or + i = -1 and + this.getQualifier() instanceof ThisAccess + ) + } + + /** + * Holds if the call `ctx` might act as a context that improves the set of + * dispatch targets of this call, depending on the type of the `i`th argument + * of `ctx`. + */ + pragma[nomagic] + private predicate relevantContext(DispatchCall ctx, int i) { + this.mayBenefitFromCallContext(ctx.getADynamicTarget().getSourceDeclaration(), i) + } + + /** + * Holds if the argument of `ctx`, which is passed for the parameter that is + * accessed in the qualifier of this call, has type `t` and `ctx` is a relevant + * call context. + */ + private predicate contextArgHasType(DispatchCall ctx, Type t, boolean isExact) { + exists(Expr arg, int i | + this.relevantContext(ctx, i) and + t = getAPossibleType(arg, isExact) + | + ctx.getArgument(i) = arg + or + ctx.getQualifier() = arg and + i = -1 + ) + } + + pragma[nomagic] + private Callable getASubsumedStaticTarget0(Type t) { + exists(Callable staticTarget, Type declType | + staticTarget = this.getAStaticTarget() and + declType = staticTarget.getDeclaringType() and + result = staticTarget.getSourceDeclaration() and + Unification::subsumes(declType, t) + ) + } + + /** + * Gets a callable whose source declaration matches the source declaration of + * some static target `target`, and whose declaring type is subsumed by the + * declaring type of `target`. + */ + pragma[nomagic] + private Callable getASubsumedStaticTarget() { + result = this.getAStaticTarget() + or + result.getSourceDeclaration() = this.getASubsumedStaticTarget0(result.getDeclaringType()) + } + + /** + * Gets a callable inherited by (or defined in) the qualifier type of this + * call that overrides (or equals) a static target of this call. + * + * Example: + * + * ```csharp + * class A + * { + * public virtual void M() { } + * } + * + * class B : A + * { + * public override void M() { } + * } + * + * class C : B { } + * + * class D + * { + * void CallM() + * { + * A x = new A(); + * x.M(); + * x = new B(); + * x.M(); + * x = new C(); + * x.M(); + * } + * } + * ``` + * + * The static target is `A.M` in all three calls on lines 14, 16, and 18, + * but the methods inherited by the actual qualifier types are `A.M`, + * `B.M`, and `B.M`, respectively. + */ + private RuntimeCallable getAViableInherited() { + exists(NonConstructedOverridableCallable c, Type t | this.hasQualifierTypeInherited(t) | + this.getASubsumedStaticTarget() = c.getAConstructingCallableOrSelf() and + result = c.getInherited(t) + or + t instanceof TypeParameter and + this.getAStaticTarget() = c.getAConstructingCallableOrSelf() and + result = c + ) + } + + /** + * Gets a callable that is defined in a subtype of the qualifier type of this + * call, and which overrides a static target of this call. + * + * Example: + * + * ```csharp + * class A + * { + * public virtual void M() { } + * } + * + * class B : A + * { + * public override void M() { } + * } + * + * class C : B + * { + * public override void M() { } + * } + * + * class D + * { + * void CallM() + * { + * A x = new A(); + * x.M(); + * x = new B(); + * x.M(); + * x = new C(); + * x.M(); + * } + * } + * ``` + * + * The static target is `A.M` in all three calls on lines 16, 18, and 20, + * but the methods overriding the static targets in subtypes of the actual + * qualifier types are `B.M` and `C.M`, `C.M`, and none, respectively. + */ + private RuntimeCallable getAViableOverrider() { + exists(ValueOrRefType t, NonConstructedOverridableCallable c | + this.hasQualifierTypeOverridden(t, c.getAConstructingCallableOrSelf()) and + result = c.getAnOverrider(t) + ) + } + + override RuntimeCallable getADynamicTarget() { + result = getAViableInherited() + or + result = getAViableOverrider() + or + // Simple case: target method cannot be overridden + result = getAStaticTarget() and + not result instanceof OverridableCallable + } + + pragma[nomagic] + private RuntimeCallable getAViableInheritedInCallContext0(ValueOrRefType t) { + this.contextArgHasType(_, t, _) and + result = this.getADynamicTarget() + } + + pragma[nomagic] + private RuntimeCallable getAViableInheritedInCallContext1( + NonConstructedOverridableCallable c, ValueOrRefType t + ) { + result = this.getAViableInheritedInCallContext0(t) and + result = c.getInherited(t) + } + + pragma[nomagic] + private RuntimeCallable getAViableInheritedInCallContext(DispatchCall ctx) { + exists(Type t, NonConstructedOverridableCallable c | this.contextArgHasType(ctx, t, _) | + this.getASubsumedStaticTarget() = c.getAConstructingCallableOrSelf() and + result = this.getAViableInheritedInCallContext1(c, t) + or + t instanceof TypeParameter and + this.getAStaticTarget() = c.getAConstructingCallableOrSelf() and + result = c + ) + } + + pragma[nomagic] + private RuntimeCallable getAViableOverriderInCallContext0( + NonConstructedOverridableCallable c, ValueOrRefType t + ) { + result = this.getAViableOverrider() and + this.contextArgHasType(_, _, false) and + result = c.getAnOverrider(t) + } + + pragma[nomagic] + private RuntimeCallable getAViableOverriderInCallContext1( + NonConstructedOverridableCallable c, DispatchCall ctx + ) { + exists(ValueOrRefType t | + result = this.getAViableOverriderInCallContext0(c, t) and + exists(Type t0, Type t1 | + this.contextArgHasType(ctx, t0, false) and + t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()] + | + t = t1 + or + Unification::subsumes(t1, t) + ) + ) + } + + pragma[nomagic] + private RuntimeCallable getAViableOverriderInCallContext(DispatchCall ctx) { + exists(NonConstructedOverridableCallable c | + result = this.getAViableOverriderInCallContext1(c, ctx) and + this.getAStaticTarget() = c.getAConstructingCallableOrSelf() + ) + } + + RuntimeCallable getADynamicTargetInCallContext(DispatchCall ctx) { + result = this.getAViableInheritedInCallContext(ctx) + or + result = this.getAViableOverriderInCallContext(ctx) + } } private class DynamicFieldOrProperty extends Assignable { @@ -386,6 +671,8 @@ private module Internal { .getQualifier() or this = any(DispatchCallImpl c).getQualifier() + or + this = any(DispatchCallImpl c).getArgument(_) } Source getASource() { stepTC(this, result) } @@ -430,109 +717,8 @@ private module Internal { override Expr getQualifier() { result = getCall().getQualifier() } override Method getAStaticTarget() { result = getCall().getTarget() } - - override RuntimeMethod getADynamicTarget() { - result = getViableInherited() - or - result = getAViableOverrider() - or - // Simple case: target method cannot be overridden - result = getAStaticTarget() and - not result instanceof OverridableMethod - } - - /** - * Gets the (unique) instance method inherited by (or defined in) the - * qualifier type of this call that overrides (or equals) the static - * target of this call. - * - * Example: - * - * ``` - * class A { - * public virtual void M() { } - * } - * - * class B : A { - * public override void M() { } - * } - * - * class C : B { } - * - * class D { - * void CallM() { - * A x = new A(); - * x.M(); - * x = new B(); - * x.M(); - * x = new C(); - * x.M(); - * } - * } - * ``` - * - * The static target is `A.M` in all three calls on lines 14, 16, and 18, - * but the methods inherited by the actual qualifier types are `A.M`, - * `B.M`, and `B.M`, respectively. - */ - private RuntimeInstanceMethod getViableInherited() { - exists(NonConstructedOverridableMethod m, SourceDeclarationType t | - this.getAStaticTarget() = m.getAConstructingMethodOrSelf() and - this.hasQualifierTypeInherited(t) - | - result = m.getInherited(t) - or - t instanceof TypeParameter and - result = m - ) - } - - /** - * Gets an instance method that is defined in a subtype of the qualifier - * type of this call, and which overrides the static target of this call. - * - * Example: - * - * ``` - * class A { - * public virtual void M() { } - * } - * - * class B : A { - * public override void M() { } - * } - * - * class C : B { - * public override void M() { } - * } - * - * class D { - * void CallM() { - * A x = new A(); - * x.M(); - * x = new B(); - * x.M(); - * x = new C(); - * x.M(); - * } - * } - * ``` - * - * The static target is `A.M` in all three calls on lines 16, 18, and 20, - * but the methods overriding the static targets in subtypes of the actual - * qualifier types are `B.M` and `C.M`, `C.M`, and none, respectively. - */ - private RuntimeInstanceMethod getAViableOverrider() { - exists(ValueOrRefType t, NonConstructedOverridableMethod m | - this.hasQualifierTypeOverridden(t, m.getAConstructingMethodOrSelf()) and - result = m.getAnOverrider(t) - ) - } } - /** A non-constructed overridable method. */ - private class NonConstructedOverridableMethod extends OverridableMethod, NonConstructedMethod { } - /** * A call to an accessor. * @@ -549,7 +735,7 @@ private module Internal { override Accessor getAStaticTarget() { result = getCall().getTarget() } override RuntimeAccessor getADynamicTarget() { - result = getADynamicTargetCandidate() and + result = DispatchMethodOrAccessorCall.super.getADynamicTarget() and // Calls to accessors may have `dynamic` expression arguments, // so we need to check that the types match forall(Type argumentType, int i | hasDynamicArg(i, argumentType) | @@ -557,16 +743,6 @@ private module Internal { ) } - private RuntimeAccessor getADynamicTargetCandidate() { - result = getViableInherited() - or - result = getAViableOverrider() - or - // Simple case: target accessor cannot be overridden - result = getAStaticTarget() and - not result instanceof OverridableAccessor - } - private predicate hasDynamicArg(int i, Type argumentType) { exists(Expr argument | argument = getArgument(i) and @@ -574,91 +750,6 @@ private module Internal { argumentType = getAPossibleType(argument, _) ) } - - /** - * Gets the (unique) accessor inherited by (or defined in) the qualifier - * type of this call that overrides (or equals) the static target of this - * call. - * - * Example: - * - * ``` - * class A { - * public virtual int P { get => 0; } - * } - * - * class B : A { - * public override int P { get => 1; } - * } - * - * class C : B { } - * - * class D { - * void CallM() { - * A x = new A(); - * x.P; - * x = new B(); - * x.P; - * x = new C(); - * x.P; - * } - * } - * ``` - * - * The static target is `A.get_P` in all three calls on lines 14, 16, and 18, - * but the accessors inherited by the actual qualifier types are `A.get_P`, - * `B.get_P`, and `B.get_P`, respectively. - */ - private RuntimeAccessor getViableInherited() { - exists(OverridableAccessor a, SourceDeclarationType t | - this.getAStaticTarget() = a and - this.hasQualifierTypeInherited(t) and - result = a.getInherited(t) - ) - } - - /** - * Gets an accessor that is defined in a subtype of the qualifier type of - * this call, and which overrides the static target of this call. - * - * Example: - * - * ``` - * class A { - * public virtual int P { get => 0; } - * } - * - * class B : A { - * public override int P { get => 1; } - * } - * - * class C : B { - * public override int P { get => 2; } - * } - * - * class D { - * void CallM() { - * A x = new A(); - * x.P; - * x = new B(); - * x.P; - * x = new C(); - * x.P; - * } - * } - * ``` - * - * The static target is `A.get_P` in all three calls on lines 16, 18, and 20, - * but the accessors overriding the static targets in subtypes of the actual - * qualifier types are `B.get_P` and `C.get_P`, `C.get_P`, and none, - * respectively. - */ - private RuntimeAccessor getAViableOverrider() { - exists(ValueOrRefType t, OverridableAccessor a | - this.hasQualifierTypeOverridden(t, a) and - result = a.getAnOverrider(t) - ) - } } /** A reflection-based call or a call using dynamic types. */ @@ -686,22 +777,25 @@ private module Internal { * For reflection/dynamic calls, unless the type of the qualifier is exact, * all subtypes of the qualifier type must be considered relevant. Example: * - * ``` - * class A { - * public void M() { Console.WriteLine("A"); } + * ```csharp + * class A + * { + * public void M() { Console.WriteLine("A"); } * } * - * class B : A { - * new public void M() { Console.WriteLine("B"); } + * class B : A + * { + * new public void M() { Console.WriteLine("B"); } * } * - * class C { - * void InvokeMDyn(A x) { ((dynamic) x).M(); } + * class C + * { + * void InvokeMDyn(A x) { ((dynamic) x).M(); } * - * void CallM() { - * InvokeMDyn(new A()); // prints "A" - * InvokeMDyn(new B()); // prints "B" - * } + * void CallM() { + * InvokeMDyn(new A()); // prints "A" + * InvokeMDyn(new B()); // prints "B" + * } * } * ``` * diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll b/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll index e8c717b8e097..cfb8c89f7bbf 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/OverridableCallable.qll @@ -38,7 +38,7 @@ class OverridableCallable extends Callable { * * Example: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public void M() { } } @@ -76,7 +76,7 @@ class OverridableCallable extends Callable { * * Note that this is generally *not* equivalent with * - * ``` + * ```ql * result = getAnImplementor() * or * result = getAnImplementor().(OverridableCallable).getAnOverrider+()` @@ -84,7 +84,7 @@ class OverridableCallable extends Callable { * * as the example below illustrates: * - * ``` + * ```csharp * interface I { void M(); } * * class A { public virtual void M() { } } @@ -118,7 +118,7 @@ class OverridableCallable extends Callable { * * Example: * - * ``` + * ```csharp * class C1 { public virtual void M() { } } * * class C2 : C1 { public override void M() { } } @@ -134,10 +134,9 @@ class OverridableCallable extends Callable { * - `C2.M = C2.M.getInherited(C2)`, and * - `C2.M = C2.M.getInherited(C3)`. */ - Callable getInherited(SourceDeclarationType t) { - exists(Callable sourceDecl | result = this.getInherited2(t, sourceDecl) | - hasSourceDeclarationCallable(t, sourceDecl) - ) + Callable getInherited(ValueOrRefType t) { + result = this.getInherited1(t) and + t.hasCallable(result) } private Callable getInherited0(ValueOrRefType t) { @@ -150,19 +149,11 @@ class OverridableCallable extends Callable { exists(ValueOrRefType mid | result = this.getInherited0(mid) | t = mid.getASubType()) } - private Callable getInherited1(SourceDeclarationType t) { - exists(ValueOrRefType t0 | result = getInherited0(t0) | t = t0.getSourceDeclaration()) + private Callable getInherited1(ValueOrRefType t) { + result = getInherited0(t) or // An interface implementation - exists(ValueOrRefType s | - result = getAnImplementorSubType(s) and - t = s.getSourceDeclaration() - ) - } - - private Callable getInherited2(SourceDeclarationType t, Callable sourceDecl) { - result = this.getInherited1(t) and - sourceDecl = result.getSourceDeclaration() + result = getAnImplementorSubType(t) } pragma[noinline] @@ -218,11 +209,6 @@ class OverridableCallable extends Callable { } } -pragma[noinline] -private predicate hasSourceDeclarationCallable(ValueOrRefType t, Callable sourceDecl) { - exists(Callable c | t.hasCallable(c) | sourceDecl = c.getSourceDeclaration()) -} - /** An overridable method. */ class OverridableMethod extends Method, OverridableCallable { override Method getAnOverrider() { result = Method.super.getAnOverrider() } @@ -231,7 +217,7 @@ class OverridableMethod extends Method, OverridableCallable { override Method getAnUltimateImplementor() { result = Method.super.getAnUltimateImplementor() } - override Method getInherited(SourceDeclarationType t) { + override Method getInherited(ValueOrRefType t) { result = OverridableCallable.super.getInherited(t) } @@ -278,7 +264,7 @@ class OverridableAccessor extends Accessor, OverridableCallable { ) } - override Accessor getInherited(SourceDeclarationType t) { + override Accessor getInherited(ValueOrRefType t) { result = OverridableCallable.super.getInherited(t) } diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/RuntimeCallable.qll b/csharp/ql/src/semmle/code/csharp/dispatch/RuntimeCallable.qll index a6d8a0ac9fac..22758fce4abd 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/RuntimeCallable.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/RuntimeCallable.qll @@ -26,6 +26,7 @@ class RuntimeMethod extends RuntimeCallable { this instanceof CIL::Method } + /** Holds if the method is `static`. */ predicate isStatic() { this.(Method).isStatic() or this.(CIL::Method).isStatic() } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Access.qll b/csharp/ql/src/semmle/code/csharp/exprs/Access.qll index 842c374ecd1a..ab3ea1820562 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Access.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Access.qll @@ -51,7 +51,7 @@ private module AccessImpl { /** * A `this` access, for example `this` on line 5 in * - * ``` + * ```csharp * class C { * int Count; * @@ -64,7 +64,7 @@ private module AccessImpl { * Note that a `this` access may be implicit, for example the implicit `this` * qualifier on line 5 in * - * ``` + * ```csharp * class C { * int Count; * @@ -78,12 +78,14 @@ class ThisAccess extends Access, @this_access_expr { override Class getTarget() { result = this.getEnclosingCallable().getDeclaringType() } override string toString() { result = "this access" } + + override string getAPrimaryQlClass() { result = "ThisAccess" } } /** * A `base` access, for example `base` on line 2 in * - * ``` + * ```csharp * public override void Dispose() { * base.Dispose(); * ... @@ -96,6 +98,8 @@ class BaseAccess extends Access, @base_access_expr { } override string toString() { result = "base access" } + + override string getAPrimaryQlClass() { result = "BaseAccess" } } /** @@ -211,7 +215,7 @@ class LocalScopeVariableWrite extends LocalScopeVariableAccess, VariableWrite { /** * An access to a parameter, for example the access to `p` on line 2 in * - * ``` + * ```csharp * int M(int p) { * return -p; * } @@ -221,13 +225,15 @@ class ParameterAccess extends LocalScopeVariableAccess, @parameter_access_expr { override Parameter getTarget() { expr_access(this, result) } override string toString() { result = "access to parameter " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "ParameterAccess" } } /** * An access to a parameter that reads the underlying value, for example * the access to `p` on line 2 in * - * ``` + * ```csharp * int M(int p) { * return -p; * } @@ -245,7 +251,7 @@ class ParameterRead extends ParameterAccess, LocalScopeVariableRead { * An access to a parameter that updates the underlying value, for example * the access to `p` on line 2 in * - * ``` + * ```csharp * int M(int p) { * p = 1; * return p; @@ -257,7 +263,7 @@ class ParameterWrite extends ParameterAccess, VariableWrite { } /** * An access to a local variable, for example the access to `x` on line 3 in * - * ``` + * ```csharp * int M(int p) { * var x = -p; * return x; @@ -273,13 +279,15 @@ class LocalVariableAccess extends LocalScopeVariableAccess, @local_variable_acce not this instanceof LocalVariableDeclExpr and result = "access to local variable " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "LocalVariableAccess" } } /** * An access to a local variable that reads the underlying value, for example * the access to `x` on line 3 in * - * ``` + * ```csharp * int M(int p) { * var x = -p; * return x; @@ -298,7 +306,7 @@ class LocalVariableRead extends LocalVariableAccess, LocalScopeVariableRead { * An access to a local variable that updates the underlying value, for example * the access to `x` on line 3 in * - * ``` + * ```csharp * int M(int p) { * int x; * x = -p; @@ -311,7 +319,7 @@ class LocalVariableWrite extends LocalVariableAccess, VariableWrite { } /** * An access to a field, for example the access to `F` on line 5 in * - * ``` + * ```csharp * class C { * int F = 0; * @@ -325,13 +333,15 @@ class FieldAccess extends AssignableMemberAccess, VariableAccess, @field_access_ override Field getTarget() { expr_access(this, result) } override string toString() { result = "access to field " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "FieldAccess" } } /** * An access to a field that reads the underlying value, for example the * access to `F` on line 5 in * - * ``` + * ```csharp * class C { * int F = 0; * @@ -347,7 +357,7 @@ class FieldRead extends FieldAccess, VariableRead { } * An access to a field that updates the underlying value, for example the * access to `F` on line 5 in * - * ``` + * ```csharp * class C { * int F = 0; * @@ -362,7 +372,7 @@ class FieldWrite extends FieldAccess, VariableWrite { } /** * An access to a member (field), for example the access to `F` on line 5 in * - * ``` + * ```csharp * class C { * const int F = 0; * @@ -378,6 +388,8 @@ class MemberConstantAccess extends FieldAccess { override MemberConstant getTarget() { expr_access(this, result) } override string toString() { result = "access to constant " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "MemberConstantAccess" } } /** @@ -399,7 +411,7 @@ library class PropertyAccessExpr extends Expr, @property_access_expr { /** * An access to a property, for example the access to `P` on line 5 in * - * ``` + * ```csharp * class C { * int P { get; private set; } * @@ -417,7 +429,7 @@ class PropertyAccess extends AssignableMemberAccess, PropertyAccessExpr { * An access to a property that reads the underlying value, for example * the access to `P` on line 5 in * - * ``` + * ```csharp * class C { * int P { get; private set; } * @@ -437,7 +449,7 @@ class PropertyRead extends PropertyAccess, AssignableRead { * An access to a property that updates the underlying value, for example the * access to `P` on line 5 in * - * ``` + * ```csharp * class C { * int P { get; private set; } * @@ -453,7 +465,7 @@ class PropertyWrite extends PropertyAccess, AssignableWrite { } * An access to a trivial property - a property with a default getter and * setter. For example, the access to `P` on line 5 in * - * ``` + * ```csharp * class C { * int P { get; private set; } * @@ -471,7 +483,7 @@ class TrivialPropertyAccess extends PropertyAccess { * An access to a virtual property - a property that is virtual or defined in * an interface. For example, the access to `P` on line 5 in * - * ``` + * ```csharp * class C { * virtual int P { get; private set; } * @@ -534,7 +546,7 @@ library class IndexerAccessExpr extends Expr, @indexer_access_expr { /** * An access to an indexer, for example the access to `c` on line 5 in * - * ``` + * ```csharp * class C { * public string this[int i] { ... } * @@ -556,7 +568,7 @@ class IndexerAccess extends AssignableMemberAccess, ElementAccess, IndexerAccess * An access to an indexer that reads the underlying value, for example the * access to `c` on line 5 in * - * ``` + * ```csharp * class C { * public string this[int i] { ... } * @@ -576,7 +588,7 @@ class IndexerRead extends IndexerAccess, ElementRead { * An access to an indexer that updates the underlying value, for example the * access to `c` on line 5 in * - * ``` + * ```csharp * class C { * public string this[int i] { ... } * @@ -592,7 +604,7 @@ class IndexerWrite extends IndexerAccess, ElementWrite { } * An access to a virtual indexer - an indexer that is virtual or defined in * an interface. For example, the access to `c` on line 5 in * - * ``` + * ```csharp * class C { * public virtual string this[int i] { ... } * @@ -600,6 +612,7 @@ class IndexerWrite extends IndexerAccess, ElementWrite { } * return c[0]; * } * } + * ``` */ class VirtualIndexerAccess extends IndexerAccess { VirtualIndexerAccess() { targetIsOverridableOrImplementable() } @@ -620,7 +633,7 @@ library class EventAccessExpr extends Expr, @event_access_expr { * An access to an event, for example the accesses to `Click` on lines * 7 and 8 in * - * ``` + * ```csharp * class C { * public delegate void EventHandler(object sender, object e); * @@ -635,13 +648,15 @@ library class EventAccessExpr extends Expr, @event_access_expr { */ class EventAccess extends AssignableMemberAccess, EventAccessExpr { override Event getTarget() { result = getEvent() } + + override string getAPrimaryQlClass() { result = "EventAccess" } } /** * An access to an event that reads the underlying value, for example the * accesses to `Click` on lines 7 and 8 in * - * ``` + * ```csharp * class C { * public delegate void EventHandler(object sender, object e); * @@ -660,7 +675,7 @@ class EventRead extends EventAccess, AssignableRead { } * An access to an event that updates the underlying value, for example the * access to `Click` on line 7 in * - * ``` + * ```csharp * class C { * public delegate void EventHandler(object sender, object e); * @@ -678,7 +693,7 @@ class EventWrite extends EventAccess, AssignableWrite { } * An access to a virtual event - an event that is virtual or defined in * an interface. For example, the accesses to `Click` on lines 7 and 8 in * - * ``` + * ```csharp * class C { * public delegate void EventHandler(object sender, object e); * @@ -708,7 +723,7 @@ class CallableAccess extends Access, @method_access_expr { /** * An access to a method, for example the access to `Filter` on line 5 in * - * ``` + * ```csharp * class C { * bool Filter(string s) { ... } * @@ -726,12 +741,14 @@ class MethodAccess extends MemberAccess, CallableAccess { override Method getTarget() { expr_access(this, result) } override string toString() { result = "access to method " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "MethodAccess" } } /** * An access to a local function, for example the access to `Filter` on line 4 in * - * ``` + * ```csharp * class C { * public IEnumerable DoFilter(IEnumerable list) { * bool Filter(string s) { ... }; @@ -749,13 +766,15 @@ class LocalFunctionAccess extends CallableAccess { override LocalFunction getTarget() { expr_access(this, result) } override string toString() { result = "access to local function " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "LocalFunctionAccess" } } /** * An access to a virtual method - a method that is virtual or defined in * an interface. For example, the access to `Filter` on line 5 in * - * ``` + * ```csharp * class C { * public virtual bool Filter(string s) { ... } * @@ -774,7 +793,7 @@ class VirtualMethodAccess extends MethodAccess { /** * An access to a type, for example the access to `C` on line 3 in * - * ``` + * ```csharp * class C { * public Type GetCType() { * return typeof(C); @@ -786,12 +805,14 @@ class TypeAccess extends MemberAccess, @type_access_expr { override Type getTarget() { result = this.getType() } override string toString() { result = "access to type " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "TypeAccess" } } /** * An access to an array, for example the access to `args` on line 3 in * - * ``` + * ```csharp * public int FirstOrNegative(params int[] args) { * return args.Length > 0 * ? args[0] @@ -806,13 +827,15 @@ class ArrayAccess extends ElementAccess, @array_access_expr { // Although an array (element) can be assigned a value, there is no // corresponding assignable (`ArrayAccess` does not extend `MemberAccess`) override Assignable getTarget() { none() } + + override string getAPrimaryQlClass() { result = "ArrayAccess" } } /** * An access to an array that reads the underlying value, for example * the access to `a` on line 2 in * - * ``` + * ```csharp * public string Get(string[] a, int i) { * return a[i]; * } @@ -824,7 +847,7 @@ class ArrayRead extends ArrayAccess, ElementRead { } * An access to an array that updates the underlying value, for example * the access to `a` on line 2 in * - * ``` + * ```csharp * public void Set(string[] a, int i, string s) { * a[i] = s; * } @@ -839,4 +862,6 @@ class NamespaceAccess extends Access, @namespace_access_expr { override Namespace getTarget() { expr_access(this, result) } override string toString() { result = "access to namespace " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "NamespaceAccess" } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/ArithmeticOperation.qll b/csharp/ql/src/semmle/code/csharp/exprs/ArithmeticOperation.qll index 38033b9a9555..ac98c0eafcf8 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/ArithmeticOperation.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/ArithmeticOperation.qll @@ -27,6 +27,8 @@ class UnaryArithmeticOperation extends ArithmeticOperation, UnaryOperation, @un_ */ class UnaryMinusExpr extends UnaryArithmeticOperation, @minus_expr { override string getOperator() { result = "-" } + + override string getAPrimaryQlClass() { result = "UnaryMinusExpr" } } /** @@ -34,6 +36,8 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @minus_expr { */ class UnaryPlusExpr extends UnaryArithmeticOperation, @plus_expr { override string getOperator() { result = "+" } + + override string getAPrimaryQlClass() { result = "UnaryPlusExpr" } } /** @@ -61,18 +65,24 @@ class DecrementOperation extends MutatorOperation, @decr_op_expr { /** * A prefix increment operation, for example `++x`. */ -class PreIncrExpr extends IncrementOperation, @pre_incr_expr { } +class PreIncrExpr extends IncrementOperation, @pre_incr_expr { + override string getAPrimaryQlClass() { result = "PreIncrExpr" } +} /** * A prefix decrement operation, for example `--x`. */ -class PreDecrExpr extends DecrementOperation, @pre_decr_expr { } +class PreDecrExpr extends DecrementOperation, @pre_decr_expr { + override string getAPrimaryQlClass() { result = "PreDecrExpr" } +} /** * A postfix increment operation, for example `x++`. */ class PostIncrExpr extends IncrementOperation, @post_incr_expr { override string toString() { result = "..." + this.getOperator() } + + override string getAPrimaryQlClass() { result = "PostIncrExpr" } } /** @@ -80,6 +90,8 @@ class PostIncrExpr extends IncrementOperation, @post_incr_expr { */ class PostDecrExpr extends DecrementOperation, @post_decr_expr { override string toString() { result = "..." + this.getOperator() } + + override string getAPrimaryQlClass() { result = "PostDecrExpr" } } /** @@ -97,6 +109,8 @@ class BinaryArithmeticOperation extends ArithmeticOperation, BinaryOperation, @b */ class AddExpr extends BinaryArithmeticOperation, @add_expr { override string getOperator() { result = "+" } + + override string getAPrimaryQlClass() { result = "AddExpr" } } /** @@ -104,6 +118,8 @@ class AddExpr extends BinaryArithmeticOperation, @add_expr { */ class SubExpr extends BinaryArithmeticOperation, @sub_expr { override string getOperator() { result = "-" } + + override string getAPrimaryQlClass() { result = "SubExpr" } } /** @@ -111,6 +127,8 @@ class SubExpr extends BinaryArithmeticOperation, @sub_expr { */ class MulExpr extends BinaryArithmeticOperation, @mul_expr { override string getOperator() { result = "*" } + + override string getAPrimaryQlClass() { result = "MulExpr" } } /** @@ -124,6 +142,8 @@ class DivExpr extends BinaryArithmeticOperation, @div_expr { /** Gets the denominator of this division operation. */ Expr getDenominator() { result = getRightOperand() } + + override string getAPrimaryQlClass() { result = "DivExpr" } } /** @@ -131,4 +151,6 @@ class DivExpr extends BinaryArithmeticOperation, @div_expr { */ class RemExpr extends BinaryArithmeticOperation, @rem_expr { override string getOperator() { result = "%" } + + override string getAPrimaryQlClass() { result = "RemExpr" } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Assignment.qll b/csharp/ql/src/semmle/code/csharp/exprs/Assignment.qll index fe1a131e46c9..88ef770160a0 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Assignment.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Assignment.qll @@ -43,6 +43,8 @@ class LocalVariableDeclAndInitExpr extends LocalVariableDeclExpr, Assignment { override LocalVariableAccess getLValue() { result = Assignment.super.getLValue() } override string toString() { result = LocalVariableDeclExpr.super.toString() + " = ..." } + + override string getAPrimaryQlClass() { result = "LocalVariableDeclAndInitExpr" } } /** @@ -52,6 +54,8 @@ class AssignExpr extends Assignment, @simple_assign_expr { override string getOperator() { result = "=" } override string toString() { result = "... = ..." } + + override string getAPrimaryQlClass() { result = "AssignExpr" } } /** @@ -101,6 +105,8 @@ class AssignArithmeticOperation extends AssignOperation, @assign_arith_expr { } */ class AssignAddExpr extends AssignArithmeticOperation, @assign_add_expr { override string getOperator() { result = "+=" } + + override string getAPrimaryQlClass() { result = "AssignAddExpr" } } /** @@ -108,6 +114,8 @@ class AssignAddExpr extends AssignArithmeticOperation, @assign_add_expr { */ class AssignSubExpr extends AssignArithmeticOperation, @assign_sub_expr { override string getOperator() { result = "-=" } + + override string getAPrimaryQlClass() { result = "AssignSubExpr" } } /** @@ -115,6 +123,8 @@ class AssignSubExpr extends AssignArithmeticOperation, @assign_sub_expr { */ class AssignMulExpr extends AssignArithmeticOperation, @assign_mul_expr { override string getOperator() { result = "*=" } + + override string getAPrimaryQlClass() { result = "AssignMulExpr" } } /** @@ -122,6 +132,8 @@ class AssignMulExpr extends AssignArithmeticOperation, @assign_mul_expr { */ class AssignDivExpr extends AssignArithmeticOperation, @assign_div_expr { override string getOperator() { result = "/=" } + + override string getAPrimaryQlClass() { result = "AssignDivExpr" } } /** @@ -129,6 +141,8 @@ class AssignDivExpr extends AssignArithmeticOperation, @assign_div_expr { */ class AssignRemExpr extends AssignArithmeticOperation, @assign_rem_expr { override string getOperator() { result = "%=" } + + override string getAPrimaryQlClass() { result = "AssignRemExpr" } } /** @@ -146,6 +160,8 @@ class AssignBitwiseOperation extends AssignOperation, @assign_bitwise_expr { } */ class AssignAndExpr extends AssignBitwiseOperation, @assign_and_expr { override string getOperator() { result = "&=" } + + override string getAPrimaryQlClass() { result = "AssignAndExpr" } } /** @@ -153,6 +169,8 @@ class AssignAndExpr extends AssignBitwiseOperation, @assign_and_expr { */ class AssignOrExpr extends AssignBitwiseOperation, @assign_or_expr { override string getOperator() { result = "|=" } + + override string getAPrimaryQlClass() { result = "AssignOrExpr" } } /** @@ -160,6 +178,8 @@ class AssignOrExpr extends AssignBitwiseOperation, @assign_or_expr { */ class AssignXorExpr extends AssignBitwiseOperation, @assign_xor_expr { override string getOperator() { result = "^=" } + + override string getAPrimaryQlClass() { result = "AssignXorExpr" } } /** @@ -167,6 +187,8 @@ class AssignXorExpr extends AssignBitwiseOperation, @assign_xor_expr { */ class AssignLShiftExpr extends AssignBitwiseOperation, @assign_lshift_expr { override string getOperator() { result = "<<=" } + + override string getAPrimaryQlClass() { result = "AssignLShiftExpr" } } /** @@ -174,6 +196,8 @@ class AssignLShiftExpr extends AssignBitwiseOperation, @assign_lshift_expr { */ class AssignRShiftExpr extends AssignBitwiseOperation, @assign_rshift_expr { override string getOperator() { result = ">>=" } + + override string getAPrimaryQlClass() { result = "AssignRShiftExpr" } } /** @@ -192,7 +216,7 @@ class AddOrRemoveEventExpr extends AssignOperation, @assign_event_expr { /** * An event addition, for example line 9 in * - * ``` + * ```csharp * class A { * public delegate void EventHandler(object sender, object e); * @@ -208,12 +232,14 @@ class AddOrRemoveEventExpr extends AssignOperation, @assign_event_expr { */ class AddEventExpr extends AddOrRemoveEventExpr, @add_event_expr { override string toString() { result = "... += ..." } + + override string getAPrimaryQlClass() { result = "AddEventExpr" } } /** * An event removal, for example line 9 in * - * ``` + * ```csharp * class A { * public delegate void EventHandler(object sender, object e); * @@ -229,6 +255,8 @@ class AddEventExpr extends AddOrRemoveEventExpr, @add_event_expr { */ class RemoveEventExpr extends AddOrRemoveEventExpr, @remove_event_expr { override string toString() { result = "... -= ..." } + + override string getAPrimaryQlClass() { result = "RemoveEventExpr" } } /** @@ -236,4 +264,6 @@ class RemoveEventExpr extends AddOrRemoveEventExpr, @remove_event_expr { */ class AssignCoalesceExpr extends AssignOperation, @assign_coalesce_expr { override string toString() { result = "... ??= ..." } + + override string getAPrimaryQlClass() { result = "AssignCoalesceExpr" } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/BitwiseOperation.qll b/csharp/ql/src/semmle/code/csharp/exprs/BitwiseOperation.qll index c96738b6961f..0ae16bb50656 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/BitwiseOperation.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/BitwiseOperation.qll @@ -23,6 +23,8 @@ class UnaryBitwiseOperation extends BitwiseOperation, UnaryOperation, @un_bit_op */ class ComplementExpr extends UnaryBitwiseOperation, @bit_not_expr { override string getOperator() { result = "~" } + + override string getAPrimaryQlClass() { result = "ComplementExpr" } } /** @@ -40,6 +42,8 @@ class BinaryBitwiseOperation extends BitwiseOperation, BinaryOperation, @bin_bit */ class LShiftExpr extends BinaryBitwiseOperation, @lshift_expr { override string getOperator() { result = "<<" } + + override string getAPrimaryQlClass() { result = "LShiftExpr" } } /** @@ -47,6 +51,8 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshift_expr { */ class RShiftExpr extends BinaryBitwiseOperation, @rshift_expr { override string getOperator() { result = ">>" } + + override string getAPrimaryQlClass() { result = "RShiftExpr" } } /** @@ -54,6 +60,8 @@ class RShiftExpr extends BinaryBitwiseOperation, @rshift_expr { */ class BitwiseAndExpr extends BinaryBitwiseOperation, @bit_and_expr { override string getOperator() { result = "&" } + + override string getAPrimaryQlClass() { result = "BitwiseAndExpr" } } /** @@ -61,6 +69,8 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @bit_and_expr { */ class BitwiseOrExpr extends BinaryBitwiseOperation, @bit_or_expr { override string getOperator() { result = "|" } + + override string getAPrimaryQlClass() { result = "BitwiseOrExpr" } } /** @@ -68,4 +78,6 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @bit_or_expr { */ class BitwiseXorExpr extends BinaryBitwiseOperation, @bit_xor_expr { override string getOperator() { result = "^" } + + override string getAPrimaryQlClass() { result = "BitwiseXorExpr" } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Call.qll b/csharp/ql/src/semmle/code/csharp/exprs/Call.qll index 9e451781c540..90225a9aec3c 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Call.qll @@ -22,7 +22,7 @@ class Call extends DotNet::Call, Expr, @call { * Gets the static (compile-time) target of this call. For example, the * static target of `x.M()` on line 9 is `A.M` in * - * ``` + * ```csharp * class A { * virtual void M() { } * } @@ -65,7 +65,7 @@ class Call extends DotNet::Call, Expr, @call { * on line 5, `o` is not an argument for `M1`'s `args` parameter, while * `new object[] { o }` on line 6 is, in * - * ``` + * ```csharp * class C { * void M1(params object[] args) { } * @@ -143,7 +143,7 @@ class Call extends DotNet::Call, Expr, @call { * Unlike `getTarget()`, this predicate takes reflection/dynamic based calls, * virtual dispatch, and delegate calls into account. Example: * - * ``` + * ```csharp * class A { * virtual void M() { } * } @@ -188,7 +188,7 @@ class Call extends DotNet::Call, Expr, @call { * Unlike `getArgument()`, this predicate takes reflection based calls and named * arguments into account. Example: * - * ``` + * ```csharp * class A { * virtual void M(int first, int second) { } * @@ -261,7 +261,7 @@ class Call extends DotNet::Call, Expr, @call { * of the arguments on lines 4 and 5, respectively, are valid for the parameter * `args` on line 1 in * - * ``` + * ```csharp * void M(params object[] args) { ... } * * void CallM(object[] os, string[] ss, string s) { @@ -280,7 +280,7 @@ private predicate isValidExplicitParamsType(Parameter p, Type t) { /** * A method call, for example `a.M()` on line 5 in * - * ``` + * ```csharp * class A { * void M() { } * @@ -297,6 +297,8 @@ class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invoca override string toString() { result = "call to method " + concat(this.getTarget().getName()) } + override string getAPrimaryQlClass() { result = "MethodCall" } + override Expr getRawArgument(int i) { if exists(getQualifier()) then @@ -310,7 +312,7 @@ class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invoca /** * A call to an extension method, for example lines 5 and 6 in * - * ``` + * ```csharp * static class A { * static void M(this int i) { } * @@ -341,7 +343,7 @@ class ExtensionMethodCall extends MethodCall { * happens to be an extension method, for example the calls on lines 6 and * 7 (but not line 5) in * - * ``` + * ```csharp * static class Extensions { * public static void Ext(int i) { } * @@ -363,7 +365,7 @@ class ExtensionMethodCall extends MethodCall { /** * A virtual method call, for example `a.M()` on line 5 in * - * ``` + * ```csharp * class A { * public virtual void M() { } * @@ -384,7 +386,7 @@ class VirtualMethodCall extends MethodCall { * A constructor initializer call, for example `base()` (line 6) and * `this(0)` (line 8) in * - * ``` + * ```csharp * class A * { * public A() { } @@ -405,6 +407,8 @@ class ConstructorInitializer extends Call, @constructor_init_expr { override string toString() { result = "call to constructor " + this.getTarget().getName() } + override string getAPrimaryQlClass() { result = "ConstructorInitializer" } + private ValueOrRefType getTargetType() { result = this.getTarget().getDeclaringType().getSourceDeclaration() } @@ -417,7 +421,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr { * Holds if this initialier is a `this` initializer, for example `this(0)` * in * - * ``` + * ```csharp * class A * { * A(int i) { } @@ -431,7 +435,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr { * Holds if this initialier is a `base` initializer, for example `base(0)` * in * - * ``` + * ```csharp * class A * { * A(int i) { } @@ -450,7 +454,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr { * the initializer call `base()` on line 7 belongs to the constructor `B` * on line 6 in * - * ``` + * ```csharp * class A * { * public A() { } @@ -475,7 +479,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr { * A call to a user-defined operator, for example `this + other` * on line 7 in * - * ``` + * ```csharp * class A { * public static A operator+(A left, A right) { * return left; @@ -493,13 +497,15 @@ class OperatorCall extends Call, LateBindableExpr, @operator_invocation_expr { override Operator getARuntimeTarget() { result = Call.super.getARuntimeTarget() } override string toString() { result = "call to operator " + this.getTarget().getName() } + + override string getAPrimaryQlClass() { result = "OperatorCall" } } /** * A call to a user-defined mutator operator, for example `a++` on * line 7 in * - * ``` + * ```csharp * class A { * public static A operator++(A a) { * return a; @@ -524,7 +530,7 @@ class MutatorOperatorCall extends OperatorCall { /** * A delegate call, for example `x()` on line 5 in * - * ``` + * ```csharp * class A { * Action X = () => { }; * @@ -581,7 +587,7 @@ class DelegateCall extends Call, @delegate_invocation_expr { * Gets the delegate expression of this delegate call. For example, the * delegate expression of `X()` on line 5 is the access to the field `X` in * - * ``` + * ```csharp * class A { * Action X = () => { }; * @@ -594,6 +600,8 @@ class DelegateCall extends Call, @delegate_invocation_expr { Expr getDelegateExpr() { result = this.getChild(-1) } override string toString() { result = "delegate call" } + + override string getAPrimaryQlClass() { result = "DelegateCall" } } /** @@ -613,7 +621,7 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr { * A call to a property accessor, for example the call to `get_P` on * line 5 in * - * ``` + * ```csharp * class A { * int P { get { return 0; } } * @@ -638,13 +646,15 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr { } override string toString() { result = PropertyAccessExpr.super.toString() } + + override string getAPrimaryQlClass() { result = "PropertyCall" } } /** * A call to an indexer accessor, for example the call to `get_Item` * (defined on line 3) on line 7 in * - * ``` + * ```csharp * class A { * string this[int i] { * get { return i.ToString(); } @@ -673,13 +683,15 @@ class IndexerCall extends AccessorCall, IndexerAccessExpr { } override string toString() { result = IndexerAccessExpr.super.toString() } + + override string getAPrimaryQlClass() { result = "IndexerCall" } } /** * A call to an event accessor, for example the call to `add_Click` * (defined on line 5) on line 12 in * - * ``` + * ```csharp * class A { * public delegate void EventHandler(object sender, object e); * @@ -717,12 +729,14 @@ class EventCall extends AccessorCall, EventAccessExpr { } override string toString() { result = EventAccessExpr.super.toString() } + + override string getAPrimaryQlClass() { result = "EventCall" } } /** * A call to a local function, for example the call `Fac(n)` on line 6 in * - * ``` + * ```csharp * int Choose(int n, int m) { * int Fac(int x) { * return x > 1 ? x * Fac(x - 1) : 1; @@ -736,4 +750,6 @@ class LocalFunctionCall extends Call, @local_function_invocation_expr { override LocalFunction getTarget() { expr_call(this, result) } override string toString() { result = "call to local function " + getTarget().getName() } + + override string getAPrimaryQlClass() { result = "LocalFunctionCall" } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll b/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll index 6ebb8e40ade9..8b94ef5b4d79 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll @@ -23,6 +23,8 @@ class EqualityOperation extends ComparisonOperation, @equality_op_expr { } */ class EQExpr extends EqualityOperation, @eq_expr { override string getOperator() { result = "==" } + + override string getAPrimaryQlClass() { result = "EQExpr" } } /** @@ -30,6 +32,8 @@ class EQExpr extends EqualityOperation, @eq_expr { */ class NEExpr extends EqualityOperation, @ne_expr { override string getOperator() { result = "!=" } + + override string getAPrimaryQlClass() { result = "NEExpr" } } /** @@ -53,6 +57,9 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr { * `x <= 20` this is `x`, and on `y > 0` it is the `0`. */ Expr getLesserOperand() { none() } + + /** Holds if this comparison is strict, i.e. `<` or `>`. */ + predicate isStrict() { this instanceof LTExpr or this instanceof GTExpr } } /** @@ -64,6 +71,8 @@ class GTExpr extends RelationalOperation, @gt_expr { override Expr getGreaterOperand() { result = getLeftOperand() } override Expr getLesserOperand() { result = getRightOperand() } + + override string getAPrimaryQlClass() { result = "GTExpr" } } /** @@ -75,6 +84,8 @@ class LTExpr extends RelationalOperation, @lt_expr { override Expr getGreaterOperand() { result = getRightOperand() } override Expr getLesserOperand() { result = getLeftOperand() } + + override string getAPrimaryQlClass() { result = "LTExpr" } } /** @@ -86,6 +97,8 @@ class GEExpr extends RelationalOperation, @ge_expr { override Expr getGreaterOperand() { result = getLeftOperand() } override Expr getLesserOperand() { result = getRightOperand() } + + override string getAPrimaryQlClass() { result = "GEExpr" } } /** @@ -97,4 +110,6 @@ class LEExpr extends RelationalOperation, @le_expr { override Expr getGreaterOperand() { result = getRightOperand() } override Expr getLesserOperand() { result = getLeftOperand() } + + override string getAPrimaryQlClass() { result = "LEExpr" } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Creation.qll b/csharp/ql/src/semmle/code/csharp/exprs/Creation.qll index f0aebc389d09..42bdcdf1a0d2 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Creation.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Creation.qll @@ -17,7 +17,7 @@ class ObjectOrCollectionInitializer extends Expr, @objectorcollection_init_expr /** * An object initializer, for example `{ X = 0, Y = 1 }` on line 6 in * - * ``` + * ```csharp * class A { * int X; * int Y; @@ -34,7 +34,7 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr * is a member initializer of the object initializer `{ X = 0, Y = 1 }` on * line 6 in * - * ``` + * ```csharp * class A { * int X; * int Y; @@ -52,7 +52,7 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr * `Y = 1` is the second (`i = 1`) member initializer of the object initializer * `{ X = 0, Y = 1 }` on line 6 in * - * ``` + * ```csharp * class A { * int X; * int Y; @@ -73,12 +73,14 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr /** Holds if this object initializer has no member initializers. */ predicate hasNoMemberInitializers() { not exists(this.getAMemberInitializer()) } + + override string getAPrimaryQlClass() { result = "ObjectInitializer" } } /** * A member initializer, for example `X = 0` on line 6 in * - * ``` + * ```csharp * class A { * int X; * int Y; @@ -94,12 +96,14 @@ class MemberInitializer extends AssignExpr { /** Gets the initialized member. */ Member getInitializedMember() { result.getAnAccess() = this.getLValue() } + + override string getAPrimaryQlClass() { result = "MemberInitializer" } } /** * A collection initializer, for example `{ {0, "a"}, {1, "b"} }` in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -111,7 +115,7 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i * Gets an element initializer of this collection initializer, for example the * implicit call to `Add(0, "a")` on line 2 in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -125,7 +129,7 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i * example the second (`i = 1`) element initializer is the implicit call to * `Add(1, "b")` in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -142,13 +146,15 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i /** Holds if this collection initializer has no element initializers. */ predicate hasNoElementInitializers() { not exists(this.getAnElementInitializer()) } + + override string getAPrimaryQlClass() { result = "CollectionInitializer" } } /** * An element initializer, for example the implicit call to `Add(0, "a")` * on line 2 in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -157,12 +163,14 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i */ class ElementInitializer extends MethodCall { ElementInitializer() { this.getParent() instanceof CollectionInitializer } + + override string getAPrimaryQlClass() { result = "ElementInitializer" } } /** * A constructor call, for example `new A()` on line 3 in * - * ``` + * ```csharp * class A { * public static A Create() { * return new A(); @@ -188,7 +196,7 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr { * Gets the object initializer or collection initializer of this constructor * call, if any. For example `{ {0, "a"}, {1, "b"} }` in * - * ``` + * ```csharp * var dict = new Dictionary{ * {0, "a"}, * {1, "b"} @@ -204,13 +212,17 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr { then result = this.getArgument(i) else result = this.getArgument(i - 1) } + + override string getAPrimaryQlClass() { + result = "ObjectCreation" and not this instanceof AnonymousObjectCreation + } } /** * An anonymous constructor call, for example * `new { First = x[0], Last = x[x.Length - 1] }` on line 2 in * - * ``` + * ```csharp * public IEnumerable FirstLast(IEnumerable list) { * return list.Select(x => new { First = x[0], Last = x[x.Length - 1] }). * Select(y => y.First + y.Last); @@ -221,6 +233,8 @@ class AnonymousObjectCreation extends ObjectCreation { AnonymousObjectCreation() { this.getObjectType() instanceof AnonymousClass } override ObjectInitializer getInitializer() { result = this.getChild(-1) } + + override string getAPrimaryQlClass() { result = "AnonymousObjectCreation" } } /** @@ -245,7 +259,7 @@ class DelegateCreation extends Expr, @delegate_creation_expr { /** * An explicit delegate creation, for example `new D(M)` on line 6 in * - * ``` + * ```csharp * class A { * delegate void D(int x); * @@ -255,12 +269,14 @@ class DelegateCreation extends Expr, @delegate_creation_expr { * } * ``` */ -class ExplicitDelegateCreation extends DelegateCreation, @explicit_delegate_creation_expr { } +class ExplicitDelegateCreation extends DelegateCreation, @explicit_delegate_creation_expr { + override string getAPrimaryQlClass() { result = "ExplicitDelegateCreation" } +} /** * An implicit delegate creation, for example the access to `M` on line 6 in * - * ``` + * ```csharp * class A { * delegate void D(int x); * @@ -270,12 +286,14 @@ class ExplicitDelegateCreation extends DelegateCreation, @explicit_delegate_crea * } * ``` */ -class ImplicitDelegateCreation extends DelegateCreation, @implicit_delegate_creation_expr { } +class ImplicitDelegateCreation extends DelegateCreation, @implicit_delegate_creation_expr { + override string getAPrimaryQlClass() { result = "ImplicitDelegateCreation" } +} /** * An array initializer, for example `{ {0, 1}, {2, 3}, {4, 5} }` in * - * ``` + * ```csharp * var ints = new int[,] { * {0, 1}, * {2, 3}, @@ -288,7 +306,7 @@ class ArrayInitializer extends Expr, @array_init_expr { * Gets an element of this array initializer, for example `{0, 1}` on line * 2 (itself an array initializer) in * - * ``` + * ```csharp * var ints = new int[,] { * {0, 1}, * {2, 3}, @@ -302,7 +320,7 @@ class ArrayInitializer extends Expr, @array_init_expr { * Gets the `i`th element of this array initializer, for example the second * (`i = 1`) element is `{2, 3}` on line 3 (itself an array initializer) in * - * ``` + * ```csharp * var ints = new int[,] { * {0, 1}, * {2, 3}, @@ -322,6 +340,8 @@ class ArrayInitializer extends Expr, @array_init_expr { predicate hasNoElements() { not exists(this.getAnElement()) } override string toString() { result = "{ ..., ... }" } + + override string getAPrimaryQlClass() { result = "ArrayInitializer" } } /** @@ -335,7 +355,7 @@ class ArrayCreation extends Expr, @array_creation_expr { * Gets a dimension's length argument of this array creation, for * example `5` in * - * ``` + * ```csharp * new int[5, 10] * ``` */ @@ -345,7 +365,7 @@ class ArrayCreation extends Expr, @array_creation_expr { * Gets the `i`th dimension's length argument of this array creation, for * example the second (`i = 1`) dimension's length is `10` in * - * ``` + * ```csharp * new int[5, 10] * ``` */ @@ -370,6 +390,8 @@ class ArrayCreation extends Expr, @array_creation_expr { predicate isImplicitlyTyped() { implicitly_typed_array_creation(this) } override string toString() { result = "array creation of type " + this.getType().getName() } + + override string getAPrimaryQlClass() { result = "ArrayCreation" } } /** @@ -377,6 +399,8 @@ class ArrayCreation extends Expr, @array_creation_expr { */ class Stackalloc extends ArrayCreation { Stackalloc() { stackalloc_array_creation(this) } + + override string getAPrimaryQlClass() { result = "Stackalloc" } } /** @@ -405,6 +429,8 @@ class AnonymousFunctionExpr extends Expr, Callable, @anonymous_function_expr { */ class LambdaExpr extends AnonymousFunctionExpr, @lambda_expr { override string toString() { result = "(...) => ..." } + + override string getAPrimaryQlClass() { result = "LambdaExpr" } } /** @@ -413,4 +439,6 @@ class LambdaExpr extends AnonymousFunctionExpr, @lambda_expr { */ class AnonymousMethodExpr extends AnonymousFunctionExpr, @anonymous_method_expr { override string toString() { result = "delegate(...) { ... }" } + + override string getAPrimaryQlClass() { result = "AnonymousMethodExpr" } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Dynamic.qll b/csharp/ql/src/semmle/code/csharp/exprs/Dynamic.qll index 6493bd0dabfb..ea6012ca3e15 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Dynamic.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Dynamic.qll @@ -22,7 +22,7 @@ class DynamicExpr extends LateBindableExpr { * A constructor call where one of the arguments is a `dynamic` expression, for * example `new A(d)` on line 8 in * - * ``` + * ```csharp * class A { * A(int i) { } * @@ -42,13 +42,15 @@ class DynamicObjectCreation extends DynamicExpr, ObjectCreation { override string toString() { result = "dynamic object creation of type " + this.getType().getName() } + + override string getAPrimaryQlClass() { result = "DynamicObjectCreation" } } /** * A method call where the qualifier or one of the arguments is a `dynamic` * expression, for example `M(d)` on line 8 in * - * ``` + * ```csharp * class A { * void M(int i) { } * @@ -66,13 +68,15 @@ class DynamicObjectCreation extends DynamicExpr, ObjectCreation { */ class DynamicMethodCall extends DynamicExpr, MethodCall { override string toString() { result = "dynamic call to method " + getLateBoundTargetName() } + + override string getAPrimaryQlClass() { result = "DynamicMethodCall" } } /** * A call to a user-defined operator where one of the operands is a `dynamic` * expression, for example `this + d` on line 12 in * - * ``` + * ```csharp * class A { * public static A operator+(A left, int right) { * return left; @@ -94,13 +98,15 @@ class DynamicMethodCall extends DynamicExpr, MethodCall { */ class DynamicOperatorCall extends DynamicExpr, OperatorCall { override string toString() { result = "dynamic call to operator " + getLateBoundTargetName() } + + override string getAPrimaryQlClass() { result = "DynamicOperatorCall" } } /** * A call to a user-defined mutator operator where the operand is a `dynamic` * expression, for example `d++` on line 20 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -147,7 +153,7 @@ class DynamicAccess extends DynamicExpr { * A member access where the qualifier is a `dynamic` expression, for example * `d.X` on line 24 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -185,6 +191,8 @@ class DynamicMemberAccess extends DynamicAccess, MemberAccess, AssignableAccess, @dynamic_member_access_expr { override string toString() { result = "dynamic access to member " + getLateBoundTargetName() } + override string getAPrimaryQlClass() { result = "DynamicMemberAccess" } + // The target is unknown when the qualifier is a `dynamic` expression override DynamicMember getTarget() { none() } } @@ -193,7 +201,7 @@ class DynamicMemberAccess extends DynamicAccess, MemberAccess, AssignableAccess, * An access to a dynamic member that reads the underlying value, for example * `d.X` on line 16 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -220,7 +228,7 @@ class DynamicMemberRead extends DynamicMemberAccess, AssignableRead { } * An access to a dynamic member that updates the underlying value, for * example `d.X` on line 16 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -259,7 +267,7 @@ class DynamicMember extends AssignableMember { * A call to an accessor where the qualifier is a `dynamic` expression, for * example `d.X` on line 20 and `d[0]` on line 25 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -315,7 +323,7 @@ class DynamicAccessorCall extends DynamicAccess { * An element access where the qualifier is a `dynamic` expression, for example * `d[0]` on line 12 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -338,13 +346,15 @@ class DynamicAccessorCall extends DynamicAccess { */ class DynamicElementAccess extends DynamicAccess, ElementAccess, @dynamic_element_access_expr { override string toString() { result = "dynamic access to element" } + + override string getAPrimaryQlClass() { result = "DynamicElementAccess" } } /** * An access to a dynamic element that reads the underlying value, for example * `d[0]` on line 12 in * - * ``` + * ```csharp * class A { * public A() { } * @@ -367,7 +377,7 @@ class DynamicElementRead extends DynamicElementAccess, ElementRead { } * An access to a dynamic element that updates the underlying value, for example * `d[0]` on line 12 in * - * ``` + * ```csharp * class A { * public A() { } * diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll index c06591768416..b133b628732e 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll @@ -4,10 +4,6 @@ * All expressions have the common base class `Expr`. */ -import semmle.code.csharp.Location -import semmle.code.csharp.Stmt -import semmle.code.csharp.Callable -import semmle.code.csharp.Type import Access import ArithmeticOperation import Assignment @@ -19,9 +15,14 @@ import Dynamic import Literal import LogicalOperation import semmle.code.csharp.controlflow.ControlFlowElement +import semmle.code.csharp.Callable +import semmle.code.csharp.Location +import semmle.code.csharp.Stmt +import semmle.code.csharp.Type +private import dotnet private import semmle.code.csharp.Enclosing::Internal private import semmle.code.csharp.frameworks.System -private import dotnet +private import semmle.code.csharp.TypeRef /** * An expression. Either an access (`Access`), a call (`Call`), an object or @@ -125,7 +126,7 @@ class LocalVariableDeclExpr extends Expr, @local_var_decl_expr { /** * Gets the local variable being declared, if any. The only case where * no variable is declared is when a discard symbol is used, for example - * ``` + * ```csharp * if (int.TryParse(s, out var _)) * ... * ``` @@ -165,6 +166,11 @@ class LocalVariableDeclExpr extends Expr, @local_var_decl_expr { * for example `M(out int x)`. */ predicate isOutArgument() { expr_argument(this, 2) } + + override string getAPrimaryQlClass() { + result = "LocalVariableDeclExpr" and + not this instanceof LocalVariableDeclAndInitExpr + } } /** @@ -237,7 +243,7 @@ class TernaryOperation extends Operation, @ternary_op { } /** * A parenthesized expression, for example `(2 + 3)` in * - * ``` + * ```csharp * 4 * (2 + 3) * ``` */ @@ -246,6 +252,8 @@ class ParenthesizedExpr extends Expr, @par_expr { Expr getExpr() { result = this.getChild(0) } override string toString() { result = "(...)" } + + override string getAPrimaryQlClass() { result = "ParenthesizedExpr" } } /** @@ -256,6 +264,8 @@ class CheckedExpr extends Expr, @checked_expr { Expr getExpr() { result = this.getChild(0) } override string toString() { result = "checked (...)" } + + override string getAPrimaryQlClass() { result = "CheckedExpr" } } /** @@ -266,6 +276,8 @@ class UncheckedExpr extends Expr, @unchecked_expr { Expr getExpr() { result = this.getChild(0) } override string toString() { result = "unchecked (...)" } + + override string getAPrimaryQlClass() { result = "UncheckedExpr" } } cached @@ -291,7 +303,7 @@ private predicate hasChildPattern(ControlFlowElement pm, Expr child) { /** * A pattern expression, for example `(_, false)` in * - * ``` + * ```csharp * (a,b) switch { * (_, false) => true, * _ => false @@ -308,7 +320,7 @@ class PatternExpr extends Expr { * (transitively). For example, `_`, `false`, and `(_, false)` belong to the * pattern match `(_, false) => true` in * - * ``` + * ```csharp * (a,b) switch { * (_, false) => true, * _ => false @@ -319,11 +331,15 @@ class PatternExpr extends Expr { } /** A discard pattern, for example `_` in `x is (_, false)` */ -class DiscardPatternExpr extends DiscardExpr, PatternExpr { } +class DiscardPatternExpr extends DiscardExpr, PatternExpr { + override string getAPrimaryQlClass() { result = "DiscardPatternExpr" } +} /** A constant pattern, for example `false` in `x is (_, false)`. */ class ConstantPatternExpr extends PatternExpr { ConstantPatternExpr() { this.hasValue() } + + override string getAPrimaryQlClass() { result = "ConstantPatternExpr" } } /** @@ -343,7 +359,9 @@ class TypePatternExpr extends PatternExpr { } /** A type access pattern, for example `string` in `x is string`. */ -class TypeAccessPatternExpr extends TypePatternExpr, TypeAccess { } +class TypeAccessPatternExpr extends TypePatternExpr, TypeAccess { + override string getAPrimaryQlClass() { result = "TypeAccessPatternExpr" } +} /** A pattern that may bind a variable, for example `string s` in `x is string s`. */ class BindingPatternExpr extends PatternExpr { @@ -362,6 +380,8 @@ class BindingPatternExpr extends PatternExpr { /** A variable declaration pattern, for example `string s` in `x is string s`. */ class VariablePatternExpr extends BindingPatternExpr, LocalVariableDeclExpr { override LocalVariableDeclExpr getVariableDeclExpr() { result = this } + + override string getAPrimaryQlClass() { result = "VariablePatternExpr" } } /** @@ -371,6 +391,8 @@ class VariablePatternExpr extends BindingPatternExpr, LocalVariableDeclExpr { class RecursivePatternExpr extends BindingPatternExpr, @recursive_pattern_expr { override string toString() { result = "{ ... }" } + override string getAPrimaryQlClass() { result = "RecursivePatternExpr" } + /** * Gets the position patterns of this recursive pattern, if any. * For example, `(1, _)`. @@ -398,6 +420,8 @@ class PropertyPatternExpr extends Expr, @property_pattern_expr { /** Gets the `n`th pattern. */ PatternExpr getPattern(int n) { result = this.getChild(n) } + + override string getAPrimaryQlClass() { result = "PropertyPatternExpr" } } /** @@ -409,6 +433,8 @@ class LabeledPatternExpr extends PatternExpr { /** Gets the label of this pattern. */ string getLabel() { exprorstmt_name(this, result) } + + override string getAPrimaryQlClass() { result = "LabeledPatternExpr" } } /** A positional pattern. For example, `(int x, int y)`. */ @@ -417,6 +443,8 @@ class PositionalPatternExpr extends Expr, @positional_pattern_expr { /** Gets the `n`th pattern. */ PatternExpr getPattern(int n) { result = this.getChild(n) } + + override string getAPrimaryQlClass() { result = "PositionalPatternExpr" } } /** @@ -439,6 +467,8 @@ class IsExpr extends Expr, PatternMatch, @is_expr { override PatternExpr getPattern() { result = this.getChild(1) } override string toString() { result = "... is ..." } + + override string getAPrimaryQlClass() { result = "IsExpr" } } /** A `switch` expression or statement. */ @@ -458,7 +488,7 @@ class Switch extends ControlFlowElement, @switch { /** * A `switch` expression, for example - * ``` + * ```csharp * (a,b) switch { * (false, false) => true, * _ => false @@ -473,6 +503,8 @@ class SwitchExpr extends Expr, Switch, @switch_expr { override SwitchCaseExpr getCase(int n) { result = this.getChild(n) } override SwitchCaseExpr getACase() { result = this.getCase(_) } + + override string getAPrimaryQlClass() { result = "SwitchExpr" } } /** A `case` expression or statement. */ @@ -510,6 +542,8 @@ class SwitchCaseExpr extends Expr, Case, @switch_case_expr { // should match all cases due to the type of the expression. this.getPattern() instanceof DiscardPatternExpr } + + override string getAPrimaryQlClass() { result = "SwitchCaseExpr" } } /** @@ -544,7 +578,7 @@ class Cast extends Expr { * An implicit cast. For example, the implicit cast from `string` to `object` * on line 3 in * - * ``` + * ```csharp * class C { * void M1(object o) { } * void M2(string s) => M1(s); @@ -559,7 +593,7 @@ class ImplicitCast extends Cast { * An explicit cast. For example, the explicit cast from `object` to `string` * on line 2 in * - * ``` + * ```csharp * class C { * string M1(object o) => (string) o; * } @@ -574,6 +608,8 @@ class ExplicitCast extends Cast { */ class AsExpr extends Cast, @as_expr { override string toString() { result = "... as ..." } + + override string getAPrimaryQlClass() { result = "AsExpr" } } /** @@ -581,6 +617,8 @@ class AsExpr extends Cast, @as_expr { */ class CastExpr extends Cast, @cast_expr { override string toString() { result = "(...) ..." } + + override string getAPrimaryQlClass() { result = "CastExpr" } } /** @@ -594,6 +632,8 @@ class TypeofExpr extends Expr, @typeof_expr { TypeAccess getTypeAccess() { result = this.getChild(0) } override string toString() { result = "typeof(...)" } + + override string getAPrimaryQlClass() { result = "TypeofExpr" } } /** @@ -609,6 +649,8 @@ class DefaultValueExpr extends Expr, @default_expr { override string toString() { if exists(getTypeAccess()) then result = "default(...)" else result = "default" } + + override string getAPrimaryQlClass() { result = "DefaultValueExpr" } } /** @@ -624,13 +666,15 @@ class SizeofExpr extends UnaryOperation, @sizeof_expr { override string getOperator() { result = "sizeof(..)" } override string toString() { result = "sizeof(..)" } + + override string getAPrimaryQlClass() { result = "SizeofExpr" } } /** * A pointer indirection operation, for example `*pn` on line 7, * `pa->M()` on line 13, and `cp[1]` on line 18 in * - * ``` + * ```csharp * struct A { * public void M() { } * @@ -662,12 +706,14 @@ class SizeofExpr extends UnaryOperation, @sizeof_expr { */ class PointerIndirectionExpr extends UnaryOperation, @pointer_indirection_expr { override string getOperator() { result = "*" } + + override string getAPrimaryQlClass() { result = "PointerIndirectionExpr" } } /** * An address-of expression, for example `&n` on line 4 in * - * ``` + * ```csharp * class A { * unsafe int DirectDerefence() { * int n = 10; @@ -679,6 +725,8 @@ class PointerIndirectionExpr extends UnaryOperation, @pointer_indirection_expr { */ class AddressOfExpr extends UnaryOperation, @address_of_expr { override string getOperator() { result = "&" } + + override string getAPrimaryQlClass() { result = "AddressOfExpr" } } /** @@ -689,12 +737,14 @@ class AwaitExpr extends Expr, @await_expr { Expr getExpr() { result = getChild(0) } override string toString() { result = "await ..." } + + override string getAPrimaryQlClass() { result = "AwaitExpr" } } /** * A `nameof` expression, for example `nameof(s)` on line 3 in * - * ``` + * ```csharp * void M(string s) { * if (s == null) * throw new ArgumentNullException(nameof(s)); @@ -710,12 +760,14 @@ class NameOfExpr extends Expr, @nameof_expr { * `nameof(x.F)`. */ Access getAccess() { result = this.getChild(0) } + + override string getAPrimaryQlClass() { result = "NameOfExpr" } } /** * An interpolated string, for example `$"Hello, {name}!"` on line 2 in * - * ``` + * ```csharp * void Hello(string name) { * Console.WriteLine($"Hello, {name}!"); * } @@ -724,6 +776,8 @@ class NameOfExpr extends Expr, @nameof_expr { class InterpolatedStringExpr extends Expr, @interpolated_string_expr { override string toString() { result = "$\"...\"" } + override string getAPrimaryQlClass() { result = "InterpolatedStringExpr" } + /** * Gets the insert at index `i` in this interpolated string, if any. For * example, the insert at index `i = 1` is `name` in `$"Hello, {name}!"`. @@ -787,6 +841,8 @@ class ThrowExpr extends Expr, ThrowElement, @throw_expr { */ // overriden for more precise qldoc override Expr getExpr() { result = ThrowElement.super.getExpr() } + + override string getAPrimaryQlClass() { result = "ThrowExpr" } } /** @@ -863,8 +919,8 @@ private Expr getAnAssignOrForeachChild() { * An expression representing a tuple, for example * `(1, 2)` on line 2 or `(var x, var y)` on line 5 in * - * ``` - * class { + * ```csharp + * class C { * (int, int) F() => (1, 2); * * void M() { @@ -884,12 +940,14 @@ class TupleExpr extends Expr, @tuple_expr { /** Holds if this tuple is a read access. */ predicate isReadAccess() { not this = getAnAssignOrForeachChild() } + + override string getAPrimaryQlClass() { result = "TupleExpr" } } /** * A reference expression, for example `ref a[i]` on line 2 in * - * ``` + * ```csharp * ref int GetElement(int[] a, int i) { * return ref a[i]; * } @@ -902,17 +960,21 @@ class RefExpr extends Expr, @ref_expr { override string toString() { result = "ref ..." } override Type getType() { result = getExpr().getType() } + + override string getAPrimaryQlClass() { result = "RefExpr" } } /** * A discard expression, for example `_` in * - * ``` + * ```csharp * (var name, _, _) = GetDetails(); * ``` */ class DiscardExpr extends Expr, @discard_expr { override string toString() { result = "_" } + + override string getAPrimaryQlClass() { result = "DiscardExpr" } } private class UnknownExpr extends Expr, @unknown_expr { @@ -921,7 +983,7 @@ private class UnknownExpr extends Expr, @unknown_expr { /** * A range expression, used to create a `System.Range`. For example - * ``` + * ```csharp * 1..3 * 1..^1 * 3.. @@ -944,18 +1006,23 @@ class RangeExpr extends Expr, @range_expr { /** Holds if this range expression has a right hand operand. */ predicate hasEnd() { exists(this.getEnd()) } + + override string getAPrimaryQlClass() { result = "RangeExpr" } } /** An index expression, for example `^1` meaning "1 from the end". */ class IndexExpr extends Expr, @index_expr { + /** Gets the sub expression of this index expression. */ Expr getExpr() { result.getParent() = this } override string toString() { result = "^..." } + + override string getAPrimaryQlClass() { result = "IndexExpr" } } /** * A nullable warning suppression expression, for example `x!` in - * ``` + * ```csharp * string GetName() * { * string? x = ...; @@ -968,4 +1035,6 @@ class SuppressNullableWarningExpr extends Expr, @suppress_nullable_warning_expr Expr getExpr() { result.getParent() = this } override string toString() { result = "...!" } + + override string getAPrimaryQlClass() { result = "SuppressNullableWarningExpr" } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Literal.qll b/csharp/ql/src/semmle/code/csharp/exprs/Literal.qll index 1a8231e7c18c..0ca9f6d0db03 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Literal.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Literal.qll @@ -27,12 +27,16 @@ class BoolLiteral extends Literal, @bool_literal_expr { or getValue() = "false" and result = false } + + override string getAPrimaryQlClass() { result = "BoolLiteral" } } /** * A Unicode character literal, for example `'a'`. */ -class CharLiteral extends Literal, @char_literal_expr { } +class CharLiteral extends Literal, @char_literal_expr { + override string getAPrimaryQlClass() { result = "CharLiteral" } +} /** * An integer literal. Either an `int` literal (`IntLiteral`), a `long` @@ -44,22 +48,30 @@ class IntegerLiteral extends DotNet::IntLiteral, Literal, @integer_literal_expr /** * An `int` literal, for example `0`. */ -class IntLiteral extends IntegerLiteral, @int_literal_expr { } +class IntLiteral extends IntegerLiteral, @int_literal_expr { + override string getAPrimaryQlClass() { result = "IntLiteral" } +} /** * A `long` literal, for example `-5L`. */ -class LongLiteral extends IntegerLiteral, @long_literal_expr { } +class LongLiteral extends IntegerLiteral, @long_literal_expr { + override string getAPrimaryQlClass() { result = "LongLiteral" } +} /** * A `uint` literal, for example `5U`. */ -class UIntLiteral extends IntegerLiteral, @uint_literal_expr { } +class UIntLiteral extends IntegerLiteral, @uint_literal_expr { + override string getAPrimaryQlClass() { result = "UIntLiteral" } +} /** * A `ulong` literal, for example `5UL`. */ -class ULongLiteral extends IntegerLiteral, @ulong_literal_expr { } +class ULongLiteral extends IntegerLiteral, @ulong_literal_expr { + override string getAPrimaryQlClass() { result = "ULongLiteral" } +} /** * A floating point literal. Either a `float` literal (`FloatLiteral`), a @@ -71,26 +83,36 @@ class RealLiteral extends Literal, @real_literal_expr { } /** * A `float` literal, for example `5F`. */ -class FloatLiteral extends RealLiteral, @float_literal_expr { } +class FloatLiteral extends RealLiteral, @float_literal_expr { + override string getAPrimaryQlClass() { result = "FloatLiteral" } +} /** * A `double` literal, for example `5D`. */ -class DoubleLiteral extends RealLiteral, @double_literal_expr { } +class DoubleLiteral extends RealLiteral, @double_literal_expr { + override string getAPrimaryQlClass() { result = "DoubleLiteral" } +} /** * A `decimal` literal, for example `5m`. */ -class DecimalLiteral extends RealLiteral, @decimal_literal_expr { } +class DecimalLiteral extends RealLiteral, @decimal_literal_expr { + override string getAPrimaryQlClass() { result = "DecimalLiteral" } +} /** * A `string` literal, for example `"Hello, World!"`. */ class StringLiteral extends DotNet::StringLiteral, Literal, @string_literal_expr { override string toString() { result = "\"" + getValue().replaceAll("\"", "\\\"") + "\"" } + + override string getAPrimaryQlClass() { result = "StringLiteral" } } /** * A `null` literal. */ -class NullLiteral extends DotNet::NullLiteral, Literal, @null_literal_expr { } +class NullLiteral extends DotNet::NullLiteral, Literal, @null_literal_expr { + override string getAPrimaryQlClass() { result = "NullLiteral" } +} diff --git a/csharp/ql/src/semmle/code/csharp/exprs/LogicalOperation.qll b/csharp/ql/src/semmle/code/csharp/exprs/LogicalOperation.qll index 95f039383dfe..e94d8ff93e7a 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/LogicalOperation.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/LogicalOperation.qll @@ -25,6 +25,8 @@ class UnaryLogicalOperation extends LogicalOperation, UnaryOperation, @un_log_op */ class LogicalNotExpr extends UnaryLogicalOperation, @log_not_expr { override string getOperator() { result = "!" } + + override string getAPrimaryQlClass() { result = "LogicalNotExpr" } } /** @@ -41,6 +43,8 @@ class BinaryLogicalOperation extends LogicalOperation, BinaryOperation, @bin_log */ class LogicalAndExpr extends BinaryLogicalOperation, @log_and_expr { override string getOperator() { result = "&&" } + + override string getAPrimaryQlClass() { result = "LogicalAndExpr" } } /** @@ -48,12 +52,14 @@ class LogicalAndExpr extends BinaryLogicalOperation, @log_and_expr { */ class LogicalOrExpr extends BinaryLogicalOperation, @log_or_expr { override string getOperator() { result = "||" } + + override string getAPrimaryQlClass() { result = "LogicalOrExpr" } } /** * A null-coalescing operation, for example `s ?? ""` on line 2 in * - * ``` + * ```csharp * string NonNullOrEmpty(string s) { * return s ?? ""; * } @@ -61,6 +67,8 @@ class LogicalOrExpr extends BinaryLogicalOperation, @log_or_expr { */ class NullCoalescingExpr extends BinaryLogicalOperation, @null_coalescing_expr { override string getOperator() { result = "??" } + + override string getAPrimaryQlClass() { result = "NullCoalescingExpr" } } /** @@ -73,7 +81,7 @@ class TernaryLogicalOperation extends LogicalOperation, TernaryOperation, @terna * A conditional expression, for example `s != null ? s.Length : -1` * on line 2 in * - * ``` + * ```csharp * int LengthOrNegative(string s) { * return s != null ? s.Length : -1; * } @@ -92,4 +100,6 @@ class ConditionalExpr extends TernaryLogicalOperation, @conditional_expr { override string getOperator() { result = "?" } override string toString() { result = "... ? ... : ..." } + + override string getAPrimaryQlClass() { result = "ConditionalExpr" } } diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/EntityFramework.qll b/csharp/ql/src/semmle/code/csharp/frameworks/EntityFramework.qll index b3c1ca9f9d37..517499ef76cd 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/EntityFramework.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/EntityFramework.qll @@ -8,7 +8,12 @@ private import semmle.code.csharp.frameworks.system.collections.Generic private import semmle.code.csharp.frameworks.Sql private import semmle.code.csharp.dataflow.LibraryTypeDataFlow +/** + * Definitions relating to the `System.ComponentModel.DataAnnotations` + * namespace. + */ module DataAnnotations { + /** Class for `NotMappedAttribute`. */ class NotMappedAttribute extends Attribute { NotMappedAttribute() { this @@ -18,6 +23,10 @@ module DataAnnotations { } } +/** + * Definitions relating to the `Microsoft.EntityFrameworkCore` or + * `System.Data.Entity` namespaces. + */ module EntityFramework { /** An EF6 or EFCore namespace. */ class EFNamespace extends Namespace { @@ -43,12 +52,14 @@ module EntityFramework { class DbContext extends EFClass { DbContext() { this.getName() = "DbContext" } + /** Gets a `Find` or `FindAsync` method in the `DbContext`. */ Method getAFindMethod() { result = this.getAMethod("Find") or result = this.getAMethod("FindAsync") } + /** Gets an `Update` method in the `DbContext`. */ Method getAnUpdateMethod() { result = this.getAMethod("Update") } } @@ -111,14 +122,15 @@ module EntityFramework { c = this.getAConstructor() and source.(CallableFlowSourceArg).getArgumentIndex() = 0 and sink instanceof CallableFlowSinkReturn and - preservesValue = true + preservesValue = false or c = this.getAConversionTo() and source.(CallableFlowSourceArg).getArgumentIndex() = 0 and sink instanceof CallableFlowSinkReturn and - preservesValue = true + preservesValue = false } + /** Gets a conversion operator from `string` to `RawSqlString`. */ ConversionOperator getAConversionTo() { result = this.getAMember() and result.getTargetType() instanceof RawSqlStringStruct and diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/JsonNET.qll b/csharp/ql/src/semmle/code/csharp/frameworks/JsonNET.qll index b439f958a018..54019d13216d 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/JsonNET.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/JsonNET.qll @@ -106,7 +106,7 @@ module JsonNET { private class SerializedMember extends TaintTracking::TaintedMember { SerializedMember() { // This member has a Json attribute - exists(Class attribute | attribute = this.(Attributable).getAnAttribute().getType() | + exists(Class attribute | attribute = this.getAnAttribute().getType() | attribute.hasName("JsonPropertyAttribute") or attribute.hasName("JsonDictionaryAttribute") @@ -135,8 +135,10 @@ module JsonNET { class JsonSerializerClass extends JsonClass, LibraryTypeDataFlow { JsonSerializerClass() { this.hasName("JsonSerializer") } + /** Gets the method for `JsonSerializer.Serialize`. */ Method getSerializeMethod() { result = this.getAMethod("Serialize") } + /** Gets the method for `JsonSerializer.Deserialize`. */ Method getDeserializeMethod() { result = this.getAMethod("Deserialize") } override predicate callableFlow( @@ -214,7 +216,7 @@ module JsonNET { any(Operator op | op.getDeclaringType() = this.getABaseType*() and op.getReturnType() instanceof StringType ) and - source = any(CallableFlowSourceArg arg | arg.getArgumentIndex() = 0) and + source.(CallableFlowSourceArg).getArgumentIndex() = 0 and sink instanceof CallableFlowSinkReturn and preservesValue = false or diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/NHibernate.qll b/csharp/ql/src/semmle/code/csharp/frameworks/NHibernate.qll index a75b7b0b3492..3e46bd130476 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/NHibernate.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/NHibernate.qll @@ -1,8 +1,13 @@ +/** + * Classes for modelling NHibernate. + */ + import csharp private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.system.Collections private import semmle.code.csharp.frameworks.Sql +/** Definitions relating to the `NHibernate` package. */ module NHibernate { /** A class that is mapped to the database. */ abstract class MappedClass extends Class { } diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/System.qll b/csharp/ql/src/semmle/code/csharp/frameworks/System.qll index 093c8da9318a..9812fe08c6ba 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/System.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/System.qll @@ -21,6 +21,11 @@ class SystemUnboundGenericClass extends UnboundGenericClass { SystemUnboundGenericClass() { this.getNamespace() instanceof SystemNamespace } } +/** An unbound generic struct in the `System` namespace. */ +class SystemUnboundGenericStruct extends UnboundGenericStruct { + SystemUnboundGenericStruct() { this.getNamespace() instanceof SystemNamespace } +} + /** An interface in the `System` namespace. */ class SystemInterface extends Interface { SystemInterface() { this.getNamespace() instanceof SystemNamespace } @@ -215,6 +220,35 @@ class SystemLazyClass extends SystemUnboundGenericClass { } } +/** The `System.Nullable` struct. */ +class SystemNullableStruct extends SystemUnboundGenericStruct { + SystemNullableStruct() { + this.hasName("Nullable<>") and + this.getNumberOfTypeParameters() = 1 + } + + /** Gets the `Value` property. */ + Property getValueProperty() { + result.getDeclaringType() = this and + result.hasName("Value") and + result.getType() = getTypeParameter(0) + } + + /** Gets the `HasValue` property. */ + Property getHasValueProperty() { + result.getDeclaringType() = this and + result.hasName("HasValue") and + result.getType() instanceof BoolType + } + + /** Gets a `GetValueOrDefault()` method. */ + Method getAGetValueOrDefaultMethod() { + result.getDeclaringType() = this and + result.hasName("GetValueOrDefault") and + result.getReturnType() = getTypeParameter(0) + } +} + /** The `System.NullReferenceException` class. */ class SystemNullReferenceExceptionClass extends SystemClass { SystemNullReferenceExceptionClass() { this.hasName("NullReferenceException") } @@ -360,12 +394,11 @@ class SystemStringClass extends StringType { result.getReturnType() instanceof StringType } - /** Gets a `Join(string, ...)` method. */ + /** Gets a `Join(...)` method. */ Method getJoinMethod() { result.getDeclaringType() = this and result.hasName("Join") and result.getNumberOfParameters() > 1 and - result.getParameter(0).getType() instanceof StringType and result.getReturnType() instanceof StringType } @@ -564,7 +597,7 @@ private EqualsMethod getInheritedEqualsMethod(ValueOrRefType t) { t.hasMethod(re * * Example: * - * ``` + * ```csharp * abstract class A : IEquatable { * public abstract bool Equals(T other); * public override bool Equals(object other) { return other != null && GetType() == other.GetType() && Equals((T)other); } @@ -653,7 +686,7 @@ private DisposeMethod getInheritedDisposeMethod(ValueOrRefType t) { t.hasMethod( * * Example: * - * ``` + * ```csharp * class A : IDisposable { * public void Dispose() { Dispose(true); } * public virtual void Dispose(bool disposing) { ... } diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/WCF.qll b/csharp/ql/src/semmle/code/csharp/frameworks/WCF.qll index a9505388a921..655648d88c90 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/WCF.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/WCF.qll @@ -53,3 +53,17 @@ class OperationMethod extends Method { ) } } + +/** + * Data flow for WCF data contracts. + * + * Flow is defined from a WCF data contract object to any of its data member + * properties. This flow model only makes sense from a taint-tracking perspective + * (a tainted data contract object implies tainted data members). + */ +private class DataContractMember extends TaintTracking::TaintedMember { + DataContractMember() { + this.getAnAttribute() instanceof DataMemberAttribute and + this.getDeclaringType() instanceof DataContractClass + } +} diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll b/csharp/ql/src/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll index cdcc9c2a37c8..3ebd70285040 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll @@ -27,6 +27,14 @@ class MicrosoftAspNetCoreMvcViewFeatures extends Namespace { } } +/** The 'Microsoft.AspNetCore.Mvc.Rendering' namespace. */ +class MicrosoftAspNetCoreMvcRendering extends Namespace { + MicrosoftAspNetCoreMvcRendering() { + getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and + hasName("Rendering") + } +} + /** An attribute whose type is in the `Microsoft.AspNetCore.Mvc` namespace. */ class MicrosoftAspNetCoreMvcAttribute extends Attribute { MicrosoftAspNetCoreMvcAttribute() { @@ -191,11 +199,11 @@ class MicrosoftAspNetCoreMvcController extends Class { } } -/** The `Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper` class. */ -class MicrosoftAspNetCoreMvcHtmlHelperClass extends Class { - MicrosoftAspNetCoreMvcHtmlHelperClass() { - getNamespace() instanceof MicrosoftAspNetCoreMvcViewFeatures and - hasName("HtmlHelper") +/** The `Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper` interface. */ +class MicrosoftAspNetCoreMvcRenderingIHtmlHelperInterface extends Interface { + MicrosoftAspNetCoreMvcRenderingIHtmlHelperInterface() { + getNamespace() instanceof MicrosoftAspNetCoreMvcRendering and + hasName("IHtmlHelper") } /** Gets the `Raw` method. */ diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/Collections.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/Collections.qll index e932740cc022..aee0a1c8f7cc 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/Collections.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/Collections.qll @@ -52,3 +52,13 @@ class SystemCollectionsIEnumeratorInterface extends SystemCollectionsInterface { class SystemCollectionsICollectionInterface extends SystemCollectionsInterface { SystemCollectionsICollectionInterface() { this.hasName("ICollection") } } + +/** The `System.Collections.IList` interface. */ +class SystemCollectionsIListInterface extends SystemCollectionsInterface { + SystemCollectionsIListInterface() { this.hasName("IList") } +} + +/** The `System.Collections.IDictionary` interface. */ +class SystemCollectionsIDictionaryInterface extends SystemCollectionsInterface { + SystemCollectionsIDictionaryInterface() { this.hasName("IDictionary") } +} diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/Linq.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/Linq.qll index d5b7d4cff4ee..a6602899eaf4 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/Linq.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/Linq.qll @@ -5,6 +5,7 @@ private import csharp as csharp private import semmle.code.csharp.frameworks.System as System +/** Definitions relating to the `System.Linq` namespace. */ module SystemLinq { /** The `System.Linq` namespace. */ class Namespace extends csharp::Namespace { diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/Xml.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/Xml.qll index 21ca14310126..c84fa47a1035 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/Xml.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/Xml.qll @@ -2,7 +2,7 @@ import csharp private import semmle.code.csharp.frameworks.System -private import semmle.code.csharp.dataflow.DataFlow2 +private import semmle.code.csharp.dataflow.DataFlow3 /** The `System.Xml` namespace. */ class SystemXmlNamespace extends Namespace { @@ -163,7 +163,7 @@ class XmlReaderSettingsCreation extends ObjectCreation { } } -private class SettingsDataFlowConfig extends DataFlow2::Configuration { +private class SettingsDataFlowConfig extends DataFlow3::Configuration { SettingsDataFlowConfig() { this = "SettingsDataFlowConfig" } override predicate isSource(DataFlow::Node source) { diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/collections/Generic.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/collections/Generic.qll index 3160f82f2d49..a3616e57522d 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/collections/Generic.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/collections/Generic.qll @@ -136,3 +136,16 @@ class SystemCollectionsGenericICollectionInterface extends SystemCollectionsGene /** Gets the `Add` method. */ Method getAddMethod() { result = this.getAMethod("Add") } } + +/** The `System.Collections.Generic.IList<>` interface. */ +class SystemCollectionsGenericIListInterface extends SystemCollectionsGenericUnboundGenericInterface { + SystemCollectionsGenericIListInterface() { this.hasName("IList<>") } +} + +/** The `System.Collections.Generic.IDictionary` interface. */ +class SystemCollectionsGenericIDictionaryInterface extends SystemCollectionsGenericUnboundGenericInterface { + SystemCollectionsGenericIDictionaryInterface() { + this.hasName("IDictionary<,>") and + this.getNumberOfTypeParameters() = 2 + } +} diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/data/Common.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/data/Common.qll index 1ccbd5e1e7ed..a4aaa25611d3 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/data/Common.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/data/Common.qll @@ -5,6 +5,7 @@ private import csharp as csharp private import semmle.code.csharp.frameworks.system.Data as Data +/** Definitions relating to the `System.Data.Common` namespace. */ module SystemDataCommon { /** The `System.Data.Common` namespace. */ class Namespace extends csharp::Namespace { diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/data/Entity.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/data/Entity.qll index 45dcda1b776b..2aaa9a5889a1 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/data/Entity.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/data/Entity.qll @@ -5,6 +5,7 @@ private import csharp as csharp private import semmle.code.csharp.frameworks.system.Data as Data +/** Definitions relating to the `System.Data.Entity` namespace. */ module SystemDataEntity { /** The `System.Data.Entity` namespace. */ class Namespace extends csharp::Namespace { @@ -78,6 +79,7 @@ module SystemDataEntity { } } +/** Definitions relating to the `System.Data.Entity.Infrastructure` namespace. */ module SystemDataEntityInfrastructure { /** The `System.Data.Entity.Infrastructure` namespace. */ class Namespace extends csharp::Namespace { diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/linq/Expressions.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/linq/Expressions.qll index 1254b8552532..c4126834d20d 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/linq/Expressions.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/linq/Expressions.qll @@ -5,6 +5,7 @@ private import csharp as csharp private import semmle.code.csharp.frameworks.system.Linq +/** Definitions relating to the `System.Linq.Expressions` namespace. */ module SystemLinqExpressions { /** The `System.Linq.Expressions` namespace. */ class Namespace extends csharp::Namespace { diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll new file mode 100644 index 000000000000..a5dd6e4c20f7 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll @@ -0,0 +1,30 @@ +/** Provides definitions related to the namespace `System.Runtime.CompilerServices`. */ + +import csharp +private import semmle.code.csharp.frameworks.system.Runtime + +/** The `System.Runtime.CompilerServices` namespace. */ +class SystemRuntimeCompilerServicesNamespace extends Namespace { + SystemRuntimeCompilerServicesNamespace() { + this.getParentNamespace() instanceof SystemRuntimeNamespace and + this.hasName("CompilerServices") + } +} + +/** An unbound generic struct in the `System.Runtime.CompilerServices` namespace. */ +class SystemRuntimeCompilerServicesNamespaceUnboundGenericStruct extends UnboundGenericStruct { + SystemRuntimeCompilerServicesNamespaceUnboundGenericStruct() { + this = any(SystemRuntimeCompilerServicesNamespace n).getATypeDeclaration() + } +} + +/** The `System.Runtime.CompilerServices.TaskAwaiter<>` struct. */ +class SystemRuntimeCompilerServicesTaskAwaiterStruct extends SystemRuntimeCompilerServicesNamespaceUnboundGenericStruct { + SystemRuntimeCompilerServicesTaskAwaiterStruct() { this.hasName("TaskAwaiter<>") } + + /** Gets the `GetResult` method. */ + Method getGetResultMethod() { result = this.getAMethod("GetResult") } + + /** Gets the field that stores the underlying task. */ + Field getUnderlyingTaskField() { result = this.getAField() and result.hasName("m_task") } +} diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/text/RegularExpressions.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/text/RegularExpressions.qll index ce03f5275d9d..1820192da11e 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/text/RegularExpressions.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/text/RegularExpressions.qll @@ -1,3 +1,7 @@ +/** + * Provides classes related to the namespace `System.Text.RegularExpressions`. + */ + import default import semmle.code.csharp.frameworks.system.Text diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/threading/Tasks.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/threading/Tasks.qll index 808ce188aac1..bf1898baf00e 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/threading/Tasks.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/threading/Tasks.qll @@ -38,4 +38,7 @@ class SystemThreadingTasksTaskTClass extends SystemThreadingTasksUnboundGenericC result.hasName("Result") and result.getType() = this.getTypeParameter(0) } + + /** Gets the `GetAwaiter` method. */ + Method getGetAwaiterMethod() { result = this.getAMethod("GetAwaiter") } } diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/xml/XPath.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/xml/XPath.qll index dd0b7a4f5e95..9374d7c282fa 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/system/xml/XPath.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/xml/XPath.qll @@ -3,6 +3,7 @@ import csharp as csharp private import semmle.code.csharp.frameworks.system.Xml as xml +/** Definitions relating to the `System.Xml.XPath` namespace. */ module SystemXmlXPath { /** The `System.Xml.XPath` namespace. */ class Namespace extends csharp::Namespace { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll deleted file mode 100644 index 71bc8ec2b0f2..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Module used to configure the IR generation process. - */ - -private import internal.IRConfigurationInternal - -private newtype TIRConfiguration = MkIRConfiguration() - -/** - * The query can extend this class to control which functions have IR generated for them. - */ -class IRConfiguration extends TIRConfiguration { - string toString() { result = "IRConfiguration" } - - /** - * Holds if IR should be created for function `func`. By default, holds for all functions. - */ - predicate shouldCreateIRForFunction(Language::Function func) { any() } - - predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() } -} - -private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration() - -/** - * The query can extend this class to control what escape analysis is used when generating SSA. - */ -class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration { - string toString() { result = "IREscapeAnalysisConfiguration" } - - /** - * Holds if the escape analysis done by SSA construction should be sound. By default, the SSA is - * built assuming that no variable's address ever escapes. - */ - predicate useSoundEscapeAnalysis() { none() } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll deleted file mode 100644 index c0b8adbe56be..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll +++ /dev/null @@ -1,720 +0,0 @@ -private import internal.OpcodeImports as Imports -private import internal.OperandTag -import Imports::MemoryAccessKind - -private newtype TOpcode = - TNoOp() or - TUninitialized() or - TError() or - TInitializeParameter() or - TInitializeIndirection() or - TInitializeThis() or - TEnterFunction() or - TExitFunction() or - TReturnValue() or - TReturnVoid() or - TReturnIndirection() or - TCopyValue() or - TLoad() or - TStore() or - TAdd() or - TSub() or - TMul() or - TDiv() or - TRem() or - TNegate() or - TShiftLeft() or - TShiftRight() or - TBitAnd() or - TBitOr() or - TBitXor() or - TBitComplement() or - TLogicalNot() or - TCompareEQ() or - TCompareNE() or - TCompareLT() or - TCompareGT() or - TCompareLE() or - TCompareGE() or - TPointerAdd() or - TPointerSub() or - TPointerDiff() or - TConvert() or - TConvertToNonVirtualBase() or - TConvertToVirtualBase() or - TConvertToDerived() or - TCheckedConvertOrNull() or - TCheckedConvertOrThrow() or - TDynamicCastToVoid() or - TVariableAddress() or - TFieldAddress() or - TFunctionAddress() or - TElementsAddress() or - TConstant() or - TStringConstant() or - TConditionalBranch() or - TSwitch() or - TCall() or - TCatchByType() or - TCatchAny() or - TThrowValue() or - TReThrow() or - TUnwind() or - TAliasedDefinition() or - TInitializeNonLocal() or - TAliasedUse() or - TPhi() or - TBuiltIn() or - TVarArgsStart() or - TVarArgsEnd() or - TVarArg() or - TNextVarArg() or - TCallSideEffect() or - TCallReadSideEffect() or - TIndirectReadSideEffect() or - TIndirectMustWriteSideEffect() or - TIndirectMayWriteSideEffect() or - TBufferReadSideEffect() or - TBufferMustWriteSideEffect() or - TBufferMayWriteSideEffect() or - TSizedBufferReadSideEffect() or - TSizedBufferMustWriteSideEffect() or - TSizedBufferMayWriteSideEffect() or - TInitializeDynamicAllocation() or - TChi() or - TInlineAsm() or - TUnreached() or - TNewObj() - -class Opcode extends TOpcode { - string toString() { result = "UnknownOpcode" } - - /** - * Gets the kind of memory access performed by this instruction's result. - * Holds only for opcodes with a memory result. - */ - MemoryAccessKind getWriteMemoryAccess() { none() } - - /** - * Gets the kind of memory access performed by this instruction's `MemoryOperand`. Holds only for - * opcodes that read from memory. - */ - MemoryAccessKind getReadMemoryAccess() { none() } - - /** - * Holds if the instruction has an `AddressOperand`. - */ - predicate hasAddressOperand() { none() } - - /** - * Holds if the instruction has a `BufferSizeOperand`. - */ - predicate hasBufferSizeOperand() { none() } - - /** - * Holds if the instruction's write memory access is a `may` write, as opposed to a `must` write. - */ - predicate hasMayWriteMemoryAccess() { none() } - - /** - * Holds if the instruction's read memory access is a `may` read, as opposed to a `must` read. - */ - predicate hasMayReadMemoryAccess() { none() } - - /** - * Holds if the instruction must have an operand with the specified `OperandTag`. - */ - final predicate hasOperand(OperandTag tag) { - hasOperandInternal(tag) - or - hasAddressOperand() and tag instanceof AddressOperandTag - or - hasBufferSizeOperand() and tag instanceof BufferSizeOperandTag - } - - /** - * Holds if the instruction must have an operand with the specified `OperandTag`, ignoring - * `AddressOperandTag` and `BufferSizeOperandTag`. - */ - predicate hasOperandInternal(OperandTag tag) { none() } -} - -abstract class UnaryOpcode extends Opcode { - final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag } -} - -abstract class BinaryOpcode extends Opcode { - final override predicate hasOperandInternal(OperandTag tag) { - tag instanceof LeftOperandTag or - tag instanceof RightOperandTag - } -} - -abstract class PointerArithmeticOpcode extends BinaryOpcode { } - -abstract class PointerOffsetOpcode extends PointerArithmeticOpcode { } - -abstract class ArithmeticOpcode extends Opcode { } - -abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode { } - -abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode { } - -abstract class BitwiseOpcode extends Opcode { } - -abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode { } - -abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode { } - -abstract class CompareOpcode extends BinaryOpcode { } - -abstract class RelationalOpcode extends CompareOpcode { } - -abstract class CopyOpcode extends Opcode { } - -abstract class ConvertToBaseOpcode extends UnaryOpcode { } - -abstract class MemoryAccessOpcode extends Opcode { } - -abstract class ReturnOpcode extends Opcode { } - -abstract class ThrowOpcode extends Opcode { } - -abstract class CatchOpcode extends Opcode { } - -abstract class OpcodeWithCondition extends Opcode { - final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag } -} - -abstract class BuiltInOperationOpcode extends Opcode { } - -abstract class SideEffectOpcode extends Opcode { } - -/** - * An opcode that accesses a single memory location via an `AddressOperand`. - */ -abstract class IndirectMemoryAccessOpcode extends Opcode { - final override predicate hasAddressOperand() { any() } -} - -/** - * An opcode that writes to a single memory location via an `AddressOperand`. - */ -abstract class IndirectWriteOpcode extends IndirectMemoryAccessOpcode { - final override MemoryAccessKind getWriteMemoryAccess() { result instanceof IndirectMemoryAccess } -} - -/** - * An opcode that reads from a single memory location via an `AddressOperand`. - */ -abstract class IndirectReadOpcode extends IndirectMemoryAccessOpcode { - final override MemoryAccessKind getReadMemoryAccess() { result instanceof IndirectMemoryAccess } -} - -/** - * An opcode that accesses a memory buffer. - */ -abstract class BufferAccessOpcode extends Opcode { - final override predicate hasAddressOperand() { any() } -} - -/** - * An opcode that accesses a memory buffer of unknown size. - */ -abstract class UnsizedBufferAccessOpcode extends BufferAccessOpcode { } - -/** - * An opcode that writes to a memory buffer of unknown size. - */ -abstract class UnsizedBufferWriteOpcode extends UnsizedBufferAccessOpcode { - final override MemoryAccessKind getWriteMemoryAccess() { result instanceof BufferMemoryAccess } -} - -/** - * An opcode that reads from a memory buffer of unknown size. - */ -abstract class UnsizedBufferReadOpcode extends UnsizedBufferAccessOpcode { - final override MemoryAccessKind getReadMemoryAccess() { result instanceof BufferMemoryAccess } -} - -/** - * An opcode that access an entire memory allocation. - */ -abstract class EntireAllocationAccessOpcode extends Opcode { - final override predicate hasAddressOperand() { any() } -} - -/** - * An opcode that write to an entire memory allocation. - */ -abstract class EntireAllocationWriteOpcode extends EntireAllocationAccessOpcode { - final override MemoryAccessKind getWriteMemoryAccess() { - result instanceof EntireAllocationMemoryAccess - } -} - -/** - * An opcode that reads from an entire memory allocation. - */ -abstract class EntireAllocationReadOpcode extends EntireAllocationAccessOpcode { - final override MemoryAccessKind getReadMemoryAccess() { - result instanceof EntireAllocationMemoryAccess - } -} - -/** - * An opcode that accesses a memory buffer whose size is determined by a `BufferSizeOperand`. - */ -abstract class SizedBufferAccessOpcode extends BufferAccessOpcode { - final override predicate hasBufferSizeOperand() { any() } -} - -/** - * An opcode that writes to a memory buffer whose size is determined by a `BufferSizeOperand`. - */ -abstract class SizedBufferWriteOpcode extends SizedBufferAccessOpcode { - final override MemoryAccessKind getWriteMemoryAccess() { - result instanceof BufferMemoryAccess //TODO: SizedBufferMemoryAccess - } -} - -/** - * An opcode that reads from a memory buffer whose size is determined by a `BufferSizeOperand`. - */ -abstract class SizedBufferReadOpcode extends SizedBufferAccessOpcode { - final override MemoryAccessKind getReadMemoryAccess() { - result instanceof BufferMemoryAccess //TODO: SizedBufferMemoryAccess - } -} - -/** - * An opcode that might write to any escaped memory location. - */ -abstract class EscapedWriteOpcode extends Opcode { - final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess } -} - -/** - * An opcode that might read from any escaped memory location. - */ -abstract class EscapedReadOpcode extends Opcode { - final override MemoryAccessKind getReadMemoryAccess() { result instanceof EscapedMemoryAccess } -} - -/** - * An opcode whose write memory access is a `may` write, as opposed to a `must` write. - */ -abstract class MayWriteOpcode extends Opcode { - final override predicate hasMayWriteMemoryAccess() { any() } -} - -/** - * An opcode whose read memory access is a `may` read, as opposed to a `must` read. - */ -abstract class MayReadOpcode extends Opcode { - final override predicate hasMayReadMemoryAccess() { any() } -} - -/** - * An opcode that reads a value from memory. - */ -abstract class OpcodeWithLoad extends IndirectReadOpcode { - final override predicate hasOperandInternal(OperandTag tag) { tag instanceof LoadOperandTag } -} - -/** - * An opcode that reads from a set of memory locations as a side effect. - */ -abstract class ReadSideEffectOpcode extends SideEffectOpcode { - final override predicate hasOperandInternal(OperandTag tag) { - tag instanceof SideEffectOperandTag - } -} - -/** - * An opcode that writes to a set of memory locations as a side effect. - */ -abstract class WriteSideEffectOpcode extends SideEffectOpcode { } - -module Opcode { - class NoOp extends Opcode, TNoOp { - final override string toString() { result = "NoOp" } - } - - class Uninitialized extends IndirectWriteOpcode, TUninitialized { - final override string toString() { result = "Uninitialized" } - } - - class Error extends Opcode, TError { - final override string toString() { result = "Error" } - } - - class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter { - final override string toString() { result = "InitializeParameter" } - } - - class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection { - final override string toString() { result = "InitializeIndirection" } - } - - class InitializeThis extends Opcode, TInitializeThis { - final override string toString() { result = "InitializeThis" } - } - - class EnterFunction extends Opcode, TEnterFunction { - final override string toString() { result = "EnterFunction" } - } - - class ExitFunction extends Opcode, TExitFunction { - final override string toString() { result = "ExitFunction" } - } - - class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue { - final override string toString() { result = "ReturnValue" } - } - - class ReturnVoid extends ReturnOpcode, TReturnVoid { - final override string toString() { result = "ReturnVoid" } - } - - class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection { - final override string toString() { result = "ReturnIndirection" } - - final override predicate hasOperandInternal(OperandTag tag) { - tag instanceof SideEffectOperandTag - } - } - - class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { - final override string toString() { result = "CopyValue" } - } - - class Load extends CopyOpcode, OpcodeWithLoad, TLoad { - final override string toString() { result = "Load" } - } - - class Store extends CopyOpcode, IndirectWriteOpcode, TStore { - final override string toString() { result = "Store" } - - final override predicate hasOperandInternal(OperandTag tag) { - tag instanceof StoreValueOperandTag - } - } - - class Add extends BinaryArithmeticOpcode, TAdd { - final override string toString() { result = "Add" } - } - - class Sub extends BinaryArithmeticOpcode, TSub { - final override string toString() { result = "Sub" } - } - - class Mul extends BinaryArithmeticOpcode, TMul { - final override string toString() { result = "Mul" } - } - - class Div extends BinaryArithmeticOpcode, TDiv { - final override string toString() { result = "Div" } - } - - class Rem extends BinaryArithmeticOpcode, TRem { - final override string toString() { result = "Rem" } - } - - class Negate extends UnaryArithmeticOpcode, TNegate { - final override string toString() { result = "Negate" } - } - - class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { - final override string toString() { result = "ShiftLeft" } - } - - class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { - final override string toString() { result = "ShiftRight" } - } - - class BitAnd extends BinaryBitwiseOpcode, TBitAnd { - final override string toString() { result = "BitAnd" } - } - - class BitOr extends BinaryBitwiseOpcode, TBitOr { - final override string toString() { result = "BitOr" } - } - - class BitXor extends BinaryBitwiseOpcode, TBitXor { - final override string toString() { result = "BitXor" } - } - - class BitComplement extends UnaryBitwiseOpcode, TBitComplement { - final override string toString() { result = "BitComplement" } - } - - class LogicalNot extends UnaryOpcode, TLogicalNot { - final override string toString() { result = "LogicalNot" } - } - - class CompareEQ extends CompareOpcode, TCompareEQ { - final override string toString() { result = "CompareEQ" } - } - - class CompareNE extends CompareOpcode, TCompareNE { - final override string toString() { result = "CompareNE" } - } - - class CompareLT extends RelationalOpcode, TCompareLT { - final override string toString() { result = "CompareLT" } - } - - class CompareGT extends RelationalOpcode, TCompareGT { - final override string toString() { result = "CompareGT" } - } - - class CompareLE extends RelationalOpcode, TCompareLE { - final override string toString() { result = "CompareLE" } - } - - class CompareGE extends RelationalOpcode, TCompareGE { - final override string toString() { result = "CompareGE" } - } - - class PointerAdd extends PointerOffsetOpcode, TPointerAdd { - final override string toString() { result = "PointerAdd" } - } - - class PointerSub extends PointerOffsetOpcode, TPointerSub { - final override string toString() { result = "PointerSub" } - } - - class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { - final override string toString() { result = "PointerDiff" } - } - - class Convert extends UnaryOpcode, TConvert { - final override string toString() { result = "Convert" } - } - - class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase { - final override string toString() { result = "ConvertToNonVirtualBase" } - } - - class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase { - final override string toString() { result = "ConvertToVirtualBase" } - } - - class ConvertToDerived extends UnaryOpcode, TConvertToDerived { - final override string toString() { result = "ConvertToDerived" } - } - - class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { - final override string toString() { result = "CheckedConvertOrNull" } - } - - class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { - final override string toString() { result = "CheckedConvertOrThrow" } - } - - class DynamicCastToVoid extends UnaryOpcode, TDynamicCastToVoid { - final override string toString() { result = "DynamicCastToVoid" } - } - - class VariableAddress extends Opcode, TVariableAddress { - final override string toString() { result = "VariableAddress" } - } - - class FieldAddress extends UnaryOpcode, TFieldAddress { - final override string toString() { result = "FieldAddress" } - } - - class ElementsAddress extends UnaryOpcode, TElementsAddress { - final override string toString() { result = "ElementsAddress" } - } - - class FunctionAddress extends Opcode, TFunctionAddress { - final override string toString() { result = "FunctionAddress" } - } - - class Constant extends Opcode, TConstant { - final override string toString() { result = "Constant" } - } - - class StringConstant extends Opcode, TStringConstant { - final override string toString() { result = "StringConstant" } - } - - class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { - final override string toString() { result = "ConditionalBranch" } - } - - class Switch extends OpcodeWithCondition, TSwitch { - final override string toString() { result = "Switch" } - } - - class Call extends Opcode, TCall { - final override string toString() { result = "Call" } - - final override predicate hasOperandInternal(OperandTag tag) { - tag instanceof CallTargetOperandTag - } - } - - class CatchByType extends CatchOpcode, TCatchByType { - final override string toString() { result = "CatchByType" } - } - - class CatchAny extends CatchOpcode, TCatchAny { - final override string toString() { result = "CatchAny" } - } - - class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue { - final override string toString() { result = "ThrowValue" } - } - - class ReThrow extends ThrowOpcode, TReThrow { - final override string toString() { result = "ReThrow" } - } - - class Unwind extends Opcode, TUnwind { - final override string toString() { result = "Unwind" } - } - - class AliasedDefinition extends Opcode, TAliasedDefinition { - final override string toString() { result = "AliasedDefinition" } - - final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess } - } - - class InitializeNonLocal extends Opcode, TInitializeNonLocal { - final override string toString() { result = "InitializeNonLocal" } - - final override MemoryAccessKind getWriteMemoryAccess() { - result instanceof NonLocalMemoryAccess - } - } - - class AliasedUse extends Opcode, TAliasedUse { - final override string toString() { result = "AliasedUse" } - - final override MemoryAccessKind getReadMemoryAccess() { result instanceof NonLocalMemoryAccess } - - final override predicate hasOperandInternal(OperandTag tag) { - tag instanceof SideEffectOperandTag - } - } - - class Phi extends Opcode, TPhi { - final override string toString() { result = "Phi" } - - final override MemoryAccessKind getWriteMemoryAccess() { result instanceof PhiMemoryAccess } - } - - class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { - final override string toString() { result = "BuiltIn" } - } - - class VarArgsStart extends UnaryOpcode, TVarArgsStart { - final override string toString() { result = "VarArgsStart" } - } - - class VarArgsEnd extends UnaryOpcode, TVarArgsEnd { - final override string toString() { result = "VarArgsEnd" } - } - - class VarArg extends UnaryOpcode, TVarArg { - final override string toString() { result = "VarArg" } - } - - class NextVarArg extends UnaryOpcode, TNextVarArg { - final override string toString() { result = "NextVarArg" } - } - - class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode, - ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect { - final override string toString() { result = "CallSideEffect" } - } - - class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, - TCallReadSideEffect { - final override string toString() { result = "CallReadSideEffect" } - } - - class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode, - TIndirectReadSideEffect { - final override string toString() { result = "IndirectReadSideEffect" } - } - - class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, - TIndirectMustWriteSideEffect { - final override string toString() { result = "IndirectMustWriteSideEffect" } - } - - class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode, - MayWriteOpcode, TIndirectMayWriteSideEffect { - final override string toString() { result = "IndirectMayWriteSideEffect" } - } - - class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode, - TBufferReadSideEffect { - final override string toString() { result = "BufferReadSideEffect" } - } - - class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, - TBufferMustWriteSideEffect { - final override string toString() { result = "BufferMustWriteSideEffect" } - } - - class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode, - MayWriteOpcode, TBufferMayWriteSideEffect { - final override string toString() { result = "BufferMayWriteSideEffect" } - } - - class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode, - TSizedBufferReadSideEffect { - final override string toString() { result = "SizedBufferReadSideEffect" } - } - - class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, - TSizedBufferMustWriteSideEffect { - final override string toString() { result = "SizedBufferMustWriteSideEffect" } - } - - class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode, - MayWriteOpcode, TSizedBufferMayWriteSideEffect { - final override string toString() { result = "SizedBufferMayWriteSideEffect" } - } - - class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode, - TInitializeDynamicAllocation { - final override string toString() { result = "InitializeDynamicAllocation" } - } - - class Chi extends Opcode, TChi { - final override string toString() { result = "Chi" } - - final override predicate hasOperandInternal(OperandTag tag) { - tag instanceof ChiTotalOperandTag - or - tag instanceof ChiPartialOperandTag - } - - final override MemoryAccessKind getWriteMemoryAccess() { - result instanceof ChiTotalMemoryAccess - } - } - - class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode, - MayReadOpcode, TInlineAsm { - final override string toString() { result = "InlineAsm" } - - final override predicate hasOperandInternal(OperandTag tag) { - tag instanceof SideEffectOperandTag - } - } - - class Unreached extends Opcode, TUnreached { - final override string toString() { result = "Unreached" } - } - - class NewObj extends Opcode, TNewObj { - final override string toString() { result = "NewObj" } - } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/EdgeKindInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/EdgeKindInternal.qll deleted file mode 100644 index 0ee01a30ac37..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/EdgeKindInternal.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRConfigurationInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRConfigurationInternal.qll deleted file mode 100644 index 0ee01a30ac37..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRConfigurationInternal.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRTypeInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRTypeInternal.qll deleted file mode 100644 index 0ee01a30ac37..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRTypeInternal.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OpcodeImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OpcodeImports.qll deleted file mode 100644 index 4fbc0f301693..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OpcodeImports.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTagInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTagInternal.qll deleted file mode 100644 index 0ee01a30ac37..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTagInternal.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariableInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariableInternal.qll deleted file mode 100644 index 4dee687dabd3..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariableInternal.qll +++ /dev/null @@ -1,7 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction as Construction -private import semmle.code.csharp.ir.implementation.TempVariableTag as TempVariableTag_ - -module Imports { - module TempVariableTag = TempVariableTag_; -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TempVariableTagInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TempVariableTagInternal.qll deleted file mode 100644 index 354a09f0f58e..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TempVariableTagInternal.qll +++ /dev/null @@ -1,6 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -private import semmle.code.csharp.ir.internal.TempVariableTag as TempVariableTag_ - -module Imports { - module TempVariableTag = TempVariableTag_; -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll deleted file mode 100644 index badd48552a5f..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll +++ /dev/null @@ -1,30 +0,0 @@ -import IRFunction -import Instruction -import IRBlock -import IRVariable -import Operand -private import internal.IRImports as Imports -import Imports::EdgeKind -import Imports::IRType -import Imports::MemoryAccessKind - -private newtype TIRPropertyProvider = MkIRPropertyProvider() - -/** - * Class that provides additional properties to be dumped for IR instructions and blocks when using - * the PrintIR module. Libraries that compute additional facts about IR elements can extend the - * single instance of this class to specify the additional properties computed by the library. - */ -class IRPropertyProvider extends TIRPropertyProvider { - string toString() { result = "IRPropertyProvider" } - - /** - * Gets the value of the property named `key` for the specified instruction. - */ - string getInstructionProperty(Instruction instruction, string key) { none() } - - /** - * Gets the value of the property named `key` for the specified block. - */ - string getBlockProperty(IRBlock block, string key) { none() } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll deleted file mode 100644 index 94ef73b27692..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll +++ /dev/null @@ -1,213 +0,0 @@ -private import internal.IRInternal -import Instruction -private import internal.IRBlockImports as Imports -import Imports::EdgeKind -private import Cached - -/** - * A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only - * incoming edges at the beginning of the sequence and the only outgoing edges at the end of the - * sequence. - * - * This class does not contain any members that query the predecessor or successor edges of the - * block. This allows different classes that extend `IRBlockBase` to expose different subsets of - * edges (e.g. ignoring unreachable edges). - * - * Most consumers should use the class `IRBlock`. - */ -class IRBlockBase extends TIRBlock { - final string toString() { result = getFirstInstruction(this).toString() } - - final Language::Location getLocation() { result = getFirstInstruction().getLocation() } - - final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } - - /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. - */ - int getDisplayIndex() { - exists(IRConfiguration::IRConfiguration config | - config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction()) - ) and - this = - rank[result + 1](IRBlock funcBlock, int sortOverride | - funcBlock.getEnclosingFunction() = getEnclosingFunction() and - // Ensure that the block containing `EnterFunction` always comes first. - if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction - then sortOverride = 0 - else sortOverride = 1 - | - funcBlock order by sortOverride, funcBlock.getUniqueId() - ) - } - - final Instruction getInstruction(int index) { result = getInstruction(this, index) } - - final PhiInstruction getAPhiInstruction() { - Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() - } - - final Instruction getAnInstruction() { - result = getInstruction(_) or - result = getAPhiInstruction() - } - - final Instruction getFirstInstruction() { result = getFirstInstruction(this) } - - final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } - - final int getInstructionCount() { result = getInstructionCount(this) } - - final IRFunction getEnclosingIRFunction() { - result = getFirstInstruction(this).getEnclosingIRFunction() - } - - final Language::Function getEnclosingFunction() { - result = getFirstInstruction(this).getEnclosingFunction() - } -} - -/** - * A basic block with additional information about its predecessor and successor edges. Each edge - * corresponds to the control flow between the last instruction of one block and the first - * instruction of another block. - */ -class IRBlock extends IRBlockBase { - final IRBlock getASuccessor() { blockSuccessor(this, result) } - - final IRBlock getAPredecessor() { blockSuccessor(result, this) } - - final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } - - final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } - - final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } - - final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } - - final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } - - pragma[noinline] - final IRBlock dominanceFrontier() { - dominates(result.getAPredecessor()) and - not strictlyDominates(result) - } - - /** - * Holds if this block is reachable from the entry point of its function - */ - final predicate isReachableFromFunctionEntry() { - this = getEnclosingIRFunction().getEntryBlock() or - getAPredecessor().isReachableFromFunctionEntry() - } -} - -private predicate startsBasicBlock(Instruction instr) { - not instr instanceof PhiInstruction and - not adjacentInBlock(_, instr) -} - -/** Holds if `i2` follows `i1` in a `IRBlock`. */ -private predicate adjacentInBlock(Instruction i1, Instruction i2) { - // - i2 must be the only successor of i1 - i2 = unique(Instruction i | i = i1.getASuccessor()) and - // - i1 must be the only predecessor of i2 - i1 = unique(Instruction i | i.getASuccessor() = i2) and - // - The edge between the two must be a GotoEdge. We just check that one - // exists since we've already checked that it's unique. - exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and - // - The edge must not be a back edge. This means we get the same back edges - // in the basic-block graph as we do in the raw CFG. - not exists(Construction::getInstructionBackEdgeSuccessor(i1, _)) - // This predicate could be simplified to remove one of the `unique`s if we - // were willing to rely on the CFG being well-formed and thus never having - // more than one successor to an instruction that has a `GotoEdge` out of it. -} - -private predicate isEntryBlock(TIRBlock block) { - block = MkIRBlock(any(EnterFunctionInstruction enter)) -} - -cached -private module Cached { - cached - newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) } - - /** Holds if `i` is the `index`th instruction the block starting with `first`. */ - private Instruction getInstructionFromFirst(Instruction first, int index) = - shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index) - - /** Holds if `i` is the `index`th instruction in `block`. */ - cached - Instruction getInstruction(TIRBlock block, int index) { - result = getInstructionFromFirst(getFirstInstruction(block), index) - } - - cached - int getInstructionCount(TIRBlock block) { result = strictcount(getInstruction(block, _)) } - - cached - predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - exists(Instruction predLast, Instruction succFirst | - predLast = getInstruction(pred, getInstructionCount(pred) - 1) and - succFirst = predLast.getSuccessor(kind) and - succ = MkIRBlock(succFirst) - ) - } - - pragma[noinline] - private predicate blockIdentity(TIRBlock b1, TIRBlock b2) { b1 = b2 } - - pragma[noopt] - cached - predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - backEdgeSuccessorRaw(pred, succ, kind) - or - // See the QLDoc on `backEdgeSuccessorRaw`. - exists(TIRBlock pred2 | - // Joining with `blockIdentity` is a performance trick to get - // `forwardEdgeRaw` on the RHS of a join, where it's fast. - blockIdentity(pred, pred2) and - forwardEdgeRaw+(pred, pred2) - ) and - blockSuccessor(pred, succ, kind) - } - - /** - * Holds if there is an edge from `pred` to `succ` that is not a back edge. - */ - private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) { - exists(EdgeKind kind | - blockSuccessor(pred, succ, kind) and - not backEdgeSuccessorRaw(pred, succ, kind) - ) - } - - /** - * Holds if the `kind`-edge from `pred` to `succ` is a back edge according to - * `Construction`. - * - * There could be loops of non-back-edges if there is a flaw in the IR - * construction or back-edge detection, and this could cause non-termination - * of subsequent analysis. To prevent that, a subsequent predicate further - * classifies all edges as back edges if they are involved in a loop of - * non-back-edges. - */ - private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - exists(Instruction predLast, Instruction succFirst | - predLast = getInstruction(pred, getInstructionCount(pred) - 1) and - succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and - succ = MkIRBlock(succFirst) - ) - } - - cached - predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { blockSuccessor(pred, succ, _) } - - cached - predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = - idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) -} - -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRConsistency.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRConsistency.qll deleted file mode 100644 index 65af34942b6b..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRConsistency.qll +++ /dev/null @@ -1,331 +0,0 @@ -private import IR -import InstructionConsistency // module is below -import IRTypeConsistency // module is in IRType.qll - -module InstructionConsistency { - private import internal.InstructionImports as Imports - private import Imports::OperandTag - private import Imports::Overlap - private import internal.IRInternal - - /** - * Holds if instruction `instr` is missing an expected operand with tag `tag`. - */ - query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) { - exists(OperandTag tag | - instr.getOpcode().hasOperand(tag) and - not exists(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - message = - "Instruction '" + instr.getOpcode().toString() + - "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } - - /** - * Holds if instruction `instr` has an unexpected operand with tag `tag`. - */ - query predicate unexpectedOperand(Instruction instr, OperandTag tag) { - exists(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - not instr.getOpcode().hasOperand(tag) and - not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and - not ( - instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag - ) and - not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) - } - - /** - * Holds if instruction `instr` has multiple operands with tag `tag`. - */ - query predicate duplicateOperand( - Instruction instr, string message, IRFunction func, string funcText - ) { - exists(OperandTag tag, int operandCount | - operandCount = - strictcount(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - operandCount > 1 and - message = - "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + - " in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } - - /** - * Holds if `Phi` instruction `instr` is missing an operand corresponding to - * the predecessor block `pred`. - */ - query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) { - pred = instr.getBlock().getAPredecessor() and - not exists(PhiInputOperand operand | - operand = instr.getAnOperand() and - operand.getPredecessorBlock() = pred - ) - } - - query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func, Instruction use | - not exists(operand.getType()) and - use = operand.getUse() and - func = use.getEnclosingFunction() and - message = - "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + - "' missing type in function '" + Language::getIdentityString(func) + "'." - ) - } - - query predicate duplicateChiOperand( - ChiInstruction chi, string message, IRFunction func, string funcText - ) { - chi.getTotal() = chi.getPartial() and - message = - "Chi instruction for " + chi.getPartial().toString() + - " has duplicate operands in function $@" and - func = chi.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - } - - query predicate sideEffectWithoutPrimary( - SideEffectInstruction instr, string message, IRFunction func, string funcText - ) { - not exists(instr.getPrimaryInstruction()) and - message = "Side effect instruction missing primary instruction in function $@" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - } - - /** - * Holds if an instruction, other than `ExitFunction`, has no successors. - */ - query predicate instructionWithoutSuccessor(Instruction instr) { - not exists(instr.getASuccessor()) and - not instr instanceof ExitFunctionInstruction and - // Phi instructions aren't linked into the instruction-level flow graph. - not instr instanceof PhiInstruction and - not instr instanceof UnreachedInstruction - } - - /** - * Holds if there are multiple (`n`) edges of kind `kind` from `source`, - * where `target` is among the targets of those edges. - */ - query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) { - n = strictcount(Instruction t | source.getSuccessor(kind) = t) and - n > 1 and - source.getSuccessor(kind) = target - } - - /** - * Holds if `instr` in `f` is part of a loop even though the AST of `f` - * contains no element that can cause loops. - */ - query predicate unexplainedLoop(Language::Function f, Instruction instr) { - exists(IRBlock block | - instr.getBlock() = block and - block.getEnclosingFunction() = f and - block.getASuccessor+() = block - ) and - not Language::hasPotentialLoop(f) - } - - /** - * Holds if a `Phi` instruction is present in a block with fewer than two - * predecessors. - */ - query predicate unnecessaryPhiInstruction(PhiInstruction instr) { - count(instr.getBlock().getAPredecessor()) < 2 - } - - /** - * Holds if a memory operand is connected to a definition with an unmodeled result. - */ - query predicate memoryOperandDefinitionIsUnmodeled( - Instruction instr, string message, IRFunction func, string funcText - ) { - exists(MemoryOperand operand, Instruction def | - operand = instr.getAnOperand() and - def = operand.getAnyDef() and - not def.isResultModeled() and - message = "Memory operand definition has unmodeled result in function '$@'" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } - - /** - * Holds if operand `operand` consumes a value that was defined in - * a different function. - */ - query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) { - operand.getUse() = instr and - operand.getAnyDef() = defInstr and - instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction() - } - - /** - * Holds if instruction `instr` is not in exactly one block. - */ - query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { - blockCount = count(instr.getBlock()) and - blockCount != 1 - } - - private predicate forwardEdge(IRBlock b1, IRBlock b2) { - b1.getASuccessor() = b2 and - not b1.getBackEdgeSuccessor(_) = b2 - } - - /** - * Holds if `f` contains a loop in which no edge is a back edge. - * - * This check ensures we don't have too _few_ back edges. - */ - query predicate containsLoopOfForwardEdges(IRFunction f) { - exists(IRBlock block | - forwardEdge+(block, block) and - block.getEnclosingIRFunction() = f - ) - } - - /** - * Holds if `block` is reachable from its function entry point but would not - * be reachable by traversing only forward edges. This check is skipped for - * functions containing `goto` statements as the property does not generally - * hold there. - * - * This check ensures we don't have too _many_ back edges. - */ - query predicate lostReachability(IRBlock block) { - exists(IRFunction f, IRBlock entry | - entry = f.getEntryBlock() and - entry.getASuccessor+() = block and - not forwardEdge+(entry, block) and - not Language::hasGoto(f.getFunction()) - ) - } - - /** - * Holds if the number of back edges differs between the `Instruction` graph - * and the `IRBlock` graph. - */ - query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) { - fromInstr = - count(Instruction i1, Instruction i2 | - i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2 - ) and - fromBlock = - count(IRBlock b1, IRBlock b2 | - b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2 - ) and - fromInstr != fromBlock - } - - /** - * Gets the point in the function at which the specified operand is evaluated. For most operands, - * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point - * of evaluation is at the end of the corresponding predecessor block. - */ - private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) { - block = operand.(PhiInputOperand).getPredecessorBlock() and - index = block.getInstructionCount() - or - exists(Instruction use | - use = operand.(NonPhiOperand).getUse() and - block.getInstruction(index) = use - ) - } - - /** - * Holds if `useOperand` has a definition that does not dominate the use. - */ - query predicate useNotDominatedByDefinition( - Operand useOperand, string message, IRFunction func, string funcText - ) { - exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | - pointOfEvaluation(useOperand, useBlock, useIndex) and - defInstr = useOperand.getAnyDef() and - ( - defInstr instanceof PhiInstruction and - defBlock = defInstr.getBlock() and - defIndex = -1 - or - defBlock.getInstruction(defIndex) = defInstr - ) and - not ( - defBlock.strictlyDominates(useBlock) - or - defBlock = useBlock and - defIndex < useIndex - ) and - message = - "Operand '" + useOperand.toString() + - "' is not dominated by its definition in function '$@'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } - - query predicate switchInstructionWithoutDefaultEdge( - SwitchInstruction switchInstr, string message, IRFunction func, string funcText - ) { - not exists(switchInstr.getDefaultSuccessor()) and - message = - "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and - func = switchInstr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - } - - /** - * Holds if `instr` is on the chain of chi/phi instructions for all aliased - * memory. - */ - private predicate isOnAliasedDefinitionChain(Instruction instr) { - instr instanceof AliasedDefinitionInstruction - or - isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) - or - isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) - } - - private predicate shouldBeConflated(Instruction instr) { - isOnAliasedDefinitionChain(instr) - or - instr.getOpcode() instanceof Opcode::InitializeNonLocal - } - - query predicate notMarkedAsConflated(Instruction instr) { - shouldBeConflated(instr) and - not instr.isResultConflated() - } - - query predicate wronglyMarkedAsConflated(Instruction instr) { - instr.isResultConflated() and - not shouldBeConflated(instr) - } - - query predicate invalidOverlap( - MemoryOperand useOperand, string message, IRFunction func, string funcText - ) { - exists(Overlap overlap | - overlap = useOperand.getDefinitionOverlap() and - overlap instanceof MayPartiallyOverlap and - message = - "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" + - overlap.toString() + "'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll deleted file mode 100644 index 9aea3e00d666..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll +++ /dev/null @@ -1,71 +0,0 @@ -private import internal.IRInternal -import Instruction - -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - -/** - * Represents the IR for a function. - */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - - /** - * Gets the entry point for this function. - */ - pragma[noinline] - final EnterFunctionInstruction getEnterFunctionInstruction() { - result.getEnclosingIRFunction() = this - } - - /** - * Gets the exit point for this function. - */ - pragma[noinline] - final ExitFunctionInstruction getExitFunctionInstruction() { - result.getEnclosingIRFunction() = this - } - - /** - * Gets the single return instruction for this function. - */ - pragma[noinline] - final ReturnInstruction getReturnInstruction() { result.getEnclosingIRFunction() = this } - - /** - * Gets the variable used to hold the return value of this function. If this - * function does not return a value, this predicate does not hold. - */ - pragma[noinline] - final IRReturnVariable getReturnVariable() { result.getEnclosingIRFunction() = this } - - /** - * Gets the block containing the entry point of this function. - */ - pragma[noinline] - final IRBlock getEntryBlock() { result.getFirstInstruction() = getEnterFunctionInstruction() } - - /** - * Gets all instructions in this function. - */ - final Instruction getAnInstruction() { result.getEnclosingIRFunction() = this } - - /** - * Gets all blocks in this function. - */ - final IRBlock getABlock() { result.getEnclosingIRFunction() = this } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll deleted file mode 100644 index 9f2a0d4ea281..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll +++ /dev/null @@ -1,276 +0,0 @@ -private import internal.IRInternal -import IRFunction -private import internal.IRVariableImports as Imports -import Imports::TempVariableTag -private import Imports::IRUtilities -private import Imports::TTempVariableTag -private import Imports::TIRVariable -private import Imports::IRType - -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - -/** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). - */ -class IRVariable extends TIRVariable { - Language::Function func; - - IRVariable() { - this = TIRUserVariable(_, _, func) or - this = TIRTempVariable(func, _, _, _) or - this = TIRStringLiteral(func, _, _, _) or - this = TIRDynamicInitializationFlag(func, _, _) - } - - string toString() { none() } - - /** - * Holds if this variable's value cannot be changed within a function. Currently used for string - * literals, but could also apply to `const` global and static variables. - */ - predicate isReadOnly() { none() } - - /** - * Gets the type of the variable. - */ - final Language::Type getType() { getLanguageType().hasType(result, false) } - - /** - * Gets the language-neutral type of the variable. - */ - final IRType getIRType() { result = getLanguageType().getIRType() } - - /** - * Gets the type of the variable. - */ - Language::LanguageType getLanguageType() { none() } - - /** - * Gets the AST node that declared this variable, or that introduced this - * variable as part of the AST-to-IR translation. - */ - Language::AST getAST() { none() } - - /** - * Gets an identifier string for the variable. This identifier is unique - * within the function. - */ - string getUniqueId() { none() } - - /** - * Gets the source location of this variable. - */ - final Language::Location getLocation() { result = getAST().getLocation() } - - /** - * Gets the IR for the function that references this variable. - */ - final IRFunction getEnclosingIRFunction() { result.getFunction() = func } - - /** - * Gets the function that references this variable. - */ - final Language::Function getEnclosingFunction() { result = func } -} - -/** - * A user-declared variable referenced by the IR for a function. - */ -class IRUserVariable extends IRVariable, TIRUserVariable { - Language::Variable var; - Language::LanguageType type; - - IRUserVariable() { this = TIRUserVariable(var, type, func) } - - final override string toString() { result = getVariable().toString() } - - final override Language::AST getAST() { result = var } - - final override string getUniqueId() { - result = getVariable().toString() + " " + getVariable().getLocation().toString() - } - - final override Language::LanguageType getLanguageType() { result = type } - - /** - * Gets the original user-declared variable. - */ - Language::Variable getVariable() { result = var } -} - -/** - * A variable (user-declared or temporary) that is allocated on the stack. This includes all - * parameters, non-static local variables, and temporary variables. - */ -class IRAutomaticVariable extends IRVariable { - IRAutomaticVariable() { - exists(Language::Variable var | - this = TIRUserVariable(var, _, func) and - Language::isVariableAutomatic(var) - ) - or - this = TIRTempVariable(func, _, _, _) - } -} - -/** - * A user-declared variable that is allocated on the stack. This includes all parameters and - * non-static local variables. - */ -class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { - override Language::AutomaticVariable var; - - final override Language::AutomaticVariable getVariable() { result = var } -} - -/** - * A user-declared variable that is not allocated on the stack. This includes all global variables, - * namespace-scope variables, static fields, and static local variables. - */ -class IRStaticUserVariable extends IRUserVariable { - override Language::StaticVariable var; - - IRStaticUserVariable() { not Language::isVariableAutomatic(var) } - - final override Language::StaticVariable getVariable() { result = var } -} - -/** - * A variable that is not user-declared. This includes temporary variables generated as part of IR - * construction, as well as string literals. - */ -class IRGeneratedVariable extends IRVariable { - Language::AST ast; - Language::LanguageType type; - - IRGeneratedVariable() { - this = TIRTempVariable(func, ast, _, type) or - this = TIRStringLiteral(func, ast, type, _) or - this = TIRDynamicInitializationFlag(func, ast, type) - } - - final override Language::LanguageType getLanguageType() { result = type } - - final override Language::AST getAST() { result = ast } - - override string toString() { result = getBaseString() + getLocationString() } - - override string getUniqueId() { none() } - - final string getLocationString() { - result = - ast.getLocation().getStartLine().toString() + ":" + - ast.getLocation().getStartColumn().toString() - } - - string getBaseString() { none() } -} - -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - -/** - * A temporary variable introduced by IR construction. The most common examples are the variable - * generated to hold the return value of a function, or the variable generated to hold the result of - * a condition operator (`a ? b : c`). - */ -class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { - TempVariableTag tag; - - IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } - - final override string getUniqueId() { - result = "Temp: " + Construction::getTempVariableUniqueId(this) - } - - final TempVariableTag getTag() { result = tag } - - override string getBaseString() { result = "#temp" } -} - -/** - * A temporary variable generated to hold the return value of a function. - */ -class IRReturnVariable extends IRTempVariable { - IRReturnVariable() { tag = ReturnValueTempVar() } - - final override string toString() { result = "#return" } -} - -/** - * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. - */ -class IRThrowVariable extends IRTempVariable { - IRThrowVariable() { tag = ThrowTempVar() } - - final override string getBaseString() { result = "#throw" } -} - -/** - * A temporary variable generated to hold the contents of all arguments passed to the `...` of a - * function that accepts a variable number of arguments. - */ -class IREllipsisVariable extends IRTempVariable { - IREllipsisVariable() { tag = EllipsisTempVar() } - - final override string toString() { result = "#ellipsis" } -} - -/** - * A temporary variable generated to hold the `this` pointer. - */ -class IRThisVariable extends IRTempVariable { - IRThisVariable() { tag = ThisTempVar() } - - final override string toString() { result = "#this" } -} - -/** - * A variable generated to represent the contents of a string literal. This variable acts much like - * a read-only global variable. - */ -class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { - Language::StringLiteral literal; - - IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) } - - final override predicate isReadOnly() { any() } - - final override string getUniqueId() { - result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal) - } - - final override string getBaseString() { result = "#string" } - - final Language::StringLiteral getLiteral() { result = literal } -} - -/** - * A variable generated to track whether a specific non-stack variable has been initialized. This is - * used to model the runtime initialization of static local variables in C++, as well as static - * fields in C#. - */ -class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag { - Language::Variable var; - - IRDynamicInitializationFlag() { - this = TIRDynamicInitializationFlag(func, var, type) and ast = var - } - - final override string toString() { result = var.toString() + "#init" } - - final Language::Variable getVariable() { result = var } - - final override string getUniqueId() { - result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString() - } - - final override string getBaseString() { result = "#init:" + var.toString() + ":" } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll deleted file mode 100644 index 9c83a3d99f0c..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll +++ /dev/null @@ -1,1374 +0,0 @@ -private import internal.IRInternal -import IRFunction -import IRBlock -import IRVariable -import Operand -private import internal.InstructionImports as Imports -import Imports::EdgeKind -import Imports::IRType -import Imports::MemoryAccessKind -import Imports::Opcode -private import Imports::OperandTag - -/** - * Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified - * `File` and line number. Used for assigning register names when printing IR. - */ -private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) { - exists(IRConfiguration::IRConfiguration config | - config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction()) - ) and - exists(Language::Location location | - irFunc = result.getEnclosingIRFunction() and - location = result.getLocation() and - file = location.getFile() and - line = location.getStartLine() - ) -} - -/** - * Represents a single operation in the IR. - */ -class Instruction extends Construction::TInstruction { - final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } - - /** - * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what - * would be printed by PrintIR.ql. For example: - * - * `mu0_28(int) = Store r0_26, r0_27` - */ - final string getDumpString() { - result = getResultString() + " = " + getOperationString() + " " + getOperandsString() - } - - private predicate shouldGenerateDumpStrings() { - exists(IRConfiguration::IRConfiguration config | - config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction()) - ) - } - - /** - * Gets a string describing the operation of this instruction. This includes - * the opcode and the immediate value, if any. For example: - * - * VariableAddress[x] - */ - final string getOperationString() { - shouldGenerateDumpStrings() and - if exists(getImmediateString()) - then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" - else result = getOperationPrefix() + getOpcode().toString() - } - - /** - * Gets a string describing the immediate value of this instruction, if any. - */ - string getImmediateString() { none() } - - private string getOperationPrefix() { - shouldGenerateDumpStrings() and - if this instanceof SideEffectInstruction then result = "^" else result = "" - } - - private string getResultPrefix() { - shouldGenerateDumpStrings() and - if getResultIRType() instanceof IRVoidType - then result = "v" - else - if hasMemoryResult() - then if isResultModeled() then result = "m" else result = "mu" - else result = "r" - } - - /** - * Gets the zero-based index of this instruction within its block. This is - * used by debugging and printing code only. - */ - int getDisplayIndexInBlock() { - shouldGenerateDumpStrings() and - exists(IRBlock block | - this = block.getInstruction(result) - or - this = - rank[-result - 1](PhiInstruction phiInstr | - phiInstr = block.getAPhiInstruction() - | - phiInstr order by phiInstr.getUniqueId() - ) - ) - } - - private int getLineRank() { - shouldGenerateDumpStrings() and - this = - rank[result](Instruction instr | - instr = - getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), - getLocation().getStartLine()) - | - instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() - ) - } - - /** - * Gets a human-readable string that uniquely identifies this instruction - * within the function. This string is used to refer to this instruction when - * printing IR dumps. - * - * Example: `r1_1` - */ - string getResultId() { - shouldGenerateDumpStrings() and - result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() - } - - /** - * Gets a string describing the result of this instruction, suitable for - * display in IR dumps. This consists of the result ID plus the type of the - * result. - * - * Example: `r1_1(int*)` - */ - final string getResultString() { - shouldGenerateDumpStrings() and - result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" - } - - /** - * Gets a string describing the operands of this instruction, suitable for - * display in IR dumps. - * - * Example: `func:r3_4, this:r3_5` - */ - string getOperandsString() { - shouldGenerateDumpStrings() and - result = - concat(Operand operand | - operand = getAnOperand() - | - operand.getDumpString(), ", " order by operand.getDumpSortOrder() - ) - } - - /** - * Gets a string identifier for this function that is unique among all - * instructions in the same function. - * - * This is used for sorting IR output for tests, and is likely to be - * inefficient for any other use. - */ - final string getUniqueId() { result = Construction::getInstructionUniqueId(this) } - - /** - * Gets the basic block that contains this instruction. - */ - final IRBlock getBlock() { result.getAnInstruction() = this } - - /** - * Gets the function that contains this instruction. - */ - final Language::Function getEnclosingFunction() { - result = getEnclosingIRFunction().getFunction() - } - - /** - * Gets the IRFunction object that contains the IR for this instruction. - */ - final IRFunction getEnclosingIRFunction() { - result = Construction::getInstructionEnclosingIRFunction(this) - } - - /** - * Gets the AST that caused this instruction to be generated. - */ - final Language::AST getAST() { result = Construction::getInstructionAST(this) } - - /** - * Gets the location of the source code for this instruction. - */ - final Language::Location getLocation() { result = getAST().getLocation() } - - /** - * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a - * conversion. - */ - final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) - } - - /** - * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. - */ - final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) - } - - final Language::LanguageType getResultLanguageType() { - result = Construction::getInstructionResultType(this) - } - - /** - * Gets the type of the result produced by this instruction. If the instruction does not produce - * a result, its result type will be `IRVoidType`. - */ - final IRType getResultIRType() { result = getResultLanguageType().getIRType() } - - /** - * Gets the type of the result produced by this instruction. If the - * instruction does not produce a result, its result type will be `VoidType`. - * - * If `isGLValue()` holds, then the result type of this instruction should be - * thought of as "pointer to `getResultType()`". - */ - final Language::Type getResultType() { - exists(Language::LanguageType resultType | - resultType = getResultLanguageType() and - ( - resultType.hasUnspecifiedType(result, _) - or - not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType - ) - ) - } - - /** - * Holds if the result produced by this instruction is a glvalue. If this - * holds, the result of the instruction represents the address of a location, - * and the type of the location is given by `getResultType()`. If this does - * not hold, the result of the instruction represents a value whose type is - * given by `getResultType()`. - * - * For example, the statement `y = x;` generates the following IR: - * r1_0(glval: int) = VariableAddress[x] - * r1_1(int) = Load r1_0, mu0_1 - * r1_2(glval: int) = VariableAddress[y] - * mu1_3(int) = Store r1_2, r1_1 - * - * The result of each `VariableAddress` instruction is a glvalue of type - * `int`, representing the address of the corresponding integer variable. The - * result of the `Load` instruction is a prvalue of type `int`, representing - * the integer value loaded from variable `x`. - */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } - - /** - * Gets the size of the result produced by this instruction, in bytes. If the - * result does not have a known constant size, this predicate does not hold. - * - * If `this.isGLValue()` holds for this instruction, the value of - * `getResultSize()` will always be the size of a pointer. - */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } - - /** - * Gets the opcode that specifies the operation performed by this instruction. - */ - final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } - - /** - * Gets all direct uses of the result of this instruction. The result can be - * an `Operand` for which `isDefinitionInexact` holds. - */ - final Operand getAUse() { result.getAnyDef() = this } - - /** - * Gets all of this instruction's operands. - */ - final Operand getAnOperand() { result.getUse() = this } - - /** - * Holds if this instruction produces a memory result. - */ - final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } - - /** - * Gets the kind of memory access performed by this instruction's result. - * Holds only for instructions with a memory result. - */ - pragma[inline] - final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } - - /** - * Holds if the memory access performed by this instruction's result will not always write to - * every bit in the memory location. This is most commonly used for memory accesses that may or - * may not actually occur depending on runtime state (for example, the write side effect of an - * output parameter that is not written to on all paths), or for accesses where the memory - * location is a conservative estimate of the memory that might actually be accessed at runtime - * (for example, the global side effects of a function call). - */ - pragma[inline] - final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } - - /** - * Gets the operand that holds the memory address to which this instruction stores its - * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` - * is `r1`. - */ - final AddressOperand getResultAddressOperand() { - getResultMemoryAccess().usesAddressOperand() and - result.getUse() = this - } - - /** - * Gets the instruction that holds the exact memory address to which this instruction stores its - * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` - * is the instruction that defines `r1`. - */ - final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } - - /** - * Holds if the result of this instruction is precisely modeled in SSA. Always - * holds for a register result. For a memory result, a modeled result is - * connected to its actual uses. An unmodeled result has no uses. - * - * For example: - * ``` - * int x = 1; - * int *p = &x; - * int y = *p; - * ``` - * In non-aliased SSA, `x` will not be modeled because it has its address - * taken. In that case, `isResultModeled()` would not hold for the result of - * the `Store` to `x`. - */ - final predicate isResultModeled() { - // Register results are always in SSA form. - not hasMemoryResult() or - Construction::hasModeledMemoryResult(this) - } - - /** - * Holds if this is an instruction with a memory result that represents a - * conflation of more than one memory allocation. - * - * This happens in practice when dereferencing a pointer that cannot be - * tracked back to a single local allocation. Such memory is instead modeled - * as originating on the `AliasedDefinitionInstruction` at the entry of the - * function. - */ - final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } - - /** - * Gets the successor of this instruction along the control flow edge - * specified by `kind`. - */ - final Instruction getSuccessor(EdgeKind kind) { - result = Construction::getInstructionSuccessor(this, kind) - } - - /** - * Gets the a _back-edge successor_ of this instruction along the control - * flow edge specified by `kind`. A back edge in the control-flow graph is - * intuitively the edge that goes back around a loop. If all back edges are - * removed from the control-flow graph, it becomes acyclic. - */ - final Instruction getBackEdgeSuccessor(EdgeKind kind) { - // We don't take these edges from - // `Construction::getInstructionBackEdgeSuccessor` since that relation has - // not been treated to remove any loops that might be left over due to - // flaws in the IR construction or back-edge detection. - exists(IRBlock block | - block = this.getBlock() and - this = block.getLastInstruction() and - result = block.getBackEdgeSuccessor(kind).getFirstInstruction() - ) - } - - /** - * Gets all direct successors of this instruction. - */ - final Instruction getASuccessor() { result = getSuccessor(_) } - - /** - * Gets a predecessor of this instruction such that the predecessor reaches - * this instruction along the control flow edge specified by `kind`. - */ - final Instruction getPredecessor(EdgeKind kind) { result.getSuccessor(kind) = this } - - /** - * Gets all direct predecessors of this instruction. - */ - final Instruction getAPredecessor() { result = getPredecessor(_) } -} - -class VariableInstruction extends Instruction { - IRVariable var; - - VariableInstruction() { var = Construction::getInstructionVariable(this) } - - override string getImmediateString() { result = var.toString() } - - final IRVariable getIRVariable() { result = var } - - /** - * Gets the AST variable that this instruction's IR variable refers to, if one exists. - */ - final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } -} - -class FieldInstruction extends Instruction { - Language::Field field; - - FieldInstruction() { field = Construction::getInstructionField(this) } - - final override string getImmediateString() { result = field.toString() } - - final Language::Field getField() { result = field } -} - -class FunctionInstruction extends Instruction { - Language::Function funcSymbol; - - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } - - final override string getImmediateString() { result = funcSymbol.toString() } - - final Language::Function getFunctionSymbol() { result = funcSymbol } -} - -class ConstantValueInstruction extends Instruction { - string value; - - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } - - final override string getImmediateString() { result = value } - - final string getValue() { result = value } -} - -class IndexedInstruction extends Instruction { - int index; - - IndexedInstruction() { index = Construction::getInstructionIndex(this) } - - final override string getImmediateString() { result = index.toString() } - - final int getIndex() { result = index } -} - -class EnterFunctionInstruction extends Instruction { - EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } -} - -class VariableAddressInstruction extends VariableInstruction { - VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } -} - -class InitializeParameterInstruction extends VariableInstruction { - InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } - - final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } -} - -class InitializeIndirectionInstruction extends VariableInstruction { - InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } - - final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } -} - -/** - * An instruction that initializes the `this` pointer parameter of the enclosing function. - */ -class InitializeThisInstruction extends Instruction { - InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } -} - -class FieldAddressInstruction extends FieldInstruction { - FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } - - final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } - - final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } -} - -/** - * An instruction that produces a well-defined but unknown result and has - * unknown side effects, including side effects that are not conservatively - * modeled in the SSA graph. - * - * This type of instruction appears when there is an `ErrorExpr` in the AST, - * meaning that the extractor could not understand the expression and therefore - * produced a partial AST. Queries that give alerts when some action is _not_ - * taken may want to ignore any function that contains an `ErrorInstruction`. - */ -class ErrorInstruction extends Instruction { - ErrorInstruction() { getOpcode() instanceof Opcode::Error } -} - -class UninitializedInstruction extends VariableInstruction { - UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } - - /** - * Gets the variable that is uninitialized. - */ - final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } -} - -class NoOpInstruction extends Instruction { - NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } -} - -class ReturnInstruction extends Instruction { - ReturnInstruction() { getOpcode() instanceof ReturnOpcode } -} - -class ReturnVoidInstruction extends ReturnInstruction { - ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } -} - -class ReturnValueInstruction extends ReturnInstruction { - ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } - - final LoadOperand getReturnValueOperand() { result = getAnOperand() } - - final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } -} - -class ReturnIndirectionInstruction extends VariableInstruction { - ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } - - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } - - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } - - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } - - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } - - /** - * Gets the parameter for which this instruction reads the final pointed-to value within the - * function. - */ - final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } -} - -class CopyInstruction extends Instruction { - CopyInstruction() { getOpcode() instanceof CopyOpcode } - - Operand getSourceValueOperand() { none() } - - final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } -} - -class CopyValueInstruction extends CopyInstruction, UnaryInstruction { - CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } - - final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } -} - -class LoadInstruction extends CopyInstruction { - LoadInstruction() { getOpcode() instanceof Opcode::Load } - - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } - - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } - - final override LoadOperand getSourceValueOperand() { result = getAnOperand() } -} - -class StoreInstruction extends CopyInstruction { - StoreInstruction() { getOpcode() instanceof Opcode::Store } - - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } - - final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } - - final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } -} - -class ConditionalBranchInstruction extends Instruction { - ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } - - final ConditionOperand getConditionOperand() { result = getAnOperand() } - - final Instruction getCondition() { result = getConditionOperand().getDef() } - - final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } - - final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } -} - -class ExitFunctionInstruction extends Instruction { - ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } -} - -class ConstantInstruction extends ConstantValueInstruction { - ConstantInstruction() { getOpcode() instanceof Opcode::Constant } -} - -class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } -} - -class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } -} - -class StringConstantInstruction extends VariableInstruction { - override IRStringLiteral var; - - final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } - - final Language::StringLiteral getValue() { result = var.getLiteral() } -} - -class BinaryInstruction extends Instruction { - BinaryInstruction() { getOpcode() instanceof BinaryOpcode } - - final LeftOperand getLeftOperand() { result = getAnOperand() } - - final RightOperand getRightOperand() { result = getAnOperand() } - - final Instruction getLeft() { result = getLeftOperand().getDef() } - - final Instruction getRight() { result = getRightOperand().getDef() } - - /** - * Holds if this instruction's operands are `op1` and `op2`, in either order. - */ - final predicate hasOperands(Operand op1, Operand op2) { - op1 = getLeftOperand() and op2 = getRightOperand() - or - op1 = getRightOperand() and op2 = getLeftOperand() - } -} - -class ArithmeticInstruction extends Instruction { - ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } -} - -class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } - -class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } - -class AddInstruction extends BinaryArithmeticInstruction { - AddInstruction() { getOpcode() instanceof Opcode::Add } -} - -class SubInstruction extends BinaryArithmeticInstruction { - SubInstruction() { getOpcode() instanceof Opcode::Sub } -} - -class MulInstruction extends BinaryArithmeticInstruction { - MulInstruction() { getOpcode() instanceof Opcode::Mul } -} - -class DivInstruction extends BinaryArithmeticInstruction { - DivInstruction() { getOpcode() instanceof Opcode::Div } -} - -class RemInstruction extends BinaryArithmeticInstruction { - RemInstruction() { getOpcode() instanceof Opcode::Rem } -} - -class NegateInstruction extends UnaryArithmeticInstruction { - NegateInstruction() { getOpcode() instanceof Opcode::Negate } -} - -class BitwiseInstruction extends Instruction { - BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } -} - -class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } - -class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } - -class BitAndInstruction extends BinaryBitwiseInstruction { - BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } -} - -class BitOrInstruction extends BinaryBitwiseInstruction { - BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } -} - -class BitXorInstruction extends BinaryBitwiseInstruction { - BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } -} - -class ShiftLeftInstruction extends BinaryBitwiseInstruction { - ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } -} - -class ShiftRightInstruction extends BinaryBitwiseInstruction { - ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } -} - -class PointerArithmeticInstruction extends BinaryInstruction { - int elementSize; - - PointerArithmeticInstruction() { - getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) - } - - final override string getImmediateString() { result = elementSize.toString() } - - final int getElementSize() { result = elementSize } -} - -class PointerOffsetInstruction extends PointerArithmeticInstruction { - PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } -} - -class PointerAddInstruction extends PointerOffsetInstruction { - PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } -} - -class PointerSubInstruction extends PointerOffsetInstruction { - PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } -} - -class PointerDiffInstruction extends PointerArithmeticInstruction { - PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } -} - -class UnaryInstruction extends Instruction { - UnaryInstruction() { getOpcode() instanceof UnaryOpcode } - - final UnaryOperand getUnaryOperand() { result = getAnOperand() } - - final Instruction getUnary() { result = getUnaryOperand().getDef() } -} - -class ConvertInstruction extends UnaryInstruction { - ConvertInstruction() { getOpcode() instanceof Opcode::Convert } -} - -class CheckedConvertOrNullInstruction extends UnaryInstruction { - CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } -} - -/** - * Represents an instruction that converts between two addresses - * related by inheritance. - */ -class InheritanceConversionInstruction extends UnaryInstruction { - Language::Class baseClass; - Language::Class derivedClass; - - InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) - } - - final override string getImmediateString() { - result = derivedClass.toString() + " : " + baseClass.toString() - } - - /** - * Gets the `ClassDerivation` for the inheritance relationship between - * the base and derived classes. This predicate does not hold if the - * conversion is to an indirect virtual base class. - */ - final Language::ClassDerivation getDerivation() { - result.getBaseClass() = baseClass and result.getDerivedClass() = derivedClass - } - - /** - * Gets the base class of the conversion. This will be either a direct - * base class of the derived class, or a virtual base class of the - * derived class. - */ - final Language::Class getBaseClass() { result = baseClass } - - /** - * Gets the derived class of the conversion. - */ - final Language::Class getDerivedClass() { result = derivedClass } -} - -/** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. - */ -class ConvertToBaseInstruction extends InheritanceConversionInstruction { - ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } -} - -/** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. - */ -class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } -} - -/** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. - */ -class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } -} - -/** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. - */ -class ConvertToDerivedInstruction extends InheritanceConversionInstruction { - ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } -} - -class BitComplementInstruction extends UnaryBitwiseInstruction { - BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } -} - -class LogicalNotInstruction extends UnaryInstruction { - LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } -} - -class CompareInstruction extends BinaryInstruction { - CompareInstruction() { getOpcode() instanceof CompareOpcode } -} - -class CompareEQInstruction extends CompareInstruction { - CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } -} - -class CompareNEInstruction extends CompareInstruction { - CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } -} - -/** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. - */ -class RelationalInstruction extends CompareInstruction { - RelationalInstruction() { getOpcode() instanceof RelationalOpcode } - - /** - * Gets the operand on the "greater" (or "greater-or-equal") side - * of this relational instruction, that is, the side that is larger - * if the overall instruction evaluates to `true`; for example on - * `x <= 20` this is the `20`, and on `y > 0` it is `y`. - */ - Instruction getGreater() { none() } - - /** - * Gets the operand on the "lesser" (or "lesser-or-equal") side - * of this relational instruction, that is, the side that is smaller - * if the overall instruction evaluates to `true`; for example on - * `x <= 20` this is `x`, and on `y > 0` it is the `0`. - */ - Instruction getLesser() { none() } - - /** - * Holds if this relational instruction is strict (is not an "or-equal" instruction). - */ - predicate isStrict() { none() } -} - -class CompareLTInstruction extends RelationalInstruction { - CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } - - override Instruction getLesser() { result = getLeft() } - - override Instruction getGreater() { result = getRight() } - - override predicate isStrict() { any() } -} - -class CompareGTInstruction extends RelationalInstruction { - CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } - - override Instruction getLesser() { result = getRight() } - - override Instruction getGreater() { result = getLeft() } - - override predicate isStrict() { any() } -} - -class CompareLEInstruction extends RelationalInstruction { - CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } - - override Instruction getLesser() { result = getLeft() } - - override Instruction getGreater() { result = getRight() } - - override predicate isStrict() { none() } -} - -class CompareGEInstruction extends RelationalInstruction { - CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } - - override Instruction getLesser() { result = getRight() } - - override Instruction getGreater() { result = getLeft() } - - override predicate isStrict() { none() } -} - -class SwitchInstruction extends Instruction { - SwitchInstruction() { getOpcode() instanceof Opcode::Switch } - - final ConditionOperand getExpressionOperand() { result = getAnOperand() } - - final Instruction getExpression() { result = getExpressionOperand().getDef() } - - final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } - - final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } -} - -/** - * An instruction that calls a function. - */ -class CallInstruction extends Instruction { - CallInstruction() { getOpcode() instanceof Opcode::Call } - - /** - * Gets the operand the specifies the target function of the call. - */ - final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } - - /** - * Gets the `Instruction` that computes the target function of the call. This is usually a - * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a - * function pointer. - */ - final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } - - /** - * Gets all of the argument operands of the call, including the `this` pointer, if any. - */ - final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } - - /** - * Gets the `Function` that the call targets, if this is statically known. - */ - final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() - } - - /** - * Gets all of the arguments of the call, including the `this` pointer, if any. - */ - final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } - - /** - * Gets the `this` pointer argument operand of the call, if any. - */ - final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } - - /** - * Gets the `this` pointer argument of the call, if any. - */ - final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } - - /** - * Gets the argument operand at the specified index. - */ - final PositionalArgumentOperand getPositionalArgumentOperand(int index) { - result = getAnOperand() and - result.getIndex() = index - } - - /** - * Gets the argument at the specified index. - */ - final Instruction getPositionalArgument(int index) { - result = getPositionalArgumentOperand(index).getDef() - } - - /** - * Gets the number of arguments of the call, including the `this` pointer, if any. - */ - final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } -} - -/** - * An instruction representing a side effect of a function call. - */ -class SideEffectInstruction extends Instruction { - SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } - - final Instruction getPrimaryInstruction() { - result = Construction::getPrimaryInstructionForSideEffect(this) - } -} - -/** - * An instruction representing the side effect of a function call on any memory that might be - * accessed by that call. - */ -class CallSideEffectInstruction extends SideEffectInstruction { - CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } -} - -/** - * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. - */ -class CallReadSideEffectInstruction extends SideEffectInstruction { - CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } -} - -/** - * An instruction representing a read side effect of a function call on a - * specific parameter. - */ -class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } - - /** Gets the operand for the value that will be read from this instruction, if known. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } - - /** Gets the value that will be read from this instruction, if known. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } - - /** Gets the operand for the address from which this instruction may read. */ - final AddressOperand getArgumentOperand() { result = getAnOperand() } - - /** Gets the address from which this instruction may read. */ - final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } -} - -/** - * An instruction representing the read of an indirect parameter within a function call. - */ -class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { - IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } -} - -/** - * An instruction representing the read of an indirect buffer parameter within a function call. - */ -class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { - BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } -} - -/** - * An instruction representing the read of an indirect buffer parameter within a function call. - */ -class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { - SizedBufferReadSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferReadSideEffect - } - - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } -} - -/** - * An instruction representing a write side effect of a function call on a - * specific parameter. - */ -class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } -} - -/** - * An instruction representing the write of an indirect parameter within a function call. - */ -class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { - IndirectMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMustWriteSideEffect - } -} - -/** - * An instruction representing the write of an indirect buffer parameter within a function call. The - * entire buffer is overwritten. - */ -class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::BufferMustWriteSideEffect - } -} - -/** - * An instruction representing the write of an indirect buffer parameter within a function call. The - * entire buffer is overwritten. - */ -class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { - SizedBufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect - } - - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } -} - -/** - * An instruction representing the potential write of an indirect parameter within a function call. - * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. - * written. - */ -class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - IndirectMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMayWriteSideEffect - } -} - -/** - * An instruction representing the write of an indirect buffer parameter within a function call. - * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. - */ -class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } -} - -/** - * An instruction representing the write of an indirect buffer parameter within a function call. - * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. - */ -class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - SizedBufferMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect - } - - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } -} - -/** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a - * call to `malloc`. - */ -class InitializeDynamicAllocationInstruction extends SideEffectInstruction { - InitializeDynamicAllocationInstruction() { - getOpcode() instanceof Opcode::InitializeDynamicAllocation - } - - /** - * Gets the address of the allocation this instruction is initializing. - */ - final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } - - /** - * Gets the operand for the allocation this instruction is initializing. - */ - final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } -} - -/** - * An instruction representing a GNU or MSVC inline assembly statement. - */ -class InlineAsmInstruction extends Instruction { - InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } -} - -/** - * An instruction that throws an exception. - */ -class ThrowInstruction extends Instruction { - ThrowInstruction() { getOpcode() instanceof ThrowOpcode } -} - -/** - * An instruction that throws a new exception. - */ -class ThrowValueInstruction extends ThrowInstruction { - ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } - - /** - * Gets the address operand of the exception thrown by this instruction. - */ - final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } - - /** - * Gets the address of the exception thrown by this instruction. - */ - final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } - - /** - * Gets the operand for the exception thrown by this instruction. - */ - final LoadOperand getExceptionOperand() { result = getAnOperand() } - - /** - * Gets the exception thrown by this instruction. - */ - final Instruction getException() { result = getExceptionOperand().getDef() } -} - -/** - * An instruction that re-throws the current exception. - */ -class ReThrowInstruction extends ThrowInstruction { - ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } -} - -/** - * An instruction that exits the current function by propagating an exception. - */ -class UnwindInstruction extends Instruction { - UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } -} - -/** - * An instruction that starts a `catch` handler. - */ -class CatchInstruction extends Instruction { - CatchInstruction() { getOpcode() instanceof CatchOpcode } -} - -/** - * An instruction that catches an exception of a specific type. - */ -class CatchByTypeInstruction extends CatchInstruction { - Language::LanguageType exceptionType; - - CatchByTypeInstruction() { - getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) - } - - final override string getImmediateString() { result = exceptionType.toString() } - - /** - * Gets the type of exception to be caught. - */ - final Language::LanguageType getExceptionType() { result = exceptionType } -} - -/** - * An instruction that catches any exception. - */ -class CatchAnyInstruction extends CatchInstruction { - CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } -} - -/** - * An instruction that initializes all escaped memory. - */ -class AliasedDefinitionInstruction extends Instruction { - AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } -} - -/** - * An instruction that consumes all escaped memory on exit from the function. - */ -class AliasedUseInstruction extends Instruction { - AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } -} - -/** - * An instruction representing the choice of one of multiple input values based on control flow. - * - * A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of - * the same variable reach that block. The `PhiInstruction` will have one operand corresponding to - * each control flow predecessor of the block, with that operand representing the version of the - * variable that flows from that predecessor. The result value of the `PhiInstruction` will be - * a copy of whichever operand corresponds to the actual predecessor that entered the block at - * runtime. - */ -class PhiInstruction extends Instruction { - PhiInstruction() { getOpcode() instanceof Opcode::Phi } - - /** - * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. - */ - final PhiInputOperand getAnInputOperand() { result = this.getAnOperand() } - - /** - * Gets an instruction that defines the input to one of the operands of this - * instruction. It's possible for more than one operand to have the same - * defining instruction, so this predicate will have the same number of - * results as `getAnInputOperand()` or fewer. - */ - pragma[noinline] - final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } -} - -/** - * An instruction representing the effect that a write to a memory may have on potential aliases of - * that memory. - * - * A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The - * `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents - * the previous state of all of the memory that might be aliased by the memory write. The second - * operand, given by `getPartialOperand()`, represents the memory that was actually modified by the - * memory write. The result of the `ChiInstruction` represents the same memory as - * `getTotalOperand()`, updated to include the changes due to the value that was actually stored by - * the memory write. - * - * As an example, suppose that variable `p` and `q` are pointers that may or may not point to the - * same memory: - * ``` - * *p = 5; - * x = *q; - * ``` - * - * The IR would look like: - * ``` - * r1_1 = VariableAddress[p] - * r1_2 = Load r1_1, m0_0 // Load the value of `p` - * r1_3 = Constant[5] - * m1_4 = Store r1_2, r1_3 // Store to `*p` - * m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory - * r1_6 = VariableAddress[x] - * r1_7 = VariableAddress[q] - * r1_8 = Load r1_7, m0_2 // Load the value of `q` - * r1_9 = Load r1_8, m1_5 // Load the value of `*q` - * m1_10 = Store r1_6, r1_9 // Store to x - * ``` - * - * Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of - * aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a - * new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of - * `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory - * pointed to by `q`. - * - * For more information about how `Chi` instructions are used to model memory side effects, see - * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. - */ -class ChiInstruction extends Instruction { - ChiInstruction() { getOpcode() instanceof Opcode::Chi } - - /** - * Gets the operand that represents the previous state of all memory that might be aliased by the - * memory write. - */ - final ChiTotalOperand getTotalOperand() { result = getAnOperand() } - - /** - * Gets the operand that represents the previous state of all memory that might be aliased by the - * memory write. - */ - final Instruction getTotal() { result = getTotalOperand().getDef() } - - /** - * Gets the operand that represents the new value written by the memory write. - */ - final ChiPartialOperand getPartialOperand() { result = getAnOperand() } - - /** - * Gets the operand that represents the new value written by the memory write. - */ - final Instruction getPartial() { result = getPartialOperand().getDef() } -} - -/** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. - */ -class UnreachedInstruction extends Instruction { - UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } -} - -/** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. - */ -class BuiltInOperationInstruction extends Instruction { - Language::BuiltInOperation operation; - - BuiltInOperationInstruction() { - getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) - } - - final Language::BuiltInOperation getBuiltInOperation() { result = operation } -} - -/** - * An instruction representing a built-in operation that does not have a specific opcode. The - * actual operation is specified by the `getBuiltInOperation()` predicate. - */ -class BuiltInInstruction extends BuiltInOperationInstruction { - BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } - - final override string getImmediateString() { result = getBuiltInOperation().toString() } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll deleted file mode 100644 index f82704094c8e..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll +++ /dev/null @@ -1,472 +0,0 @@ -private import internal.IRInternal -private import Instruction -private import IRBlock -private import internal.OperandImports as Imports -private import Imports::MemoryAccessKind -private import Imports::IRType -private import Imports::Overlap -private import Imports::OperandTag - -cached -private newtype TOperand = - TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) { - defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and - not Construction::isInCycle(useInstr) and - strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1 - } or - TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) { - useInstr.getOpcode().hasOperand(tag) - } or - TPhiOperand( - PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap - ) { - defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) - } - -/** - * Base class for all register operands. This is a placeholder for the IPA union type that we will - * eventually use for this purpose. - */ -private class RegisterOperandBase extends TRegisterOperand { - /** Gets a textual representation of this element. */ - abstract string toString(); -} - -/** - * Returns the register operand with the specified parameters. - */ -private RegisterOperandBase registerOperand( - Instruction useInstr, RegisterOperandTag tag, Instruction defInstr -) { - result = TRegisterOperand(useInstr, tag, defInstr) -} - -/** - * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we - * will eventually use for this purpose. - */ -private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand { - /** Gets a textual representation of this element. */ - abstract string toString(); -} - -/** - * Returns the non-Phi memory operand with the specified parameters. - */ -private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) { - result = TNonPhiMemoryOperand(useInstr, tag) -} - -/** - * Base class for all Phi operands. This is a placeholder for the IPA union type that we will - * eventually use for this purpose. - */ -private class PhiOperandBase extends TPhiOperand { - abstract string toString(); -} - -/** - * Returns the Phi operand with the specified parameters. - */ -private PhiOperandBase phiOperand( - Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap -) { - result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) -} - -/** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. - */ -class Operand extends TOperand { - string toString() { result = "Operand" } - - final Language::Location getLocation() { result = getUse().getLocation() } - - final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } - - /** - * Gets the `Instruction` that consumes this operand. - */ - Instruction getUse() { none() } - - /** - * Gets the `Instruction` whose result is the value of the operand. Unlike - * `getDef`, this also has a result when `isDefinitionInexact` holds, which - * means that the resulting instruction may only _partially_ or _potentially_ - * be the value of this operand. - */ - Instruction getAnyDef() { none() } - - /** - * Gets the `Instruction` whose result is the value of the operand. Unlike - * `getAnyDef`, this also has no result when `isDefinitionInexact` holds, - * which means that the resulting instruction must always be exactly the be - * the value of this operand. - */ - final Instruction getDef() { - result = this.getAnyDef() and - getDefinitionOverlap() instanceof MustExactlyOverlap - } - - /** - * DEPRECATED: renamed to `getUse`. - * - * Gets the `Instruction` that consumes this operand. - */ - deprecated final Instruction getUseInstruction() { result = getUse() } - - /** - * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this - * predicate is `getAnyDef`, but most uses of this predicate should probably - * be replaced with `getDef`. - * - * Gets the `Instruction` whose result is the value of the operand. - */ - deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } - - /** - * Gets the overlap relationship between the operand's definition and its use. - */ - Overlap getDefinitionOverlap() { none() } - - /** - * Holds if the result of the definition instruction does not exactly overlap this use. - */ - final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } - - /** - * Gets a prefix to use when dumping the operand in an operand list. - */ - string getDumpLabel() { result = "" } - - /** - * Gets a string describing this operand, suitable for display in IR dumps. This consists of the - * result ID of the instruction consumed by the operand, plus a label identifying the operand - * kind. - * - * For example: `this:r3_5` - */ - final string getDumpString() { - result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() - } - - /** - * Gets a string containing the identifier of the definition of this use, or `m?` if the - * definition is not modeled in SSA. - */ - private string getDefinitionId() { - result = getAnyDef().getResultId() - or - not exists(getAnyDef()) and result = "m?" - } - - /** - * Gets a string prefix to prepend to the operand's definition ID in an IR dump, specifying whether the operand is - * an exact or inexact use of its definition. For an inexact use, the prefix is "~". For an exact use, the prefix is - * the empty string. - */ - private string getInexactSpecifier() { - if isDefinitionInexact() then result = "~" else result = "" - } - - /** - * Get the order in which the operand should be sorted in the operand list. - */ - int getDumpSortOrder() { result = -1 } - - /** - * Gets the type of the value consumed by this operand. This is usually the same as the - * result type of the definition instruction consumed by this operand. For register operands, - * this is always the case. For some memory operands, the operand type may be different from - * the definition type, such as in the case of a partial read or a read from a pointer that - * has been cast to a different type. - */ - Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } - - /** - * Gets the language-neutral type of the value consumed by this operand. This is usually the same - * as the result type of the definition instruction consumed by this operand. For register - * operands, this is always the case. For some memory operands, the operand type may be different - * from the definition type, such as in the case of a partial read or a read from a pointer that - * has been cast to a different type. - */ - final IRType getIRType() { result = getLanguageType().getIRType() } - - /** - * Gets the type of the value consumed by this operand. This is usually the same as the - * result type of the definition instruction consumed by this operand. For register operands, - * this is always the case. For some memory operands, the operand type may be different from - * the definition type, such as in the case of a partial read or a read from a pointer that - * has been cast to a different type. - */ - final Language::Type getType() { getLanguageType().hasType(result, _) } - - /** - * Holds if the value consumed by this operand is a glvalue. If this - * holds, the value of the operand represents the address of a location, - * and the type of the location is given by `getType()`. If this does - * not hold, the value of the operand represents a value whose type is - * given by `getType()`. - */ - final predicate isGLValue() { getLanguageType().hasType(_, true) } - - /** - * Gets the size of the value consumed by this operand, in bytes. If the operand does not have - * a known constant size, this predicate does not hold. - */ - final int getSize() { result = getLanguageType().getByteSize() } -} - -/** - * An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction). - */ -class MemoryOperand extends Operand { - MemoryOperand() { - this instanceof NonPhiMemoryOperandBase or - this instanceof PhiOperandBase - } - - /** - * Gets the kind of memory access performed by the operand. - */ - MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } - - /** - * Holds if the memory access performed by this operand will not always read from every bit in the - * memory location. This is most commonly used for memory accesses that may or may not actually - * occur depending on runtime state (for example, the write side effect of an output parameter - * that is not written to on all paths), or for accesses where the memory location is a - * conservative estimate of the memory that might actually be accessed at runtime (for example, - * the global side effects of a function call). - */ - predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } - - /** - * Returns the operand that holds the memory address from which the current operand loads its - * value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2` - * is `r1`. - */ - final AddressOperand getAddressOperand() { - getMemoryAccess().usesAddressOperand() and - result.getUse() = getUse() - } -} - -/** - * An operand that is not an operand of a `PhiInstruction`. - */ -class NonPhiOperand extends Operand { - Instruction useInstr; - OperandTag tag; - - NonPhiOperand() { - this = registerOperand(useInstr, tag, _) or - this = nonPhiMemoryOperand(useInstr, tag) - } - - final override Instruction getUse() { result = useInstr } - - final override string getDumpLabel() { result = tag.getLabel() } - - final override int getDumpSortOrder() { result = tag.getSortOrder() } - - final OperandTag getOperandTag() { result = tag } -} - -/** - * An operand that consumes a register (non-memory) result. - */ -class RegisterOperand extends NonPhiOperand, RegisterOperandBase { - override RegisterOperandTag tag; - Instruction defInstr; - - RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) } - - final override string toString() { result = tag.toString() } - - final override Instruction getAnyDef() { result = defInstr } - - final override Overlap getDefinitionOverlap() { - // All register results overlap exactly with their uses. - result instanceof MustExactlyOverlap - } -} - -class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { - override MemoryOperandTag tag; - - NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) } - - final override string toString() { result = tag.toString() } - - final override Instruction getAnyDef() { - result = unique(Instruction defInstr | hasDefinition(defInstr, _)) - } - - final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } - - pragma[noinline] - private predicate hasDefinition(Instruction defInstr, Overlap overlap) { - defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and - not Construction::isInCycle(useInstr) and - strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1 - } -} - -class TypedOperand extends NonPhiMemoryOperand { - override TypedOperandTag tag; - - final override Language::LanguageType getLanguageType() { - result = Construction::getInstructionOperandType(useInstr, tag) - } -} - -/** - * The address operand of an instruction that loads or stores a value from - * memory (e.g. `Load`, `Store`). - */ -class AddressOperand extends RegisterOperand { - override AddressOperandTag tag; -} - -/** - * The buffer size operand of an instruction that represents a read or write of - * a buffer. - */ -class BufferSizeOperand extends RegisterOperand { - override BufferSizeOperandTag tag; -} - -/** - * The source value operand of an instruction that loads a value from memory (e.g. `Load`, - * `ReturnValue`, `ThrowValue`). - */ -class LoadOperand extends TypedOperand { - override LoadOperandTag tag; -} - -/** - * The source value operand of a `Store` instruction. - */ -class StoreValueOperand extends RegisterOperand { - override StoreValueOperandTag tag; -} - -/** - * The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`). - */ -class UnaryOperand extends RegisterOperand { - override UnaryOperandTag tag; -} - -/** - * The left operand of a binary instruction (e.g. `Add`, `CompareEQ`). - */ -class LeftOperand extends RegisterOperand { - override LeftOperandTag tag; -} - -/** - * The right operand of a binary instruction (e.g. `Add`, `CompareEQ`). - */ -class RightOperand extends RegisterOperand { - override RightOperandTag tag; -} - -/** - * The condition operand of a `ConditionalBranch` or `Switch` instruction. - */ -class ConditionOperand extends RegisterOperand { - override ConditionOperandTag tag; -} - -/** - * The operand representing the target function of an `Call` instruction. - */ -class CallTargetOperand extends RegisterOperand { - override CallTargetOperandTag tag; -} - -/** - * An operand representing an argument to a function call. This includes both - * positional arguments (represented by `PositionalArgumentOperand`) and the - * implicit `this` argument, if any (represented by `ThisArgumentOperand`). - */ -class ArgumentOperand extends RegisterOperand { - override ArgumentOperandTag tag; -} - -/** - * An operand representing the implicit 'this' argument to a member function - * call. - */ -class ThisArgumentOperand extends ArgumentOperand { - override ThisArgumentOperandTag tag; -} - -/** - * An operand representing an argument to a function call. - */ -class PositionalArgumentOperand extends ArgumentOperand { - override PositionalArgumentOperandTag tag; - - /** - * Gets the zero-based index of the argument. - */ - final int getIndex() { result = tag.getArgIndex() } -} - -class SideEffectOperand extends TypedOperand { - override SideEffectOperandTag tag; -} - -/** - * An operand of a `PhiInstruction`. - */ -class PhiInputOperand extends MemoryOperand, PhiOperandBase { - PhiInstruction useInstr; - Instruction defInstr; - IRBlock predecessorBlock; - Overlap overlap; - - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } - - override string toString() { result = "Phi" } - - final override PhiInstruction getUse() { result = useInstr } - - final override Instruction getAnyDef() { result = defInstr } - - final override Overlap getDefinitionOverlap() { result = overlap } - - final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } - - final override string getDumpLabel() { - result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" - } - - /** - * Gets the predecessor block from which this value comes. - */ - final IRBlock getPredecessorBlock() { result = predecessorBlock } - - final override MemoryAccessKind getMemoryAccess() { result instanceof PhiMemoryAccess } -} - -/** - * The total operand of a Chi node, representing the previous value of the memory. - */ -class ChiTotalOperand extends NonPhiMemoryOperand { - override ChiTotalOperandTag tag; - - final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess } -} - -/** - * The partial operand of a Chi node, representing the value being written to part of the memory. - */ -class ChiPartialOperand extends NonPhiMemoryOperand { - override ChiPartialOperandTag tag; - - final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll deleted file mode 100644 index d9c0df44e12e..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll +++ /dev/null @@ -1,261 +0,0 @@ -private import internal.IRInternal -private import IR -private import internal.PrintIRImports as Imports -import Imports::IRConfiguration - -private newtype TPrintIRConfiguration = MkPrintIRConfiguration() - -/** - * The query can extend this class to control which functions are printed. - */ -class PrintIRConfiguration extends TPrintIRConfiguration { - string toString() { result = "PrintIRConfiguration" } - - /** - * Holds if the IR for `func` should be printed. By default, holds for all - * functions. - */ - predicate shouldPrintFunction(Language::Function func) { any() } -} - -/** - * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. - */ -private class FilteredIRConfiguration extends IRConfiguration { - override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { - shouldPrintFunction(func) - } -} - -private predicate shouldPrintFunction(Language::Function func) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) -} - -private string getAdditionalInstructionProperty(Instruction instr, string key) { - exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key)) -} - -private string getAdditionalBlockProperty(IRBlock block, string key) { - exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) -} - -private newtype TPrintableIRNode = - TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or - TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or - TPrintableInstruction(Instruction instr) { shouldPrintFunction(instr.getEnclosingFunction()) } - -/** - * A node to be emitted in the IR graph. - */ -abstract class PrintableIRNode extends TPrintableIRNode { - abstract string toString(); - - /** - * Gets the location to be emitted for the node. - */ - abstract Language::Location getLocation(); - - /** - * Gets the label to be emitted for the node. - */ - abstract string getLabel(); - - /** - * Gets the order in which the node appears in its parent node. - */ - abstract int getOrder(); - - /** - * Gets the parent of this node. - */ - abstract PrintableIRNode getParent(); - - /** - * Gets the kind of graph represented by this node ("graph" or "tree"). - */ - string getGraphKind() { none() } - - /** - * Holds if this node should always be rendered as text, even in a graphical - * viewer. - */ - predicate forceText() { none() } - - /** - * Gets the value of the node property with the specified key. - */ - string getProperty(string key) { - key = "semmle.label" and result = getLabel() - or - key = "semmle.order" and result = getOrder().toString() - or - key = "semmle.graphKind" and result = getGraphKind() - or - key = "semmle.forceText" and forceText() and result = "true" - } -} - -/** - * An IR graph node representing a `IRFunction` object. - */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { - IRFunction irFunc; - - PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } - - override string toString() { result = irFunc.toString() } - - override Language::Location getLocation() { result = irFunc.getLocation() } - - override string getLabel() { result = Language::getIdentityString(irFunc.getFunction()) } - - override int getOrder() { - this = - rank[result + 1](PrintableIRFunction orderedFunc, Language::Location location | - location = orderedFunc.getIRFunction().getLocation() - | - orderedFunc - order by - location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), - orderedFunc.getLabel() - ) - } - - final override PrintableIRNode getParent() { none() } - - final IRFunction getIRFunction() { result = irFunc } -} - -/** - * An IR graph node representing an `IRBlock` object. - */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { - IRBlock block; - - PrintableIRBlock() { this = TPrintableIRBlock(block) } - - override string toString() { result = getLabel() } - - override Language::Location getLocation() { result = block.getLocation() } - - override string getLabel() { result = "Block " + block.getDisplayIndex().toString() } - - override int getOrder() { result = block.getDisplayIndex() } - - final override string getGraphKind() { result = "tree" } - - final override predicate forceText() { any() } - - final override PrintableIRFunction getParent() { - result.getIRFunction() = block.getEnclosingIRFunction() - } - - override string getProperty(string key) { - result = PrintableIRNode.super.getProperty(key) or - result = getAdditionalBlockProperty(block, key) - } - - final IRBlock getBlock() { result = block } -} - -/** - * An IR graph node representing an `Instruction`. - */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { - Instruction instr; - - PrintableInstruction() { this = TPrintableInstruction(instr) } - - override string toString() { result = instr.toString() } - - override Language::Location getLocation() { result = instr.getLocation() } - - override string getLabel() { - exists(IRBlock block | - instr = block.getAnInstruction() and - exists( - string resultString, string operationString, string operandsString, int resultWidth, - int operationWidth - | - resultString = instr.getResultString() and - operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and - columnWidths(block, resultWidth, operationWidth) and - result = - resultString + getPaddingString(resultWidth - resultString.length()) + " = " + - operationString + getPaddingString(operationWidth - operationString.length()) + " : " + - operandsString - ) - ) - } - - override int getOrder() { result = instr.getDisplayIndexInBlock() } - - final override PrintableIRBlock getParent() { result.getBlock() = instr.getBlock() } - - final Instruction getInstruction() { result = instr } - - override string getProperty(string key) { - result = PrintableIRNode.super.getProperty(key) or - result = getAdditionalInstructionProperty(instr, key) - } -} - -private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { - resultWidth = max(Instruction instr | instr.getBlock() = block | instr.getResultString().length()) and - operationWidth = - max(Instruction instr | instr.getBlock() = block | instr.getOperationString().length()) -} - -private int maxColumnWidth() { - result = - max(Instruction instr, int width | - width = instr.getResultString().length() or - width = instr.getOperationString().length() or - width = instr.getOperandsString().length() - | - width - ) -} - -private string getPaddingString(int n) { - n = 0 and result = "" - or - n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " -} - -query predicate nodes(PrintableIRNode node, string key, string value) { - value = node.getProperty(key) -} - -private int getSuccessorIndex(IRBlock pred, IRBlock succ) { - succ = - rank[result + 1](IRBlock aSucc, EdgeKind kind | - aSucc = pred.getSuccessor(kind) - | - aSucc order by kind.toString() - ) -} - -query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { - exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | - predBlock = pred.getBlock() and - succBlock = succ.getBlock() and - predBlock.getSuccessor(kind) = succBlock and - ( - ( - key = "semmle.label" and - if predBlock.getBackEdgeSuccessor(kind) = succBlock - then value = kind.toString() + " (back edge)" - else value = kind.toString() - ) - or - key = "semmle.order" and - value = getSuccessorIndex(predBlock, succBlock).toString() - ) - ) -} - -query predicate parents(PrintableIRNode child, PrintableIRNode parent) { - parent = child.getParent() -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/ConstantAnalysis.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/ConstantAnalysis.qll deleted file mode 100644 index 196949579f72..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/ConstantAnalysis.qll +++ /dev/null @@ -1,62 +0,0 @@ -private import internal.ConstantAnalysisInternal -private import semmle.code.csharp.ir.internal.IntegerPartial -private import IR - -language[monotonicAggregates] -int getConstantValue(Instruction instr) { - result = instr.(IntegerConstantInstruction).getValue().toInt() - or - result = getBinaryInstructionValue(instr) - or - result = neg(getConstantValue(instr.(NegateInstruction).getUnary())) - or - result = getConstantValue(instr.(CopyInstruction).getSourceValue()) - or - exists(PhiInstruction phi | - phi = instr and - result = max(Instruction def | def = phi.getAnInput() | getConstantValueToPhi(def)) and - result = min(Instruction def | def = phi.getAnInput() | getConstantValueToPhi(def)) - ) -} - -pragma[noinline] -int getConstantValueToPhi(Instruction def) { - exists(PhiInstruction phi | - result = getConstantValue(def) and - def = phi.getAnInput() - ) -} - -pragma[noinline] -private predicate binaryInstructionOperands(BinaryInstruction instr, int left, int right) { - left = getConstantValue(instr.getLeft()) and - right = getConstantValue(instr.getRight()) -} - -pragma[noinline] -private int getBinaryInstructionValue(BinaryInstruction instr) { - exists(int left, int right | - binaryInstructionOperands(instr, left, right) and - ( - instr instanceof AddInstruction and result = add(left, right) - or - instr instanceof SubInstruction and result = sub(left, right) - or - instr instanceof MulInstruction and result = mul(left, right) - or - instr instanceof DivInstruction and result = div(left, right) - or - instr instanceof CompareEQInstruction and result = compareEQ(left, right) - or - instr instanceof CompareNEInstruction and result = compareNE(left, right) - or - instr instanceof CompareLTInstruction and result = compareLT(left, right) - or - instr instanceof CompareGTInstruction and result = compareGT(left, right) - or - instr instanceof CompareLEInstruction and result = compareLE(left, right) - or - instr instanceof CompareGEInstruction and result = compareGE(left, right) - ) - ) -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/PrintConstantAnalysis.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/PrintConstantAnalysis.qll deleted file mode 100644 index 1145d5bb2abb..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/PrintConstantAnalysis.qll +++ /dev/null @@ -1,11 +0,0 @@ -private import internal.ConstantAnalysisInternal -private import semmle.code.csharp.ir.internal.IntegerConstant -private import ConstantAnalysis -import IR - -private class ConstantAnalysisPropertyProvider extends IRPropertyProvider { - override string getInstructionProperty(Instruction instr, string key) { - key = "ConstantValue" and - result = getValue(getConstantValue(instr)).toString() - } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll deleted file mode 100644 index b49dacc701bf..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.implementation.raw.IR as IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll deleted file mode 100644 index 3d200900445b..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll +++ /dev/null @@ -1,3 +0,0 @@ -import semmle.code.csharp.ir.internal.Overlap -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -import semmle.code.csharp.ir.implementation.unaliased_ssa.IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll deleted file mode 100644 index 9aa0d93de1f3..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll deleted file mode 100644 index 3b716c201ac3..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll +++ /dev/null @@ -1,3 +0,0 @@ -import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind -import semmle.code.csharp.ir.implementation.IRType as IRType -import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRInternal.qll deleted file mode 100644 index 421091e00d3f..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRInternal.qll +++ /dev/null @@ -1,3 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -import IRConstruction as Construction -import semmle.code.csharp.ir.implementation.IRConfiguration as IRConfiguration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll deleted file mode 100644 index 9ab8de412594..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll +++ /dev/null @@ -1,5 +0,0 @@ -import semmle.code.csharp.ir.implementation.IRType as IRType -import semmle.code.csharp.ir.implementation.TempVariableTag as TempVariableTag -import semmle.code.csharp.ir.internal.IRUtilities as IRUtilities -import semmle.code.csharp.ir.internal.TempVariableTag as TTempVariableTag -import semmle.code.csharp.ir.implementation.internal.TIRVariable as TIRVariable diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll deleted file mode 100644 index 05b119f6c5b9..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll +++ /dev/null @@ -1,6 +0,0 @@ -import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind -import semmle.code.csharp.ir.implementation.IRType as IRType -import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind -import semmle.code.csharp.ir.implementation.Opcode as Opcode -import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag -import semmle.code.csharp.ir.internal.Overlap as Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll deleted file mode 100644 index 997185fb9f70..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind -import semmle.code.csharp.ir.implementation.IRType as IRType -import semmle.code.csharp.ir.internal.Overlap as Overlap -import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll deleted file mode 100644 index a74b4bffbc4d..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.IRConfiguration as IRConfiguration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedExprBase.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedExprBase.qll deleted file mode 100644 index d0046d0862ad..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedExprBase.qll +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Contains an abstract class that serves as a Base for classes that deal with the translation of exprs - * (both AST generated and compiler generated). - */ - -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language - -abstract class TranslatedExprBase extends TranslatedElement { - /** - * Gets the instruction that produces the result of the expression. - */ - abstract Instruction getResult(); -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCall.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCall.qll deleted file mode 100644 index a02ecd26f05b..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCall.qll +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Contains an abstract class that is the super class of the classes that deal with compiler generated calls. - */ - -import csharp -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedFunction -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedCallBase -private import TranslatedCompilerGeneratedElement -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language - -abstract class TranslatedCompilerGeneratedCall extends TranslatedCallBase, - TranslatedCompilerGeneratedElement { - final override string toString() { - result = "compiler generated call (" + generatedBy.toString() + ")" - } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCondition.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCondition.qll deleted file mode 100644 index 635db7670787..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedCondition.qll +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Contains an abstract class that is the super class of the classes that deal with compiler generated conditions. - */ - -import csharp -private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedConditionBase -private import TranslatedCompilerGeneratedElement -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language - -abstract class TranslatedCompilerGeneratedValueCondition extends TranslatedCompilerGeneratedElement, - ValueConditionBase { - final override string toString() { - result = "compiler generated condition (" + generatedBy.toString() + ")" - } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedExpr.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedExpr.qll deleted file mode 100644 index 1c0ad500fc65..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedExpr.qll +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Contains an abstract class, which is the super class of all the classes that represent compiler - * generated expressions. - */ - -import csharp -private import TranslatedCompilerGeneratedElement -private import semmle.code.csharp.ir.implementation.raw.Instruction -private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedExprBase -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language - -abstract class TranslatedCompilerGeneratedExpr extends TranslatedCompilerGeneratedElement, - TranslatedExprBase { - override string toString() { result = "compiler generated expr (" + generatedBy.toString() + ")" } - - abstract Type getResultType(); -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll deleted file mode 100644 index 8464941e0a74..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll +++ /dev/null @@ -1,2 +0,0 @@ -import semmle.code.csharp.ir.implementation.raw.IR as IR -import semmle.code.csharp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll deleted file mode 100644 index badd48552a5f..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll +++ /dev/null @@ -1,30 +0,0 @@ -import IRFunction -import Instruction -import IRBlock -import IRVariable -import Operand -private import internal.IRImports as Imports -import Imports::EdgeKind -import Imports::IRType -import Imports::MemoryAccessKind - -private newtype TIRPropertyProvider = MkIRPropertyProvider() - -/** - * Class that provides additional properties to be dumped for IR instructions and blocks when using - * the PrintIR module. Libraries that compute additional facts about IR elements can extend the - * single instance of this class to specify the additional properties computed by the library. - */ -class IRPropertyProvider extends TIRPropertyProvider { - string toString() { result = "IRPropertyProvider" } - - /** - * Gets the value of the property named `key` for the specified instruction. - */ - string getInstructionProperty(Instruction instruction, string key) { none() } - - /** - * Gets the value of the property named `key` for the specified block. - */ - string getBlockProperty(IRBlock block, string key) { none() } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll deleted file mode 100644 index 94ef73b27692..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll +++ /dev/null @@ -1,213 +0,0 @@ -private import internal.IRInternal -import Instruction -private import internal.IRBlockImports as Imports -import Imports::EdgeKind -private import Cached - -/** - * A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only - * incoming edges at the beginning of the sequence and the only outgoing edges at the end of the - * sequence. - * - * This class does not contain any members that query the predecessor or successor edges of the - * block. This allows different classes that extend `IRBlockBase` to expose different subsets of - * edges (e.g. ignoring unreachable edges). - * - * Most consumers should use the class `IRBlock`. - */ -class IRBlockBase extends TIRBlock { - final string toString() { result = getFirstInstruction(this).toString() } - - final Language::Location getLocation() { result = getFirstInstruction().getLocation() } - - final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } - - /** - * Gets the zero-based index of the block within its function. This is used - * by debugging and printing code only. - */ - int getDisplayIndex() { - exists(IRConfiguration::IRConfiguration config | - config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction()) - ) and - this = - rank[result + 1](IRBlock funcBlock, int sortOverride | - funcBlock.getEnclosingFunction() = getEnclosingFunction() and - // Ensure that the block containing `EnterFunction` always comes first. - if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction - then sortOverride = 0 - else sortOverride = 1 - | - funcBlock order by sortOverride, funcBlock.getUniqueId() - ) - } - - final Instruction getInstruction(int index) { result = getInstruction(this, index) } - - final PhiInstruction getAPhiInstruction() { - Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() - } - - final Instruction getAnInstruction() { - result = getInstruction(_) or - result = getAPhiInstruction() - } - - final Instruction getFirstInstruction() { result = getFirstInstruction(this) } - - final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } - - final int getInstructionCount() { result = getInstructionCount(this) } - - final IRFunction getEnclosingIRFunction() { - result = getFirstInstruction(this).getEnclosingIRFunction() - } - - final Language::Function getEnclosingFunction() { - result = getFirstInstruction(this).getEnclosingFunction() - } -} - -/** - * A basic block with additional information about its predecessor and successor edges. Each edge - * corresponds to the control flow between the last instruction of one block and the first - * instruction of another block. - */ -class IRBlock extends IRBlockBase { - final IRBlock getASuccessor() { blockSuccessor(this, result) } - - final IRBlock getAPredecessor() { blockSuccessor(result, this) } - - final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } - - final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } - - final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } - - final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } - - final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } - - pragma[noinline] - final IRBlock dominanceFrontier() { - dominates(result.getAPredecessor()) and - not strictlyDominates(result) - } - - /** - * Holds if this block is reachable from the entry point of its function - */ - final predicate isReachableFromFunctionEntry() { - this = getEnclosingIRFunction().getEntryBlock() or - getAPredecessor().isReachableFromFunctionEntry() - } -} - -private predicate startsBasicBlock(Instruction instr) { - not instr instanceof PhiInstruction and - not adjacentInBlock(_, instr) -} - -/** Holds if `i2` follows `i1` in a `IRBlock`. */ -private predicate adjacentInBlock(Instruction i1, Instruction i2) { - // - i2 must be the only successor of i1 - i2 = unique(Instruction i | i = i1.getASuccessor()) and - // - i1 must be the only predecessor of i2 - i1 = unique(Instruction i | i.getASuccessor() = i2) and - // - The edge between the two must be a GotoEdge. We just check that one - // exists since we've already checked that it's unique. - exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and - // - The edge must not be a back edge. This means we get the same back edges - // in the basic-block graph as we do in the raw CFG. - not exists(Construction::getInstructionBackEdgeSuccessor(i1, _)) - // This predicate could be simplified to remove one of the `unique`s if we - // were willing to rely on the CFG being well-formed and thus never having - // more than one successor to an instruction that has a `GotoEdge` out of it. -} - -private predicate isEntryBlock(TIRBlock block) { - block = MkIRBlock(any(EnterFunctionInstruction enter)) -} - -cached -private module Cached { - cached - newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) } - - /** Holds if `i` is the `index`th instruction the block starting with `first`. */ - private Instruction getInstructionFromFirst(Instruction first, int index) = - shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index) - - /** Holds if `i` is the `index`th instruction in `block`. */ - cached - Instruction getInstruction(TIRBlock block, int index) { - result = getInstructionFromFirst(getFirstInstruction(block), index) - } - - cached - int getInstructionCount(TIRBlock block) { result = strictcount(getInstruction(block, _)) } - - cached - predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - exists(Instruction predLast, Instruction succFirst | - predLast = getInstruction(pred, getInstructionCount(pred) - 1) and - succFirst = predLast.getSuccessor(kind) and - succ = MkIRBlock(succFirst) - ) - } - - pragma[noinline] - private predicate blockIdentity(TIRBlock b1, TIRBlock b2) { b1 = b2 } - - pragma[noopt] - cached - predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - backEdgeSuccessorRaw(pred, succ, kind) - or - // See the QLDoc on `backEdgeSuccessorRaw`. - exists(TIRBlock pred2 | - // Joining with `blockIdentity` is a performance trick to get - // `forwardEdgeRaw` on the RHS of a join, where it's fast. - blockIdentity(pred, pred2) and - forwardEdgeRaw+(pred, pred2) - ) and - blockSuccessor(pred, succ, kind) - } - - /** - * Holds if there is an edge from `pred` to `succ` that is not a back edge. - */ - private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) { - exists(EdgeKind kind | - blockSuccessor(pred, succ, kind) and - not backEdgeSuccessorRaw(pred, succ, kind) - ) - } - - /** - * Holds if the `kind`-edge from `pred` to `succ` is a back edge according to - * `Construction`. - * - * There could be loops of non-back-edges if there is a flaw in the IR - * construction or back-edge detection, and this could cause non-termination - * of subsequent analysis. To prevent that, a subsequent predicate further - * classifies all edges as back edges if they are involved in a loop of - * non-back-edges. - */ - private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - exists(Instruction predLast, Instruction succFirst | - predLast = getInstruction(pred, getInstructionCount(pred) - 1) and - succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and - succ = MkIRBlock(succFirst) - ) - } - - cached - predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { blockSuccessor(pred, succ, _) } - - cached - predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = - idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) -} - -Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRConsistency.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRConsistency.qll deleted file mode 100644 index 65af34942b6b..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRConsistency.qll +++ /dev/null @@ -1,331 +0,0 @@ -private import IR -import InstructionConsistency // module is below -import IRTypeConsistency // module is in IRType.qll - -module InstructionConsistency { - private import internal.InstructionImports as Imports - private import Imports::OperandTag - private import Imports::Overlap - private import internal.IRInternal - - /** - * Holds if instruction `instr` is missing an expected operand with tag `tag`. - */ - query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) { - exists(OperandTag tag | - instr.getOpcode().hasOperand(tag) and - not exists(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - message = - "Instruction '" + instr.getOpcode().toString() + - "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } - - /** - * Holds if instruction `instr` has an unexpected operand with tag `tag`. - */ - query predicate unexpectedOperand(Instruction instr, OperandTag tag) { - exists(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - not instr.getOpcode().hasOperand(tag) and - not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and - not ( - instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag - ) and - not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) - } - - /** - * Holds if instruction `instr` has multiple operands with tag `tag`. - */ - query predicate duplicateOperand( - Instruction instr, string message, IRFunction func, string funcText - ) { - exists(OperandTag tag, int operandCount | - operandCount = - strictcount(NonPhiOperand operand | - operand = instr.getAnOperand() and - operand.getOperandTag() = tag - ) and - operandCount > 1 and - message = - "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + - " in function '$@'." and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } - - /** - * Holds if `Phi` instruction `instr` is missing an operand corresponding to - * the predecessor block `pred`. - */ - query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) { - pred = instr.getBlock().getAPredecessor() and - not exists(PhiInputOperand operand | - operand = instr.getAnOperand() and - operand.getPredecessorBlock() = pred - ) - } - - query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func, Instruction use | - not exists(operand.getType()) and - use = operand.getUse() and - func = use.getEnclosingFunction() and - message = - "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + - "' missing type in function '" + Language::getIdentityString(func) + "'." - ) - } - - query predicate duplicateChiOperand( - ChiInstruction chi, string message, IRFunction func, string funcText - ) { - chi.getTotal() = chi.getPartial() and - message = - "Chi instruction for " + chi.getPartial().toString() + - " has duplicate operands in function $@" and - func = chi.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - } - - query predicate sideEffectWithoutPrimary( - SideEffectInstruction instr, string message, IRFunction func, string funcText - ) { - not exists(instr.getPrimaryInstruction()) and - message = "Side effect instruction missing primary instruction in function $@" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - } - - /** - * Holds if an instruction, other than `ExitFunction`, has no successors. - */ - query predicate instructionWithoutSuccessor(Instruction instr) { - not exists(instr.getASuccessor()) and - not instr instanceof ExitFunctionInstruction and - // Phi instructions aren't linked into the instruction-level flow graph. - not instr instanceof PhiInstruction and - not instr instanceof UnreachedInstruction - } - - /** - * Holds if there are multiple (`n`) edges of kind `kind` from `source`, - * where `target` is among the targets of those edges. - */ - query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) { - n = strictcount(Instruction t | source.getSuccessor(kind) = t) and - n > 1 and - source.getSuccessor(kind) = target - } - - /** - * Holds if `instr` in `f` is part of a loop even though the AST of `f` - * contains no element that can cause loops. - */ - query predicate unexplainedLoop(Language::Function f, Instruction instr) { - exists(IRBlock block | - instr.getBlock() = block and - block.getEnclosingFunction() = f and - block.getASuccessor+() = block - ) and - not Language::hasPotentialLoop(f) - } - - /** - * Holds if a `Phi` instruction is present in a block with fewer than two - * predecessors. - */ - query predicate unnecessaryPhiInstruction(PhiInstruction instr) { - count(instr.getBlock().getAPredecessor()) < 2 - } - - /** - * Holds if a memory operand is connected to a definition with an unmodeled result. - */ - query predicate memoryOperandDefinitionIsUnmodeled( - Instruction instr, string message, IRFunction func, string funcText - ) { - exists(MemoryOperand operand, Instruction def | - operand = instr.getAnOperand() and - def = operand.getAnyDef() and - not def.isResultModeled() and - message = "Memory operand definition has unmodeled result in function '$@'" and - func = instr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } - - /** - * Holds if operand `operand` consumes a value that was defined in - * a different function. - */ - query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) { - operand.getUse() = instr and - operand.getAnyDef() = defInstr and - instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction() - } - - /** - * Holds if instruction `instr` is not in exactly one block. - */ - query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { - blockCount = count(instr.getBlock()) and - blockCount != 1 - } - - private predicate forwardEdge(IRBlock b1, IRBlock b2) { - b1.getASuccessor() = b2 and - not b1.getBackEdgeSuccessor(_) = b2 - } - - /** - * Holds if `f` contains a loop in which no edge is a back edge. - * - * This check ensures we don't have too _few_ back edges. - */ - query predicate containsLoopOfForwardEdges(IRFunction f) { - exists(IRBlock block | - forwardEdge+(block, block) and - block.getEnclosingIRFunction() = f - ) - } - - /** - * Holds if `block` is reachable from its function entry point but would not - * be reachable by traversing only forward edges. This check is skipped for - * functions containing `goto` statements as the property does not generally - * hold there. - * - * This check ensures we don't have too _many_ back edges. - */ - query predicate lostReachability(IRBlock block) { - exists(IRFunction f, IRBlock entry | - entry = f.getEntryBlock() and - entry.getASuccessor+() = block and - not forwardEdge+(entry, block) and - not Language::hasGoto(f.getFunction()) - ) - } - - /** - * Holds if the number of back edges differs between the `Instruction` graph - * and the `IRBlock` graph. - */ - query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) { - fromInstr = - count(Instruction i1, Instruction i2 | - i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2 - ) and - fromBlock = - count(IRBlock b1, IRBlock b2 | - b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2 - ) and - fromInstr != fromBlock - } - - /** - * Gets the point in the function at which the specified operand is evaluated. For most operands, - * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point - * of evaluation is at the end of the corresponding predecessor block. - */ - private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) { - block = operand.(PhiInputOperand).getPredecessorBlock() and - index = block.getInstructionCount() - or - exists(Instruction use | - use = operand.(NonPhiOperand).getUse() and - block.getInstruction(index) = use - ) - } - - /** - * Holds if `useOperand` has a definition that does not dominate the use. - */ - query predicate useNotDominatedByDefinition( - Operand useOperand, string message, IRFunction func, string funcText - ) { - exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | - pointOfEvaluation(useOperand, useBlock, useIndex) and - defInstr = useOperand.getAnyDef() and - ( - defInstr instanceof PhiInstruction and - defBlock = defInstr.getBlock() and - defIndex = -1 - or - defBlock.getInstruction(defIndex) = defInstr - ) and - not ( - defBlock.strictlyDominates(useBlock) - or - defBlock = useBlock and - defIndex < useIndex - ) and - message = - "Operand '" + useOperand.toString() + - "' is not dominated by its definition in function '$@'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } - - query predicate switchInstructionWithoutDefaultEdge( - SwitchInstruction switchInstr, string message, IRFunction func, string funcText - ) { - not exists(switchInstr.getDefaultSuccessor()) and - message = - "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and - func = switchInstr.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - } - - /** - * Holds if `instr` is on the chain of chi/phi instructions for all aliased - * memory. - */ - private predicate isOnAliasedDefinitionChain(Instruction instr) { - instr instanceof AliasedDefinitionInstruction - or - isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal()) - or - isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef()) - } - - private predicate shouldBeConflated(Instruction instr) { - isOnAliasedDefinitionChain(instr) - or - instr.getOpcode() instanceof Opcode::InitializeNonLocal - } - - query predicate notMarkedAsConflated(Instruction instr) { - shouldBeConflated(instr) and - not instr.isResultConflated() - } - - query predicate wronglyMarkedAsConflated(Instruction instr) { - instr.isResultConflated() and - not shouldBeConflated(instr) - } - - query predicate invalidOverlap( - MemoryOperand useOperand, string message, IRFunction func, string funcText - ) { - exists(Overlap overlap | - overlap = useOperand.getDefinitionOverlap() and - overlap instanceof MayPartiallyOverlap and - message = - "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" + - overlap.toString() + "'." and - func = useOperand.getEnclosingIRFunction() and - funcText = Language::getIdentityString(func.getFunction()) - ) - } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll deleted file mode 100644 index 9aea3e00d666..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll +++ /dev/null @@ -1,71 +0,0 @@ -private import internal.IRInternal -import Instruction - -private newtype TIRFunction = - MkIRFunction(Language::Function func) { Construction::functionHasIR(func) } - -/** - * Represents the IR for a function. - */ -class IRFunction extends TIRFunction { - Language::Function func; - - IRFunction() { this = MkIRFunction(func) } - - final string toString() { result = "IR: " + func.toString() } - - /** - * Gets the function whose IR is represented. - */ - final Language::Function getFunction() { result = func } - - /** - * Gets the location of the function. - */ - final Language::Location getLocation() { result = func.getLocation() } - - /** - * Gets the entry point for this function. - */ - pragma[noinline] - final EnterFunctionInstruction getEnterFunctionInstruction() { - result.getEnclosingIRFunction() = this - } - - /** - * Gets the exit point for this function. - */ - pragma[noinline] - final ExitFunctionInstruction getExitFunctionInstruction() { - result.getEnclosingIRFunction() = this - } - - /** - * Gets the single return instruction for this function. - */ - pragma[noinline] - final ReturnInstruction getReturnInstruction() { result.getEnclosingIRFunction() = this } - - /** - * Gets the variable used to hold the return value of this function. If this - * function does not return a value, this predicate does not hold. - */ - pragma[noinline] - final IRReturnVariable getReturnVariable() { result.getEnclosingIRFunction() = this } - - /** - * Gets the block containing the entry point of this function. - */ - pragma[noinline] - final IRBlock getEntryBlock() { result.getFirstInstruction() = getEnterFunctionInstruction() } - - /** - * Gets all instructions in this function. - */ - final Instruction getAnInstruction() { result.getEnclosingIRFunction() = this } - - /** - * Gets all blocks in this function. - */ - final IRBlock getABlock() { result.getEnclosingIRFunction() = this } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll deleted file mode 100644 index 9f2a0d4ea281..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll +++ /dev/null @@ -1,276 +0,0 @@ -private import internal.IRInternal -import IRFunction -private import internal.IRVariableImports as Imports -import Imports::TempVariableTag -private import Imports::IRUtilities -private import Imports::TTempVariableTag -private import Imports::TIRVariable -private import Imports::IRType - -IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { - result.getVariable() = var and - result.getEnclosingFunction() = func -} - -/** - * A variable referenced by the IR for a function. The variable may be a user-declared variable - * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation - * (`IRTempVariable`). - */ -class IRVariable extends TIRVariable { - Language::Function func; - - IRVariable() { - this = TIRUserVariable(_, _, func) or - this = TIRTempVariable(func, _, _, _) or - this = TIRStringLiteral(func, _, _, _) or - this = TIRDynamicInitializationFlag(func, _, _) - } - - string toString() { none() } - - /** - * Holds if this variable's value cannot be changed within a function. Currently used for string - * literals, but could also apply to `const` global and static variables. - */ - predicate isReadOnly() { none() } - - /** - * Gets the type of the variable. - */ - final Language::Type getType() { getLanguageType().hasType(result, false) } - - /** - * Gets the language-neutral type of the variable. - */ - final IRType getIRType() { result = getLanguageType().getIRType() } - - /** - * Gets the type of the variable. - */ - Language::LanguageType getLanguageType() { none() } - - /** - * Gets the AST node that declared this variable, or that introduced this - * variable as part of the AST-to-IR translation. - */ - Language::AST getAST() { none() } - - /** - * Gets an identifier string for the variable. This identifier is unique - * within the function. - */ - string getUniqueId() { none() } - - /** - * Gets the source location of this variable. - */ - final Language::Location getLocation() { result = getAST().getLocation() } - - /** - * Gets the IR for the function that references this variable. - */ - final IRFunction getEnclosingIRFunction() { result.getFunction() = func } - - /** - * Gets the function that references this variable. - */ - final Language::Function getEnclosingFunction() { result = func } -} - -/** - * A user-declared variable referenced by the IR for a function. - */ -class IRUserVariable extends IRVariable, TIRUserVariable { - Language::Variable var; - Language::LanguageType type; - - IRUserVariable() { this = TIRUserVariable(var, type, func) } - - final override string toString() { result = getVariable().toString() } - - final override Language::AST getAST() { result = var } - - final override string getUniqueId() { - result = getVariable().toString() + " " + getVariable().getLocation().toString() - } - - final override Language::LanguageType getLanguageType() { result = type } - - /** - * Gets the original user-declared variable. - */ - Language::Variable getVariable() { result = var } -} - -/** - * A variable (user-declared or temporary) that is allocated on the stack. This includes all - * parameters, non-static local variables, and temporary variables. - */ -class IRAutomaticVariable extends IRVariable { - IRAutomaticVariable() { - exists(Language::Variable var | - this = TIRUserVariable(var, _, func) and - Language::isVariableAutomatic(var) - ) - or - this = TIRTempVariable(func, _, _, _) - } -} - -/** - * A user-declared variable that is allocated on the stack. This includes all parameters and - * non-static local variables. - */ -class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable { - override Language::AutomaticVariable var; - - final override Language::AutomaticVariable getVariable() { result = var } -} - -/** - * A user-declared variable that is not allocated on the stack. This includes all global variables, - * namespace-scope variables, static fields, and static local variables. - */ -class IRStaticUserVariable extends IRUserVariable { - override Language::StaticVariable var; - - IRStaticUserVariable() { not Language::isVariableAutomatic(var) } - - final override Language::StaticVariable getVariable() { result = var } -} - -/** - * A variable that is not user-declared. This includes temporary variables generated as part of IR - * construction, as well as string literals. - */ -class IRGeneratedVariable extends IRVariable { - Language::AST ast; - Language::LanguageType type; - - IRGeneratedVariable() { - this = TIRTempVariable(func, ast, _, type) or - this = TIRStringLiteral(func, ast, type, _) or - this = TIRDynamicInitializationFlag(func, ast, type) - } - - final override Language::LanguageType getLanguageType() { result = type } - - final override Language::AST getAST() { result = ast } - - override string toString() { result = getBaseString() + getLocationString() } - - override string getUniqueId() { none() } - - final string getLocationString() { - result = - ast.getLocation().getStartLine().toString() + ":" + - ast.getLocation().getStartColumn().toString() - } - - string getBaseString() { none() } -} - -IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { - result.getAST() = ast and - result.getTag() = tag -} - -/** - * A temporary variable introduced by IR construction. The most common examples are the variable - * generated to hold the return value of a function, or the variable generated to hold the result of - * a condition operator (`a ? b : c`). - */ -class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable { - TempVariableTag tag; - - IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } - - final override string getUniqueId() { - result = "Temp: " + Construction::getTempVariableUniqueId(this) - } - - final TempVariableTag getTag() { result = tag } - - override string getBaseString() { result = "#temp" } -} - -/** - * A temporary variable generated to hold the return value of a function. - */ -class IRReturnVariable extends IRTempVariable { - IRReturnVariable() { tag = ReturnValueTempVar() } - - final override string toString() { result = "#return" } -} - -/** - * A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction. - */ -class IRThrowVariable extends IRTempVariable { - IRThrowVariable() { tag = ThrowTempVar() } - - final override string getBaseString() { result = "#throw" } -} - -/** - * A temporary variable generated to hold the contents of all arguments passed to the `...` of a - * function that accepts a variable number of arguments. - */ -class IREllipsisVariable extends IRTempVariable { - IREllipsisVariable() { tag = EllipsisTempVar() } - - final override string toString() { result = "#ellipsis" } -} - -/** - * A temporary variable generated to hold the `this` pointer. - */ -class IRThisVariable extends IRTempVariable { - IRThisVariable() { tag = ThisTempVar() } - - final override string toString() { result = "#this" } -} - -/** - * A variable generated to represent the contents of a string literal. This variable acts much like - * a read-only global variable. - */ -class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral { - Language::StringLiteral literal; - - IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) } - - final override predicate isReadOnly() { any() } - - final override string getUniqueId() { - result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal) - } - - final override string getBaseString() { result = "#string" } - - final Language::StringLiteral getLiteral() { result = literal } -} - -/** - * A variable generated to track whether a specific non-stack variable has been initialized. This is - * used to model the runtime initialization of static local variables in C++, as well as static - * fields in C#. - */ -class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag { - Language::Variable var; - - IRDynamicInitializationFlag() { - this = TIRDynamicInitializationFlag(func, var, type) and ast = var - } - - final override string toString() { result = var.toString() + "#init" } - - final Language::Variable getVariable() { result = var } - - final override string getUniqueId() { - result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString() - } - - final override string getBaseString() { result = "#init:" + var.toString() + ":" } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll deleted file mode 100644 index 9c83a3d99f0c..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll +++ /dev/null @@ -1,1374 +0,0 @@ -private import internal.IRInternal -import IRFunction -import IRBlock -import IRVariable -import Operand -private import internal.InstructionImports as Imports -import Imports::EdgeKind -import Imports::IRType -import Imports::MemoryAccessKind -import Imports::Opcode -private import Imports::OperandTag - -/** - * Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified - * `File` and line number. Used for assigning register names when printing IR. - */ -private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) { - exists(IRConfiguration::IRConfiguration config | - config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction()) - ) and - exists(Language::Location location | - irFunc = result.getEnclosingIRFunction() and - location = result.getLocation() and - file = location.getFile() and - line = location.getStartLine() - ) -} - -/** - * Represents a single operation in the IR. - */ -class Instruction extends Construction::TInstruction { - final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } - - /** - * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what - * would be printed by PrintIR.ql. For example: - * - * `mu0_28(int) = Store r0_26, r0_27` - */ - final string getDumpString() { - result = getResultString() + " = " + getOperationString() + " " + getOperandsString() - } - - private predicate shouldGenerateDumpStrings() { - exists(IRConfiguration::IRConfiguration config | - config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction()) - ) - } - - /** - * Gets a string describing the operation of this instruction. This includes - * the opcode and the immediate value, if any. For example: - * - * VariableAddress[x] - */ - final string getOperationString() { - shouldGenerateDumpStrings() and - if exists(getImmediateString()) - then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" - else result = getOperationPrefix() + getOpcode().toString() - } - - /** - * Gets a string describing the immediate value of this instruction, if any. - */ - string getImmediateString() { none() } - - private string getOperationPrefix() { - shouldGenerateDumpStrings() and - if this instanceof SideEffectInstruction then result = "^" else result = "" - } - - private string getResultPrefix() { - shouldGenerateDumpStrings() and - if getResultIRType() instanceof IRVoidType - then result = "v" - else - if hasMemoryResult() - then if isResultModeled() then result = "m" else result = "mu" - else result = "r" - } - - /** - * Gets the zero-based index of this instruction within its block. This is - * used by debugging and printing code only. - */ - int getDisplayIndexInBlock() { - shouldGenerateDumpStrings() and - exists(IRBlock block | - this = block.getInstruction(result) - or - this = - rank[-result - 1](PhiInstruction phiInstr | - phiInstr = block.getAPhiInstruction() - | - phiInstr order by phiInstr.getUniqueId() - ) - ) - } - - private int getLineRank() { - shouldGenerateDumpStrings() and - this = - rank[result](Instruction instr | - instr = - getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), - getLocation().getStartLine()) - | - instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() - ) - } - - /** - * Gets a human-readable string that uniquely identifies this instruction - * within the function. This string is used to refer to this instruction when - * printing IR dumps. - * - * Example: `r1_1` - */ - string getResultId() { - shouldGenerateDumpStrings() and - result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() - } - - /** - * Gets a string describing the result of this instruction, suitable for - * display in IR dumps. This consists of the result ID plus the type of the - * result. - * - * Example: `r1_1(int*)` - */ - final string getResultString() { - shouldGenerateDumpStrings() and - result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" - } - - /** - * Gets a string describing the operands of this instruction, suitable for - * display in IR dumps. - * - * Example: `func:r3_4, this:r3_5` - */ - string getOperandsString() { - shouldGenerateDumpStrings() and - result = - concat(Operand operand | - operand = getAnOperand() - | - operand.getDumpString(), ", " order by operand.getDumpSortOrder() - ) - } - - /** - * Gets a string identifier for this function that is unique among all - * instructions in the same function. - * - * This is used for sorting IR output for tests, and is likely to be - * inefficient for any other use. - */ - final string getUniqueId() { result = Construction::getInstructionUniqueId(this) } - - /** - * Gets the basic block that contains this instruction. - */ - final IRBlock getBlock() { result.getAnInstruction() = this } - - /** - * Gets the function that contains this instruction. - */ - final Language::Function getEnclosingFunction() { - result = getEnclosingIRFunction().getFunction() - } - - /** - * Gets the IRFunction object that contains the IR for this instruction. - */ - final IRFunction getEnclosingIRFunction() { - result = Construction::getInstructionEnclosingIRFunction(this) - } - - /** - * Gets the AST that caused this instruction to be generated. - */ - final Language::AST getAST() { result = Construction::getInstructionAST(this) } - - /** - * Gets the location of the source code for this instruction. - */ - final Language::Location getLocation() { result = getAST().getLocation() } - - /** - * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a - * conversion. - */ - final Language::Expr getConvertedResultExpression() { - result = Construction::getInstructionConvertedResultExpression(this) - } - - /** - * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any. - */ - final Language::Expr getUnconvertedResultExpression() { - result = Construction::getInstructionUnconvertedResultExpression(this) - } - - final Language::LanguageType getResultLanguageType() { - result = Construction::getInstructionResultType(this) - } - - /** - * Gets the type of the result produced by this instruction. If the instruction does not produce - * a result, its result type will be `IRVoidType`. - */ - final IRType getResultIRType() { result = getResultLanguageType().getIRType() } - - /** - * Gets the type of the result produced by this instruction. If the - * instruction does not produce a result, its result type will be `VoidType`. - * - * If `isGLValue()` holds, then the result type of this instruction should be - * thought of as "pointer to `getResultType()`". - */ - final Language::Type getResultType() { - exists(Language::LanguageType resultType | - resultType = getResultLanguageType() and - ( - resultType.hasUnspecifiedType(result, _) - or - not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType - ) - ) - } - - /** - * Holds if the result produced by this instruction is a glvalue. If this - * holds, the result of the instruction represents the address of a location, - * and the type of the location is given by `getResultType()`. If this does - * not hold, the result of the instruction represents a value whose type is - * given by `getResultType()`. - * - * For example, the statement `y = x;` generates the following IR: - * r1_0(glval: int) = VariableAddress[x] - * r1_1(int) = Load r1_0, mu0_1 - * r1_2(glval: int) = VariableAddress[y] - * mu1_3(int) = Store r1_2, r1_1 - * - * The result of each `VariableAddress` instruction is a glvalue of type - * `int`, representing the address of the corresponding integer variable. The - * result of the `Load` instruction is a prvalue of type `int`, representing - * the integer value loaded from variable `x`. - */ - final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } - - /** - * Gets the size of the result produced by this instruction, in bytes. If the - * result does not have a known constant size, this predicate does not hold. - * - * If `this.isGLValue()` holds for this instruction, the value of - * `getResultSize()` will always be the size of a pointer. - */ - final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } - - /** - * Gets the opcode that specifies the operation performed by this instruction. - */ - final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } - - /** - * Gets all direct uses of the result of this instruction. The result can be - * an `Operand` for which `isDefinitionInexact` holds. - */ - final Operand getAUse() { result.getAnyDef() = this } - - /** - * Gets all of this instruction's operands. - */ - final Operand getAnOperand() { result.getUse() = this } - - /** - * Holds if this instruction produces a memory result. - */ - final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } - - /** - * Gets the kind of memory access performed by this instruction's result. - * Holds only for instructions with a memory result. - */ - pragma[inline] - final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } - - /** - * Holds if the memory access performed by this instruction's result will not always write to - * every bit in the memory location. This is most commonly used for memory accesses that may or - * may not actually occur depending on runtime state (for example, the write side effect of an - * output parameter that is not written to on all paths), or for accesses where the memory - * location is a conservative estimate of the memory that might actually be accessed at runtime - * (for example, the global side effects of a function call). - */ - pragma[inline] - final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } - - /** - * Gets the operand that holds the memory address to which this instruction stores its - * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` - * is `r1`. - */ - final AddressOperand getResultAddressOperand() { - getResultMemoryAccess().usesAddressOperand() and - result.getUse() = this - } - - /** - * Gets the instruction that holds the exact memory address to which this instruction stores its - * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` - * is the instruction that defines `r1`. - */ - final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } - - /** - * Holds if the result of this instruction is precisely modeled in SSA. Always - * holds for a register result. For a memory result, a modeled result is - * connected to its actual uses. An unmodeled result has no uses. - * - * For example: - * ``` - * int x = 1; - * int *p = &x; - * int y = *p; - * ``` - * In non-aliased SSA, `x` will not be modeled because it has its address - * taken. In that case, `isResultModeled()` would not hold for the result of - * the `Store` to `x`. - */ - final predicate isResultModeled() { - // Register results are always in SSA form. - not hasMemoryResult() or - Construction::hasModeledMemoryResult(this) - } - - /** - * Holds if this is an instruction with a memory result that represents a - * conflation of more than one memory allocation. - * - * This happens in practice when dereferencing a pointer that cannot be - * tracked back to a single local allocation. Such memory is instead modeled - * as originating on the `AliasedDefinitionInstruction` at the entry of the - * function. - */ - final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) } - - /** - * Gets the successor of this instruction along the control flow edge - * specified by `kind`. - */ - final Instruction getSuccessor(EdgeKind kind) { - result = Construction::getInstructionSuccessor(this, kind) - } - - /** - * Gets the a _back-edge successor_ of this instruction along the control - * flow edge specified by `kind`. A back edge in the control-flow graph is - * intuitively the edge that goes back around a loop. If all back edges are - * removed from the control-flow graph, it becomes acyclic. - */ - final Instruction getBackEdgeSuccessor(EdgeKind kind) { - // We don't take these edges from - // `Construction::getInstructionBackEdgeSuccessor` since that relation has - // not been treated to remove any loops that might be left over due to - // flaws in the IR construction or back-edge detection. - exists(IRBlock block | - block = this.getBlock() and - this = block.getLastInstruction() and - result = block.getBackEdgeSuccessor(kind).getFirstInstruction() - ) - } - - /** - * Gets all direct successors of this instruction. - */ - final Instruction getASuccessor() { result = getSuccessor(_) } - - /** - * Gets a predecessor of this instruction such that the predecessor reaches - * this instruction along the control flow edge specified by `kind`. - */ - final Instruction getPredecessor(EdgeKind kind) { result.getSuccessor(kind) = this } - - /** - * Gets all direct predecessors of this instruction. - */ - final Instruction getAPredecessor() { result = getPredecessor(_) } -} - -class VariableInstruction extends Instruction { - IRVariable var; - - VariableInstruction() { var = Construction::getInstructionVariable(this) } - - override string getImmediateString() { result = var.toString() } - - final IRVariable getIRVariable() { result = var } - - /** - * Gets the AST variable that this instruction's IR variable refers to, if one exists. - */ - final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() } -} - -class FieldInstruction extends Instruction { - Language::Field field; - - FieldInstruction() { field = Construction::getInstructionField(this) } - - final override string getImmediateString() { result = field.toString() } - - final Language::Field getField() { result = field } -} - -class FunctionInstruction extends Instruction { - Language::Function funcSymbol; - - FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) } - - final override string getImmediateString() { result = funcSymbol.toString() } - - final Language::Function getFunctionSymbol() { result = funcSymbol } -} - -class ConstantValueInstruction extends Instruction { - string value; - - ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) } - - final override string getImmediateString() { result = value } - - final string getValue() { result = value } -} - -class IndexedInstruction extends Instruction { - int index; - - IndexedInstruction() { index = Construction::getInstructionIndex(this) } - - final override string getImmediateString() { result = index.toString() } - - final int getIndex() { result = index } -} - -class EnterFunctionInstruction extends Instruction { - EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } -} - -class VariableAddressInstruction extends VariableInstruction { - VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } -} - -class InitializeParameterInstruction extends VariableInstruction { - InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } - - final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } -} - -class InitializeIndirectionInstruction extends VariableInstruction { - InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } - - final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } -} - -/** - * An instruction that initializes the `this` pointer parameter of the enclosing function. - */ -class InitializeThisInstruction extends Instruction { - InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } -} - -class FieldAddressInstruction extends FieldInstruction { - FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } - - final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } - - final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } -} - -/** - * An instruction that produces a well-defined but unknown result and has - * unknown side effects, including side effects that are not conservatively - * modeled in the SSA graph. - * - * This type of instruction appears when there is an `ErrorExpr` in the AST, - * meaning that the extractor could not understand the expression and therefore - * produced a partial AST. Queries that give alerts when some action is _not_ - * taken may want to ignore any function that contains an `ErrorInstruction`. - */ -class ErrorInstruction extends Instruction { - ErrorInstruction() { getOpcode() instanceof Opcode::Error } -} - -class UninitializedInstruction extends VariableInstruction { - UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } - - /** - * Gets the variable that is uninitialized. - */ - final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() } -} - -class NoOpInstruction extends Instruction { - NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } -} - -class ReturnInstruction extends Instruction { - ReturnInstruction() { getOpcode() instanceof ReturnOpcode } -} - -class ReturnVoidInstruction extends ReturnInstruction { - ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } -} - -class ReturnValueInstruction extends ReturnInstruction { - ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } - - final LoadOperand getReturnValueOperand() { result = getAnOperand() } - - final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } -} - -class ReturnIndirectionInstruction extends VariableInstruction { - ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } - - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } - - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } - - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } - - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } - - /** - * Gets the parameter for which this instruction reads the final pointed-to value within the - * function. - */ - final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() } -} - -class CopyInstruction extends Instruction { - CopyInstruction() { getOpcode() instanceof CopyOpcode } - - Operand getSourceValueOperand() { none() } - - final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } -} - -class CopyValueInstruction extends CopyInstruction, UnaryInstruction { - CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } - - final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } -} - -class LoadInstruction extends CopyInstruction { - LoadInstruction() { getOpcode() instanceof Opcode::Load } - - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } - - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } - - final override LoadOperand getSourceValueOperand() { result = getAnOperand() } -} - -class StoreInstruction extends CopyInstruction { - StoreInstruction() { getOpcode() instanceof Opcode::Store } - - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } - - final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } - - final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } -} - -class ConditionalBranchInstruction extends Instruction { - ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } - - final ConditionOperand getConditionOperand() { result = getAnOperand() } - - final Instruction getCondition() { result = getConditionOperand().getDef() } - - final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } - - final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } -} - -class ExitFunctionInstruction extends Instruction { - ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } -} - -class ConstantInstruction extends ConstantValueInstruction { - ConstantInstruction() { getOpcode() instanceof Opcode::Constant } -} - -class IntegerConstantInstruction extends ConstantInstruction { - IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType } -} - -class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType } -} - -class StringConstantInstruction extends VariableInstruction { - override IRStringLiteral var; - - final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } - - final Language::StringLiteral getValue() { result = var.getLiteral() } -} - -class BinaryInstruction extends Instruction { - BinaryInstruction() { getOpcode() instanceof BinaryOpcode } - - final LeftOperand getLeftOperand() { result = getAnOperand() } - - final RightOperand getRightOperand() { result = getAnOperand() } - - final Instruction getLeft() { result = getLeftOperand().getDef() } - - final Instruction getRight() { result = getRightOperand().getDef() } - - /** - * Holds if this instruction's operands are `op1` and `op2`, in either order. - */ - final predicate hasOperands(Operand op1, Operand op2) { - op1 = getLeftOperand() and op2 = getRightOperand() - or - op1 = getRightOperand() and op2 = getLeftOperand() - } -} - -class ArithmeticInstruction extends Instruction { - ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } -} - -class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { } - -class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { } - -class AddInstruction extends BinaryArithmeticInstruction { - AddInstruction() { getOpcode() instanceof Opcode::Add } -} - -class SubInstruction extends BinaryArithmeticInstruction { - SubInstruction() { getOpcode() instanceof Opcode::Sub } -} - -class MulInstruction extends BinaryArithmeticInstruction { - MulInstruction() { getOpcode() instanceof Opcode::Mul } -} - -class DivInstruction extends BinaryArithmeticInstruction { - DivInstruction() { getOpcode() instanceof Opcode::Div } -} - -class RemInstruction extends BinaryArithmeticInstruction { - RemInstruction() { getOpcode() instanceof Opcode::Rem } -} - -class NegateInstruction extends UnaryArithmeticInstruction { - NegateInstruction() { getOpcode() instanceof Opcode::Negate } -} - -class BitwiseInstruction extends Instruction { - BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } -} - -class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { } - -class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } - -class BitAndInstruction extends BinaryBitwiseInstruction { - BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } -} - -class BitOrInstruction extends BinaryBitwiseInstruction { - BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } -} - -class BitXorInstruction extends BinaryBitwiseInstruction { - BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } -} - -class ShiftLeftInstruction extends BinaryBitwiseInstruction { - ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } -} - -class ShiftRightInstruction extends BinaryBitwiseInstruction { - ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } -} - -class PointerArithmeticInstruction extends BinaryInstruction { - int elementSize; - - PointerArithmeticInstruction() { - getOpcode() instanceof PointerArithmeticOpcode and - elementSize = Construction::getInstructionElementSize(this) - } - - final override string getImmediateString() { result = elementSize.toString() } - - final int getElementSize() { result = elementSize } -} - -class PointerOffsetInstruction extends PointerArithmeticInstruction { - PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } -} - -class PointerAddInstruction extends PointerOffsetInstruction { - PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } -} - -class PointerSubInstruction extends PointerOffsetInstruction { - PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } -} - -class PointerDiffInstruction extends PointerArithmeticInstruction { - PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } -} - -class UnaryInstruction extends Instruction { - UnaryInstruction() { getOpcode() instanceof UnaryOpcode } - - final UnaryOperand getUnaryOperand() { result = getAnOperand() } - - final Instruction getUnary() { result = getUnaryOperand().getDef() } -} - -class ConvertInstruction extends UnaryInstruction { - ConvertInstruction() { getOpcode() instanceof Opcode::Convert } -} - -class CheckedConvertOrNullInstruction extends UnaryInstruction { - CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } -} - -/** - * Represents an instruction that converts between two addresses - * related by inheritance. - */ -class InheritanceConversionInstruction extends UnaryInstruction { - Language::Class baseClass; - Language::Class derivedClass; - - InheritanceConversionInstruction() { - Construction::getInstructionInheritance(this, baseClass, derivedClass) - } - - final override string getImmediateString() { - result = derivedClass.toString() + " : " + baseClass.toString() - } - - /** - * Gets the `ClassDerivation` for the inheritance relationship between - * the base and derived classes. This predicate does not hold if the - * conversion is to an indirect virtual base class. - */ - final Language::ClassDerivation getDerivation() { - result.getBaseClass() = baseClass and result.getDerivedClass() = derivedClass - } - - /** - * Gets the base class of the conversion. This will be either a direct - * base class of the derived class, or a virtual base class of the - * derived class. - */ - final Language::Class getBaseClass() { result = baseClass } - - /** - * Gets the derived class of the conversion. - */ - final Language::Class getDerivedClass() { result = derivedClass } -} - -/** - * Represents an instruction that converts from the address of a derived class - * to the address of a base class. - */ -class ConvertToBaseInstruction extends InheritanceConversionInstruction { - ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } -} - -/** - * Represents an instruction that converts from the address of a derived class - * to the address of a direct non-virtual base class. - */ -class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } -} - -/** - * Represents an instruction that converts from the address of a derived class - * to the address of a virtual base class. - */ -class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } -} - -/** - * Represents an instruction that converts from the address of a base class - * to the address of a direct non-virtual derived class. - */ -class ConvertToDerivedInstruction extends InheritanceConversionInstruction { - ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } -} - -class BitComplementInstruction extends UnaryBitwiseInstruction { - BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } -} - -class LogicalNotInstruction extends UnaryInstruction { - LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } -} - -class CompareInstruction extends BinaryInstruction { - CompareInstruction() { getOpcode() instanceof CompareOpcode } -} - -class CompareEQInstruction extends CompareInstruction { - CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } -} - -class CompareNEInstruction extends CompareInstruction { - CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } -} - -/** - * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`. - */ -class RelationalInstruction extends CompareInstruction { - RelationalInstruction() { getOpcode() instanceof RelationalOpcode } - - /** - * Gets the operand on the "greater" (or "greater-or-equal") side - * of this relational instruction, that is, the side that is larger - * if the overall instruction evaluates to `true`; for example on - * `x <= 20` this is the `20`, and on `y > 0` it is `y`. - */ - Instruction getGreater() { none() } - - /** - * Gets the operand on the "lesser" (or "lesser-or-equal") side - * of this relational instruction, that is, the side that is smaller - * if the overall instruction evaluates to `true`; for example on - * `x <= 20` this is `x`, and on `y > 0` it is the `0`. - */ - Instruction getLesser() { none() } - - /** - * Holds if this relational instruction is strict (is not an "or-equal" instruction). - */ - predicate isStrict() { none() } -} - -class CompareLTInstruction extends RelationalInstruction { - CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } - - override Instruction getLesser() { result = getLeft() } - - override Instruction getGreater() { result = getRight() } - - override predicate isStrict() { any() } -} - -class CompareGTInstruction extends RelationalInstruction { - CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } - - override Instruction getLesser() { result = getRight() } - - override Instruction getGreater() { result = getLeft() } - - override predicate isStrict() { any() } -} - -class CompareLEInstruction extends RelationalInstruction { - CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } - - override Instruction getLesser() { result = getLeft() } - - override Instruction getGreater() { result = getRight() } - - override predicate isStrict() { none() } -} - -class CompareGEInstruction extends RelationalInstruction { - CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } - - override Instruction getLesser() { result = getRight() } - - override Instruction getGreater() { result = getLeft() } - - override predicate isStrict() { none() } -} - -class SwitchInstruction extends Instruction { - SwitchInstruction() { getOpcode() instanceof Opcode::Switch } - - final ConditionOperand getExpressionOperand() { result = getAnOperand() } - - final Instruction getExpression() { result = getExpressionOperand().getDef() } - - final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } - - final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } -} - -/** - * An instruction that calls a function. - */ -class CallInstruction extends Instruction { - CallInstruction() { getOpcode() instanceof Opcode::Call } - - /** - * Gets the operand the specifies the target function of the call. - */ - final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } - - /** - * Gets the `Instruction` that computes the target function of the call. This is usually a - * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a - * function pointer. - */ - final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } - - /** - * Gets all of the argument operands of the call, including the `this` pointer, if any. - */ - final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } - - /** - * Gets the `Function` that the call targets, if this is statically known. - */ - final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionInstruction).getFunctionSymbol() - } - - /** - * Gets all of the arguments of the call, including the `this` pointer, if any. - */ - final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } - - /** - * Gets the `this` pointer argument operand of the call, if any. - */ - final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } - - /** - * Gets the `this` pointer argument of the call, if any. - */ - final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } - - /** - * Gets the argument operand at the specified index. - */ - final PositionalArgumentOperand getPositionalArgumentOperand(int index) { - result = getAnOperand() and - result.getIndex() = index - } - - /** - * Gets the argument at the specified index. - */ - final Instruction getPositionalArgument(int index) { - result = getPositionalArgumentOperand(index).getDef() - } - - /** - * Gets the number of arguments of the call, including the `this` pointer, if any. - */ - final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } -} - -/** - * An instruction representing a side effect of a function call. - */ -class SideEffectInstruction extends Instruction { - SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } - - final Instruction getPrimaryInstruction() { - result = Construction::getPrimaryInstructionForSideEffect(this) - } -} - -/** - * An instruction representing the side effect of a function call on any memory that might be - * accessed by that call. - */ -class CallSideEffectInstruction extends SideEffectInstruction { - CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } -} - -/** - * An instruction representing the side effect of a function call on any memory - * that might be read by that call. This instruction is emitted instead of - * `CallSideEffectInstruction` when it's certain that the call target cannot - * write to escaped memory. - */ -class CallReadSideEffectInstruction extends SideEffectInstruction { - CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } -} - -/** - * An instruction representing a read side effect of a function call on a - * specific parameter. - */ -class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } - - /** Gets the operand for the value that will be read from this instruction, if known. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } - - /** Gets the value that will be read from this instruction, if known. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } - - /** Gets the operand for the address from which this instruction may read. */ - final AddressOperand getArgumentOperand() { result = getAnOperand() } - - /** Gets the address from which this instruction may read. */ - final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } -} - -/** - * An instruction representing the read of an indirect parameter within a function call. - */ -class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { - IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } -} - -/** - * An instruction representing the read of an indirect buffer parameter within a function call. - */ -class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { - BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } -} - -/** - * An instruction representing the read of an indirect buffer parameter within a function call. - */ -class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { - SizedBufferReadSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferReadSideEffect - } - - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } -} - -/** - * An instruction representing a write side effect of a function call on a - * specific parameter. - */ -class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } - - Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() } -} - -/** - * An instruction representing the write of an indirect parameter within a function call. - */ -class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { - IndirectMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMustWriteSideEffect - } -} - -/** - * An instruction representing the write of an indirect buffer parameter within a function call. The - * entire buffer is overwritten. - */ -class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::BufferMustWriteSideEffect - } -} - -/** - * An instruction representing the write of an indirect buffer parameter within a function call. The - * entire buffer is overwritten. - */ -class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { - SizedBufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect - } - - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } -} - -/** - * An instruction representing the potential write of an indirect parameter within a function call. - * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. - * written. - */ -class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - IndirectMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMayWriteSideEffect - } -} - -/** - * An instruction representing the write of an indirect buffer parameter within a function call. - * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. - */ -class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } -} - -/** - * An instruction representing the write of an indirect buffer parameter within a function call. - * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. - */ -class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - SizedBufferMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect - } - - Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() } -} - -/** - * An instruction representing the initial value of newly allocated memory, e.g. the result of a - * call to `malloc`. - */ -class InitializeDynamicAllocationInstruction extends SideEffectInstruction { - InitializeDynamicAllocationInstruction() { - getOpcode() instanceof Opcode::InitializeDynamicAllocation - } - - /** - * Gets the address of the allocation this instruction is initializing. - */ - final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } - - /** - * Gets the operand for the allocation this instruction is initializing. - */ - final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } -} - -/** - * An instruction representing a GNU or MSVC inline assembly statement. - */ -class InlineAsmInstruction extends Instruction { - InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } -} - -/** - * An instruction that throws an exception. - */ -class ThrowInstruction extends Instruction { - ThrowInstruction() { getOpcode() instanceof ThrowOpcode } -} - -/** - * An instruction that throws a new exception. - */ -class ThrowValueInstruction extends ThrowInstruction { - ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } - - /** - * Gets the address operand of the exception thrown by this instruction. - */ - final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } - - /** - * Gets the address of the exception thrown by this instruction. - */ - final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } - - /** - * Gets the operand for the exception thrown by this instruction. - */ - final LoadOperand getExceptionOperand() { result = getAnOperand() } - - /** - * Gets the exception thrown by this instruction. - */ - final Instruction getException() { result = getExceptionOperand().getDef() } -} - -/** - * An instruction that re-throws the current exception. - */ -class ReThrowInstruction extends ThrowInstruction { - ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } -} - -/** - * An instruction that exits the current function by propagating an exception. - */ -class UnwindInstruction extends Instruction { - UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } -} - -/** - * An instruction that starts a `catch` handler. - */ -class CatchInstruction extends Instruction { - CatchInstruction() { getOpcode() instanceof CatchOpcode } -} - -/** - * An instruction that catches an exception of a specific type. - */ -class CatchByTypeInstruction extends CatchInstruction { - Language::LanguageType exceptionType; - - CatchByTypeInstruction() { - getOpcode() instanceof Opcode::CatchByType and - exceptionType = Construction::getInstructionExceptionType(this) - } - - final override string getImmediateString() { result = exceptionType.toString() } - - /** - * Gets the type of exception to be caught. - */ - final Language::LanguageType getExceptionType() { result = exceptionType } -} - -/** - * An instruction that catches any exception. - */ -class CatchAnyInstruction extends CatchInstruction { - CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } -} - -/** - * An instruction that initializes all escaped memory. - */ -class AliasedDefinitionInstruction extends Instruction { - AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } -} - -/** - * An instruction that consumes all escaped memory on exit from the function. - */ -class AliasedUseInstruction extends Instruction { - AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } -} - -/** - * An instruction representing the choice of one of multiple input values based on control flow. - * - * A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of - * the same variable reach that block. The `PhiInstruction` will have one operand corresponding to - * each control flow predecessor of the block, with that operand representing the version of the - * variable that flows from that predecessor. The result value of the `PhiInstruction` will be - * a copy of whichever operand corresponds to the actual predecessor that entered the block at - * runtime. - */ -class PhiInstruction extends Instruction { - PhiInstruction() { getOpcode() instanceof Opcode::Phi } - - /** - * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. - */ - final PhiInputOperand getAnInputOperand() { result = this.getAnOperand() } - - /** - * Gets an instruction that defines the input to one of the operands of this - * instruction. It's possible for more than one operand to have the same - * defining instruction, so this predicate will have the same number of - * results as `getAnInputOperand()` or fewer. - */ - pragma[noinline] - final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } -} - -/** - * An instruction representing the effect that a write to a memory may have on potential aliases of - * that memory. - * - * A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The - * `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents - * the previous state of all of the memory that might be aliased by the memory write. The second - * operand, given by `getPartialOperand()`, represents the memory that was actually modified by the - * memory write. The result of the `ChiInstruction` represents the same memory as - * `getTotalOperand()`, updated to include the changes due to the value that was actually stored by - * the memory write. - * - * As an example, suppose that variable `p` and `q` are pointers that may or may not point to the - * same memory: - * ``` - * *p = 5; - * x = *q; - * ``` - * - * The IR would look like: - * ``` - * r1_1 = VariableAddress[p] - * r1_2 = Load r1_1, m0_0 // Load the value of `p` - * r1_3 = Constant[5] - * m1_4 = Store r1_2, r1_3 // Store to `*p` - * m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory - * r1_6 = VariableAddress[x] - * r1_7 = VariableAddress[q] - * r1_8 = Load r1_7, m0_2 // Load the value of `q` - * r1_9 = Load r1_8, m1_5 // Load the value of `*q` - * m1_10 = Store r1_6, r1_9 // Store to x - * ``` - * - * Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of - * aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a - * new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of - * `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory - * pointed to by `q`. - * - * For more information about how `Chi` instructions are used to model memory side effects, see - * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. - */ -class ChiInstruction extends Instruction { - ChiInstruction() { getOpcode() instanceof Opcode::Chi } - - /** - * Gets the operand that represents the previous state of all memory that might be aliased by the - * memory write. - */ - final ChiTotalOperand getTotalOperand() { result = getAnOperand() } - - /** - * Gets the operand that represents the previous state of all memory that might be aliased by the - * memory write. - */ - final Instruction getTotal() { result = getTotalOperand().getDef() } - - /** - * Gets the operand that represents the new value written by the memory write. - */ - final ChiPartialOperand getPartialOperand() { result = getAnOperand() } - - /** - * Gets the operand that represents the new value written by the memory write. - */ - final Instruction getPartial() { result = getPartialOperand().getDef() } -} - -/** - * An instruction representing unreachable code. Inserted in place of the original target - * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is - * infeasible. - */ -class UnreachedInstruction extends Instruction { - UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } -} - -/** - * An instruction representing a built-in operation. This is used to represent - * operations such as access to variable argument lists. - */ -class BuiltInOperationInstruction extends Instruction { - Language::BuiltInOperation operation; - - BuiltInOperationInstruction() { - getOpcode() instanceof BuiltInOperationOpcode and - operation = Construction::getInstructionBuiltInOperation(this) - } - - final Language::BuiltInOperation getBuiltInOperation() { result = operation } -} - -/** - * An instruction representing a built-in operation that does not have a specific opcode. The - * actual operation is specified by the `getBuiltInOperation()` predicate. - */ -class BuiltInInstruction extends BuiltInOperationInstruction { - BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } - - final override string getImmediateString() { result = getBuiltInOperation().toString() } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll deleted file mode 100644 index f82704094c8e..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll +++ /dev/null @@ -1,472 +0,0 @@ -private import internal.IRInternal -private import Instruction -private import IRBlock -private import internal.OperandImports as Imports -private import Imports::MemoryAccessKind -private import Imports::IRType -private import Imports::Overlap -private import Imports::OperandTag - -cached -private newtype TOperand = - TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) { - defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and - not Construction::isInCycle(useInstr) and - strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1 - } or - TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) { - useInstr.getOpcode().hasOperand(tag) - } or - TPhiOperand( - PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap - ) { - defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) - } - -/** - * Base class for all register operands. This is a placeholder for the IPA union type that we will - * eventually use for this purpose. - */ -private class RegisterOperandBase extends TRegisterOperand { - /** Gets a textual representation of this element. */ - abstract string toString(); -} - -/** - * Returns the register operand with the specified parameters. - */ -private RegisterOperandBase registerOperand( - Instruction useInstr, RegisterOperandTag tag, Instruction defInstr -) { - result = TRegisterOperand(useInstr, tag, defInstr) -} - -/** - * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we - * will eventually use for this purpose. - */ -private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand { - /** Gets a textual representation of this element. */ - abstract string toString(); -} - -/** - * Returns the non-Phi memory operand with the specified parameters. - */ -private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) { - result = TNonPhiMemoryOperand(useInstr, tag) -} - -/** - * Base class for all Phi operands. This is a placeholder for the IPA union type that we will - * eventually use for this purpose. - */ -private class PhiOperandBase extends TPhiOperand { - abstract string toString(); -} - -/** - * Returns the Phi operand with the specified parameters. - */ -private PhiOperandBase phiOperand( - Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap -) { - result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) -} - -/** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. - */ -class Operand extends TOperand { - string toString() { result = "Operand" } - - final Language::Location getLocation() { result = getUse().getLocation() } - - final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } - - /** - * Gets the `Instruction` that consumes this operand. - */ - Instruction getUse() { none() } - - /** - * Gets the `Instruction` whose result is the value of the operand. Unlike - * `getDef`, this also has a result when `isDefinitionInexact` holds, which - * means that the resulting instruction may only _partially_ or _potentially_ - * be the value of this operand. - */ - Instruction getAnyDef() { none() } - - /** - * Gets the `Instruction` whose result is the value of the operand. Unlike - * `getAnyDef`, this also has no result when `isDefinitionInexact` holds, - * which means that the resulting instruction must always be exactly the be - * the value of this operand. - */ - final Instruction getDef() { - result = this.getAnyDef() and - getDefinitionOverlap() instanceof MustExactlyOverlap - } - - /** - * DEPRECATED: renamed to `getUse`. - * - * Gets the `Instruction` that consumes this operand. - */ - deprecated final Instruction getUseInstruction() { result = getUse() } - - /** - * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this - * predicate is `getAnyDef`, but most uses of this predicate should probably - * be replaced with `getDef`. - * - * Gets the `Instruction` whose result is the value of the operand. - */ - deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } - - /** - * Gets the overlap relationship between the operand's definition and its use. - */ - Overlap getDefinitionOverlap() { none() } - - /** - * Holds if the result of the definition instruction does not exactly overlap this use. - */ - final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } - - /** - * Gets a prefix to use when dumping the operand in an operand list. - */ - string getDumpLabel() { result = "" } - - /** - * Gets a string describing this operand, suitable for display in IR dumps. This consists of the - * result ID of the instruction consumed by the operand, plus a label identifying the operand - * kind. - * - * For example: `this:r3_5` - */ - final string getDumpString() { - result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() - } - - /** - * Gets a string containing the identifier of the definition of this use, or `m?` if the - * definition is not modeled in SSA. - */ - private string getDefinitionId() { - result = getAnyDef().getResultId() - or - not exists(getAnyDef()) and result = "m?" - } - - /** - * Gets a string prefix to prepend to the operand's definition ID in an IR dump, specifying whether the operand is - * an exact or inexact use of its definition. For an inexact use, the prefix is "~". For an exact use, the prefix is - * the empty string. - */ - private string getInexactSpecifier() { - if isDefinitionInexact() then result = "~" else result = "" - } - - /** - * Get the order in which the operand should be sorted in the operand list. - */ - int getDumpSortOrder() { result = -1 } - - /** - * Gets the type of the value consumed by this operand. This is usually the same as the - * result type of the definition instruction consumed by this operand. For register operands, - * this is always the case. For some memory operands, the operand type may be different from - * the definition type, such as in the case of a partial read or a read from a pointer that - * has been cast to a different type. - */ - Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } - - /** - * Gets the language-neutral type of the value consumed by this operand. This is usually the same - * as the result type of the definition instruction consumed by this operand. For register - * operands, this is always the case. For some memory operands, the operand type may be different - * from the definition type, such as in the case of a partial read or a read from a pointer that - * has been cast to a different type. - */ - final IRType getIRType() { result = getLanguageType().getIRType() } - - /** - * Gets the type of the value consumed by this operand. This is usually the same as the - * result type of the definition instruction consumed by this operand. For register operands, - * this is always the case. For some memory operands, the operand type may be different from - * the definition type, such as in the case of a partial read or a read from a pointer that - * has been cast to a different type. - */ - final Language::Type getType() { getLanguageType().hasType(result, _) } - - /** - * Holds if the value consumed by this operand is a glvalue. If this - * holds, the value of the operand represents the address of a location, - * and the type of the location is given by `getType()`. If this does - * not hold, the value of the operand represents a value whose type is - * given by `getType()`. - */ - final predicate isGLValue() { getLanguageType().hasType(_, true) } - - /** - * Gets the size of the value consumed by this operand, in bytes. If the operand does not have - * a known constant size, this predicate does not hold. - */ - final int getSize() { result = getLanguageType().getByteSize() } -} - -/** - * An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction). - */ -class MemoryOperand extends Operand { - MemoryOperand() { - this instanceof NonPhiMemoryOperandBase or - this instanceof PhiOperandBase - } - - /** - * Gets the kind of memory access performed by the operand. - */ - MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } - - /** - * Holds if the memory access performed by this operand will not always read from every bit in the - * memory location. This is most commonly used for memory accesses that may or may not actually - * occur depending on runtime state (for example, the write side effect of an output parameter - * that is not written to on all paths), or for accesses where the memory location is a - * conservative estimate of the memory that might actually be accessed at runtime (for example, - * the global side effects of a function call). - */ - predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } - - /** - * Returns the operand that holds the memory address from which the current operand loads its - * value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2` - * is `r1`. - */ - final AddressOperand getAddressOperand() { - getMemoryAccess().usesAddressOperand() and - result.getUse() = getUse() - } -} - -/** - * An operand that is not an operand of a `PhiInstruction`. - */ -class NonPhiOperand extends Operand { - Instruction useInstr; - OperandTag tag; - - NonPhiOperand() { - this = registerOperand(useInstr, tag, _) or - this = nonPhiMemoryOperand(useInstr, tag) - } - - final override Instruction getUse() { result = useInstr } - - final override string getDumpLabel() { result = tag.getLabel() } - - final override int getDumpSortOrder() { result = tag.getSortOrder() } - - final OperandTag getOperandTag() { result = tag } -} - -/** - * An operand that consumes a register (non-memory) result. - */ -class RegisterOperand extends NonPhiOperand, RegisterOperandBase { - override RegisterOperandTag tag; - Instruction defInstr; - - RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) } - - final override string toString() { result = tag.toString() } - - final override Instruction getAnyDef() { result = defInstr } - - final override Overlap getDefinitionOverlap() { - // All register results overlap exactly with their uses. - result instanceof MustExactlyOverlap - } -} - -class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase { - override MemoryOperandTag tag; - - NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) } - - final override string toString() { result = tag.toString() } - - final override Instruction getAnyDef() { - result = unique(Instruction defInstr | hasDefinition(defInstr, _)) - } - - final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } - - pragma[noinline] - private predicate hasDefinition(Instruction defInstr, Overlap overlap) { - defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and - not Construction::isInCycle(useInstr) and - strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1 - } -} - -class TypedOperand extends NonPhiMemoryOperand { - override TypedOperandTag tag; - - final override Language::LanguageType getLanguageType() { - result = Construction::getInstructionOperandType(useInstr, tag) - } -} - -/** - * The address operand of an instruction that loads or stores a value from - * memory (e.g. `Load`, `Store`). - */ -class AddressOperand extends RegisterOperand { - override AddressOperandTag tag; -} - -/** - * The buffer size operand of an instruction that represents a read or write of - * a buffer. - */ -class BufferSizeOperand extends RegisterOperand { - override BufferSizeOperandTag tag; -} - -/** - * The source value operand of an instruction that loads a value from memory (e.g. `Load`, - * `ReturnValue`, `ThrowValue`). - */ -class LoadOperand extends TypedOperand { - override LoadOperandTag tag; -} - -/** - * The source value operand of a `Store` instruction. - */ -class StoreValueOperand extends RegisterOperand { - override StoreValueOperandTag tag; -} - -/** - * The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`). - */ -class UnaryOperand extends RegisterOperand { - override UnaryOperandTag tag; -} - -/** - * The left operand of a binary instruction (e.g. `Add`, `CompareEQ`). - */ -class LeftOperand extends RegisterOperand { - override LeftOperandTag tag; -} - -/** - * The right operand of a binary instruction (e.g. `Add`, `CompareEQ`). - */ -class RightOperand extends RegisterOperand { - override RightOperandTag tag; -} - -/** - * The condition operand of a `ConditionalBranch` or `Switch` instruction. - */ -class ConditionOperand extends RegisterOperand { - override ConditionOperandTag tag; -} - -/** - * The operand representing the target function of an `Call` instruction. - */ -class CallTargetOperand extends RegisterOperand { - override CallTargetOperandTag tag; -} - -/** - * An operand representing an argument to a function call. This includes both - * positional arguments (represented by `PositionalArgumentOperand`) and the - * implicit `this` argument, if any (represented by `ThisArgumentOperand`). - */ -class ArgumentOperand extends RegisterOperand { - override ArgumentOperandTag tag; -} - -/** - * An operand representing the implicit 'this' argument to a member function - * call. - */ -class ThisArgumentOperand extends ArgumentOperand { - override ThisArgumentOperandTag tag; -} - -/** - * An operand representing an argument to a function call. - */ -class PositionalArgumentOperand extends ArgumentOperand { - override PositionalArgumentOperandTag tag; - - /** - * Gets the zero-based index of the argument. - */ - final int getIndex() { result = tag.getArgIndex() } -} - -class SideEffectOperand extends TypedOperand { - override SideEffectOperandTag tag; -} - -/** - * An operand of a `PhiInstruction`. - */ -class PhiInputOperand extends MemoryOperand, PhiOperandBase { - PhiInstruction useInstr; - Instruction defInstr; - IRBlock predecessorBlock; - Overlap overlap; - - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } - - override string toString() { result = "Phi" } - - final override PhiInstruction getUse() { result = useInstr } - - final override Instruction getAnyDef() { result = defInstr } - - final override Overlap getDefinitionOverlap() { result = overlap } - - final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } - - final override string getDumpLabel() { - result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" - } - - /** - * Gets the predecessor block from which this value comes. - */ - final IRBlock getPredecessorBlock() { result = predecessorBlock } - - final override MemoryAccessKind getMemoryAccess() { result instanceof PhiMemoryAccess } -} - -/** - * The total operand of a Chi node, representing the previous value of the memory. - */ -class ChiTotalOperand extends NonPhiMemoryOperand { - override ChiTotalOperandTag tag; - - final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess } -} - -/** - * The partial operand of a Chi node, representing the value being written to part of the memory. - */ -class ChiPartialOperand extends NonPhiMemoryOperand { - override ChiPartialOperandTag tag; - - final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll deleted file mode 100644 index d9c0df44e12e..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll +++ /dev/null @@ -1,261 +0,0 @@ -private import internal.IRInternal -private import IR -private import internal.PrintIRImports as Imports -import Imports::IRConfiguration - -private newtype TPrintIRConfiguration = MkPrintIRConfiguration() - -/** - * The query can extend this class to control which functions are printed. - */ -class PrintIRConfiguration extends TPrintIRConfiguration { - string toString() { result = "PrintIRConfiguration" } - - /** - * Holds if the IR for `func` should be printed. By default, holds for all - * functions. - */ - predicate shouldPrintFunction(Language::Function func) { any() } -} - -/** - * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. - */ -private class FilteredIRConfiguration extends IRConfiguration { - override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { - shouldPrintFunction(func) - } -} - -private predicate shouldPrintFunction(Language::Function func) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) -} - -private string getAdditionalInstructionProperty(Instruction instr, string key) { - exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key)) -} - -private string getAdditionalBlockProperty(IRBlock block, string key) { - exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) -} - -private newtype TPrintableIRNode = - TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or - TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or - TPrintableInstruction(Instruction instr) { shouldPrintFunction(instr.getEnclosingFunction()) } - -/** - * A node to be emitted in the IR graph. - */ -abstract class PrintableIRNode extends TPrintableIRNode { - abstract string toString(); - - /** - * Gets the location to be emitted for the node. - */ - abstract Language::Location getLocation(); - - /** - * Gets the label to be emitted for the node. - */ - abstract string getLabel(); - - /** - * Gets the order in which the node appears in its parent node. - */ - abstract int getOrder(); - - /** - * Gets the parent of this node. - */ - abstract PrintableIRNode getParent(); - - /** - * Gets the kind of graph represented by this node ("graph" or "tree"). - */ - string getGraphKind() { none() } - - /** - * Holds if this node should always be rendered as text, even in a graphical - * viewer. - */ - predicate forceText() { none() } - - /** - * Gets the value of the node property with the specified key. - */ - string getProperty(string key) { - key = "semmle.label" and result = getLabel() - or - key = "semmle.order" and result = getOrder().toString() - or - key = "semmle.graphKind" and result = getGraphKind() - or - key = "semmle.forceText" and forceText() and result = "true" - } -} - -/** - * An IR graph node representing a `IRFunction` object. - */ -class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction { - IRFunction irFunc; - - PrintableIRFunction() { this = TPrintableIRFunction(irFunc) } - - override string toString() { result = irFunc.toString() } - - override Language::Location getLocation() { result = irFunc.getLocation() } - - override string getLabel() { result = Language::getIdentityString(irFunc.getFunction()) } - - override int getOrder() { - this = - rank[result + 1](PrintableIRFunction orderedFunc, Language::Location location | - location = orderedFunc.getIRFunction().getLocation() - | - orderedFunc - order by - location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), - orderedFunc.getLabel() - ) - } - - final override PrintableIRNode getParent() { none() } - - final IRFunction getIRFunction() { result = irFunc } -} - -/** - * An IR graph node representing an `IRBlock` object. - */ -class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { - IRBlock block; - - PrintableIRBlock() { this = TPrintableIRBlock(block) } - - override string toString() { result = getLabel() } - - override Language::Location getLocation() { result = block.getLocation() } - - override string getLabel() { result = "Block " + block.getDisplayIndex().toString() } - - override int getOrder() { result = block.getDisplayIndex() } - - final override string getGraphKind() { result = "tree" } - - final override predicate forceText() { any() } - - final override PrintableIRFunction getParent() { - result.getIRFunction() = block.getEnclosingIRFunction() - } - - override string getProperty(string key) { - result = PrintableIRNode.super.getProperty(key) or - result = getAdditionalBlockProperty(block, key) - } - - final IRBlock getBlock() { result = block } -} - -/** - * An IR graph node representing an `Instruction`. - */ -class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { - Instruction instr; - - PrintableInstruction() { this = TPrintableInstruction(instr) } - - override string toString() { result = instr.toString() } - - override Language::Location getLocation() { result = instr.getLocation() } - - override string getLabel() { - exists(IRBlock block | - instr = block.getAnInstruction() and - exists( - string resultString, string operationString, string operandsString, int resultWidth, - int operationWidth - | - resultString = instr.getResultString() and - operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and - columnWidths(block, resultWidth, operationWidth) and - result = - resultString + getPaddingString(resultWidth - resultString.length()) + " = " + - operationString + getPaddingString(operationWidth - operationString.length()) + " : " + - operandsString - ) - ) - } - - override int getOrder() { result = instr.getDisplayIndexInBlock() } - - final override PrintableIRBlock getParent() { result.getBlock() = instr.getBlock() } - - final Instruction getInstruction() { result = instr } - - override string getProperty(string key) { - result = PrintableIRNode.super.getProperty(key) or - result = getAdditionalInstructionProperty(instr, key) - } -} - -private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { - resultWidth = max(Instruction instr | instr.getBlock() = block | instr.getResultString().length()) and - operationWidth = - max(Instruction instr | instr.getBlock() = block | instr.getOperationString().length()) -} - -private int maxColumnWidth() { - result = - max(Instruction instr, int width | - width = instr.getResultString().length() or - width = instr.getOperationString().length() or - width = instr.getOperandsString().length() - | - width - ) -} - -private string getPaddingString(int n) { - n = 0 and result = "" - or - n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " " -} - -query predicate nodes(PrintableIRNode node, string key, string value) { - value = node.getProperty(key) -} - -private int getSuccessorIndex(IRBlock pred, IRBlock succ) { - succ = - rank[result + 1](IRBlock aSucc, EdgeKind kind | - aSucc = pred.getSuccessor(kind) - | - aSucc order by kind.toString() - ) -} - -query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) { - exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock | - predBlock = pred.getBlock() and - succBlock = succ.getBlock() and - predBlock.getSuccessor(kind) = succBlock and - ( - ( - key = "semmle.label" and - if predBlock.getBackEdgeSuccessor(kind) = succBlock - then value = kind.toString() + " (back edge)" - else value = kind.toString() - ) - or - key = "semmle.order" and - value = getSuccessorIndex(predBlock, succBlock).toString() - ) - ) -} - -query predicate parents(PrintableIRNode child, PrintableIRNode parent) { - parent = child.getParent() -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll deleted file mode 100644 index f9e1cc7f4128..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll +++ /dev/null @@ -1,54 +0,0 @@ -private import internal.ConstantAnalysisInternal -private import semmle.code.csharp.ir.internal.IntegerPartial -private import IR - -language[monotonicAggregates] -int getConstantValue(Instruction instr) { - result = instr.(IntegerConstantInstruction).getValue().toInt() - or - result = getBinaryInstructionValue(instr) - or - result = neg(getConstantValue(instr.(NegateInstruction).getUnary())) - or - result = getConstantValue(instr.(CopyInstruction).getSourceValue()) - or - exists(PhiInstruction phi | - phi = instr and - result = max(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) and - result = min(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) - ) -} - -pragma[noinline] -private predicate binaryInstructionOperands(BinaryInstruction instr, int left, int right) { - left = getConstantValue(instr.getLeft()) and - right = getConstantValue(instr.getRight()) -} - -pragma[noinline] -private int getBinaryInstructionValue(BinaryInstruction instr) { - exists(int left, int right | - binaryInstructionOperands(instr, left, right) and - ( - instr instanceof AddInstruction and result = add(left, right) - or - instr instanceof SubInstruction and result = sub(left, right) - or - instr instanceof MulInstruction and result = mul(left, right) - or - instr instanceof DivInstruction and result = div(left, right) - or - instr instanceof CompareEQInstruction and result = compareEQ(left, right) - or - instr instanceof CompareNEInstruction and result = compareNE(left, right) - or - instr instanceof CompareLTInstruction and result = compareLT(left, right) - or - instr instanceof CompareGTInstruction and result = compareGT(left, right) - or - instr instanceof CompareLEInstruction and result = compareLE(left, right) - or - instr instanceof CompareGEInstruction and result = compareGE(left, right) - ) - ) -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll deleted file mode 100644 index 1145d5bb2abb..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll +++ /dev/null @@ -1,11 +0,0 @@ -private import internal.ConstantAnalysisInternal -private import semmle.code.csharp.ir.internal.IntegerConstant -private import ConstantAnalysis -import IR - -private class ConstantAnalysisPropertyProvider extends IRPropertyProvider { - override string getInstructionProperty(Instruction instr, string key) { - key = "ConstantValue" and - result = getValue(getConstantValue(instr)).toString() - } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll deleted file mode 100644 index 188c68483ad9..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.implementation.unaliased_ssa.IR as IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll deleted file mode 100644 index 3d200900445b..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll +++ /dev/null @@ -1,3 +0,0 @@ -import semmle.code.csharp.ir.internal.Overlap -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -import semmle.code.csharp.ir.implementation.unaliased_ssa.IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll deleted file mode 100644 index e7884ce20b6f..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysisInternal.qll +++ /dev/null @@ -1,3 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -import semmle.code.csharp.ir.implementation.raw.IR as InputIR -import AliasConfiguration as Configuration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll deleted file mode 100644 index 0db2b9566107..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfigurationImports.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.implementation.raw.IR diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll deleted file mode 100644 index 9aa0d93de1f3..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll deleted file mode 100644 index 3b716c201ac3..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll +++ /dev/null @@ -1,3 +0,0 @@ -import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind -import semmle.code.csharp.ir.implementation.IRType as IRType -import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRInternal.qll deleted file mode 100644 index 7d27b9aa92fe..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRInternal.qll +++ /dev/null @@ -1,3 +0,0 @@ -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -import SSAConstruction as Construction -import semmle.code.csharp.ir.implementation.IRConfiguration as IRConfiguration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll deleted file mode 100644 index 9ab8de412594..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll +++ /dev/null @@ -1,5 +0,0 @@ -import semmle.code.csharp.ir.implementation.IRType as IRType -import semmle.code.csharp.ir.implementation.TempVariableTag as TempVariableTag -import semmle.code.csharp.ir.internal.IRUtilities as IRUtilities -import semmle.code.csharp.ir.internal.TempVariableTag as TTempVariableTag -import semmle.code.csharp.ir.implementation.internal.TIRVariable as TIRVariable diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll deleted file mode 100644 index 05b119f6c5b9..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll +++ /dev/null @@ -1,6 +0,0 @@ -import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind -import semmle.code.csharp.ir.implementation.IRType as IRType -import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind -import semmle.code.csharp.ir.implementation.Opcode as Opcode -import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag -import semmle.code.csharp.ir.internal.Overlap as Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll deleted file mode 100644 index 997185fb9f70..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind -import semmle.code.csharp.ir.implementation.IRType as IRType -import semmle.code.csharp.ir.internal.Overlap as Overlap -import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll deleted file mode 100644 index a74b4bffbc4d..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.IRConfiguration as IRConfiguration diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll deleted file mode 100644 index 6ee403226bc0..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll +++ /dev/null @@ -1,3 +0,0 @@ -import semmle.code.csharp.ir.implementation.Opcode -import semmle.code.csharp.ir.implementation.internal.OperandTag -import semmle.code.csharp.ir.internal.Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll deleted file mode 100644 index 44ff1f110c1f..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll +++ /dev/null @@ -1,6 +0,0 @@ -import semmle.code.csharp.ir.implementation.raw.IR as OldIR -import semmle.code.csharp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability -import semmle.code.csharp.ir.implementation.raw.internal.reachability.Dominance as Dominance -import semmle.code.csharp.ir.implementation.unaliased_ssa.IR as NewIR -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language -import SimpleSSA as Alias diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll deleted file mode 100644 index 91d4124f558a..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll +++ /dev/null @@ -1,4 +0,0 @@ -import semmle.code.csharp.ir.implementation.raw.IR -import semmle.code.csharp.ir.internal.IntegerConstant as Ints -import semmle.code.csharp.ir.implementation.internal.OperandTag -import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAPublicImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAPublicImports.qll deleted file mode 100644 index 555cb581d375..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAPublicImports.qll +++ /dev/null @@ -1 +0,0 @@ -import semmle.code.csharp.ir.internal.Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll deleted file mode 100644 index 1f9f8c40003a..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll +++ /dev/null @@ -1,2 +0,0 @@ -import semmle.code.csharp.ir.implementation.unaliased_ssa.IR as IR -import semmle.code.csharp.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll deleted file mode 100644 index 8ce0549b2b4d..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll +++ /dev/null @@ -1,20 +0,0 @@ -private newtype TOverlap = - TMayPartiallyOverlap() or - TMustTotallyOverlap() or - TMustExactlyOverlap() - -abstract class Overlap extends TOverlap { - abstract string toString(); -} - -class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { - final override string toString() { result = "MayPartiallyOverlap" } -} - -class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { - final override string toString() { result = "MustTotallyOverlap" } -} - -class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { - final override string toString() { result = "MustExactlyOverlap" } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll deleted file mode 100644 index ac65c1f32bdd..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll +++ /dev/null @@ -1,16 +0,0 @@ -private import csharp -private import semmle.code.csharp.ir.implementation.TempVariableTag -private import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction as Construction -private import semmle.code.csharp.ir.Util -private import IRCSharpLanguage as Language - -newtype TIRVariable = - TIRAutomaticUserVariable(LocalScopeVariable var, Callable callable) { - Construction::functionHasIR(callable) and - var.getCallable() = callable - } or - TIRTempVariable( - Callable callable, Language::AST ast, TempVariableTag tag, Language::LanguageType type - ) { - Construction::hasTempVariable(callable, ast, tag, type) - } diff --git a/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/Bound.qll b/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/Bound.qll deleted file mode 100644 index 8cde0baddfc5..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/Bound.qll +++ /dev/null @@ -1,79 +0,0 @@ -import csharp -private import semmle.code.csharp.ir.IR -private import semmle.code.csharp.ir.ValueNumbering - -private newtype TBound = - TBoundZero() or - TBoundValueNumber(ValueNumber vn) { - exists(Instruction i | - vn.getAnInstruction() = i and - ( - i.getResultType() instanceof IntegralType or - i.getResultType() instanceof PointerType - ) and - not vn.getAnInstruction() instanceof ConstantInstruction - | - i instanceof PhiInstruction - or - i instanceof InitializeParameterInstruction - or - i instanceof CallInstruction - or - i instanceof VariableAddressInstruction - or - i instanceof FieldAddressInstruction - or - i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction - or - i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction - or - i.getAUse() instanceof ArgumentOperand - ) - } - -/** - * A bound that may be inferred for an expression plus/minus an integer delta. - */ -abstract class Bound extends TBound { - abstract string toString(); - - /** Gets an expression that equals this bound plus `delta`. */ - abstract Instruction getInstruction(int delta); - - /** Gets an expression that equals this bound. */ - Instruction getInstruction() { result = getInstruction(0) } - - abstract Location getLocation(); -} - -/** - * The bound that corresponds to the integer 0. This is used to represent all - * integer bounds as bounds are always accompanied by an added integer delta. - */ -class ZeroBound extends Bound, TBoundZero { - override string toString() { result = "0" } - - override Instruction getInstruction(int delta) { - result.(ConstantValueInstruction).getValue().toInt() = delta - } - - override Location getLocation() { result instanceof EmptyLocation } -} - -/** - * A bound corresponding to the value of an `Instruction`. - */ -class ValueNumberBound extends Bound, TBoundValueNumber { - ValueNumber vn; - - ValueNumberBound() { this = TBoundValueNumber(vn) } - - /** Gets the SSA variable that equals this bound. */ - override Instruction getInstruction(int delta) { - this = TBoundValueNumber(valueNumber(result)) and delta = 0 - } - - override string toString() { result = vn.getExampleInstruction().toString() } - - override Location getLocation() { result = vn.getLocation() } -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/RangeAnalysis.qll b/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/RangeAnalysis.qll deleted file mode 100644 index e3403728633d..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/RangeAnalysis.qll +++ /dev/null @@ -1,635 +0,0 @@ -/** - * Provides classes and predicates for range analysis. - * - * An inferred bound can either be a specific integer or a `ValueNumber` - * representing the abstract value of a set of `Instruction`s. - * - * If an inferred bound relies directly on a condition, then this condition is - * reported as the reason for the bound. - */ - -/* - * This library tackles range analysis as a flow problem. Consider e.g.: - * ``` - * len = arr.length; - * if (x < len) { ... y = x-1; ... y ... } - * ``` - * In this case we would like to infer `y <= arr.length - 2`, and this is - * accomplished by tracking the bound through a sequence of steps: - * ``` - * arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y - * ``` - * - * In its simplest form the step relation `I1 --> I2` relates two `Instruction`s - * such that `I1 <= B` implies `I2 <= B` for any `B` (with a second separate - * step relation handling lower bounds). Examples of such steps include - * assignments `I2 = I1` and conditions `x <= I1` where `I2` is a use of `x` - * guarded by the condition. - * - * In order to handle subtractions and additions with constants, and strict - * comparisons, the step relation is augmented with an integer delta. With this - * generalization `I1 --(delta)--> I2` relates two `Instruction`s and an integer - * such that `I1 <= B` implies `I2 <= B + delta` for any `B`. This corresponds - * to the predicate `boundFlowStep`. - * - * The complete range analysis is then implemented as the transitive closure of - * the step relation summing the deltas along the way. If `I1` transitively - * steps to `I2`, `delta` is the sum of deltas along the path, and `B` is an - * interesting bound equal to the value of `I1` then `I2 <= B + delta`. This - * corresponds to the predicate `boundedInstruction`. - * - * Bounds come in two forms: either they are relative to zero (and thus provide - * a constant bound), or they are relative to some program value. This value is - * represented by the `ValueNumber` class, each instance of which represents a - * set of `Instructions` that must have the same value. - * - * Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`. - * There are essentially two cases: - * - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`. - * - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`. - * The first case is for whenever a bound can be proven without taking looping - * into account. The second case is relevant when `x2` comes from a back-edge - * where we can prove that the variable has been non-increasing through the - * loop-iteration as this means that any upper bound that holds prior to the - * loop also holds for the variable during the loop. - * This generalizes to a phi node with `n` inputs, so if - * `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we - * also have `x0 <= B + delta` if we can prove either: - * - `xj <= B + d` with `d <= delta` or - * - `xj <= x0 + d` with `d <= 0` - * for each input `xj`. - * - * As all inferred bounds can be related directly to a path in the source code - * the only source of non-termination is if successive redundant (and thereby - * increasingly worse) bounds are calculated along a loop in the source code. - * We prevent this by weakening the bound to a small finite set of bounds when - * a path follows a second back-edge (we postpone weakening till the second - * back-edge as a precise bound might require traversing a loop once). - */ - -import csharp -private import semmle.code.csharp.ir.IR -private import semmle.code.csharp.ir.internal.IRGuards -private import semmle.code.csharp.ir.ValueNumbering -private import RangeUtils -private import SignAnalysis -import Bound - -cached -private module RangeAnalysisCache { - cached - module RangeAnalysisPublic { - /** - * Holds if `b + delta` is a valid bound for `i`. - * - `upper = true` : `i <= b + delta` - * - `upper = false` : `i >= b + delta` - * - * The reason for the bound is given by `reason` and may be either a condition - * or `NoReason` if the bound was proven directly without the use of a bounding - * condition. - */ - cached - predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) { - boundedInstruction(i, b, delta, upper, _, _, reason) - } - - /** - * Holds if `b + delta` is a valid bound for `op`. - * - `upper = true` : `op <= b + delta` - * - `upper = false` : `op >= b + delta` - * - * The reason for the bound is given by `reason` and may be either a condition - * or `NoReason` if the bound was proven directly without the use of a bounding - * condition. - */ - cached - predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) { - boundedNonPhiOperand(op, b, delta, upper, _, _, reason) - or - boundedPhiOperand(op, b, delta, upper, _, _, reason) - } - } - - /** - * Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`. - */ - cached - predicate possibleReason(IRGuardCondition guard) { - guard = boundFlowCond(_, _, _, _, _) - or - guard = eqFlowCond(_, _, _, _, _) - } -} - -private import RangeAnalysisCache -import RangeAnalysisPublic - -/** - * Gets a condition that tests whether `vn` equals `bound + delta`. - * - * If the condition evaluates to `testIsTrue`: - * - `isEq = true` : `vn == bound + delta` - * - `isEq = false` : `vn != bound + delta` - */ -private IRGuardCondition eqFlowCond( - ValueNumber vn, Operand bound, int delta, boolean isEq, boolean testIsTrue -) { - result.comparesEq(vn.getAUse(), bound, delta, isEq, testIsTrue) -} - -/** - * Holds if `op1 + delta` is a valid bound for `op2`. - * - `upper = true` : `op2 <= op1 + delta` - * - `upper = false` : `op2 >= op1 + delta` - */ -private predicate boundFlowStepSsa( - NonPhiOperand op2, Operand op1, int delta, boolean upper, Reason reason -) { - exists(IRGuardCondition guard, boolean testIsTrue | - guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and - guard.controls(op2.getUse().getBlock(), testIsTrue) and - reason = TCondReason(guard) - ) -} - -/** - * Gets a condition that tests whether `vn` is bounded by `bound + delta`. - * - * If the condition evaluates to `testIsTrue`: - * - `upper = true` : `vn <= bound + delta` - * - `upper = false` : `vn >= bound + delta` - */ -private IRGuardCondition boundFlowCond( - ValueNumber vn, NonPhiOperand bound, int delta, boolean upper, boolean testIsTrue -) { - exists(int d | - result.comparesLt(vn.getAUse(), bound, d, upper, testIsTrue) and - // `comparesLt` provides bounds of the form `x < y + k` or `x >= y + k`, but we need - // `x <= y + k` so we strengthen here. `testIsTrue` has the same semantics in `comparesLt` as - // it does here, so we don't need to account for it. - if upper = true then delta = d - 1 else delta = d - ) - or - result = eqFlowCond(vn, bound, delta, true, testIsTrue) and - (upper = true or upper = false) -} - -private newtype TReason = - TNoReason() or - TCondReason(IRGuardCondition guard) { possibleReason(guard) } - -/** - * A reason for an inferred bound. This can either be `CondReason` if the bound - * is due to a specific condition, or `NoReason` if the bound is inferred - * without going through a bounding condition. - */ -abstract class Reason extends TReason { - abstract string toString(); -} - -class NoReason extends Reason, TNoReason { - override string toString() { result = "NoReason" } -} - -class CondReason extends Reason, TCondReason { - IRGuardCondition getCond() { this = TCondReason(result) } - - override string toString() { result = getCond().toString() } -} - -/** - * Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of - * range analysis. - */ -pragma[inline] -private predicate safeCast(IntegralType fromtyp, IntegralType totyp) { - fromtyp.getSize() < totyp.getSize() and - ( - fromtyp instanceof UnsignedIntegralType - or - totyp instanceof SignedIntegralType - ) - or - fromtyp.getSize() <= totyp.getSize() and - ( - fromtyp instanceof SignedIntegralType and - totyp instanceof SignedIntegralType - or - fromtyp instanceof UnsignedIntegralType and - totyp instanceof UnsignedIntegralType - ) -} - -private class SafeCastInstruction extends ConvertInstruction { - SafeCastInstruction() { - safeCast(getResultType(), getUnary().getResultType()) - or - getResultType() instanceof PointerType and - getUnary().getResultType() instanceof PointerType - } -} - -/** - * Holds if `typ` is a small integral type with the given lower and upper bounds. - */ -private predicate typeBound(IntegralType typ, int lowerbound, int upperbound) { - typ instanceof SignedIntegralType and - typ.getSize() = 1 and - lowerbound = typ.minValue() and - upperbound = typ.maxValue() - or - typ instanceof UnsignedIntegralType and - typ.getSize() = 1 and - lowerbound = typ.minValue() and - upperbound = typ.maxValue() - or - typ instanceof SignedIntegralType and - typ.getSize() = 2 and - lowerbound = typ.minValue() and - upperbound = typ.maxValue() - or - typ instanceof UnsignedIntegralType and - typ.getSize() = 2 and - lowerbound = typ.minValue() and - upperbound = typ.maxValue() -} - -/** - * A cast to a small integral type that may overflow or underflow. - */ -private class NarrowingCastInstruction extends ConvertInstruction { - NarrowingCastInstruction() { - not this instanceof SafeCastInstruction and - typeBound(getResultType(), _, _) - } - - /** Gets the lower bound of the resulting type. */ - int getLowerBound() { typeBound(getResultType(), result, _) } - - /** Gets the upper bound of the resulting type. */ - int getUpperBound() { typeBound(getResultType(), _, result) } -} - -/** - * Holds if `op + delta` is a valid bound for `i`. - * - `upper = true` : `i <= op + delta` - * - `upper = false` : `i >= op + delta` - */ -private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, boolean upper) { - valueFlowStep(i, op, delta) and - (upper = true or upper = false) - or - i.(SafeCastInstruction).getAnOperand() = op and - delta = 0 and - (upper = true or upper = false) - or - exists(Operand x | - i.(AddInstruction).getAnOperand() = op and - i.(AddInstruction).getAnOperand() = x and - op != x - | - not exists(getValue(getConstantValue(op.getUse()))) and - not exists(getValue(getConstantValue(x.getUse()))) and - if strictlyPositive(x) - then upper = false and delta = 1 - else - if positive(x) - then upper = false and delta = 0 - else - if strictlyNegative(x) - then upper = true and delta = -1 - else - if negative(x) - then upper = true and delta = 0 - else none() - ) - or - exists(Operand x | - exists(SubInstruction sub | - i = sub and - sub.getLeftOperand() = op and - sub.getRightOperand() = x - ) - | - // `x` with constant value is covered by valueFlowStep - not exists(getValue(getConstantValue(x.getUse()))) and - if strictlyPositive(x) - then upper = true and delta = -1 - else - if positive(x) - then upper = true and delta = 0 - else - if strictlyNegative(x) - then upper = false and delta = 1 - else - if negative(x) - then upper = false and delta = 0 - else none() - ) - or - i.(RemInstruction).getRightOperand() = op and positive(op) and delta = -1 and upper = true - or - i.(RemInstruction).getLeftOperand() = op and positive(op) and delta = 0 and upper = true - or - i.(BitAndInstruction).getAnOperand() = op and positive(op) and delta = 0 and upper = true - or - i.(BitOrInstruction).getAnOperand() = op and - positiveInstruction(i) and - delta = 0 and - upper = false - // TODO: min, max, rand -} - -private predicate boundFlowStepMul(Instruction i1, Operand op, int factor) { - exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | - i1.(MulInstruction).hasOperands(op, c.getAUse()) and factor = k - or - exists(ShiftLeftInstruction i | - i = i1 and i.getLeftOperand() = op and i.getRightOperand() = c.getAUse() and factor = 2.pow(k) - ) - ) -} - -private predicate boundFlowStepDiv(Instruction i1, Operand op, int factor) { - exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | - exists(DivInstruction i | - i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = k - ) - or - exists(ShiftRightInstruction i | - i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = 2.pow(k) - ) - ) -} - -/** - * Holds if `b` is a valid bound for `op` - */ -pragma[noinline] -private predicate boundedNonPhiOperand( - NonPhiOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, - Reason reason -) { - exists(NonPhiOperand op2, int d1, int d2 | - boundFlowStepSsa(op, op2, d1, upper, reason) and - boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, _) and - delta = d1 + d2 - ) - or - boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) - or - exists(int d, Reason r1, Reason r2 | - boundedNonPhiOperand(op, b, d, upper, fromBackEdge, origdelta, r2) - | - unequalOperand(op, b, d, r1) and - ( - upper = true and delta = d - 1 - or - upper = false and delta = d + 1 - ) and - ( - reason = r1 - or - reason = r2 and not r2 instanceof NoReason - ) - ) -} - -/** - * Holds if `op1 + delta` is a valid bound for `op2`. - * - `upper = true` : `op2 <= op1 + delta` - * - `upper = false` : `op2 >= op1 + delta` - */ -private predicate boundFlowStepPhi( - PhiInputOperand op2, Operand op1, int delta, boolean upper, Reason reason -) { - op2.getDef().(CopyInstruction).getSourceValueOperand() = op1 and - (upper = true or upper = false) and - reason = TNoReason() and - delta = 0 - or - exists(IRGuardCondition guard, boolean testIsTrue | - guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and - guard.controlsEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and - reason = TCondReason(guard) - ) -} - -private predicate boundedPhiOperand( - PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, - Reason reason -) { - exists(NonPhiOperand op2, int d1, int d2, Reason r1, Reason r2 | - boundFlowStepPhi(op, op2, d1, upper, r1) and - boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, r2) and - delta = d1 + d2 and - (if r1 instanceof NoReason then reason = r2 else reason = r1) - ) - or - boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) - or - exists(int d, Reason r1, Reason r2 | - boundedInstruction(op.getDef(), b, d, upper, fromBackEdge, origdelta, r2) - | - unequalOperand(op, b, d, r1) and - ( - upper = true and delta = d - 1 - or - upper = false and delta = d + 1 - ) and - ( - reason = r1 - or - reason = r2 and not r2 instanceof NoReason - ) - ) -} - -/** Holds if `op2 != op1 + delta` at `pos`. */ -private predicate unequalFlowStep(Operand op2, Operand op1, int delta, Reason reason) { - exists(IRGuardCondition guard, boolean testIsTrue | - guard = eqFlowCond(valueNumberOfOperand(op2), op1, delta, false, testIsTrue) and - guard.controls(op2.getUse().getBlock(), testIsTrue) and - reason = TCondReason(guard) - ) -} - -/** - * Holds if `op != b + delta` at `pos`. - */ -private predicate unequalOperand(Operand op, Bound b, int delta, Reason reason) { - exists(Operand op2, int d1, int d2 | - unequalFlowStep(op, op2, d1, reason) and - boundedNonPhiOperand(op2, b, d2, true, _, _, _) and - boundedNonPhiOperand(op2, b, d2, false, _, _, _) and - delta = d1 + d2 - ) -} - -private predicate boundedPhiCandValidForEdge( - PhiInstruction phi, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, - Reason reason, PhiInputOperand op -) { - boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and - ( - exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = true and d <= delta) - or - exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = false and d >= delta) - or - selfBoundedPhiInp(phi, op, upper) - ) -} - -/** Weakens a delta to lie in the range `[-1..1]`. */ -bindingset[delta, upper] -private int weakenDelta(boolean upper, int delta) { - delta in [-1 .. 1] and result = delta - or - upper = true and result = -1 and delta < -1 - or - upper = false and result = 1 and delta > 1 -} - -private predicate boundedPhiInp( - PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, - int origdelta, Reason reason -) { - phi.getAnOperand() = op and - exists(int d, boolean fromBackEdge0 | - boundedPhiOperand(op, b, d, upper, fromBackEdge0, origdelta, reason) - or - b.(ValueNumberBound).getInstruction() = op.getDef() and - d = 0 and - (upper = true or upper = false) and - fromBackEdge0 = false and - origdelta = 0 and - reason = TNoReason() - | - if backEdge(phi, op) - then - fromBackEdge = true and - ( - fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta - or - fromBackEdge0 = false and delta = d - ) - else ( - delta = d and fromBackEdge = fromBackEdge0 - ) - ) -} - -pragma[noinline] -private predicate boundedPhiInp1( - PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper -) { - boundedPhiInp(phi, op, b, delta, upper, _, _, _) -} - -private predicate selfBoundedPhiInp(PhiInstruction phi, PhiInputOperand op, boolean upper) { - exists(int d, ValueNumberBound phibound | - phibound.getInstruction() = phi and - boundedPhiInp(phi, op, phibound, d, upper, _, _, _) and - ( - upper = true and d <= 0 - or - upper = false and d >= 0 - ) - ) -} - -pragma[noinline] -private predicate boundedPhiCand( - PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta, - Reason reason -) { - exists(PhiInputOperand op | - boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason) - ) -} - -/** - * Holds if the value being cast has an upper (for `upper = true`) or lower - * (for `upper = false`) bound within the bounds of the resulting type. - * For `upper = true` this means that the cast will not overflow and for - * `upper = false` this means that the cast will not underflow. - */ -private predicate safeNarrowingCast(NarrowingCastInstruction cast, boolean upper) { - exists(int bound | - boundedNonPhiOperand(cast.getAnOperand(), any(ZeroBound zb), bound, upper, _, _, _) - | - upper = true and bound <= cast.getUpperBound() - or - upper = false and bound >= cast.getLowerBound() - ) -} - -pragma[noinline] -private predicate boundedCastExpr( - NarrowingCastInstruction cast, Bound b, int delta, boolean upper, boolean fromBackEdge, - int origdelta, Reason reason -) { - boundedNonPhiOperand(cast.getAnOperand(), b, delta, upper, fromBackEdge, origdelta, reason) -} - -/** - * Holds if `b + delta` is a valid bound for `i`. - * - `upper = true` : `i <= b + delta` - * - `upper = false` : `i >= b + delta` - */ -private predicate boundedInstruction( - Instruction i, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, - Reason reason -) { - i instanceof PhiInstruction and - forex(PhiInputOperand op | op = i.getAnOperand() | - boundedPhiCandValidForEdge(i, b, delta, upper, fromBackEdge, origdelta, reason, op) - ) - or - i = b.getInstruction(delta) and - (upper = true or upper = false) and - fromBackEdge = false and - origdelta = delta and - reason = TNoReason() - or - exists(Operand mid, int d1, int d2 | - boundFlowStep(i, mid, d1, upper) and - boundedNonPhiOperand(mid, b, d2, upper, fromBackEdge, origdelta, reason) and - delta = d1 + d2 and - not exists(getValue(getConstantValue(i))) - ) - or - exists(Operand mid, int factor, int d | - boundFlowStepMul(i, mid, factor) and - boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and - b instanceof ZeroBound and - delta = d * factor and - not exists(getValue(getConstantValue(i))) - ) - or - exists(Operand mid, int factor, int d | - boundFlowStepDiv(i, mid, factor) and - boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and - d >= 0 and - b instanceof ZeroBound and - delta = d / factor and - not exists(getValue(getConstantValue(i))) - ) - or - exists(NarrowingCastInstruction cast | - cast = i and - safeNarrowingCast(cast, upper.booleanNot()) and - boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason) - ) - or - exists(PropertyAccess pa | - i.(CallInstruction).getAST() = pa and - pa.getProperty().getName() = "Length" and - b instanceof ZeroBound and - delta = origdelta and - (upper = true or upper = false) and - fromBackEdge = false and - delta = getArrayDim(pa.getQualifier().(VariableAccess).getTarget()) and - reason = TNoReason() - ) -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/RangeUtils.qll b/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/RangeUtils.qll deleted file mode 100644 index f3b94c4ef011..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/RangeUtils.qll +++ /dev/null @@ -1,99 +0,0 @@ -import csharp -private import semmle.code.csharp.ir.IR -// TODO: move this dependency -import semmle.code.csharp.ir.internal.IntegerConstant - -// TODO: move this out of test code -language[monotonicAggregates] -IntValue getConstantValue(Instruction instr) { - result = instr.(IntegerConstantInstruction).getValue().toInt() - or - exists(BinaryInstruction binInstr, IntValue left, IntValue right | - binInstr = instr and - left = getConstantValue(binInstr.getLeft()) and - right = getConstantValue(binInstr.getRight()) and - ( - binInstr instanceof AddInstruction and result = add(left, right) - or - binInstr instanceof SubInstruction and result = sub(left, right) - or - binInstr instanceof MulInstruction and result = mul(left, right) - or - binInstr instanceof DivInstruction and result = div(left, right) - ) - ) - or - result = getConstantValue(instr.(CopyInstruction).getSourceValue()) - or - exists(PhiInstruction phi | - phi = instr and - result = - max(PhiInputOperand operand | - operand = phi.getAnOperand() - | - getConstantValue(operand.getDef()) - ) and - result = - min(PhiInputOperand operand | - operand = phi.getAnOperand() - | - getConstantValue(operand.getDef()) - ) - ) -} - -/** - * Gets the dimension of the array (either the declared size, or the - * size of the initializer); if no size is declared and no initializer used, - * the predicate does not hold. - */ -IntValue getArrayDim(Variable arr) { - exists(ArrayCreation ac | - arr.getInitializer() = ac and - if exists(ac.getLengthArgument(0)) - then result = ac.getLengthArgument(0).getValue().toInt() - else - if exists(ac.getInitializer()) - then result = ac.getInitializer().getNumberOfElements() - else none() - ) -} - -predicate valueFlowStep(Instruction i, Operand op, int delta) { - i.(CopyInstruction).getSourceValueOperand() = op and delta = 0 - or - exists(Operand x | - i.(AddInstruction).getAnOperand() = op and - i.(AddInstruction).getAnOperand() = x and - op != x - | - delta = getValue(getConstantValue(x.getDef())) - ) - or - exists(Operand x | - i.(SubInstruction).getLeftOperand() = op and - i.(SubInstruction).getRightOperand() = x - | - delta = -getValue(getConstantValue(x.getDef())) - ) - or - exists(Operand x | - i.(PointerAddInstruction).getAnOperand() = op and - i.(PointerAddInstruction).getAnOperand() = x and - op != x - | - delta = i.(PointerAddInstruction).getElementSize() * getValue(getConstantValue(x.getDef())) - ) - or - exists(Operand x | - i.(PointerSubInstruction).getLeftOperand() = op and - i.(PointerSubInstruction).getRightOperand() = x - | - delta = i.(PointerSubInstruction).getElementSize() * -getValue(getConstantValue(x.getDef())) - ) -} - -predicate backEdge(PhiInstruction phi, PhiInputOperand op) { - phi.getAnOperand() = op and - phi.getBlock() = op.getPredecessorBlock().getBackEdgeSuccessor(_) -} diff --git a/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/SignAnalysis.qll b/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/SignAnalysis.qll deleted file mode 100644 index 366a1ca047c6..000000000000 --- a/csharp/ql/src/semmle/code/csharp/ir/rangeanalysis/SignAnalysis.qll +++ /dev/null @@ -1,583 +0,0 @@ -/** - * Provides sign analysis to determine whether expression are always positive - * or negative. - * - * The analysis is implemented as an abstract interpretation over the - * three-valued domain `{negative, zero, positive}`. - */ - -import csharp -private import semmle.code.csharp.ir.IR -private import semmle.code.csharp.ir.internal.IRGuards -private import semmle.code.csharp.ir.ValueNumbering -private import SignAnalysisCached - -private newtype TSign = - TNeg() or - TZero() or - TPos() - -private class Sign extends TSign { - string toString() { - result = "-" and this = TNeg() - or - result = "0" and this = TZero() - or - result = "+" and this = TPos() - } - - Sign inc() { - this = TNeg() and result = TNeg() - or - this = TNeg() and result = TZero() - or - this = TZero() and result = TPos() - or - this = TPos() and result = TPos() - } - - Sign dec() { result.inc() = this } - - Sign neg() { - this = TNeg() and result = TPos() - or - this = TZero() and result = TZero() - or - this = TPos() and result = TNeg() - } - - Sign bitnot() { - this = TNeg() and result = TPos() - or - this = TNeg() and result = TZero() - or - this = TZero() and result = TNeg() - or - this = TPos() and result = TNeg() - } - - Sign add(Sign s) { - this = TZero() and result = s - or - s = TZero() and result = this - or - this = s and this = result - or - this = TPos() and s = TNeg() - or - this = TNeg() and s = TPos() - } - - Sign mul(Sign s) { - result = TZero() and this = TZero() - or - result = TZero() and s = TZero() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign div(Sign s) { - result = TZero() and s = TNeg() - or - result = TZero() and s = TPos() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign rem(Sign s) { - result = TZero() and s = TNeg() - or - result = TZero() and s = TPos() - or - result = this and s = TNeg() - or - result = this and s = TPos() - } - - Sign bitand(Sign s) { - result = TZero() and this = TZero() - or - result = TZero() and s = TZero() - or - result = TZero() and this = TPos() - or - result = TZero() and s = TPos() - or - result = TNeg() and this = TNeg() and s = TNeg() - or - result = TPos() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TNeg() - or - result = TPos() and this = TPos() and s = TPos() - } - - Sign bitor(Sign s) { - result = TZero() and this = TZero() and s = TZero() - or - result = TNeg() and this = TNeg() - or - result = TNeg() and s = TNeg() - or - result = TPos() and this = TPos() and s = TZero() - or - result = TPos() and this = TZero() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - } - - Sign bitxor(Sign s) { - result = TZero() and this = s - or - result = this and s = TZero() - or - result = s and this = TZero() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign lshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - this != TZero() and s != TZero() - } - - Sign rshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - result = TNeg() and this = TNeg() - or - result != TNeg() and this = TPos() and s != TZero() - } - - Sign urshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - result != TZero() and this = TNeg() and s != TZero() - or - result != TNeg() and this = TPos() and s != TZero() - } -} - -private Sign certainInstructionSign(Instruction inst) { - exists(int i | inst.(IntegerConstantInstruction).getValue().toInt() = i | - i < 0 and result = TNeg() - or - i = 0 and result = TZero() - or - i > 0 and result = TPos() - ) - or - exists(float f | f = inst.(FloatConstantInstruction).getValue().toFloat() | - f < 0 and result = TNeg() - or - f = 0 and result = TZero() - or - f > 0 and result = TPos() - ) -} - -private newtype CastKind = - TWiden() or - TSame() or - TNarrow() - -private CastKind getCastKind(ConvertInstruction ci) { - exists(int fromSize, int toSize | - toSize = ci.getResultSize() and - fromSize = ci.getUnary().getResultSize() - | - fromSize < toSize and - result = TWiden() - or - fromSize = toSize and - result = TSame() - or - fromSize > toSize and - result = TNarrow() - ) -} - -private predicate bindBool(boolean bool) { - bool = true or - bool = false -} - -private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck) { - result = TZero() and - ( - bindBool(fromSigned) and - bindBool(toSigned) and - s = TZero() - or - bindBool(fromSigned) and - bindBool(toSigned) and - ck = TNarrow() - ) - or - result = TPos() and - ( - bindBool(fromSigned) and - bindBool(toSigned) and - s = TPos() - or - bindBool(fromSigned) and - bindBool(toSigned) and - s = TNeg() and - ck = TNarrow() - or - fromSigned = true and - toSigned = false and - s = TNeg() - ) - or - result = TNeg() and - ( - fromSigned = true and - toSigned = true and - s = TNeg() - or - fromSigned = false and - toSigned = true and - s = TPos() and - ck != TWiden() - ) -} - -/** Holds if the sign of `e` is too complicated to determine. */ -private predicate unknownSign(Instruction i) { - // REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than - // the ones we don't understand. Currently, if we try to compute the sign of an instruction that - // we don't understand, and it isn't on this list, we incorrectly compute the sign as "none" - // instead of "+,0,-". - // Even better, we could track the state of each instruction as a power set of {non-negative, - // non-positive, non-zero}, which would mean that the representation of the sign of an unknown - // value would be the empty set. - ( - i instanceof UninitializedInstruction - or - i instanceof InitializeParameterInstruction - or - i instanceof BuiltInOperationInstruction - or - i instanceof CallInstruction - or - i instanceof ChiInstruction - ) -} - -/** - * Holds if `lowerbound` is a lower bound for `bounded`. This is restricted - * to only include bounds for which we might determine a sign. - */ -private predicate lowerBound( - IRGuardCondition comp, Operand lowerbound, Operand bounded, boolean isStrict -) { - exists(int adjustment, Operand compared | - valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and - ( - isStrict = true and - adjustment = 0 - or - isStrict = false and - adjustment = 1 - ) and - comp.ensuresLt(lowerbound, compared, adjustment, bounded.getUse().getBlock(), true) - ) -} - -/** - * Holds if `upperbound` is an upper bound for `bounded` at `pos`. This is restricted - * to only include bounds for which we might determine a sign. - */ -private predicate upperBound( - IRGuardCondition comp, Operand upperbound, Operand bounded, boolean isStrict -) { - exists(int adjustment, Operand compared | - valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and - ( - isStrict = true and - adjustment = 0 - or - isStrict = false and - adjustment = 1 - ) and - comp.ensuresLt(compared, upperbound, adjustment, bounded.getUse().getBlock(), true) - ) -} - -/** - * Holds if `eqbound` is an equality/inequality for `bounded` at `pos`. This is - * restricted to only include bounds for which we might determine a sign. The - * boolean `isEq` gives the polarity: - * - `isEq = true` : `bounded = eqbound` - * - `isEq = false` : `bounded != eqbound` - */ -private predicate eqBound(IRGuardCondition guard, Operand eqbound, Operand bounded, boolean isEq) { - exists(Operand compared | - valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and - guard.ensuresEq(compared, eqbound, 0, bounded.getUse().getBlock(), isEq) - ) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in - * order for `v` to be positive. - */ -private predicate posBound(IRGuardCondition comp, Operand bound, Operand op) { - upperBound(comp, bound, op, _) or - eqBound(comp, bound, op, true) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in - * order for `v` to be negative. - */ -private predicate negBound(IRGuardCondition comp, Operand bound, Operand op) { - lowerBound(comp, bound, op, _) or - eqBound(comp, bound, op, true) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` - * can be zero. - */ -private predicate zeroBound(IRGuardCondition comp, Operand bound, Operand op) { - lowerBound(comp, bound, op, _) or - upperBound(comp, bound, op, _) or - eqBound(comp, bound, op, _) -} - -/** Holds if `bound` allows `v` to be positive at `pos`. */ -private predicate posBoundOk(IRGuardCondition comp, Operand bound, Operand op) { - posBound(comp, bound, op) and TPos() = operandSign(bound) -} - -/** Holds if `bound` allows `v` to be negative at `pos`. */ -private predicate negBoundOk(IRGuardCondition comp, Operand bound, Operand op) { - negBound(comp, bound, op) and TNeg() = operandSign(bound) -} - -/** Holds if `bound` allows `v` to be zero at `pos`. */ -private predicate zeroBoundOk(IRGuardCondition comp, Operand bound, Operand op) { - lowerBound(comp, bound, op, _) and TNeg() = operandSign(bound) - or - lowerBound(comp, bound, op, false) and TZero() = operandSign(bound) - or - upperBound(comp, bound, op, _) and TPos() = operandSign(bound) - or - upperBound(comp, bound, op, false) and TZero() = operandSign(bound) - or - eqBound(comp, bound, op, true) and TZero() = operandSign(bound) - or - eqBound(comp, bound, op, false) and TZero() != operandSign(bound) -} - -private Sign binaryOpLhsSign(BinaryInstruction i) { result = operandSign(i.getLeftOperand()) } - -private Sign binaryOpRhsSign(BinaryInstruction i) { result = operandSign(i.getRightOperand()) } - -pragma[noinline] -private predicate binaryOpSigns(BinaryInstruction i, Sign lhs, Sign rhs) { - lhs = binaryOpLhsSign(i) and - rhs = binaryOpRhsSign(i) -} - -private Sign unguardedOperandSign(Operand operand) { - result = instructionSign(operand.getDef()) and - not hasGuard(operand, result) -} - -private Sign guardedOperandSign(Operand operand) { - result = instructionSign(operand.getDef()) and - hasGuard(operand, result) -} - -private Sign guardedOperandSignOk(Operand operand) { - result = TPos() and - forex(IRGuardCondition guard, Operand bound | posBound(guard, bound, operand) | - posBoundOk(guard, bound, operand) - ) - or - result = TNeg() and - forex(IRGuardCondition guard, Operand bound | negBound(guard, bound, operand) | - negBoundOk(guard, bound, operand) - ) - or - result = TZero() and - forex(IRGuardCondition guard, Operand bound | zeroBound(guard, bound, operand) | - zeroBoundOk(guard, bound, operand) - ) -} - -/** - * Holds if there is a bound that might restrict whether `v` has the sign `s` - * at `pos`. - */ -private predicate hasGuard(Operand op, Sign s) { - s = TPos() and posBound(_, _, op) - or - s = TNeg() and negBound(_, _, op) - or - s = TZero() and zeroBound(_, _, op) -} - -cached -module SignAnalysisCached { - /** - * Gets a sign that `operand` may have at `pos`, taking guards into account. - */ - cached - Sign operandSign(Operand operand) { - result = unguardedOperandSign(operand) - or - result = guardedOperandSign(operand) and - result = guardedOperandSignOk(operand) - or - // `result` is unconstrained if the definition is inexact. Then any sign is possible. - operand.isDefinitionInexact() - } - - cached - Sign instructionSign(Instruction i) { - result = certainInstructionSign(i) - or - not exists(certainInstructionSign(i)) and - not ( - result = TNeg() and - i.getResultType() instanceof UnsignedIntegralType - ) and - ( - unknownSign(i) - or - exists(ConvertInstruction ci, Instruction prior, boolean fromSigned, boolean toSigned | - i = ci and - prior = ci.getUnary() and - ( - if ci.getResultType() instanceof SignedIntegralType - then toSigned = true - else toSigned = false - ) and - ( - if prior.getResultType() instanceof SignedIntegralType - then fromSigned = true - else fromSigned = false - ) and - result = castSign(operandSign(ci.getAnOperand()), fromSigned, toSigned, getCastKind(ci)) - ) - or - result = operandSign(i.(CopyInstruction).getSourceValueOperand()) - or - result = operandSign(i.(BitComplementInstruction).getAnOperand()).bitnot() - or - result = operandSign(i.(NegateInstruction).getAnOperand()).neg() - or - exists(Sign s1, Sign s2 | binaryOpSigns(i, s1, s2) | - i instanceof AddInstruction and result = s1.add(s2) - or - i instanceof SubInstruction and result = s1.add(s2.neg()) - or - i instanceof MulInstruction and result = s1.mul(s2) - or - i instanceof DivInstruction and result = s1.div(s2) - or - i instanceof RemInstruction and result = s1.rem(s2) - or - i instanceof BitAndInstruction and result = s1.bitand(s2) - or - i instanceof BitOrInstruction and result = s1.bitor(s2) - or - i instanceof BitXorInstruction and result = s1.bitxor(s2) - or - i instanceof ShiftLeftInstruction and result = s1.lshift(s2) - or - i instanceof ShiftRightInstruction and - i.getResultType().(IntegralType) instanceof SignedIntegralType and - result = s1.rshift(s2) - or - i instanceof ShiftRightInstruction and - not i.getResultType().(IntegralType) instanceof SignedIntegralType and - result = s1.urshift(s2) - ) - or - // use hasGuard here? - result = operandSign(i.(PhiInstruction).getAnOperand()) - ) - } -} - -/** Holds if `i` can be positive and cannot be negative. */ -predicate positiveInstruction(Instruction i) { - instructionSign(i) = TPos() and - not instructionSign(i) = TNeg() -} - -/** Holds if `i` at `pos` can be positive at and cannot be negative. */ -predicate positive(Operand op) { - operandSign(op) = TPos() and - not operandSign(op) = TNeg() -} - -/** Holds if `i` can be negative and cannot be positive. */ -predicate negativeInstruction(Instruction i) { - instructionSign(i) = TNeg() and - not instructionSign(i) = TPos() -} - -/** Holds if `i` at `pos` can be negative and cannot be positive. */ -predicate negative(Operand op) { - operandSign(op) = TNeg() and - not operandSign(op) = TPos() -} - -/** Holds if `i` is strictly positive. */ -predicate strictlyPositiveInstruction(Instruction i) { - instructionSign(i) = TPos() and - not instructionSign(i) = TNeg() and - not instructionSign(i) = TZero() -} - -/** Holds if `i` is strictly positive at `pos`. */ -predicate strictlyPositive(Operand op) { - operandSign(op) = TPos() and - not operandSign(op) = TNeg() and - not operandSign(op) = TZero() -} - -/** Holds if `i` is strictly negative. */ -predicate strictlyNegativeInstruction(Instruction i) { - instructionSign(i) = TNeg() and - not instructionSign(i) = TPos() and - not instructionSign(i) = TZero() -} - -/** Holds if `i` is strictly negative at `pos`. */ -predicate strictlyNegative(Operand op) { - operandSign(op) = TNeg() and - not operandSign(op) = TPos() and - not operandSign(op) = TZero() -} diff --git a/csharp/ql/src/semmle/code/csharp/metrics/Coupling.qll b/csharp/ql/src/semmle/code/csharp/metrics/Coupling.qll index f2262f2c4b58..b5330ce770e3 100644 --- a/csharp/ql/src/semmle/code/csharp/metrics/Coupling.qll +++ b/csharp/ql/src/semmle/code/csharp/metrics/Coupling.qll @@ -222,6 +222,10 @@ predicate shareFieldOrProperty(ValueOrRefType t, Method m1, Method m2) { ) } +/** + * Holds if the declaring type of method `m` is `t` and `m` accesses declaration + * `d`, which is either a field or a property. + */ predicate methodUsesFieldOrProperty(ValueOrRefType t, Method m, Declaration d) { m.getDeclaringType() = t and ( diff --git a/csharp/ql/src/semmle/code/csharp/security/dataflow/ConditionalBypass.qll b/csharp/ql/src/semmle/code/csharp/security/dataflow/ConditionalBypass.qll index 0694b27f9851..21399d502372 100644 --- a/csharp/ql/src/semmle/code/csharp/security/dataflow/ConditionalBypass.qll +++ b/csharp/ql/src/semmle/code/csharp/security/dataflow/ConditionalBypass.qll @@ -61,7 +61,9 @@ module UserControlledBypassOfSensitiveMethod { private predicate conditionControlsCall0( SensitiveExecutionMethodCall call, Expr e, ControlFlow::SuccessorTypes::BooleanSuccessor s ) { - forex(BasicBlock bb | bb = call.getAControlFlowNode().getBasicBlock() | e.controlsBlock(bb, s)) + forex(BasicBlock bb | bb = call.getAControlFlowNode().getBasicBlock() | + e.controlsBlock(bb, s, _) + ) } private predicate conditionControlsCall( diff --git a/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsinks/Html.qll b/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsinks/Html.qll index 2363e24ffeb3..97b81b6561ed 100644 --- a/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsinks/Html.qll +++ b/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsinks/Html.qll @@ -176,13 +176,18 @@ class WebPageWriteLiteralToSink extends HtmlSink { abstract class AspNetCoreHtmlSink extends HtmlSink { } /** - * An expression that is used as an argument to `HtmlHelper.Raw`, typically in + * An expression that is used as an argument to `IHtmlHelper.Raw`, typically in * a `.cshtml` file. */ class MicrosoftAspNetCoreMvcHtmlHelperRawSink extends AspNetCoreHtmlSink { MicrosoftAspNetCoreMvcHtmlHelperRawSink() { - this.getExpr() = - any(MicrosoftAspNetCoreMvcHtmlHelperClass h).getRawMethod().getACall().getAnArgument() + exists(Call c, Callable target | + c.getTarget() = target and + target.hasName("Raw") and + target.getDeclaringType().getABaseType*() instanceof + MicrosoftAspNetCoreMvcRenderingIHtmlHelperInterface and + this.getExpr() = c.getAnArgument() + ) } } diff --git a/csharp/ql/src/semmle/code/csharp/serialization/Deserializers.qll b/csharp/ql/src/semmle/code/csharp/serialization/Deserializers.qll index 685a48b7079b..ece42b188c00 100644 --- a/csharp/ql/src/semmle/code/csharp/serialization/Deserializers.qll +++ b/csharp/ql/src/semmle/code/csharp/serialization/Deserializers.qll @@ -8,6 +8,7 @@ import csharp /** An unsafe deserializer. */ abstract class UnsafeDeserializer extends Callable { } +/** An unsafe deserializer method in the `System.*` namespace. */ class SystemDeserializer extends UnsafeDeserializer { SystemDeserializer() { this @@ -48,12 +49,17 @@ class SystemDeserializer extends UnsafeDeserializer { } } +/** An unsafe deserializer method in the `Microsoft.*` namespace. */ class MicrosoftDeserializer extends UnsafeDeserializer { MicrosoftDeserializer() { this.hasQualifiedName("Microsoft.Web.Design.Remote.ProxyObject", "DecodeValue") } } +/** + * An unsafe deserializer method that calls any unsafe deserializer on any of + * the parameters. + */ class WrapperDeserializer extends UnsafeDeserializer { WrapperDeserializer() { exists(Call call | diff --git a/csharp/ql/src/semmle/code/csharp/serialization/Serialization.qll b/csharp/ql/src/semmle/code/csharp/serialization/Serialization.qll index bf7dff102ec4..047d1c411c7f 100644 --- a/csharp/ql/src/semmle/code/csharp/serialization/Serialization.qll +++ b/csharp/ql/src/semmle/code/csharp/serialization/Serialization.qll @@ -1,5 +1,14 @@ +/** + * Provides classes to identify any .Net serializable type such as types + * attributed with `SerializableAttribute` and types implementing the + * `ISerializable` interface. + */ + import csharp +/** + * A constructor with `SerializationInfo` and `StreamingContext` parameters. + */ class SerializationConstructor extends Constructor { SerializationConstructor() { this.getNumberOfParameters() = 2 and @@ -91,16 +100,3 @@ class CustomBinarySerializableType extends BinarySerializableType { result.(SerializationConstructor).getDeclaringType() = this } } - -class DangerousCallable extends Callable { - DangerousCallable() { - //files - this.(Method).getQualifiedName().matches("System.IO.File.Write%") or - this.(Method).getQualifiedName().matches("System.IO.File.%Copy%") or - this.(Method).getQualifiedName().matches("System.IO.File.%Move%") or - this.(Method).getQualifiedName().matches("System.IO.File.%Append%") or - this.(Method).getQualifiedName().matches("System.IO.%.%Delete%") or - //assembly - this.(Method).getQualifiedName().matches("System.Reflection.Assembly.%Load%") - } -} diff --git a/csharp/ql/src/semmle/code/dotnet/Callable.qll b/csharp/ql/src/semmle/code/dotnet/Callable.qll index 443c0040a584..8e8de83f8811 100644 --- a/csharp/ql/src/semmle/code/dotnet/Callable.qll +++ b/csharp/ql/src/semmle/code/dotnet/Callable.qll @@ -1,3 +1,8 @@ +/** + * Provides `Callable` classes, which are things that can be called + * such as methods and constructors. + */ + import Declaration import Variable import Expr diff --git a/csharp/ql/src/semmle/code/dotnet/Element.qll b/csharp/ql/src/semmle/code/dotnet/Element.qll index 9d8b35023aad..10a688d8ddd0 100644 --- a/csharp/ql/src/semmle/code/dotnet/Element.qll +++ b/csharp/ql/src/semmle/code/dotnet/Element.qll @@ -50,7 +50,7 @@ class NamedElement extends Element, @dotnet_named_element { * Gets the fully qualified name of this element, for example the * fully qualified name of `M` on line 3 is `N.C.M` in * - * ``` + * ```csharp * namespace N { * class C { * void M(int i, string s) { } diff --git a/csharp/ql/src/semmle/code/dotnet/Variable.qll b/csharp/ql/src/semmle/code/dotnet/Variable.qll index 0ae3706b7115..c004b9ab6b38 100644 --- a/csharp/ql/src/semmle/code/dotnet/Variable.qll +++ b/csharp/ql/src/semmle/code/dotnet/Variable.qll @@ -1,3 +1,5 @@ +/** Provides classes for .Net variables, such as fields and parameters. */ + import Declaration import Callable diff --git a/csharp/ql/src/semmlecode.csharp.dbscheme b/csharp/ql/src/semmlecode.csharp.dbscheme index f2aa2d4ac313..ddd39829bb71 100644 --- a/csharp/ql/src/semmlecode.csharp.dbscheme +++ b/csharp/ql/src/semmlecode.csharp.dbscheme @@ -692,7 +692,7 @@ overrides( int base_id: @callable ref); explicitly_implements( - unique int id: @member ref, + int id: @member ref, int interface_id: @interface_or_ref ref); local_functions( diff --git a/csharp/ql/test/experimental/Security Features/Serialization/DefiningDatasetRelatedType.expected b/csharp/ql/test/experimental/Security Features/Serialization/DefiningDatasetRelatedType.expected new file mode 100644 index 000000000000..8d01c5e62b4a --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/DefiningDatasetRelatedType.expected @@ -0,0 +1,2 @@ +| test0.cs:13:18:13:43 | DerivesFromDeprecatedType1 | Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | +| test0.cs:59:18:59:38 | AttributeSerializer01 | Defining a class that inherits or has a property derived from the obsolete DataSet or DataTable types. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | diff --git a/csharp/ql/test/experimental/Security Features/Serialization/DefiningDatasetRelatedType.qlref b/csharp/ql/test/experimental/Security Features/Serialization/DefiningDatasetRelatedType.qlref new file mode 100644 index 000000000000..7283db95daf5 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/DefiningDatasetRelatedType.qlref @@ -0,0 +1 @@ +experimental/Security Features/Serialization/DefiningDatasetRelatedType.ql \ No newline at end of file diff --git a/csharp/ql/test/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.expected b/csharp/ql/test/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.expected new file mode 100644 index 000000000000..5b90d0c0a642 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.expected @@ -0,0 +1,2 @@ +| test0.cs:15:24:15:32 | MyDataSet | Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | test0.cs:13:18:13:43 | DerivesFromDeprecatedType1 | DerivesFromDeprecatedType1 | test0.cs:15:24:15:32 | MyDataSet | MyDataSet | +| test0.cs:61:25:61:33 | MyDataSet | Defining an serializable class $@ that has member $@ of a type that is derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | test0.cs:59:18:59:38 | AttributeSerializer01 | AttributeSerializer01 | test0.cs:61:25:61:33 | MyDataSet | MyDataSet | diff --git a/csharp/ql/test/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.qlref b/csharp/ql/test/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.qlref new file mode 100644 index 000000000000..8a8632c6ee3b --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.qlref @@ -0,0 +1 @@ +experimental/Security Features/Serialization/DefiningPotentiallyUnsafeXmlSerializer.ql \ No newline at end of file diff --git a/csharp/ql/test/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.expected b/csharp/ql/test/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.expected new file mode 100644 index 000000000000..0c9ce7297e97 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.expected @@ -0,0 +1,2 @@ +| test0.cs:95:49:95:63 | typeof(...) | Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source. | test0.cs:95:49:95:63 | typeof(...) | typeof(...) | +| test0.cs:96:49:96:77 | typeof(...) | Unsafe type is used in data contract serializer. Make sure $@ comes from the trusted source. | test0.cs:96:49:96:77 | typeof(...) | typeof(...) | diff --git a/csharp/ql/test/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.qlref b/csharp/ql/test/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.qlref new file mode 100644 index 000000000000..1593497c7932 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.qlref @@ -0,0 +1 @@ +experimental/Security Features/Serialization/UnsafeTypeUsedDataContractSerializer.ql \ No newline at end of file diff --git a/csharp/ql/test/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.expected b/csharp/ql/test/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.expected new file mode 100644 index 000000000000..4f451559aaf0 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.expected @@ -0,0 +1 @@ +| test0.cs:88:17:88:46 | call to method ReadXmlSchema | Making an XML deserialization call with a type derived from DataSet or DataTable types and may lead to a security problem. Please visit https://go.microsoft.com/fwlink/?linkid=2132227 for details. | diff --git a/csharp/ql/test/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.qlref b/csharp/ql/test/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.qlref new file mode 100644 index 000000000000..8054e46f929b --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/XmlDeserializationWithDataSet.qlref @@ -0,0 +1 @@ +experimental/Security Features/Serialization/XmlDeserializationWithDataSet.ql \ No newline at end of file diff --git a/csharp/ql/test/experimental/Security Features/Serialization/test0.cs b/csharp/ql/test/experimental/Security Features/Serialization/test0.cs new file mode 100644 index 000000000000..30d266203fac --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/Serialization/test0.cs @@ -0,0 +1,101 @@ +// semmle-extractor-options: /r:System.Data.Common.dll /r:System.Runtime.WindowsRuntime.dll /r:System.Xml.XmlSerializer.dll /r:System.Runtime.Serialization.Xml.dll /r:System.Runtime.Serialization.Xml.dll /r:System.Collections.dll /r:System.Private.Xml.dll /r:System.Private.DataContractSerialization.dll /r:System.Runtime.Extensions.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Xml.ReaderWriter.dll /r:System.IO.FileSystem.dll + +using System; +using System.Data; +using System.IO; +using System.Xml.Serialization; +using System.Runtime.Serialization; +using System.Xml; +using System.Collections.Generic; + +namespace DataSetSerializationTest +{ + public class DerivesFromDeprecatedType1 : XmlSerializer // warning:DefiningDatasetRelatedType.ql + { + public DataSet MyDataSet { get; set; } // bug:DefiningPotentiallyUnsafeXmlSerializer.ql + + public DerivesFromDeprecatedType1() + { + } + } + + /* + * TODO: I cannot use DataContract on a QL unit test + * + [DataContract(Name = "Customer", Namespace = "http://www.contoso.com")] + public class PatternDataContractSerializer : XmlObjectSerializer + { + [DataMember()] + public DataSet MyDataSet { get; set; } + [DataMember()] + public DataTable MyDataTable { get; set; } + + PatternDataContractSerializer() { } + private ExtensionDataObject extensionData_Value; + public ExtensionDataObject ExtensionData + { + get + { + return extensionData_Value; + } + set + { + extensionData_Value = value; + } + } + + public override void WriteObject(System.IO.Stream stream, object graph) { } + public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object graph) { } + public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { return false; } + public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object graph) { } + public override void WriteEndObject(System.Xml.XmlWriter writer) { } + public override void WriteEndObject(XmlDictionaryWriter writer) { } + public override object ReadObject(System.IO.Stream stream) { return null; } + public override object ReadObject(XmlDictionaryReader reader, bool b) { return null; } + } + */ + + [Serializable()] + public class AttributeSerializer01 // warning:DefiningDatasetRelatedType.ql + { + private DataSet MyDataSet; // bug:DefiningPotentiallyUnsafeXmlSerializer.ql + + AttributeSerializer01() + { + } + } + + class Program + { + static string GetSerializedDataSet(DataSet dataSet) + { + DataTable dataTable = new DataTable("MyTable"); + dataTable.Columns.Add("FirstName", typeof(string)); + dataTable.Columns.Add("LastName", typeof(string)); + dataTable.Columns.Add("Age", typeof(int)); + + StringWriter writer = new StringWriter(); + dataSet.WriteXml(writer, XmlWriteMode.DiffGram); + return writer.ToString(); + } + + static void datatable_readxmlschema_01(string fileName) + { + using (FileStream fs = File.OpenRead(fileName)) + { + DataTable newTable = new DataTable(); + System.Xml.XmlTextReader reader = new System.Xml.XmlTextReader(fs); + newTable.ReadXmlSchema(reader); //bug:XmlDeserializationWithDataSet.ql + } + } + + static void Main(string[] args) + { + + XmlSerializer x = new XmlSerializer(typeof(DataSet)); // bug:UnsafeTypeUsedDataContractSerializer.ql + XmlSerializer y = new XmlSerializer(typeof(AttributeSerializer01)); //bug:UnsafeTypeUsedDataContractSerializer.ql + + Console.WriteLine("Hello World!"); + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/experimental/ir/ir/PrintAst.expected b/csharp/ql/test/experimental/ir/ir/PrintAst.expected new file mode 100644 index 000000000000..25ea10bc4766 --- /dev/null +++ b/csharp/ql/test/experimental/ir/ir/PrintAst.expected @@ -0,0 +1,1151 @@ +array.cs: +# 1| [Class] ArrayTest +# 2| 5: [Method] one_dim_init_acc +# 3| 4: [BlockStmt] {...} +# 4| 0: [LocalVariableDeclStmt] ... ...; +# 4| 0: [LocalVariableDeclAndInitExpr] Int32[] one_dim = ... +# 4| 0: [ArrayCreation] array creation of type Int32[] +# 4| -1: [ArrayInitializer] { ..., ... } +# 4| 0: [IntLiteral] 100 +# 4| 1: [IntLiteral] 101 +# 4| 2: [IntLiteral] 102 +# 4| 1: [LocalVariableAccess] access to local variable one_dim +# 5| 1: [ExprStmt] ...; +# 5| 0: [AssignExpr] ... = ... +# 5| 0: [IntLiteral] 1000 +# 5| 1: [ArrayAccess] access to array element +# 5| -1: [LocalVariableAccess] access to local variable one_dim +# 5| 0: [IntLiteral] 0 +# 6| 2: [ExprStmt] ...; +# 6| 0: [AssignExpr] ... = ... +# 6| 0: [ArrayAccess] access to array element +# 6| -1: [LocalVariableAccess] access to local variable one_dim +# 6| 0: [IntLiteral] 0 +# 6| 1: [ArrayAccess] access to array element +# 6| -1: [LocalVariableAccess] access to local variable one_dim +# 6| 0: [IntLiteral] 1 +# 7| 3: [ExprStmt] ...; +# 7| 0: [AssignExpr] ... = ... +# 7| 0: [IntLiteral] 1003 +# 7| 1: [ArrayAccess] access to array element +# 7| -1: [LocalVariableAccess] access to local variable one_dim +# 7| 0: [IntLiteral] 1 +# 9| 4: [LocalVariableDeclStmt] ... ...; +# 9| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 9| 0: [IntLiteral] 0 +# 9| 1: [LocalVariableAccess] access to local variable i +# 10| 5: [ExprStmt] ...; +# 10| 0: [AssignExpr] ... = ... +# 10| 0: [IntLiteral] 0 +# 10| 1: [ArrayAccess] access to array element +# 10| -1: [LocalVariableAccess] access to local variable one_dim +# 10| 0: [LocalVariableAccess] access to local variable i +# 13| 6: [Method] twod_and_init_acc +# 14| 4: [BlockStmt] {...} +# 15| 0: [LocalVariableDeclStmt] ... ...; +# 15| 0: [LocalVariableDeclAndInitExpr] Int32[,] a = ... +# 15| 0: [ArrayCreation] array creation of type Int32[,] +# 15| -1: [ArrayInitializer] { ..., ... } +# 15| 0: [ArrayInitializer] { ..., ... } +# 15| 0: [IntLiteral] 100 +# 15| 1: [IntLiteral] 101 +# 15| 1: [ArrayInitializer] { ..., ... } +# 15| 0: [IntLiteral] 102 +# 15| 1: [IntLiteral] 103 +# 15| 1: [LocalVariableAccess] access to local variable a +# 16| 1: [LocalVariableDeclStmt] ... ...; +# 16| 0: [LocalVariableDeclAndInitExpr] Int32[,] b = ... +# 16| 0: [ArrayCreation] array creation of type Int32[,] +# 16| 0: [IntLiteral] 5 +# 16| 1: [IntLiteral] 5 +# 16| 1: [LocalVariableAccess] access to local variable b +# 17| 2: [LocalVariableDeclStmt] ... ...; +# 17| 0: [LocalVariableDeclAndInitExpr] Int32[,] c = ... +# 17| 0: [ArrayCreation] array creation of type Int32[,] +# 17| -1: [ArrayInitializer] { ..., ... } +# 17| 0: [ArrayInitializer] { ..., ... } +# 17| 0: [IntLiteral] 100 +# 17| 1: [IntLiteral] 101 +# 17| 1: [ArrayInitializer] { ..., ... } +# 17| 0: [IntLiteral] 102 +# 17| 1: [IntLiteral] 103 +# 17| 0: [IntLiteral] 2 +# 17| 1: [IntLiteral] 2 +# 17| 1: [LocalVariableAccess] access to local variable c +# 18| 3: [LocalVariableDeclStmt] ... ...; +# 18| 0: [LocalVariableDeclAndInitExpr] Int32[,] d = ... +# 18| 0: [ArrayCreation] array creation of type Int32[,] +# 18| -1: [ArrayInitializer] { ..., ... } +# 18| 0: [ArrayInitializer] { ..., ... } +# 18| 0: [IntLiteral] 100 +# 18| 1: [IntLiteral] 101 +# 18| 1: [ArrayInitializer] { ..., ... } +# 18| 0: [IntLiteral] 102 +# 18| 1: [IntLiteral] 103 +# 18| 1: [LocalVariableAccess] access to local variable d +# 19| 4: [LocalVariableDeclStmt] ... ...; +# 19| 0: [LocalVariableDeclAndInitExpr] Int32[,] e = ... +# 19| 0: [LocalVariableAccess] access to local variable a +# 19| 1: [LocalVariableAccess] access to local variable e +# 20| 5: [ExprStmt] ...; +# 20| 0: [AssignExpr] ... = ... +# 20| 0: [UnaryMinusExpr] -... +# 20| 0: [IntLiteral] 1 +# 20| 1: [ArrayAccess] access to array element +# 20| -1: [LocalVariableAccess] access to local variable e +# 20| 0: [IntLiteral] 1 +# 20| 1: [IntLiteral] 1 +assignop.cs: +# 3| [Class] AssignOp +# 4| 5: [Method] Main +# 4| 4: [BlockStmt] {...} +# 5| 0: [LocalVariableDeclStmt] ... ...; +# 5| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 5| 0: [IntLiteral] 1 +# 5| 1: [LocalVariableAccess] access to local variable a +# 6| 1: [LocalVariableDeclStmt] ... ...; +# 6| 0: [LocalVariableDeclAndInitExpr] Int32 c = ... +# 6| 0: [IntLiteral] 1 +# 6| 1: [LocalVariableAccess] access to local variable c +# 8| 2: [ExprStmt] ...; +# 8| 0: [AssignAddExpr] ... += ... +# 8| 0: [LocalVariableAccess] access to local variable a +# 8| 1: [LocalVariableAccess] access to local variable c +# 9| 3: [ExprStmt] ...; +# 9| 0: [AssignSubExpr] ... -= ... +# 9| 0: [LocalVariableAccess] access to local variable a +# 9| 1: [LocalVariableAccess] access to local variable c +# 10| 4: [ExprStmt] ...; +# 10| 0: [AssignMulExpr] ... *= ... +# 10| 0: [LocalVariableAccess] access to local variable a +# 10| 1: [LocalVariableAccess] access to local variable c +# 11| 5: [ExprStmt] ...; +# 11| 0: [AssignDivExpr] ... /= ... +# 11| 0: [LocalVariableAccess] access to local variable a +# 11| 1: [LocalVariableAccess] access to local variable c +# 12| 6: [ExprStmt] ...; +# 12| 0: [AssignRemExpr] ... %= ... +# 12| 0: [LocalVariableAccess] access to local variable a +# 12| 1: [LocalVariableAccess] access to local variable c +# 13| 7: [ExprStmt] ...; +# 13| 0: [AssignLShiftExpr] ... <<= ... +# 13| 0: [IntLiteral] 2 +# 13| 1: [LocalVariableAccess] access to local variable c +# 14| 8: [ExprStmt] ...; +# 14| 0: [AssignRShiftExpr] ... >>= ... +# 14| 0: [IntLiteral] 2 +# 14| 1: [LocalVariableAccess] access to local variable c +# 15| 9: [ExprStmt] ...; +# 15| 0: [AssignAndExpr] ... &= ... +# 15| 0: [IntLiteral] 2 +# 15| 1: [LocalVariableAccess] access to local variable c +# 16| 10: [ExprStmt] ...; +# 16| 0: [AssignXorExpr] ... ^= ... +# 16| 0: [IntLiteral] 2 +# 16| 1: [LocalVariableAccess] access to local variable c +# 17| 11: [ExprStmt] ...; +# 17| 0: [AssignOrExpr] ... |= ... +# 17| 0: [IntLiteral] 2 +# 17| 1: [LocalVariableAccess] access to local variable c +casts.cs: +# 1| [Class] Casts_A +# 5| [Class] Casts_B +#-----| 3: (Base types) +# 5| 0: [Class] Casts_A +# 9| [Class] Casts +# 11| 5: [Method] Main +# 12| 4: [BlockStmt] {...} +# 13| 0: [LocalVariableDeclStmt] ... ...; +# 13| 0: [LocalVariableDeclAndInitExpr] Casts_A Aobj = ... +# 13| 0: [ObjectCreation] object creation of type Casts_A +# 13| 1: [LocalVariableAccess] access to local variable Aobj +# 14| 1: [LocalVariableDeclStmt] ... ...; +# 14| 0: [LocalVariableDeclAndInitExpr] Casts_B bobjCE = ... +# 14| 0: [CastExpr] (...) ... +# 14| 0: [LocalVariableAccess] access to local variable Aobj +# 14| 1: [TypeAccess] access to type Casts_B +# 14| 1: [LocalVariableAccess] access to local variable bobjCE +# 15| 2: [LocalVariableDeclStmt] ... ...; +# 15| 0: [LocalVariableDeclAndInitExpr] Casts_B bobjAS = ... +# 15| 0: [AsExpr] ... as ... +# 15| 0: [LocalVariableAccess] access to local variable Aobj +# 15| 1: [TypeAccess] access to type Casts_B +# 15| 1: [LocalVariableAccess] access to local variable bobjAS +collections.cs: +# 3| [Class] Collections +# 5| 5: [Class] MyClass +# 7| 5: [Field] a +# 8| 6: [Field] b +# 11| 6: [Method] Main +# 12| 4: [BlockStmt] {...} +# 13| 0: [LocalVariableDeclStmt] ... ...; +# 13| 0: [LocalVariableDeclAndInitExpr] Dictionary dict = ... +# 13| 0: [ObjectCreation] object creation of type Dictionary +# 14| -1: [CollectionInitializer] { ..., ... } +# 15| 0: [ElementInitializer] call to method Add +# 15| 0: [IntLiteral] 0 +# 15| 1: [ObjectCreation] object creation of type MyClass +# 15| -1: [ObjectInitializer] { ..., ... } +# 15| 0: [MemberInitializer] ... = ... +# 15| 0: [StringLiteral] "Hello" +# 15| 1: [FieldAccess] access to field a +# 15| 1: [MemberInitializer] ... = ... +# 15| 0: [StringLiteral] "World" +# 15| 1: [FieldAccess] access to field b +# 16| 1: [ElementInitializer] call to method Add +# 16| 0: [IntLiteral] 1 +# 16| 1: [ObjectCreation] object creation of type MyClass +# 16| -1: [ObjectInitializer] { ..., ... } +# 16| 0: [MemberInitializer] ... = ... +# 16| 0: [StringLiteral] "Foo" +# 16| 1: [FieldAccess] access to field a +# 16| 1: [MemberInitializer] ... = ... +# 16| 0: [StringLiteral] "Bar" +# 16| 1: [FieldAccess] access to field b +# 13| 1: [LocalVariableAccess] access to local variable dict +constructor_init.cs: +# 1| [Class] BaseClass +# 3| 4: [Field] num +# 5| 5: [InstanceConstructor] BaseClass +# 6| 4: [BlockStmt] {...} +# 9| 6: [InstanceConstructor] BaseClass +#-----| 2: (Parameters) +# 9| 0: [Parameter] i +# 10| 4: [BlockStmt] {...} +# 11| 0: [ExprStmt] ...; +# 11| 0: [AssignExpr] ... = ... +# 11| 0: [ParameterAccess] access to parameter i +# 11| 1: [FieldAccess] access to field num +# 15| [Class] DerivedClass +#-----| 3: (Base types) +# 15| 0: [Class] BaseClass +# 17| 4: [InstanceConstructor] DerivedClass +# 17| 3: [ConstructorInitializer] call to constructor BaseClass +# 18| 4: [BlockStmt] {...} +# 21| 5: [InstanceConstructor] DerivedClass +#-----| 2: (Parameters) +# 21| 0: [Parameter] i +# 21| 3: [ConstructorInitializer] call to constructor BaseClass +# 21| 0: [ParameterAccess] access to parameter i +# 22| 4: [BlockStmt] {...} +# 25| 6: [InstanceConstructor] DerivedClass +#-----| 2: (Parameters) +# 25| 0: [Parameter] i +# 25| 1: [Parameter] j +# 25| 3: [ConstructorInitializer] call to constructor DerivedClass +# 25| 0: [ParameterAccess] access to parameter i +# 26| 4: [BlockStmt] {...} +# 29| 7: [Method] Main +# 30| 4: [BlockStmt] {...} +# 31| 0: [LocalVariableDeclStmt] ... ...; +# 31| 0: [LocalVariableDeclAndInitExpr] DerivedClass obj1 = ... +# 31| 0: [ObjectCreation] object creation of type DerivedClass +# 31| 1: [LocalVariableAccess] access to local variable obj1 +# 32| 1: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclAndInitExpr] DerivedClass obj2 = ... +# 32| 0: [ObjectCreation] object creation of type DerivedClass +# 32| 0: [IntLiteral] 1 +# 32| 1: [LocalVariableAccess] access to local variable obj2 +# 33| 2: [LocalVariableDeclStmt] ... ...; +# 33| 0: [LocalVariableDeclAndInitExpr] DerivedClass obj3 = ... +# 33| 0: [ObjectCreation] object creation of type DerivedClass +# 33| 0: [IntLiteral] 1 +# 33| 1: [IntLiteral] 2 +# 33| 1: [LocalVariableAccess] access to local variable obj3 +crement.cs: +# 1| [Class] CrementOpsTest +# 3| 5: [Method] Main +# 4| 4: [BlockStmt] {...} +# 5| 0: [LocalVariableDeclStmt] ... ...; +# 5| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 5| 0: [IntLiteral] 10 +# 5| 1: [LocalVariableAccess] access to local variable x +# 6| 1: [LocalVariableDeclStmt] ... ...; +# 6| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 6| 0: [PostIncrExpr] ...++ +# 6| 0: [LocalVariableAccess] access to local variable x +# 6| 1: [LocalVariableAccess] access to local variable a +# 7| 2: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclAndInitExpr] Int32 b = ... +# 7| 0: [PreDecrExpr] --... +# 7| 0: [LocalVariableAccess] access to local variable x +# 7| 1: [LocalVariableAccess] access to local variable b +# 8| 3: [LocalVariableDeclStmt] ... ...; +# 8| 0: [LocalVariableDeclAndInitExpr] Int32 c = ... +# 8| 0: [PreIncrExpr] ++... +# 8| 0: [LocalVariableAccess] access to local variable x +# 8| 1: [LocalVariableAccess] access to local variable c +# 9| 4: [ExprStmt] ...; +# 9| 0: [AssignExpr] ... = ... +# 9| 0: [PostDecrExpr] ...-- +# 9| 0: [LocalVariableAccess] access to local variable x +# 9| 1: [LocalVariableAccess] access to local variable x +delegates.cs: +# 3| [Class] Delegates +# 4| 5: [DelegateType] Del +#-----| 2: (Parameters) +# 4| 0: [Parameter] num +# 6| 6: [Method] returns +#-----| 2: (Parameters) +# 6| 0: [Parameter] ret +# 7| 4: [BlockStmt] {...} +# 8| 0: [ReturnStmt] return ...; +# 8| 0: [ParameterAccess] access to parameter ret +# 11| 7: [Method] Main +# 11| 4: [BlockStmt] {...} +# 12| 0: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclAndInitExpr] Del del1 = ... +# 12| 0: [ExplicitDelegateCreation] delegate creation of type Del +# 12| 0: [MethodAccess] access to method returns +# 12| 1: [LocalVariableAccess] access to local variable del1 +# 13| 1: [ExprStmt] ...; +# 13| 0: [DelegateCall] delegate call +# 13| -1: [LocalVariableAccess] access to local variable del1 +# 13| 0: [IntLiteral] 5 +events.cs: +# 1| [Class] Events +# 3| 4: [DelegateType] MyDel +#-----| 2: (Parameters) +# 3| 0: [Parameter] str +# 4| 5: [Field] Inst +# 6| 6: [Event] MyEvent +# 6| 3: [AddEventAccessor] add_MyEvent +#-----| 2: (Parameters) +# 6| 0: [Parameter] value +# 6| 4: [RemoveEventAccessor] remove_MyEvent +#-----| 2: (Parameters) +# 6| 0: [Parameter] value +# 8| 7: [InstanceConstructor] Events +# 9| 4: [BlockStmt] {...} +# 10| 0: [ExprStmt] ...; +# 10| 0: [AssignExpr] ... = ... +# 10| 0: [ExplicitDelegateCreation] delegate creation of type MyDel +# 10| 0: [MethodAccess] access to method Fun +# 10| -1: [ThisAccess] this access +# 10| 1: [FieldAccess] access to field Inst +# 10| -1: [ThisAccess] this access +# 13| 8: [Method] AddEvent +# 14| 4: [BlockStmt] {...} +# 15| 0: [ExprStmt] ...; +# 15| 0: [AddEventExpr] ... += ... +# 15| 0: [FieldAccess] access to field Inst +# 15| -1: [ThisAccess] this access +# 15| 1: [EventAccess,EventCall] access to event MyEvent +# 15| -1: [ThisAccess] this access +# 18| 9: [Method] RemoveEvent +# 19| 4: [BlockStmt] {...} +# 20| 0: [ExprStmt] ...; +# 20| 0: [RemoveEventExpr] ... -= ... +# 20| 0: [FieldAccess] access to field Inst +# 20| -1: [ThisAccess] this access +# 20| 1: [EventAccess,EventCall] access to event MyEvent +# 20| -1: [ThisAccess] this access +# 23| 10: [Method] Fun +#-----| 2: (Parameters) +# 23| 0: [Parameter] str +# 24| 4: [BlockStmt] {...} +# 25| 0: [ReturnStmt] return ...; +# 25| 0: [ParameterAccess] access to parameter str +# 28| 11: [Method] Main +#-----| 2: (Parameters) +# 28| 0: [Parameter] args +# 29| 4: [BlockStmt] {...} +# 30| 0: [LocalVariableDeclStmt] ... ...; +# 30| 0: [LocalVariableDeclAndInitExpr] Events obj = ... +# 30| 0: [ObjectCreation] object creation of type Events +# 30| 1: [LocalVariableAccess] access to local variable obj +# 31| 1: [ExprStmt] ...; +# 31| 0: [MethodCall] call to method AddEvent +# 31| -1: [LocalVariableAccess] access to local variable obj +# 32| 2: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclAndInitExpr] String result = ... +# 32| 0: [DelegateCall] delegate call +# 32| -1: [LocalVariableAccess] access to local variable obj +# 32| 0: [StringLiteral] "string" +# 32| 1: [LocalVariableAccess] access to local variable result +# 33| 3: [ExprStmt] ...; +# 33| 0: [MethodCall] call to method RemoveEvent +# 33| -1: [LocalVariableAccess] access to local variable obj +foreach.cs: +# 3| [Class] ForEach +# 4| 5: [Method] Main +# 4| 4: [BlockStmt] {...} +# 5| 0: [LocalVariableDeclStmt] ... ...; +# 5| 0: [LocalVariableDeclAndInitExpr] Int32[] a_array = ... +# 5| 0: [ArrayCreation] array creation of type Int32[] +# 5| -1: [ArrayInitializer] { ..., ... } +# 5| 0: [IntLiteral] 1 +# 5| 1: [IntLiteral] 2 +# 5| 2: [IntLiteral] 3 +# 5| 3: [IntLiteral] 4 +# 5| 4: [IntLiteral] 5 +# 5| 5: [IntLiteral] 6 +# 5| 6: [IntLiteral] 7 +# 5| 1: [LocalVariableAccess] access to local variable a_array +# 7| 1: [ForeachStmt] foreach (... ... in ...) ... +# 7| 0: [LocalVariableDeclExpr] Int32 items +# 7| 1: [LocalVariableAccess] access to local variable a_array +# 8| 2: [BlockStmt] {...} +# 9| 0: [LocalVariableDeclStmt] ... ...; +# 9| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 9| 0: [LocalVariableAccess] access to local variable items +# 9| 1: [LocalVariableAccess] access to local variable x +func_with_param_call.cs: +# 3| [Class] test_call_with_param +# 5| 5: [Method] f +#-----| 2: (Parameters) +# 5| 0: [Parameter] x +# 5| 1: [Parameter] y +# 6| 4: [BlockStmt] {...} +# 7| 0: [ReturnStmt] return ...; +# 7| 0: [AddExpr] ... + ... +# 7| 0: [ParameterAccess] access to parameter x +# 7| 1: [ParameterAccess] access to parameter y +# 10| 6: [Method] g +# 11| 4: [BlockStmt] {...} +# 12| 0: [ReturnStmt] return ...; +# 12| 0: [MethodCall] call to method f +# 12| 0: [IntLiteral] 2 +# 12| 1: [IntLiteral] 3 +indexers.cs: +# 1| [Class] Indexers +# 3| 5: [Class] MyClass +# 5| 5: [Field] address +# 5| 1: [AssignExpr] ... = ... +# 5| 0: [ArrayCreation] array creation of type String[] +# 5| 0: [IntLiteral] 2 +# 5| 1: [FieldAccess] access to field address +# 6| 6: [Indexer] Item +#-----| 1: (Parameters) +# 6| 0: [Parameter] index +# 8| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 6| 0: [Parameter] index +# 9| 4: [BlockStmt] {...} +# 10| 0: [ReturnStmt] return ...; +# 10| 0: [ArrayAccess] access to array element +# 10| -1: [FieldAccess] access to field address +# 10| 0: [ParameterAccess] access to parameter index +# 12| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 6| 0: [Parameter] index +# 12| 1: [Parameter] value +# 13| 4: [BlockStmt] {...} +# 14| 0: [ExprStmt] ...; +# 14| 0: [AssignExpr] ... = ... +# 14| 0: [ParameterAccess] access to parameter value +# 14| 1: [ArrayAccess] access to array element +# 14| -1: [FieldAccess] access to field address +# 14| 0: [ParameterAccess] access to parameter index +# 19| 6: [Method] Main +# 20| 4: [BlockStmt] {...} +# 21| 0: [LocalVariableDeclStmt] ... ...; +# 21| 0: [LocalVariableDeclAndInitExpr] MyClass inst = ... +# 21| 0: [ObjectCreation] object creation of type MyClass +# 21| 1: [LocalVariableAccess] access to local variable inst +# 22| 1: [ExprStmt] ...; +# 22| 0: [AssignExpr] ... = ... +# 22| 0: [StringLiteral] "str1" +# 22| 1: [IndexerCall] access to indexer +# 22| -1: [LocalVariableAccess] access to local variable inst +# 22| 0: [IntLiteral] 0 +# 23| 2: [ExprStmt] ...; +# 23| 0: [AssignExpr] ... = ... +# 23| 0: [StringLiteral] "str1" +# 23| 1: [IndexerCall] access to indexer +# 23| -1: [LocalVariableAccess] access to local variable inst +# 23| 0: [IntLiteral] 1 +# 24| 3: [ExprStmt] ...; +# 24| 0: [AssignExpr] ... = ... +# 24| 0: [IndexerCall] access to indexer +# 24| -1: [LocalVariableAccess] access to local variable inst +# 24| 0: [IntLiteral] 0 +# 24| 1: [IndexerCall] access to indexer +# 24| -1: [LocalVariableAccess] access to local variable inst +# 24| 0: [IntLiteral] 1 +inheritance_polymorphism.cs: +# 1| [Class] A +# 3| 5: [Method] function +# 4| 4: [BlockStmt] {...} +# 5| 0: [ReturnStmt] return ...; +# 5| 0: [IntLiteral] 0 +# 9| [Class] B +#-----| 3: (Base types) +# 9| 0: [Class] A +# 13| [Class] C +#-----| 3: (Base types) +# 13| 0: [Class] B +# 15| 5: [Method] function +# 16| 4: [BlockStmt] {...} +# 17| 0: [ReturnStmt] return ...; +# 17| 0: [IntLiteral] 1 +# 21| [Class] Program +# 23| 5: [Method] Main +# 24| 4: [BlockStmt] {...} +# 25| 0: [LocalVariableDeclStmt] ... ...; +# 25| 0: [LocalVariableDeclAndInitExpr] B objB = ... +# 25| 0: [ObjectCreation] object creation of type B +# 25| 1: [LocalVariableAccess] access to local variable objB +# 26| 1: [ExprStmt] ...; +# 26| 0: [MethodCall] call to method function +# 26| -1: [LocalVariableAccess] access to local variable objB +# 29| 2: [LocalVariableDeclStmt] ... ...; +# 29| 0: [LocalVariableDeclExpr] A objA +# 30| 3: [ExprStmt] ...; +# 30| 0: [AssignExpr] ... = ... +# 30| 0: [LocalVariableAccess] access to local variable objB +# 30| 1: [LocalVariableAccess] access to local variable objA +# 31| 4: [ExprStmt] ...; +# 31| 0: [MethodCall] call to method function +# 31| -1: [LocalVariableAccess] access to local variable objA +# 33| 5: [LocalVariableDeclStmt] ... ...; +# 33| 0: [LocalVariableDeclAndInitExpr] A objC = ... +# 33| 0: [ObjectCreation] object creation of type C +# 33| 1: [LocalVariableAccess] access to local variable objC +# 34| 6: [ExprStmt] ...; +# 34| 0: [MethodCall] call to method function +# 34| -1: [LocalVariableAccess] access to local variable objC +inoutref.cs: +# 1| [Class] MyClass +# 2| 5: [Field] fld +# 5| [Struct] MyStruct +# 6| 5: [Field] fld +# 9| [Class] InOutRef +# 11| 5: [Method] set +#-----| 2: (Parameters) +# 11| 0: [Parameter] o1 +# 11| 1: [Parameter] o2 +# 12| 4: [BlockStmt] {...} +# 13| 0: [ExprStmt] ...; +# 13| 0: [AssignExpr] ... = ... +# 13| 0: [ParameterAccess] access to parameter o2 +# 13| 1: [ParameterAccess] access to parameter o1 +# 16| 6: [Method] F +#-----| 2: (Parameters) +# 16| 0: [Parameter] a +# 16| 1: [Parameter] b +# 16| 2: [Parameter] b1 +# 16| 3: [Parameter] c +# 16| 4: [Parameter] c1 +# 17| 4: [BlockStmt] {...} +# 18| 0: [ExprStmt] ...; +# 18| 0: [AssignExpr] ... = ... +# 18| 0: [IntLiteral] 0 +# 18| 1: [FieldAccess] access to field fld +# 18| -1: [ParameterAccess] access to parameter b +# 19| 1: [ExprStmt] ...; +# 19| 0: [AssignExpr] ... = ... +# 19| 0: [FieldAccess] access to field fld +# 19| -1: [ParameterAccess] access to parameter b +# 19| 1: [ParameterAccess] access to parameter a +# 21| 2: [ExprStmt] ...; +# 21| 0: [AssignExpr] ... = ... +# 21| 0: [IntLiteral] 10 +# 21| 1: [FieldAccess] access to field fld +# 21| -1: [ParameterAccess] access to parameter c +# 22| 3: [ExprStmt] ...; +# 22| 0: [AssignExpr] ... = ... +# 22| 0: [FieldAccess] access to field fld +# 22| -1: [ParameterAccess] access to parameter c +# 22| 1: [ParameterAccess] access to parameter a +# 24| 4: [ExprStmt] ...; +# 24| 0: [AssignExpr] ... = ... +# 24| 0: [ParameterAccess] access to parameter b1 +# 24| 1: [ParameterAccess] access to parameter b +# 26| 5: [ExprStmt] ...; +# 26| 0: [MethodCall] call to method set +# 26| 0: [ParameterAccess] access to parameter c +# 26| 1: [ParameterAccess] access to parameter c1 +# 29| 7: [Method] Main +# 30| 4: [BlockStmt] {...} +# 31| 0: [LocalVariableDeclStmt] ... ...; +# 31| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 31| 0: [IntLiteral] 0 +# 31| 1: [LocalVariableAccess] access to local variable a +# 32| 1: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclAndInitExpr] MyStruct b = ... +# 32| 0: [ObjectCreation] object creation of type MyStruct +# 32| 1: [LocalVariableAccess] access to local variable b +# 33| 2: [LocalVariableDeclStmt] ... ...; +# 33| 0: [LocalVariableDeclAndInitExpr] MyClass c = ... +# 33| 0: [ObjectCreation] object creation of type MyClass +# 33| 1: [LocalVariableAccess] access to local variable c +# 34| 3: [ExprStmt] ...; +# 34| 0: [MethodCall] call to method F +# 34| 0: [LocalVariableAccess] access to local variable a +# 34| 1: [LocalVariableAccess] access to local variable b +# 34| 2: [LocalVariableAccess] access to local variable b +# 34| 3: [LocalVariableAccess] access to local variable c +# 34| 4: [LocalVariableAccess] access to local variable c +# 36| 4: [LocalVariableDeclStmt] ... ...; +# 36| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 36| 0: [FieldAccess] access to field fld +# 36| -1: [LocalVariableAccess] access to local variable b +# 36| 1: [LocalVariableAccess] access to local variable x +isexpr.cs: +# 1| [Class] Is_A +# 3| 5: [Field] x +# 6| [Class] IsExpr +# 8| 5: [Method] Main +# 9| 4: [BlockStmt] {...} +# 10| 0: [LocalVariableDeclStmt] ... ...; +# 10| 0: [LocalVariableDeclAndInitExpr] Is_A obj = ... +# 10| 0: [NullLiteral] null +# 10| 1: [LocalVariableAccess] access to local variable obj +# 12| 1: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclAndInitExpr] Object o = ... +# 12| 0: [LocalVariableAccess] access to local variable obj +# 12| 1: [LocalVariableAccess] access to local variable o +# 13| 2: [IfStmt] if (...) ... +# 13| 0: [IsExpr] ... is ... +# 13| 0: [LocalVariableAccess] access to local variable o +# 13| 1: [VariablePatternExpr] Is_A tmp +# 14| 1: [BlockStmt] {...} +# 15| 0: [LocalVariableDeclStmt] ... ...; +# 15| 0: [LocalVariableDeclAndInitExpr] Int32 res = ... +# 15| 0: [FieldAccess] access to field x +# 15| -1: [LocalVariableAccess] access to local variable tmp +# 15| 1: [LocalVariableAccess] access to local variable res +# 17| 3: [IfStmt] if (...) ... +# 17| 0: [IsExpr] ... is ... +# 17| 0: [LocalVariableAccess] access to local variable o +# 17| 1: [TypeAccessPatternExpr] access to type Is_A +# 18| 1: [BlockStmt] {...} +jumps.cs: +# 3| [Class] Jumps +# 5| 5: [Method] Main +# 6| 4: [BlockStmt] {...} +# 7| 0: [ForStmt] for (...;...;...) ... +# 7| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 7| 0: [IntLiteral] 1 +# 7| 1: [LocalVariableAccess] access to local variable i +# 7| 0: [LEExpr] ... <= ... +# 7| 0: [LocalVariableAccess] access to local variable i +# 7| 1: [IntLiteral] 10 +# 7| 1: [PostIncrExpr] ...++ +# 7| 0: [LocalVariableAccess] access to local variable i +# 8| 2: [BlockStmt] {...} +# 9| 0: [IfStmt] if (...) ... +# 9| 0: [EQExpr] ... == ... +# 9| 0: [LocalVariableAccess] access to local variable i +# 9| 1: [IntLiteral] 3 +# 10| 1: [ContinueStmt] continue; +# 11| 2: [IfStmt] if (...) ... +# 11| 0: [EQExpr] ... == ... +# 11| 0: [LocalVariableAccess] access to local variable i +# 11| 1: [IntLiteral] 5 +# 12| 1: [BreakStmt] break; +# 13| 1: [ExprStmt] ...; +# 13| 0: [MethodCall] call to method WriteLine +# 13| -1: [TypeAccess] access to type Console +# 13| 0: [StringLiteral] "BreakAndContinue" +# 16| 1: [ForStmt] for (...;...;...) ... +# 16| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 16| 0: [IntLiteral] 0 +# 16| 1: [LocalVariableAccess] access to local variable i +# 16| 0: [LTExpr] ... < ... +# 16| 0: [LocalVariableAccess] access to local variable i +# 16| 1: [IntLiteral] 10 +# 17| 1: [BlockStmt] {...} +# 18| 0: [ExprStmt] ...; +# 18| 0: [PostIncrExpr] ...++ +# 18| 0: [LocalVariableAccess] access to local variable i +# 19| 1: [ContinueStmt] continue; +# 22| 2: [LocalVariableDeclStmt] ... ...; +# 22| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 22| 0: [IntLiteral] 0 +# 22| 1: [LocalVariableAccess] access to local variable a +# 23| 3: [WhileStmt] while (...) ... +# 23| 0: [BoolLiteral] true +# 24| 1: [BlockStmt] {...} +# 25| 0: [ExprStmt] ...; +# 25| 0: [PostIncrExpr] ...++ +# 25| 0: [LocalVariableAccess] access to local variable a +# 26| 1: [IfStmt] if (...) ... +# 26| 0: [EQExpr] ... == ... +# 26| 0: [LocalVariableAccess] access to local variable a +# 26| 1: [IntLiteral] 5 +# 27| 1: [ContinueStmt] continue; +# 28| 2: [IfStmt] if (...) ... +# 28| 0: [EQExpr] ... == ... +# 28| 0: [LocalVariableAccess] access to local variable a +# 28| 1: [IntLiteral] 10 +# 29| 1: [BreakStmt] break; +# 32| 4: [ForStmt] for (...;...;...) ... +# 32| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 32| 0: [IntLiteral] 1 +# 32| 1: [LocalVariableAccess] access to local variable i +# 32| 0: [LEExpr] ... <= ... +# 32| 0: [LocalVariableAccess] access to local variable i +# 32| 1: [IntLiteral] 10 +# 32| 1: [PostIncrExpr] ...++ +# 32| 0: [LocalVariableAccess] access to local variable i +# 33| 2: [BlockStmt] {...} +# 34| 0: [IfStmt] if (...) ... +# 34| 0: [EQExpr] ... == ... +# 34| 0: [LocalVariableAccess] access to local variable i +# 34| 1: [IntLiteral] 5 +# 35| 1: [GotoLabelStmt] goto ...; +# 37| 5: [LabelStmt] done: +# 38| 6: [ExprStmt] ...; +# 38| 0: [MethodCall] call to method WriteLine +# 38| -1: [TypeAccess] access to type Console +# 38| 0: [StringLiteral] "Done" +lock.cs: +# 3| [Class] LockTest +# 5| 5: [Method] A +# 6| 4: [BlockStmt] {...} +# 7| 0: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclAndInitExpr] Object object = ... +# 7| 0: [ObjectCreation] object creation of type Object +# 7| 1: [LocalVariableAccess] access to local variable object +# 8| 1: [LockStmt] lock (...) {...} +# 8| 0: [LocalVariableAccess] access to local variable object +# 9| 1: [BlockStmt] {...} +# 10| 0: [ExprStmt] ...; +# 10| 0: [MethodCall] call to method WriteLine +# 10| -1: [TypeAccess] access to type Console +# 10| 0: [MethodCall] call to method ToString +# 10| -1: [LocalVariableAccess] access to local variable object +obj_creation.cs: +# 1| [Class] ObjCreation +# 3| 5: [Class] MyClass +# 5| 4: [Field] x +# 7| 5: [InstanceConstructor] MyClass +# 8| 4: [BlockStmt] {...} +# 11| 6: [InstanceConstructor] MyClass +#-----| 2: (Parameters) +# 11| 0: [Parameter] _x +# 12| 4: [BlockStmt] {...} +# 13| 0: [ExprStmt] ...; +# 13| 0: [AssignExpr] ... = ... +# 13| 0: [ParameterAccess] access to parameter _x +# 13| 1: [FieldAccess] access to field x +# 17| 6: [Method] SomeFun +#-----| 2: (Parameters) +# 17| 0: [Parameter] x +# 18| 4: [BlockStmt] {...} +# 21| 7: [Method] Main +# 22| 4: [BlockStmt] {...} +# 23| 0: [LocalVariableDeclStmt] ... ...; +# 23| 0: [LocalVariableDeclAndInitExpr] MyClass obj = ... +# 23| 0: [ObjectCreation] object creation of type MyClass +# 23| 0: [IntLiteral] 100 +# 23| 1: [LocalVariableAccess] access to local variable obj +# 24| 1: [LocalVariableDeclStmt] ... ...; +# 24| 0: [LocalVariableDeclAndInitExpr] MyClass obj_initlist = ... +# 24| 0: [ObjectCreation] object creation of type MyClass +# 24| -1: [ObjectInitializer] { ..., ... } +# 24| 0: [MemberInitializer] ... = ... +# 24| 0: [IntLiteral] 101 +# 24| 1: [FieldAccess] access to field x +# 24| 1: [LocalVariableAccess] access to local variable obj_initlist +# 25| 2: [LocalVariableDeclStmt] ... ...; +# 25| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 25| 0: [FieldAccess] access to field x +# 25| -1: [LocalVariableAccess] access to local variable obj +# 25| 1: [LocalVariableAccess] access to local variable a +# 27| 3: [ExprStmt] ...; +# 27| 0: [MethodCall] call to method SomeFun +# 27| 0: [ObjectCreation] object creation of type MyClass +# 27| 0: [IntLiteral] 100 +pointers.cs: +# 1| [Class] Pointers +# 3| 5: [Method] addone +#-----| 2: (Parameters) +# 3| 0: [Parameter] arr +# 4| 4: [BlockStmt] {...} +# 5| 0: [LocalVariableDeclStmt] ... ...; +# 5| 0: [LocalVariableDeclAndInitExpr] Int32 length = ... +# 5| 0: [PropertyCall] access to property Length +# 5| -1: [ParameterAccess] access to parameter arr +# 5| 1: [LocalVariableAccess] access to local variable length +# 6| 1: [FixedStmt] fixed(...) { ... } +# 6| -1: [LocalVariableDeclAndInitExpr] Int32* b = ... +# 6| 0: [CastExpr] (...) ... +# 6| 0: [ParameterAccess] access to parameter arr +# 6| 1: [LocalVariableAccess] access to local variable b +# 7| 0: [BlockStmt] {...} +# 8| 0: [LocalVariableDeclStmt] ... ...; +# 8| 0: [LocalVariableDeclAndInitExpr] Int32* p = ... +# 8| 0: [LocalVariableAccess] access to local variable b +# 8| 1: [LocalVariableAccess] access to local variable p +# 9| 1: [ForStmt] for (...;...;...) ... +# 9| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 9| 0: [IntLiteral] 0 +# 9| 1: [LocalVariableAccess] access to local variable i +# 9| 0: [LTExpr] ... < ... +# 9| 0: [LocalVariableAccess] access to local variable i +# 9| 1: [LocalVariableAccess] access to local variable length +# 9| 1: [PostIncrExpr] ...++ +# 9| 0: [LocalVariableAccess] access to local variable i +# 10| 2: [ExprStmt] ...; +# 10| 0: [AssignAddExpr] ... += ... +# 10| 0: [IntLiteral] 1 +# 10| 1: [PointerIndirectionExpr] *... +# 10| 0: [PostIncrExpr] ...++ +# 10| 0: [LocalVariableAccess] access to local variable p +# 14| 6: [Class] MyClass +# 16| 5: [Field] fld1 +# 17| 6: [Field] fld2 +# 20| 7: [Struct] MyStruct +# 22| 5: [Field] fld +# 25| 8: [Method] Main +# 25| 4: [BlockStmt] {...} +# 26| 0: [LocalVariableDeclStmt] ... ...; +# 26| 0: [LocalVariableDeclAndInitExpr] MyClass o = ... +# 26| 0: [ObjectCreation] object creation of type MyClass +# 26| 1: [LocalVariableAccess] access to local variable o +# 27| 1: [LocalVariableDeclStmt] ... ...; +# 27| 0: [LocalVariableDeclAndInitExpr] MyStruct s = ... +# 27| 0: [ObjectCreation] object creation of type MyStruct +# 27| 1: [LocalVariableAccess] access to local variable s +# 28| 2: [UnsafeStmt] unsafe {...} +# 29| 0: [BlockStmt] {...} +# 30| 0: [FixedStmt] fixed(...) { ... } +# 30| -2: [LocalVariableDeclAndInitExpr] Int32* q = ... +# 30| 0: [AddressOfExpr] &... +# 30| 0: [FieldAccess] access to field fld2 +# 30| -1: [LocalVariableAccess] access to local variable o +# 30| 1: [LocalVariableAccess] access to local variable q +# 30| -1: [LocalVariableDeclAndInitExpr] Int32* p = ... +# 30| 0: [AddressOfExpr] &... +# 30| 0: [FieldAccess] access to field fld1 +# 30| -1: [LocalVariableAccess] access to local variable o +# 30| 1: [LocalVariableAccess] access to local variable p +# 31| 0: [BlockStmt] {...} +# 32| 0: [ExprStmt] ...; +# 32| 0: [AssignExpr] ... = ... +# 32| 0: [IntLiteral] 0 +# 32| 1: [PointerIndirectionExpr] *... +# 32| 0: [LocalVariableAccess] access to local variable p +# 33| 1: [ExprStmt] ...; +# 33| 0: [AssignExpr] ... = ... +# 33| 0: [IntLiteral] 0 +# 33| 1: [PointerIndirectionExpr] *... +# 33| 0: [LocalVariableAccess] access to local variable q +# 34| 2: [LocalVariableDeclStmt] ... ...; +# 34| 0: [LocalVariableDeclAndInitExpr] MyStruct* r = ... +# 34| 0: [AddressOfExpr] &... +# 34| 0: [LocalVariableAccess] access to local variable s +# 34| 1: [LocalVariableAccess] access to local variable r +# 35| 3: [ExprStmt] ...; +# 35| 0: [AssignExpr] ... = ... +# 35| 0: [IntLiteral] 0 +# 35| 1: [FieldAccess] access to field fld +# 35| -1: [PointerIndirectionExpr] *... +# 35| 0: [LocalVariableAccess] access to local variable r +# 39| 3: [LocalVariableDeclStmt] ... ...; +# 39| 0: [LocalVariableDeclAndInitExpr] Int32[] arr = ... +# 39| 0: [ArrayCreation] array creation of type Int32[] +# 39| -1: [ArrayInitializer] { ..., ... } +# 39| 0: [IntLiteral] 1 +# 39| 1: [IntLiteral] 2 +# 39| 2: [IntLiteral] 3 +# 39| 1: [LocalVariableAccess] access to local variable arr +# 40| 4: [ExprStmt] ...; +# 40| 0: [MethodCall] call to method addone +# 40| 0: [LocalVariableAccess] access to local variable arr +prop.cs: +# 1| [Class] PropClass +# 3| 5: [Field] prop +# 5| 6: [Property] Prop +# 7| 3: [Getter] get_Prop +# 8| 4: [BlockStmt] {...} +# 9| 0: [ReturnStmt] return ...; +# 9| 0: [MethodCall] call to method func +# 12| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 12| 0: [Parameter] value +# 13| 4: [BlockStmt] {...} +# 14| 0: [ExprStmt] ...; +# 14| 0: [AssignExpr] ... = ... +# 14| 0: [ParameterAccess] access to parameter value +# 14| 1: [FieldAccess] access to field prop +# 18| 7: [Method] func +# 19| 4: [BlockStmt] {...} +# 20| 0: [ReturnStmt] return ...; +# 20| 0: [IntLiteral] 0 +# 24| [Class] Prog +# 26| 5: [Method] Main +# 27| 4: [BlockStmt] {...} +# 28| 0: [LocalVariableDeclStmt] ... ...; +# 28| 0: [LocalVariableDeclAndInitExpr] PropClass obj = ... +# 28| 0: [ObjectCreation] object creation of type PropClass +# 28| 1: [LocalVariableAccess] access to local variable obj +# 29| 1: [ExprStmt] ...; +# 29| 0: [AssignExpr] ... = ... +# 29| 0: [IntLiteral] 5 +# 29| 1: [PropertyCall] access to property Prop +# 29| -1: [LocalVariableAccess] access to local variable obj +# 30| 2: [LocalVariableDeclStmt] ... ...; +# 30| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 30| 0: [PropertyCall] access to property Prop +# 30| -1: [LocalVariableAccess] access to local variable obj +# 30| 1: [LocalVariableAccess] access to local variable x +simple_call.cs: +# 3| [Class] test_simple_call +# 5| 5: [Method] f +# 6| 4: [BlockStmt] {...} +# 7| 0: [ReturnStmt] return ...; +# 7| 0: [IntLiteral] 0 +# 10| 6: [Method] g +# 11| 4: [BlockStmt] {...} +# 12| 0: [ReturnStmt] return ...; +# 12| 0: [MethodCall] call to method f +simple_function.cs: +# 3| [Class] test_simple_function +# 5| 5: [Method] f +# 6| 4: [BlockStmt] {...} +# 7| 0: [ReturnStmt] return ...; +# 7| 0: [IntLiteral] 0 +stmts.cs: +# 3| [Class] test_stmts +# 5| 5: [Method] ifStmt +#-----| 2: (Parameters) +# 5| 0: [Parameter] x +# 6| 4: [BlockStmt] {...} +# 7| 0: [IfStmt] if (...) ... +# 7| 0: [EQExpr] ... == ... +# 7| 0: [ParameterAccess] access to parameter x +# 7| 1: [IntLiteral] 5 +# 8| 1: [ReturnStmt] return ...; +# 8| 0: [IntLiteral] 0 +# 10| 2: [ReturnStmt] return ...; +# 10| 0: [IntLiteral] 1 +# 13| 6: [Method] whileStmt +#-----| 2: (Parameters) +# 13| 0: [Parameter] x +# 14| 4: [BlockStmt] {...} +# 15| 0: [LocalVariableDeclStmt] ... ...; +# 15| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 15| 0: [IntLiteral] 0 +# 15| 1: [LocalVariableAccess] access to local variable i +# 16| 1: [WhileStmt] while (...) ... +# 16| 0: [LTExpr] ... < ... +# 16| 0: [LocalVariableAccess] access to local variable i +# 16| 1: [IntLiteral] 10 +# 17| 1: [BlockStmt] {...} +# 18| 0: [ExprStmt] ...; +# 18| 0: [AssignExpr] ... = ... +# 18| 0: [AddExpr] ... + ... +# 18| 0: [ParameterAccess] access to parameter x +# 18| 1: [IntLiteral] 1 +# 18| 1: [ParameterAccess] access to parameter x +# 22| 7: [Method] switchStmt +# 23| 4: [BlockStmt] {...} +# 24| 0: [LocalVariableDeclStmt] ... ...; +# 24| 0: [LocalVariableDeclAndInitExpr] Object caseSwitch = ... +# 24| 0: [ObjectCreation] object creation of type Object +# 24| 1: [LocalVariableAccess] access to local variable caseSwitch +# 25| 1: [LocalVariableDeclStmt] ... ...; +# 25| 0: [LocalVariableDeclAndInitExpr] Int32 select = ... +# 25| 0: [IntLiteral] 0 +# 25| 1: [LocalVariableAccess] access to local variable select +# 27| 2: [SwitchStmt] switch (...) {...} +# 27| 0: [LocalVariableAccess] access to local variable caseSwitch +# 29| 0: [ConstCase] case ...: +# 29| 0: [ConstantPatternExpr,UnaryMinusExpr] -... +# 29| 0: [IntLiteral] 1 +# 30| 1: [GotoCaseStmt] goto case ...; +# 30| 0: [BoolLiteral] true +# 31| 2: [ConstCase] case ...: +# 31| 0: [ConstantPatternExpr,IntLiteral] 0 +# 32| 3: [GotoCaseStmt] goto case ...; +# 32| 0: [StringLiteral] "123" +# 33| 4: [ConstCase] case ...: +# 33| 0: [ConstantPatternExpr,StringLiteral] "123" +# 34| 5: [ExprStmt] ...; +# 34| 0: [AssignExpr] ... = ... +# 34| 0: [IntLiteral] 100 +# 34| 1: [LocalVariableAccess] access to local variable select +# 35| 6: [BreakStmt] break; +# 36| 7: [ConstCase] case ...: +# 36| 0: [BoolLiteral,ConstantPatternExpr] true +# 37| 8: [ExprStmt] ...; +# 37| 0: [AssignExpr] ... = ... +# 37| 0: [IntLiteral] 101 +# 37| 1: [LocalVariableAccess] access to local variable select +# 38| 9: [GotoDefaultStmt] goto default; +# 39| 10: [DefaultCase] default: +# 40| 11: [ReturnStmt] return ...; +# 40| 0: [LocalVariableAccess] access to local variable select +# 42| 3: [ReturnStmt] return ...; +# 42| 0: [IntLiteral] 0 +# 46| 8: [Method] tryCatchFinally +# 47| 4: [BlockStmt] {...} +# 48| 0: [LocalVariableDeclStmt] ... ...; +# 48| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 48| 0: [IntLiteral] 5 +# 48| 1: [LocalVariableAccess] access to local variable x +# 49| 1: [TryStmt] try {...} ... +# 64| -1: [BlockStmt] {...} +# 65| 0: [ExprStmt] ...; +# 65| 0: [AssignExpr] ... = ... +# 65| 0: [IntLiteral] 2 +# 65| 1: [LocalVariableAccess] access to local variable x +# 50| 0: [BlockStmt] {...} +# 51| 0: [IfStmt] if (...) ... +# 51| 0: [NEExpr] ... != ... +# 51| 0: [LocalVariableAccess] access to local variable x +# 51| 1: [IntLiteral] 0 +# 52| 1: [ThrowStmt] throw ...; +# 52| 0: [ObjectCreation] object creation of type Exception +# 53| 1: [ExprStmt] ...; +# 53| 0: [AssignExpr] ... = ... +# 53| 0: [IntLiteral] 0 +# 53| 1: [LocalVariableAccess] access to local variable x +# 55| 1: [SpecificCatchClause] catch (...) {...} +# 55| 0: [LocalVariableDeclExpr] Exception ex +# 56| 1: [BlockStmt] {...} +# 57| 0: [ExprStmt] ...; +# 57| 0: [AssignExpr] ... = ... +# 57| 0: [IntLiteral] 1 +# 57| 1: [LocalVariableAccess] access to local variable x +# 59| 2: [GeneralCatchClause] catch {...} +# 60| 1: [BlockStmt] {...} +# 61| 0: [ThrowStmt] throw ...; +# 69| 9: [Method] forStmt +# 70| 4: [BlockStmt] {...} +# 71| 0: [LocalVariableDeclStmt] ... ...; +# 71| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 71| 0: [IntLiteral] 0 +# 71| 1: [LocalVariableAccess] access to local variable x +# 72| 1: [ForStmt] for (...;...;...) ... +# 72| -2: [LocalVariableDeclAndInitExpr] Int32 j = ... +# 72| 0: [IntLiteral] 10 +# 72| 1: [LocalVariableAccess] access to local variable j +# 72| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 72| 0: [IntLiteral] 0 +# 72| 1: [LocalVariableAccess] access to local variable i +# 72| 0: [LTExpr] ... < ... +# 72| 0: [LocalVariableAccess] access to local variable i +# 72| 1: [LocalVariableAccess] access to local variable j +# 72| 1: [PostIncrExpr] ...++ +# 72| 0: [LocalVariableAccess] access to local variable i +# 72| 2: [PostDecrExpr] ...-- +# 72| 0: [LocalVariableAccess] access to local variable j +# 73| 3: [BlockStmt] {...} +# 74| 0: [ExprStmt] ...; +# 74| 0: [AssignExpr] ... = ... +# 74| 0: [SubExpr] ... - ... +# 74| 0: [LocalVariableAccess] access to local variable x +# 74| 1: [IntLiteral] 1 +# 74| 1: [LocalVariableAccess] access to local variable x +# 77| 2: [LocalVariableDeclStmt] ... ...; +# 77| 0: [LocalVariableDeclExpr] Int32 a +# 77| 1: [LocalVariableDeclAndInitExpr] Int32 b = ... +# 77| 0: [IntLiteral] 10 +# 77| 1: [LocalVariableAccess] access to local variable b +# 78| 3: [ForStmt] for (...;...;...) ... +# 78| -1: [AssignExpr] ... = ... +# 78| 0: [IntLiteral] 0 +# 78| 1: [LocalVariableAccess] access to local variable a +# 78| 0: [LTExpr] ... < ... +# 78| 0: [LocalVariableAccess] access to local variable a +# 78| 1: [LocalVariableAccess] access to local variable b +# 79| 1: [BlockStmt] {...} +# 80| 0: [ExprStmt] ...; +# 80| 0: [PostIncrExpr] ...++ +# 80| 0: [LocalVariableAccess] access to local variable a +# 83| 4: [ForStmt] for (...;...;...) ... +# 84| 1: [BlockStmt] {...} +# 89| 10: [Method] doWhile +# 90| 4: [BlockStmt] {...} +# 91| 0: [LocalVariableDeclStmt] ... ...; +# 91| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 91| 0: [IntLiteral] 0 +# 91| 1: [LocalVariableAccess] access to local variable x +# 92| 1: [DoStmt] do ... while (...); +# 96| 0: [LTExpr] ... < ... +# 96| 0: [LocalVariableAccess] access to local variable x +# 96| 1: [IntLiteral] 10 +# 93| 1: [BlockStmt] {...} +# 94| 0: [ExprStmt] ...; +# 94| 0: [AssignExpr] ... = ... +# 94| 0: [AddExpr] ... + ... +# 94| 0: [LocalVariableAccess] access to local variable x +# 94| 1: [IntLiteral] 1 +# 94| 1: [LocalVariableAccess] access to local variable x +# 99| 11: [Method] checkedUnchecked +# 100| 4: [BlockStmt] {...} +# 101| 0: [LocalVariableDeclStmt] ... ...; +# 101| 0: [LocalVariableDeclAndInitExpr] Int32 num = ... +# 101| 0: [MemberConstantAccess] access to constant MaxValue +# 101| -1: [TypeAccess] access to type Int32 +# 101| 1: [LocalVariableAccess] access to local variable num +# 102| 1: [UncheckedStmt] unchecked {...} +# 103| 0: [BlockStmt] {...} +# 104| 0: [ExprStmt] ...; +# 104| 0: [AssignExpr] ... = ... +# 104| 0: [AddExpr] ... + ... +# 104| 0: [LocalVariableAccess] access to local variable num +# 104| 1: [IntLiteral] 1 +# 104| 1: [LocalVariableAccess] access to local variable num +# 106| 2: [CheckedStmt] checked {...} +# 107| 0: [BlockStmt] {...} +# 108| 0: [ExprStmt] ...; +# 108| 0: [AssignExpr] ... = ... +# 108| 0: [AddExpr] ... + ... +# 108| 0: [LocalVariableAccess] access to local variable num +# 108| 1: [IntLiteral] 1 +# 108| 1: [LocalVariableAccess] access to local variable num +using.cs: +# 3| [Class] UsingStmt +# 5| 5: [Class] MyDisposable +#-----| 3: (Base types) +# 5| 1: [Interface] IDisposable +# 7| 4: [InstanceConstructor] MyDisposable +# 7| 4: [BlockStmt] {...} +# 8| 5: [Method] DoSomething +# 8| 4: [BlockStmt] {...} +# 9| 6: [Method] Dispose +# 9| 4: [BlockStmt] {...} +# 12| 6: [Method] Main +# 13| 4: [BlockStmt] {...} +# 14| 0: [UsingBlockStmt] using (...) {...} +# 14| -1: [LocalVariableDeclAndInitExpr] MyDisposable o1 = ... +# 14| 0: [ObjectCreation] object creation of type MyDisposable +# 14| 1: [LocalVariableAccess] access to local variable o1 +# 15| 1: [BlockStmt] {...} +# 16| 0: [ExprStmt] ...; +# 16| 0: [MethodCall] call to method DoSomething +# 16| -1: [LocalVariableAccess] access to local variable o1 +# 19| 1: [LocalVariableDeclStmt] ... ...; +# 19| 0: [LocalVariableDeclAndInitExpr] MyDisposable o2 = ... +# 19| 0: [ObjectCreation] object creation of type MyDisposable +# 19| 1: [LocalVariableAccess] access to local variable o2 +# 20| 2: [UsingBlockStmt] using (...) {...} +# 20| 0: [LocalVariableAccess] access to local variable o2 +# 21| 1: [BlockStmt] {...} +# 22| 0: [ExprStmt] ...; +# 22| 0: [MethodCall] call to method DoSomething +# 22| -1: [LocalVariableAccess] access to local variable o2 +# 25| 3: [UsingDeclStmt] using ... ...; +# 25| 0: [LocalVariableDeclAndInitExpr] MyDisposable o3 = ... +# 25| 0: [ObjectCreation] object creation of type MyDisposable +# 25| 1: [LocalVariableAccess] access to local variable o3 +# 26| 4: [ExprStmt] ...; +# 26| 0: [MethodCall] call to method DoSomething +# 26| -1: [LocalVariableAccess] access to local variable o3 +variables.cs: +# 3| [Class] test_variables +# 5| 5: [Method] f +# 6| 4: [BlockStmt] {...} +# 7| 0: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclExpr] Int32 x +# 7| 1: [LocalVariableDeclAndInitExpr] Int32 y = ... +# 7| 0: [IntLiteral] 5 +# 7| 1: [LocalVariableAccess] access to local variable y +# 8| 1: [ExprStmt] ...; +# 8| 0: [AssignExpr] ... = ... +# 8| 0: [IntLiteral] 4 +# 8| 1: [LocalVariableAccess] access to local variable x +# 9| 2: [ExprStmt] ...; +# 9| 0: [AssignExpr] ... = ... +# 9| 0: [LocalVariableAccess] access to local variable y +# 9| 1: [LocalVariableAccess] access to local variable x +# 10| 3: [LocalVariableDeclStmt] ... ...; +# 10| 0: [LocalVariableDeclAndInitExpr] Int32 z = ... +# 10| 0: [LocalVariableAccess] access to local variable y +# 10| 1: [LocalVariableAccess] access to local variable z diff --git a/csharp/ql/test/experimental/ir/ir/PrintAst.qlref b/csharp/ql/test/experimental/ir/ir/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/experimental/ir/ir/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/ir/ir/array.cs b/csharp/ql/test/experimental/ir/ir/array.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/array.cs rename to csharp/ql/test/experimental/ir/ir/array.cs diff --git a/csharp/ql/test/library-tests/ir/ir/assignop.cs b/csharp/ql/test/experimental/ir/ir/assignop.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/assignop.cs rename to csharp/ql/test/experimental/ir/ir/assignop.cs diff --git a/csharp/ql/test/library-tests/ir/ir/casts.cs b/csharp/ql/test/experimental/ir/ir/casts.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/casts.cs rename to csharp/ql/test/experimental/ir/ir/casts.cs diff --git a/csharp/ql/test/library-tests/ir/ir/collections.cs b/csharp/ql/test/experimental/ir/ir/collections.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/collections.cs rename to csharp/ql/test/experimental/ir/ir/collections.cs diff --git a/csharp/ql/test/library-tests/ir/ir/constructor_init.cs b/csharp/ql/test/experimental/ir/ir/constructor_init.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/constructor_init.cs rename to csharp/ql/test/experimental/ir/ir/constructor_init.cs diff --git a/csharp/ql/test/library-tests/ir/ir/crement.cs b/csharp/ql/test/experimental/ir/ir/crement.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/crement.cs rename to csharp/ql/test/experimental/ir/ir/crement.cs diff --git a/csharp/ql/test/library-tests/ir/ir/delegates.cs b/csharp/ql/test/experimental/ir/ir/delegates.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/delegates.cs rename to csharp/ql/test/experimental/ir/ir/delegates.cs diff --git a/csharp/ql/test/library-tests/ir/ir/events.cs b/csharp/ql/test/experimental/ir/ir/events.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/events.cs rename to csharp/ql/test/experimental/ir/ir/events.cs diff --git a/csharp/ql/test/library-tests/ir/ir/foreach.cs b/csharp/ql/test/experimental/ir/ir/foreach.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/foreach.cs rename to csharp/ql/test/experimental/ir/ir/foreach.cs diff --git a/csharp/ql/test/library-tests/ir/ir/func_with_param_call.cs b/csharp/ql/test/experimental/ir/ir/func_with_param_call.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/func_with_param_call.cs rename to csharp/ql/test/experimental/ir/ir/func_with_param_call.cs diff --git a/csharp/ql/test/library-tests/ir/ir/indexers.cs b/csharp/ql/test/experimental/ir/ir/indexers.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/indexers.cs rename to csharp/ql/test/experimental/ir/ir/indexers.cs diff --git a/csharp/ql/test/library-tests/ir/ir/inheritance_polymorphism.cs b/csharp/ql/test/experimental/ir/ir/inheritance_polymorphism.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/inheritance_polymorphism.cs rename to csharp/ql/test/experimental/ir/ir/inheritance_polymorphism.cs diff --git a/csharp/ql/test/library-tests/ir/ir/inoutref.cs b/csharp/ql/test/experimental/ir/ir/inoutref.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/inoutref.cs rename to csharp/ql/test/experimental/ir/ir/inoutref.cs diff --git a/csharp/ql/test/library-tests/ir/ir/isexpr.cs b/csharp/ql/test/experimental/ir/ir/isexpr.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/isexpr.cs rename to csharp/ql/test/experimental/ir/ir/isexpr.cs diff --git a/csharp/ql/test/library-tests/ir/ir/jumps.cs b/csharp/ql/test/experimental/ir/ir/jumps.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/jumps.cs rename to csharp/ql/test/experimental/ir/ir/jumps.cs diff --git a/csharp/ql/test/library-tests/ir/ir/lock.cs b/csharp/ql/test/experimental/ir/ir/lock.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/lock.cs rename to csharp/ql/test/experimental/ir/ir/lock.cs diff --git a/csharp/ql/test/library-tests/ir/ir/obj_creation.cs b/csharp/ql/test/experimental/ir/ir/obj_creation.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/obj_creation.cs rename to csharp/ql/test/experimental/ir/ir/obj_creation.cs diff --git a/csharp/ql/test/library-tests/ir/ir/pointers.cs b/csharp/ql/test/experimental/ir/ir/pointers.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/pointers.cs rename to csharp/ql/test/experimental/ir/ir/pointers.cs diff --git a/csharp/ql/test/library-tests/ir/ir/prop.cs b/csharp/ql/test/experimental/ir/ir/prop.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/prop.cs rename to csharp/ql/test/experimental/ir/ir/prop.cs diff --git a/csharp/ql/test/library-tests/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected similarity index 99% rename from csharp/ql/test/library-tests/ir/ir/raw_ir.expected rename to csharp/ql/test/experimental/ir/ir/raw_ir.expected index 785f38727876..e426955f9da3 100644 --- a/csharp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -1327,8 +1327,8 @@ pointers.cs: # 6| r6_1(glval) = VariableAddress[b] : # 6| r6_2(glval) = VariableAddress[arr] : # 6| r6_3(Int32[]) = Load : &:r6_2, ~m? -# 6| r6_4(Int32*) = Convert : r6_3 -# 6| mu6_5(Int32*) = Store : &:r6_1, r6_3 +# 6| r6_4(Int32*) = CheckedConvertOrThrow : r6_3 +# 6| mu6_5(Int32*) = Store : &:r6_1, r6_4 # 8| r8_1(glval) = VariableAddress[p] : # 8| r8_2(glval) = VariableAddress[b] : # 8| r8_3(Int32*) = Load : &:r8_2, ~m? diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.qlref b/csharp/ql/test/experimental/ir/ir/raw_ir.qlref new file mode 100644 index 000000000000..336afc397f56 --- /dev/null +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.qlref @@ -0,0 +1 @@ +experimental/ir/implementation/raw/PrintIR.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/ir/ir/raw_ir_consistency.expected b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected similarity index 95% rename from csharp/ql/test/library-tests/ir/ir/raw_ir_consistency.expected rename to csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected index 61bc9b2261c1..5d16b01eaca5 100644 --- a/csharp/ql/test/library-tests/ir/ir/raw_ir_consistency.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected @@ -20,6 +20,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.qlref b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.qlref new file mode 100644 index 000000000000..3059c9b7b77b --- /dev/null +++ b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.qlref @@ -0,0 +1 @@ +experimental/ir/implementation/raw/IRConsistency.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/ir/ir/simple_call.cs b/csharp/ql/test/experimental/ir/ir/simple_call.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/simple_call.cs rename to csharp/ql/test/experimental/ir/ir/simple_call.cs diff --git a/csharp/ql/test/library-tests/ir/ir/simple_function.cs b/csharp/ql/test/experimental/ir/ir/simple_function.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/simple_function.cs rename to csharp/ql/test/experimental/ir/ir/simple_function.cs diff --git a/csharp/ql/test/library-tests/ir/ir/stmts.cs b/csharp/ql/test/experimental/ir/ir/stmts.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/stmts.cs rename to csharp/ql/test/experimental/ir/ir/stmts.cs diff --git a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected similarity index 95% rename from csharp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected rename to csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected index 61bc9b2261c1..5d16b01eaca5 100644 --- a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected +++ b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected @@ -20,6 +20,7 @@ switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated invalidOverlap +nonUniqueEnclosingIRFunction missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.qlref b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.qlref new file mode 100644 index 000000000000..65c394825298 --- /dev/null +++ b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.qlref @@ -0,0 +1 @@ +experimental/ir/implementation/unaliased_ssa/IRConsistency.ql diff --git a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_consistency.expected b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_ssa_consistency.expected similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_consistency.expected rename to csharp/ql/test/experimental/ir/ir/unaliased_ssa_ssa_consistency.expected diff --git a/csharp/ql/test/experimental/ir/ir/unaliased_ssa_ssa_consistency.qlref b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_ssa_consistency.qlref new file mode 100644 index 000000000000..8d24936eceab --- /dev/null +++ b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_ssa_consistency.qlref @@ -0,0 +1 @@ +experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/ir/ir/using.cs b/csharp/ql/test/experimental/ir/ir/using.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/using.cs rename to csharp/ql/test/experimental/ir/ir/using.cs diff --git a/csharp/ql/test/library-tests/ir/ir/variables.cs b/csharp/ql/test/experimental/ir/ir/variables.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/ir/variables.cs rename to csharp/ql/test/experimental/ir/ir/variables.cs diff --git a/csharp/ql/test/library-tests/ir/offbyone/OffByOneRA.expected b/csharp/ql/test/experimental/ir/offbyone/OffByOneRA.expected similarity index 100% rename from csharp/ql/test/library-tests/ir/offbyone/OffByOneRA.expected rename to csharp/ql/test/experimental/ir/offbyone/OffByOneRA.expected diff --git a/csharp/ql/test/library-tests/ir/offbyone/OffByOneRA.ql b/csharp/ql/test/experimental/ir/offbyone/OffByOneRA.ql similarity index 91% rename from csharp/ql/test/library-tests/ir/offbyone/OffByOneRA.ql rename to csharp/ql/test/experimental/ir/offbyone/OffByOneRA.ql index 16ba1c861a19..7518386e6d21 100644 --- a/csharp/ql/test/library-tests/ir/offbyone/OffByOneRA.ql +++ b/csharp/ql/test/experimental/ir/offbyone/OffByOneRA.ql @@ -1,7 +1,7 @@ import csharp -import semmle.code.csharp.ir.IR -import semmle.code.csharp.ir.rangeanalysis.RangeAnalysis -import semmle.code.csharp.ir.rangeanalysis.RangeUtils +import experimental.ir.IR +import experimental.ir.rangeanalysis.RangeAnalysis +import experimental.ir.rangeanalysis.RangeUtils /** * Holds if the index expression of `aa` is less than or equal to the array length plus `k`. diff --git a/csharp/ql/test/library-tests/ir/offbyone/null.cs b/csharp/ql/test/experimental/ir/offbyone/null.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/offbyone/null.cs rename to csharp/ql/test/experimental/ir/offbyone/null.cs diff --git a/csharp/ql/test/library-tests/ir/offbyone/test.cs b/csharp/ql/test/experimental/ir/offbyone/test.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/offbyone/test.cs rename to csharp/ql/test/experimental/ir/offbyone/test.cs diff --git a/csharp/ql/test/library-tests/ir/rangeanalysis/RangeAnalysis.expected b/csharp/ql/test/experimental/ir/rangeanalysis/RangeAnalysis.expected similarity index 100% rename from csharp/ql/test/library-tests/ir/rangeanalysis/RangeAnalysis.expected rename to csharp/ql/test/experimental/ir/rangeanalysis/RangeAnalysis.expected diff --git a/csharp/ql/test/experimental/ir/rangeanalysis/RangeAnalysis.ql b/csharp/ql/test/experimental/ir/rangeanalysis/RangeAnalysis.ql new file mode 100644 index 000000000000..ab62db5e5ffc --- /dev/null +++ b/csharp/ql/test/experimental/ir/rangeanalysis/RangeAnalysis.ql @@ -0,0 +1,25 @@ +import experimental.ir.rangeanalysis.RangeAnalysis +import experimental.ir.IR +import experimental.ir.internal.IRGuards +import experimental.ir.ValueNumbering + +query predicate instructionBounds( + Instruction i, Bound b, int delta, boolean upper, Reason reason, Location reasonLoc +) { + ( + i.getAUse() instanceof ArgumentOperand + or + exists(ReturnValueInstruction retInstr | retInstr.getReturnValueOperand() = i.getAUse()) + ) and + ( + upper = true and + delta = min(int d | boundedInstruction(i, b, d, upper, reason)) + or + upper = false and + delta = max(int d | boundedInstruction(i, b, d, upper, reason)) + ) and + not valueNumber(b.getInstruction()) = valueNumber(i) and + if reason instanceof CondReason + then reasonLoc = reason.(CondReason).getCond().getLocation() + else reasonLoc instanceof EmptyLocation +} diff --git a/csharp/ql/test/library-tests/ir/rangeanalysis/null.cs b/csharp/ql/test/experimental/ir/rangeanalysis/null.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/rangeanalysis/null.cs rename to csharp/ql/test/experimental/ir/rangeanalysis/null.cs diff --git a/csharp/ql/test/library-tests/ir/rangeanalysis/test.cs b/csharp/ql/test/experimental/ir/rangeanalysis/test.cs similarity index 100% rename from csharp/ql/test/library-tests/ir/rangeanalysis/test.cs rename to csharp/ql/test/experimental/ir/rangeanalysis/test.cs diff --git a/csharp/ql/test/library-tests/aliases/aliases1.expected b/csharp/ql/test/library-tests/aliases/aliases1.expected index f6289f38f51c..9417df43012e 100644 --- a/csharp/ql/test/library-tests/aliases/aliases1.expected +++ b/csharp/ql/test/library-tests/aliases/aliases1.expected @@ -1,3 +1 @@ -| Assembly1.dll:0:0:0:0 | Class | -| Assembly2.dll:0:0:0:0 | Class | | Program.cs:10:7:10:11 | Class | diff --git a/csharp/ql/test/library-tests/aliases/aliases2.expected b/csharp/ql/test/library-tests/aliases/aliases2.expected index 45870c5dda24..50cfbf37f1ed 100644 --- a/csharp/ql/test/library-tests/aliases/aliases2.expected +++ b/csharp/ql/test/library-tests/aliases/aliases2.expected @@ -1,3 +1,3 @@ -| Program.cs:18:21:18:22 | c1 | Assembly1.dll:0:0:0:0 | Class | -| Program.cs:19:21:19:22 | c2 | Assembly2.dll:0:0:0:0 | Class | +| Program.cs:18:21:18:22 | c1 | Program.cs:10:7:10:11 | Class | +| Program.cs:19:21:19:22 | c2 | Program.cs:10:7:10:11 | Class | | Program.cs:20:15:20:16 | c3 | Program.cs:10:7:10:11 | Class | diff --git a/csharp/ql/test/library-tests/arguments/PrintAst.expected b/csharp/ql/test/library-tests/arguments/PrintAst.expected new file mode 100644 index 000000000000..528cfa5ca3e9 --- /dev/null +++ b/csharp/ql/test/library-tests/arguments/PrintAst.expected @@ -0,0 +1,211 @@ +arguments.cs: +# 3| [Class] ArgumentsTest +# 5| 4: [InstanceConstructor] ArgumentsTest +#-----| 2: (Parameters) +# 5| 0: [Parameter] x +# 5| 1: [IntLiteral] 0 +# 5| 1: [Parameter] y +# 5| 1: [IntLiteral] 0 +# 6| 4: [BlockStmt] {...} +# 9| 5: [InstanceConstructor] ArgumentsTest +#-----| 2: (Parameters) +# 9| 0: [Parameter] x +# 9| 1: [Parameter] y +# 9| 2: [Parameter] z +# 10| 4: [BlockStmt] {...} +# 11| 0: [ExprStmt] ...; +# 11| 0: [AssignExpr] ... = ... +# 11| 0: [ParameterAccess] access to parameter x +# 11| 1: [ParameterAccess] access to parameter y +# 14| 6: [Method] f1 +#-----| 2: (Parameters) +# 14| 0: [Parameter] x +# 14| 1: [IntLiteral] 1 +# 14| 1: [Parameter] y +# 14| 1: [IntLiteral] 2 +# 15| 4: [BlockStmt] {...} +# 18| 7: [Method] f2 +#-----| 2: (Parameters) +# 18| 0: [Parameter] x +# 18| 1: [Parameter] y +# 18| 2: [Parameter] z +# 19| 4: [BlockStmt] {...} +# 20| 0: [ExprStmt] ...; +# 20| 0: [AssignExpr] ... = ... +# 20| 0: [ParameterAccess] access to parameter x +# 20| 1: [ParameterAccess] access to parameter y +# 23| 8: [Method] f +# 24| 4: [BlockStmt] {...} +# 25| 0: [LocalVariableDeclStmt] ... ...; +# 25| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 25| 0: [IntLiteral] 1 +# 25| 1: [LocalVariableAccess] access to local variable x +# 27| 1: [ExprStmt] ...; +# 27| 0: [MethodCall] call to method f1 +# 27| 0: [IntLiteral] 2 +# 28| 2: [ExprStmt] ...; +# 28| 0: [MethodCall] call to method f2 +# 28| 0: [LocalVariableAccess] access to local variable x +# 28| 1: [LocalVariableAccess] access to local variable x +# 28| 2: [LocalVariableAccess] access to local variable x +# 29| 3: [ExprStmt] ...; +# 29| 0: [ObjectCreation] object creation of type ArgumentsTest +# 29| 0: [LocalVariableAccess] access to local variable x +# 29| 1: [LocalVariableAccess] access to local variable x +# 29| 2: [LocalVariableAccess] access to local variable x +# 30| 4: [ExprStmt] ...; +# 30| 0: [ObjectCreation] object creation of type ArgumentsTest +# 30| 0: [IntLiteral] 10 +# 30| 1: [IntLiteral] 5 +# 33| 9: [Method] f3 +#-----| 2: (Parameters) +# 33| 0: [Parameter] o +# 33| 1: [Parameter] args +# 34| 4: [BlockStmt] {...} +# 35| 0: [ExprStmt] ...; +# 35| 0: [MethodCall] call to method f3 +# 35| 0: [IntLiteral] 0 +# 35| 1: [IntLiteral] 1 +# 35| 2: [IntLiteral] 2 +# 36| 1: [ExprStmt] ...; +# 36| 0: [MethodCall] call to method f3 +# 36| 0: [IntLiteral] 0 +# 36| 1: [ArrayCreation] array creation of type Int32[] +# 36| -1: [ArrayInitializer] { ..., ... } +# 36| 0: [IntLiteral] 1 +# 36| 1: [IntLiteral] 2 +# 37| 2: [ExprStmt] ...; +# 37| 0: [MethodCall] call to method f3 +# 37| 0: [IntLiteral] 1 +# 37| 1: [IntLiteral] 0 +# 38| 3: [ExprStmt] ...; +# 38| 0: [MethodCall] call to method f3 +# 38| 0: [IntLiteral] 0 +# 38| 1: [ParameterAccess] access to parameter args +# 39| 4: [ExprStmt] ...; +# 39| 0: [MethodCall] call to method f3 +# 39| 0: [ParameterAccess] access to parameter args +# 39| 1: [IntLiteral] 0 +# 40| 5: [ExprStmt] ...; +# 40| 0: [MethodCall] call to method f3 +# 40| 0: [ArrayCreation] array creation of type Int32[] +# 40| -1: [ArrayInitializer] { ..., ... } +# 40| 0: [IntLiteral] 1 +# 40| 1: [IntLiteral] 2 +# 40| 1: [IntLiteral] 0 +# 43| 10: [Method] f4 +#-----| 2: (Parameters) +# 43| 0: [Parameter] args +# 44| 4: [BlockStmt] {...} +# 45| 0: [ExprStmt] ...; +# 45| 0: [MethodCall] call to method f4 +# 45| 0: [ArrayCreation] array creation of type Object[] +# 45| -1: [ArrayInitializer] { ..., ... } +# 45| 0: [NullLiteral] null +# 45| 1: [NullLiteral] null +# 48| 11: [Property] Prop +# 48| 3: [Getter] get_Prop +# 48| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 48| 0: [Parameter] value +# 50| 12: [Indexer] Item +#-----| 1: (Parameters) +# 50| 0: [Parameter] a +# 50| 1: [Parameter] b +# 50| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 50| 0: [Parameter] a +# 50| 1: [Parameter] b +# 50| 4: [AddExpr] ... + ... +# 50| 0: [ParameterAccess] access to parameter a +# 50| 1: [ParameterAccess] access to parameter b +# 50| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 50| 0: [Parameter] a +# 50| 1: [Parameter] b +# 50| 2: [Parameter] value +# 50| 4: [BlockStmt] {...} +# 52| 13: [Method] f5 +# 53| 4: [BlockStmt] {...} +# 54| 0: [ExprStmt] ...; +# 54| 0: [AssignExpr] ... = ... +# 54| 0: [IntLiteral] 0 +# 54| 1: [PropertyCall] access to property Prop +# 55| 1: [ExprStmt] ...; +# 55| 0: [AssignExpr] ... = ... +# 55| 0: [IndexerCall] access to indexer +# 55| -1: [ThisAccess] this access +# 55| 0: [IntLiteral] 1 +# 55| 1: [IntLiteral] 2 +# 55| 1: [PropertyCall] access to property Prop +# 56| 2: [ExprStmt] ...; +# 56| 0: [AssignExpr] ... = ... +# 56| 0: [TupleExpr] (..., ...) +# 56| 0: [IntLiteral] 5 +# 56| 1: [IntLiteral] 6 +# 56| 1: [TupleExpr] (..., ...) +# 56| 0: [PropertyCall] access to property Prop +# 56| 1: [IndexerCall] access to indexer +# 56| -1: [ThisAccess] this access +# 56| 0: [IntLiteral] 3 +# 56| 1: [IntLiteral] 4 +# 57| 3: [ExprStmt] ...; +# 57| 0: [PostIncrExpr] ...++ +# 57| 0: [PropertyCall] access to property Prop +# 58| 4: [ExprStmt] ...; +# 58| 0: [AssignAddExpr] ... += ... +# 58| 0: [IntLiteral] 7 +# 58| 1: [PropertyCall] access to property Prop +# 59| 5: [ExprStmt] ...; +# 59| 0: [PostIncrExpr] ...++ +# 59| 0: [IndexerCall] access to indexer +# 59| -1: [ThisAccess] this access +# 59| 0: [IntLiteral] 8 +# 59| 1: [IntLiteral] 9 +# 60| 6: [ExprStmt] ...; +# 60| 0: [AssignAddExpr] ... += ... +# 60| 0: [IntLiteral] 12 +# 60| 1: [IndexerCall] access to indexer +# 60| -1: [ThisAccess] this access +# 60| 0: [IntLiteral] 10 +# 60| 1: [IntLiteral] 11 +# 61| 7: [LocalVariableDeclStmt] ... ...; +# 61| 0: [LocalVariableDeclAndInitExpr] (Int32,Int32) tuple = ... +# 61| 0: [TupleExpr] (..., ...) +# 61| 0: [IntLiteral] 13 +# 61| 1: [IntLiteral] 14 +# 61| 1: [LocalVariableAccess] access to local variable tuple +# 62| 8: [ExprStmt] ...; +# 62| 0: [AssignExpr] ... = ... +# 62| 0: [LocalVariableAccess] access to local variable tuple +# 62| 1: [TupleExpr] (..., ...) +# 62| 0: [PropertyCall] access to property Prop +# 62| 1: [IndexerCall] access to indexer +# 62| -1: [ThisAccess] this access +# 62| 0: [IntLiteral] 15 +# 62| 1: [IntLiteral] 16 +# 66| 14: [Method] f6 +#-----| 0: (Attributes) +# 65| 1: [Attribute] [My(...)] +# 65| 0: [BoolLiteral] false +# 66| 4: [BlockStmt] {...} +# 69| 15: [Method] f7 +#-----| 0: (Attributes) +# 68| 1: [Attribute] [My(...)] +# 68| 0: [BoolLiteral] true +# 68| 1: [StringLiteral] "" +# 68| 2: [IntLiteral] 0 +# 69| 4: [BlockStmt] {...} +# 72| [Class] MyAttribute +#-----| 3: (Base types) +# 72| 0: [Class] Attribute +# 74| 4: [Field] x +# 75| 5: [IndexerProperty] y +# 75| 3: [Getter] get_y +# 75| 4: [Setter] set_y +#-----| 2: (Parameters) +# 75| 0: [Parameter] value +# 76| 6: [InstanceConstructor] MyAttribute +#-----| 2: (Parameters) +# 76| 0: [Parameter] b +# 76| 4: [BlockStmt] {...} diff --git a/csharp/ql/test/library-tests/arguments/PrintAst.qlref b/csharp/ql/test/library-tests/arguments/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/arguments/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/assignments/PrintAst.expected b/csharp/ql/test/library-tests/assignments/PrintAst.expected new file mode 100644 index 000000000000..d3353805cdb3 --- /dev/null +++ b/csharp/ql/test/library-tests/assignments/PrintAst.expected @@ -0,0 +1,55 @@ +Assignments.cs: +# 1| [Class] Assignments +# 3| 5: [Method] M +# 4| 4: [BlockStmt] {...} +# 5| 0: [LocalVariableDeclStmt] ... ...; +# 5| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 5| 0: [IntLiteral] 0 +# 5| 1: [LocalVariableAccess] access to local variable x +# 6| 1: [ExprStmt] ...; +# 6| 0: [AssignAddExpr] ... += ... +# 6| 0: [IntLiteral] 1 +# 6| 1: [LocalVariableAccess] access to local variable x +# 8| 2: [LocalVariableDeclStmt] ... ...; +# 8| 0: [LocalVariableDeclAndInitExpr] dynamic d = ... +# 8| 0: [CastExpr] (...) ... +# 8| 0: [IntLiteral] 0 +# 8| 1: [LocalVariableAccess] access to local variable d +# 9| 3: [ExprStmt] ...; +# 9| 0: [AssignSubExpr] ... -= ... +# 9| 0: [IntLiteral] 2 +# 9| 1: [LocalVariableAccess] access to local variable d +# 11| 4: [LocalVariableDeclStmt] ... ...; +# 11| 0: [LocalVariableDeclAndInitExpr] Assignments a = ... +# 11| 0: [ObjectCreation] object creation of type Assignments +# 11| 1: [LocalVariableAccess] access to local variable a +# 12| 5: [ExprStmt] ...; +# 12| 0: [AssignAddExpr] ... += ... +# 12| 0: [ThisAccess] this access +# 12| 1: [LocalVariableAccess] access to local variable a +# 14| 6: [ExprStmt] ...; +# 14| 0: [AddEventExpr] ... += ... +# 14| 0: [LambdaExpr] (...) => ... +#-----| 2: (Parameters) +# 14| 0: [Parameter] sender +# 14| 1: [Parameter] e +# 14| 4: [BlockStmt] {...} +# 14| 1: [EventAccess,EventCall] access to event Event +# 17| 6: [AddOperator] + +#-----| 2: (Parameters) +# 17| 0: [Parameter] x +# 17| 1: [Parameter] y +# 18| 4: [BlockStmt] {...} +# 19| 0: [ReturnStmt] return ...; +# 19| 0: [ParameterAccess] access to parameter x +# 22| 7: [DelegateType] EventHandler +#-----| 2: (Parameters) +# 22| 0: [Parameter] sender +# 22| 1: [Parameter] e +# 23| 8: [Event] Event +# 23| 3: [AddEventAccessor] add_Event +#-----| 2: (Parameters) +# 23| 0: [Parameter] value +# 23| 4: [RemoveEventAccessor] remove_Event +#-----| 2: (Parameters) +# 23| 0: [Parameter] value diff --git a/csharp/ql/test/library-tests/assignments/PrintAst.qlref b/csharp/ql/test/library-tests/assignments/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/assignments/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/attributes/PrintAst.expected b/csharp/ql/test/library-tests/attributes/PrintAst.expected new file mode 100644 index 000000000000..87114afe0cb0 --- /dev/null +++ b/csharp/ql/test/library-tests/attributes/PrintAst.expected @@ -0,0 +1,73 @@ +attributes.cs: +# 10| [Attribute] [AssemblyTitle(...)] +# 10| 0: [StringLiteral] "C# attributes test" +# 11| [Attribute] [AssemblyDescription(...)] +# 11| 0: [StringLiteral] "A test of C# attributes" +# 12| [Attribute] [AssemblyConfiguration(...)] +# 12| 0: [StringLiteral] "" +# 13| [Attribute] [AssemblyCompany(...)] +# 13| 0: [StringLiteral] "Semmle Plc" +# 14| [Attribute] [AssemblyProduct(...)] +# 14| 0: [StringLiteral] "Odasa" +# 15| [Attribute] [AssemblyCopyright(...)] +# 15| 0: [StringLiteral] "Copyright īŋŊ Semmle 2018" +# 16| [Attribute] [AssemblyTrademark(...)] +# 16| 0: [StringLiteral] "" +# 17| [Attribute] [AssemblyCulture(...)] +# 17| 0: [StringLiteral] "" +# 22| [Attribute] [ComVisible(...)] +# 22| 0: [BoolLiteral] false +# 25| [Attribute] [Guid(...)] +# 25| 0: [StringLiteral] "2f70fdd6-14aa-4850-b053-d547adb1f476" +# 37| [Attribute] [AssemblyVersion(...)] +# 37| 0: [StringLiteral] "1.0.0.0" +# 38| [Attribute] [AssemblyFileVersion(...)] +# 38| 0: [StringLiteral] "1.0.0.0" +# 41| [Class] Foo +#-----| 0: (Attributes) +# 40| 1: [Attribute] [AttributeUsage(...)] +# 40| 0: [MemberConstantAccess] access to constant All +# 40| -1: [TypeAccess] access to type AttributeTargets +#-----| 3: (Base types) +# 41| 0: [Class] Attribute +# 44| 5: [Method] foo +#-----| 0: (Attributes) +# 43| 1: [Attribute] [Conditional(...)] +# 43| 0: [StringLiteral] "DEBUG2" +# 44| 4: [BlockStmt] {...} +# 47| [Class] Bar +# 49| 5: [Method] inc +#-----| 2: (Parameters) +# 49| 0: [Parameter] x +#-----| 0: (Attributes) +# 49| 1: [Attribute] [Foo(...)] +# 49| 4: [BlockStmt] {...} +# 49| 0: [ReturnStmt] return ...; +# 49| 0: [AddExpr] ... + ... +# 49| 0: [ParameterAccess] access to parameter x +# 49| 1: [IntLiteral] 1 +# 52| 6: [Method] M1 +#-----| 0: (Attributes) +# 51| 1: [Attribute] [My(...)] +# 51| 0: [BoolLiteral] false +# 52| 4: [BlockStmt] {...} +# 55| 7: [Method] M2 +#-----| 0: (Attributes) +# 54| 1: [Attribute] [My(...)] +# 54| 0: [BoolLiteral] true +# 54| 1: [StringLiteral] "" +# 54| 2: [IntLiteral] 0 +# 55| 4: [BlockStmt] {...} +# 58| [Class] MyAttribute +#-----| 3: (Base types) +# 58| 0: [Class] Attribute +# 60| 4: [Field] x +# 61| 5: [IndexerProperty] y +# 61| 3: [Getter] get_y +# 61| 4: [Setter] set_y +#-----| 2: (Parameters) +# 61| 0: [Parameter] value +# 62| 6: [InstanceConstructor] MyAttribute +#-----| 2: (Parameters) +# 62| 0: [Parameter] b +# 62| 4: [BlockStmt] {...} diff --git a/csharp/ql/test/library-tests/attributes/PrintAst.qlref b/csharp/ql/test/library-tests/attributes/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/attributes/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/cil/consistency/Handles.expected b/csharp/ql/test/library-tests/cil/consistency/Handles.expected index dfc2c225ff9a..d48a89c24a34 100644 --- a/csharp/ql/test/library-tests/cil/consistency/Handles.expected +++ b/csharp/ql/test/library-tests/cil/consistency/Handles.expected @@ -1,3 +1,4 @@ +tooManyHandles tooManyMatchingHandles missingCil csharpLocationViolation diff --git a/csharp/ql/test/library-tests/cil/consistency/Handles.ql b/csharp/ql/test/library-tests/cil/consistency/Handles.ql index 4ceeea6eeab8..a64ea94eb98f 100644 --- a/csharp/ql/test/library-tests/cil/consistency/Handles.ql +++ b/csharp/ql/test/library-tests/cil/consistency/Handles.ql @@ -10,8 +10,27 @@ class MetadataEntity extends DotNet::NamedElement, @metadata_entity { Assembly getAssembly() { metadata_handle(this, result, _) } } -query predicate tooManyMatchingHandles(MetadataEntity e) { - strictcount(MetadataEntity e2 | e.matchesHandle(e2)) > 2 +query predicate tooManyHandles(string s) { + exists(MetadataEntity e, Assembly a | + strictcount(int handle | metadata_handle(e, a, handle)) > 1 and + s = e.getQualifiedName() + ) +} + +private class UniqueMetadataEntity extends MetadataEntity { + UniqueMetadataEntity() { + // Tuple types such as `(,)` and `ValueTuple`2` share the same handle + not this instanceof TupleType and + not this.getQualifiedName().matches("System.ValueTuple%") + } +} + +query predicate tooManyMatchingHandles(string s) { + exists(UniqueMetadataEntity e, Assembly a, int handle | + metadata_handle(e, a, handle) and + strictcount(UniqueMetadataEntity e2 | metadata_handle(e2, a, handle)) > 2 and + s = e.getQualifiedName() + ) } query predicate missingCil(Element e) { diff --git a/csharp/ql/test/library-tests/cil/dataflow/Nullness.expected b/csharp/ql/test/library-tests/cil/dataflow/Nullness.expected index adb291634d96..f9f0d31b5cb8 100644 --- a/csharp/ql/test/library-tests/cil/dataflow/Nullness.expected +++ b/csharp/ql/test/library-tests/cil/dataflow/Nullness.expected @@ -8,7 +8,10 @@ alwaysNull | dataflow.cs:80:21:80:44 | access to property NullProperty | | dataflow.cs:89:31:89:44 | call to method NullFunction | alwaysNotNull +| dataflow.cs:71:13:71:20 | access to local variable nonNull1 | +| dataflow.cs:71:13:71:35 | Int32 nonNull1 = ... | | dataflow.cs:71:24:71:35 | default(...) | +| dataflow.cs:71:32:71:34 | access to type Int32 | | dataflow.cs:72:27:72:30 | this access | | dataflow.cs:72:27:72:40 | call to method GetType | | dataflow.cs:73:30:73:33 | true | @@ -25,6 +28,7 @@ alwaysNotNull | dataflow.cs:85:24:85:30 | access to local variable nonNull | | dataflow.cs:85:24:85:55 | call to method ReturnsNonNullIndirect | | dataflow.cs:86:24:86:30 | access to local variable nonNull | +| dataflow.cs:89:24:89:27 | access to field cond | | dataflow.cs:89:24:89:27 | this access | | dataflow.cs:89:31:89:44 | this access | | dataflow.cs:89:48:89:51 | this access | diff --git a/csharp/ql/test/library-tests/constructors/PrintAst.expected b/csharp/ql/test/library-tests/constructors/PrintAst.expected new file mode 100644 index 000000000000..84a08a3522eb --- /dev/null +++ b/csharp/ql/test/library-tests/constructors/PrintAst.expected @@ -0,0 +1,17 @@ +constructors.cs: +# 1| [NamespaceDeclaration] namespace ... { ... } +# 3| 1: [Class] Class +# 5| 4: [InstanceConstructor] Class +# 6| 4: [BlockStmt] {...} +# 8| 5: [InstanceConstructor] Class +#-----| 2: (Parameters) +# 8| 0: [Parameter] i +# 9| 4: [BlockStmt] {...} +# 11| 6: [StaticConstructor] Class +# 12| 4: [BlockStmt] {...} +# 14| 7: [Destructor] ~Class +# 15| 4: [BlockStmt] {...} +# 16| 0: [LocalVariableDeclStmt] ... ...; +# 16| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 16| 0: [IntLiteral] 0 +# 16| 1: [LocalVariableAccess] access to local variable i diff --git a/csharp/ql/test/library-tests/constructors/PrintAst.qlref b/csharp/ql/test/library-tests/constructors/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/constructors/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/controlflow/graph/Assert.cs b/csharp/ql/test/library-tests/controlflow/graph/Assert.cs new file mode 100644 index 000000000000..3c7ebb08ff1d --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/graph/Assert.cs @@ -0,0 +1,130 @@ +using System; +using System.Diagnostics; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +class AssertTests +{ + void M1(bool b) + { + string s = b ? null : ""; + Debug.Assert(s != null); + Console.WriteLine(s.Length); + } + + void M2(bool b) + { + string s = b ? null : ""; + Assert.IsNull(s); + Console.WriteLine(s.Length); + } + + void M3(bool b) + { + string s = b ? null : ""; + Assert.IsNotNull(s); + Console.WriteLine(s.Length); + } + + void M4(bool b) + { + string s = b ? null : ""; + Assert.IsTrue(s == null); + Console.WriteLine(s.Length); + } + + void M5(bool b) + { + string s = b ? null : ""; + Assert.IsTrue(s != null); + Console.WriteLine(s.Length); + } + + void M6(bool b) + { + string s = b ? null : ""; + Assert.IsFalse(s != null); + Console.WriteLine(s.Length); + } + + void M7(bool b) + { + string s = b ? null : ""; + Assert.IsFalse(s == null); + Console.WriteLine(s.Length); + } + + void M8(bool b) + { + string s = b ? null : ""; + Assert.IsTrue(s != null && b); + Console.WriteLine(s.Length); + } + + void M9(bool b) + { + string s = b ? null : ""; + Assert.IsFalse(s == null || b); + Console.WriteLine(s.Length); + } + + void M10(bool b) + { + string s = b ? null : ""; + Assert.IsTrue(s == null && b); + Console.WriteLine(s.Length); + } + + void M11(bool b) + { + string s = b ? null : ""; + Assert.IsFalse(s != null || b); + Console.WriteLine(s.Length); + } + + void M12(bool b) + { + string s = b ? null : ""; + Debug.Assert(s != null); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsNull(s); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsNotNull(s); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsTrue(s == null); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsTrue(s != null); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsFalse(s != null); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsFalse(s == null); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsTrue(s != null && b); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsFalse(s == null || !b); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsTrue(s == null && b); + Console.WriteLine(s.Length); + + s = b ? null : ""; + Assert.IsFalse(s != null || !b); + Console.WriteLine(s.Length); + } +} diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index 6409269ce5b9..2ac6070c1ddb 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -13,8 +13,127 @@ | AccessorCalls.cs:66:10:66:11 | enter M9 | AccessorCalls.cs:66:10:66:11 | exit M9 | 57 | | ArrayCreation.cs:3:11:3:12 | enter M1 | ArrayCreation.cs:3:11:3:12 | exit M1 | 4 | | ArrayCreation.cs:5:12:5:13 | enter M2 | ArrayCreation.cs:5:12:5:13 | exit M2 | 5 | -| ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:11:7:12 | exit M3 | 6 | -| ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:12:9:13 | exit M4 | 10 | +| ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:11:7:12 | exit M3 | 7 | +| ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:12:9:13 | exit M4 | 12 | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:9:20:9:20 | access to parameter b | 5 | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:7:10:7:11 | exit M1 | 1 | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:10:22:10:30 | ... != ... | 5 | +| Assert.cs:9:24:9:27 | null | Assert.cs:9:24:9:27 | null | 1 | +| Assert.cs:9:31:9:32 | "" | Assert.cs:9:31:9:32 | "" | 1 | +| Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | 1 | +| Assert.cs:10:9:10:31 | [assertion success] call to method Assert | Assert.cs:11:9:11:35 | call to method WriteLine | 5 | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:16:20:16:20 | access to parameter b | 5 | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:14:10:14:11 | exit M2 | 1 | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:17:23:17:23 | access to local variable s | 3 | +| Assert.cs:16:24:16:27 | null | Assert.cs:16:24:16:27 | null | 1 | +| Assert.cs:16:31:16:32 | "" | Assert.cs:16:31:16:32 | "" | 1 | +| Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | 1 | +| Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | Assert.cs:18:9:18:35 | call to method WriteLine | 5 | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:23:20:23:20 | access to parameter b | 5 | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:21:10:21:11 | exit M3 | 1 | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:24:26:24:26 | access to local variable s | 3 | +| Assert.cs:23:24:23:27 | null | Assert.cs:23:24:23:27 | null | 1 | +| Assert.cs:23:31:23:32 | "" | Assert.cs:23:31:23:32 | "" | 1 | +| Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | 1 | +| Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | Assert.cs:25:9:25:35 | call to method WriteLine | 5 | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:30:20:30:20 | access to parameter b | 5 | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:28:10:28:11 | exit M4 | 1 | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:31:23:31:31 | ... == ... | 5 | +| Assert.cs:30:24:30:27 | null | Assert.cs:30:24:30:27 | null | 1 | +| Assert.cs:30:31:30:32 | "" | Assert.cs:30:31:30:32 | "" | 1 | +| Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | 1 | +| Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | Assert.cs:32:9:32:35 | call to method WriteLine | 5 | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:37:20:37:20 | access to parameter b | 5 | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:35:10:35:11 | exit M5 | 1 | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:38:23:38:31 | ... != ... | 5 | +| Assert.cs:37:24:37:27 | null | Assert.cs:37:24:37:27 | null | 1 | +| Assert.cs:37:31:37:32 | "" | Assert.cs:37:31:37:32 | "" | 1 | +| Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | 1 | +| Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | Assert.cs:39:9:39:35 | call to method WriteLine | 5 | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:44:20:44:20 | access to parameter b | 5 | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:42:10:42:11 | exit M6 | 1 | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:45:24:45:32 | ... != ... | 5 | +| Assert.cs:44:24:44:27 | null | Assert.cs:44:24:44:27 | null | 1 | +| Assert.cs:44:31:44:32 | "" | Assert.cs:44:31:44:32 | "" | 1 | +| Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | 1 | +| Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | Assert.cs:46:9:46:35 | call to method WriteLine | 5 | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:51:20:51:20 | access to parameter b | 5 | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:49:10:49:11 | exit M7 | 1 | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:52:24:52:32 | ... == ... | 5 | +| Assert.cs:51:24:51:27 | null | Assert.cs:51:24:51:27 | null | 1 | +| Assert.cs:51:31:51:32 | "" | Assert.cs:51:31:51:32 | "" | 1 | +| Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | 1 | +| Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | Assert.cs:53:9:53:35 | call to method WriteLine | 5 | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:58:20:58:20 | access to parameter b | 5 | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:56:10:56:11 | exit M8 | 1 | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | 7 | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | 7 | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | 1 | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | 1 | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:60:9:60:35 | call to method WriteLine | 6 | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:65:20:65:20 | access to parameter b | 5 | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:63:10:63:11 | exit M9 | 1 | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | 7 | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | 7 | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | 1 | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:67:9:67:35 | call to method WriteLine | 6 | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | 1 | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:72:20:72:20 | access to parameter b | 5 | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:70:10:70:12 | exit M10 | 1 | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | 7 | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | 7 | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | 1 | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | 1 | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:74:9:74:35 | call to method WriteLine | 6 | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:79:20:79:20 | access to parameter b | 5 | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:77:10:77:12 | exit M11 | 1 | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | 7 | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | 7 | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | 1 | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:81:9:81:35 | call to method WriteLine | 6 | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | 1 | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:86:20:86:20 | access to parameter b | 5 | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:84:10:84:12 | exit M12 | 1 | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | 6 | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | 6 | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | 1 | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | 1 | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | 12 | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | 12 | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | 1 | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | 1 | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | 12 | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | 12 | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | 1 | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | 1 | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | 14 | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | 14 | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | 1 | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | 1 | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | 14 | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | 14 | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | 1 | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | 1 | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | 14 | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | 14 | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | 1 | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | 1 | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | 14 | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | 14 | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | 1 | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | 1 | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | 15 | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | 15 | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | 1 | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | 1 | +| Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | 1 | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | 16 | +| Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | 1 | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | 17 | +| Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | 1 | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | 16 | +| Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | 1 | +| Assert.cs:127:37:127:38 | [b (line 84): true] !... | Assert.cs:128:9:128:35 | call to method WriteLine | 7 | | Assignments.cs:3:10:3:10 | enter M | Assignments.cs:3:10:3:10 | exit M | 33 | | Assignments.cs:14:18:14:35 | enter (...) => ... | Assignments.cs:14:18:14:35 | exit (...) => ... | 3 | | Assignments.cs:17:40:17:40 | enter + | Assignments.cs:17:40:17:40 | exit + | 5 | @@ -59,6 +178,8 @@ | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | CompileTimeOperators.cs:15:10:15:15 | exit Typeof | 5 | | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | CompileTimeOperators.cs:20:12:20:17 | exit Nameof | 5 | | CompileTimeOperators.cs:28:10:28:10 | enter M | CompileTimeOperators.cs:28:10:28:10 | exit M | 14 | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:30:28:30:32 | ... = ... | 3 | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | 1 | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | 2 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | exit M1 | 1 | | ConditionalAccess.cs:3:28:3:38 | call to method ToString | ConditionalAccess.cs:3:28:3:38 | call to method ToString | 1 | @@ -84,7 +205,12 @@ | ConditionalAccess.cs:19:12:19:13 | exit M6 | ConditionalAccess.cs:19:12:19:13 | exit M6 | 1 | | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | ConditionalAccess.cs:19:43:19:60 | call to method CommaJoinWith | 2 | | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | exit M7 | 17 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | 7 | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:28:30:32 | ... = ... | 3 | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | 1 | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:35:9:35:12 | access to property Prop | 8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | 1 | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:14:35:24 | call to method Out | 1 | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | 7 | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:5:13:5:15 | access to parameter inc | 4 | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | exit IncrOrDecr | 1 | | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | Conditions.cs:7:14:7:16 | [inc (line 3): true] access to parameter inc | 6 | @@ -132,7 +258,7 @@ | Conditions.cs:77:17:77:20 | ...; | Conditions.cs:77:17:77:19 | ...++ | 3 | | Conditions.cs:78:13:79:26 | if (...) ... | Conditions.cs:78:17:78:21 | ... > ... | 4 | | Conditions.cs:79:17:79:26 | ...; | Conditions.cs:79:17:79:25 | ... = ... | 3 | -| Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:12:81:12 | access to local variable b | 2 | +| Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:13:81:13 | access to local variable b | 2 | | Conditions.cs:82:13:82:16 | ...; | Conditions.cs:82:13:82:15 | ...++ | 3 | | Conditions.cs:83:16:83:16 | access to local variable x | Conditions.cs:70:9:70:10 | exit M6 | 3 | | Conditions.cs:86:9:86:10 | enter M7 | Conditions.cs:90:27:90:28 | access to parameter ss | 12 | @@ -150,10 +276,10 @@ | Conditions.cs:108:13:109:24 | [b (line 102): false] if (...) ... | Conditions.cs:109:17:109:23 | ... = ... | 8 | | Conditions.cs:108:13:109:24 | [b (line 102): true] if (...) ... | Conditions.cs:108:18:108:18 | [b (line 102): true] access to parameter b | 3 | | Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:102:12:102:13 | exit M8 | 3 | -| Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:116:17:116:21 | Int32 i = ... | 8 | +| Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:116:18:116:22 | Int32 i = ... | 8 | | Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:113:10:113:11 | exit M9 | 1 | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:24:116:38 | ... < ... | 4 | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:116:41:116:43 | ...++ | 2 | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:25:116:39 | ... < ... | 4 | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:116:42:116:44 | ...++ | 2 | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:119:18:119:21 | access to local variable last | 12 | | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | Conditions.cs:121:17:121:20 | [last (line 118): false] access to local variable last | 5 | | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | Conditions.cs:122:17:122:24 | ... = ... | 5 | @@ -162,6 +288,10 @@ | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): false] true | Conditions.cs:135:21:135:26 | [Field1 (line 129): true, Field2 (line 129): false] access to field Field2 | 9 | | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | Conditions.cs:135:21:135:26 | [Field1 (line 129): true] access to field Field2 | 4 | | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | Conditions.cs:135:21:135:26 | [Field1 (line 129): true, Field2 (line 129): true] access to field Field2 | 14 | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:145:17:145:17 | access to parameter b | 5 | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:143:10:143:12 | exit M11 | 1 | +| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | Conditions.cs:147:13:147:48 | call to method WriteLine | 9 | +| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | Conditions.cs:149:13:149:48 | call to method WriteLine | 9 | | ExitMethods.cs:7:10:7:11 | enter M1 | ExitMethods.cs:7:10:7:11 | exit M1 | 7 | | ExitMethods.cs:13:10:13:11 | enter M2 | ExitMethods.cs:13:10:13:11 | exit M2 | 7 | | ExitMethods.cs:19:10:19:11 | enter M3 | ExitMethods.cs:19:10:19:11 | exit M3 | 6 | @@ -197,7 +327,10 @@ | ExitMethods.cs:116:38:116:38 | 1 | ExitMethods.cs:116:38:116:38 | 1 | 1 | | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | ExitMethods.cs:119:17:119:32 | exit FailingAssertion | 6 | | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | ExitMethods.cs:125:17:125:33 | exit FailingAssertion2 | 6 | -| ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | 4 | +| ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:48:131:48 | access to parameter b | 2 | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | 1 | +| ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | 1 | +| ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | 1 | | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | ExitMethods.cs:133:17:133:33 | exit FailingAssertion3 | 7 | | Extensions.cs:5:23:5:29 | enter ToInt32 | Extensions.cs:5:23:5:29 | exit ToInt32 | 6 | | Extensions.cs:10:24:10:29 | enter ToBool | Extensions.cs:10:24:10:29 | exit ToBool | 7 | @@ -402,7 +535,7 @@ | Foreach.cs:38:26:38:26 | String x | Foreach.cs:39:11:39:11 | ; | 4 | | Initializers.cs:8:5:8:16 | enter Initializers | Initializers.cs:8:5:8:16 | exit Initializers | 14 | | Initializers.cs:10:5:10:16 | enter Initializers | Initializers.cs:10:5:10:16 | exit Initializers | 14 | -| Initializers.cs:12:10:12:10 | enter M | Initializers.cs:12:10:12:10 | exit M | 20 | +| Initializers.cs:12:10:12:10 | enter M | Initializers.cs:12:10:12:10 | exit M | 21 | | Initializers.cs:18:20:18:20 | 1 | Initializers.cs:18:16:18:20 | ... = ... | 2 | | Initializers.cs:20:11:20:23 | enter NoConstructor | Initializers.cs:20:11:20:23 | exit NoConstructor | 8 | | Initializers.cs:31:9:31:11 | enter Sub | Initializers.cs:31:9:31:11 | exit Sub | 11 | @@ -412,39 +545,182 @@ | LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:9:13:9:28 | ... == ... | 7 | | LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:7:10:7:11 | exit M1 | 1 | | LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:10:13:10:19 | return ...; | 1 | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | 5 | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | 2 | -| LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | 11 | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | 5 | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | 2 | +| LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | 12 | | LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:15:10:15:11 | exit M2 | 1 | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | 5 | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | 5 | | LoopUnrolling.cs:22:10:22:11 | enter M3 | LoopUnrolling.cs:24:29:24:32 | access to parameter args | 3 | | LoopUnrolling.cs:22:10:22:11 | exit M3 | LoopUnrolling.cs:22:10:22:11 | exit M3 | 1 | | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | 1 | | LoopUnrolling.cs:24:22:24:24 | Char arg | LoopUnrolling.cs:25:13:26:40 | [unroll (line 25)] foreach (... ... in ...) ... | 3 | | LoopUnrolling.cs:25:26:25:29 | Char arg0 | LoopUnrolling.cs:25:13:26:40 | foreach (... ... in ...) ... | 5 | -| LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:32:26:32:27 | access to local variable xs | 7 | -| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:29:10:29:11 | exit M4 | 1 | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | 1 | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | 4 | -| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | 18 | +| LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:29:10:29:11 | exit M4 | 9 | +| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | 20 | | LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:36:10:36:11 | exit M5 | 1 | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | 1 | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | 3 | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | 7 | -| LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:49:9:52:9 | {...} | 13 | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:51:13:51:23 | goto ...; | 5 | -| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:60:17:60:17 | access to parameter b | 15 | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | 3 | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | 7 | +| LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:49:9:52:9 | {...} | 14 | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:51:13:51:23 | goto ...; | 5 | +| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:60:17:60:17 | access to parameter b | 16 | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:55:10:55:11 | exit M7 | 1 | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:60:17:60:17 | [b (line 55): false] access to parameter b | 4 | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:60:17:60:17 | [b (line 55): true] access to parameter b | 4 | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | LoopUnrolling.cs:60:17:60:17 | [b (line 55): false] access to parameter b | 4 | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | LoopUnrolling.cs:60:17:60:17 | [b (line 55): true] access to parameter b | 4 | | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | 9 | | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | 3 | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:69:14:69:23 | call to method Any | 6 | | LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:67:10:67:11 | exit M8 | 1 | | LoopUnrolling.cs:70:13:70:19 | return ...; | LoopUnrolling.cs:70:13:70:19 | return ...; | 1 | -| LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:72:28:72:31 | access to parameter args | 4 | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | 1 | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | 4 | +| LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:72:9:73:35 | [skip (line 72)] foreach (... ... in ...) ... | 5 | +| LoopUnrolling.cs:76:10:76:11 | enter M9 | LoopUnrolling.cs:76:10:76:11 | exit M9 | 10 | +| LoopUnrolling.cs:85:10:85:12 | enter M10 | LoopUnrolling.cs:85:10:85:12 | exit M10 | 10 | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:97:9:100:9 | [unroll (line 97)] foreach (... ... in ...) ... | 9 | +| LoopUnrolling.cs:94:10:94:12 | exit M11 | LoopUnrolling.cs:94:10:94:12 | exit M11 | 1 | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | 6 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | enter get_P1 | 1 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | enter get_P1 | 1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | 1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | 1 | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:22:6:31 | throw ... | 2 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | enter get_P2 | 1 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | enter get_P2 | 1 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | 1 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | 1 | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:27:7:37 | throw ...; | 3 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | enter set_P2 | 1 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | enter set_P2 | 1 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | 1 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | 1 | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:47:7:57 | throw ...; | 3 | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:16:8:16 | enter M | 1 | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:16:5:16 | enter M | 1 | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | 1 | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | 1 | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:23:8:32 | throw ... | 2 | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:20 | ... = ... | 3 | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | 1 | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | 1 | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | 1 | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | 1 | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | exit get_Item | 1 | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | enter get_Item | 1 | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | enter get_Item | 1 | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | exit get_Item | 1 | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | exit get_Item | 1 | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:42:15:50 | return ...; | 3 | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | enter set_Item | 1 | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | enter set_Item | 1 | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | exit set_Item | 1 | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | exit set_Item | 1 | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationA.cs:15:58:15:60 | {...} | 1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | enter M1 | 1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | enter M1 | 1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | exit M1 | 1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | exit M1 | 1 | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:18:9:18:22 | M2(...) | 2 | +| MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | exit M2 | 3 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | 1 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | 1 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | 1 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | 1 | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:24:20:28 | ... = ... | 5 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | 1 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | 1 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 | 1 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 | 1 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | 2 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | 1 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | 1 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | 1 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | 1 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | 1 | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:11:22:13 | {...} | 1 | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | 1 | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | 1 | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | 1 | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | 1 | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | 1 | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:32:24:34 | ... = ... | 4 | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | 4 | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | 4 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | 1 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | enter M1 | 1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | exit M1 | 1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | exit M1 | 1 | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:16:36:26 | throw ...; | 3 | +| MultiImplementationA.cs:37:9:37:10 | enter M2 | MultiImplementationA.cs:37:9:37:10 | exit M2 | 5 | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationB.cs:3:22:3:22 | 0 | 1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | enter get_P1 | 1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | enter get_P1 | 1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | 1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | 1 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | enter get_P2 | 1 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | enter get_P2 | 1 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | 1 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | 1 | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:27:4:35 | return ...; | 3 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | enter set_P2 | 1 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | enter set_P2 | 1 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | 1 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | 1 | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationB.cs:4:43:4:45 | {...} | 1 | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:16:8:16 | enter M | 1 | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:16:5:16 | enter M | 1 | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | 1 | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | 1 | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | 1 | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:20 | ... = ... | 3 | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | 1 | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | 1 | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | 1 | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | exit get_Item | 1 | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:31:12:40 | throw ... | 2 | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | enter get_Item | 1 | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | enter get_Item | 1 | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | exit get_Item | 1 | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | exit get_Item | 1 | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:42:13:52 | throw ...; | 3 | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | enter set_Item | 1 | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | enter set_Item | 1 | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | exit set_Item | 1 | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | exit set_Item | 1 | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationB.cs:13:60:13:62 | {...} | 1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | enter M1 | 1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | enter M1 | 1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | exit M1 | 1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | exit M1 | 1 | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:16:9:16:31 | M2(...) | 2 | +| MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | exit M2 | 4 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | 1 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | 1 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | 1 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | 1 | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:24:18:34 | throw ...; | 3 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | 1 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | 1 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 | 1 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 | 1 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | 2 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | 1 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | 1 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | 1 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | 1 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | 1 | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:13:20:23 | throw ...; | 3 | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | 1 | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | 1 | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | 1 | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | 1 | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:50:21:59 | throw ... | 2 | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:32:22:34 | ... = ... | 4 | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | 4 | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | 4 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | 1 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | enter M1 | 1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | exit M1 | 1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | exit M1 | 1 | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationB.cs:32:17:32:17 | 0 | 1 | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:23:3:23 | access to parameter i | 3 | | NullCoalescing.cs:3:9:3:10 | exit M1 | NullCoalescing.cs:3:9:3:10 | exit M1 | 1 | | NullCoalescing.cs:3:28:3:28 | 0 | NullCoalescing.cs:3:28:3:28 | 0 | 1 | @@ -520,35 +796,35 @@ | Switch.cs:55:10:55:11 | enter M5 | Switch.cs:55:10:55:11 | exit M5 | 12 | | Switch.cs:66:10:66:11 | enter M6 | Switch.cs:72:18:72:19 | "" | 9 | | Switch.cs:66:10:66:11 | exit M6 | Switch.cs:66:10:66:11 | exit M6 | 1 | -| Switch.cs:73:15:73:20 | break; | Switch.cs:73:15:73:20 | break; | 1 | +| Switch.cs:73:17:73:22 | break; | Switch.cs:73:17:73:22 | break; | 1 | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:81:18:81:18 | 1 | 6 | | Switch.cs:77:10:77:11 | exit M7 | Switch.cs:77:10:77:11 | exit M7 | 1 | -| Switch.cs:82:22:82:25 | true | Switch.cs:82:15:82:26 | return ...; | 2 | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:83:18:83:18 | 2 | 2 | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:84:19:84:23 | ... > ... | 4 | -| Switch.cs:85:17:85:22 | break; | Switch.cs:85:17:85:22 | break; | 1 | -| Switch.cs:86:22:86:25 | true | Switch.cs:86:15:86:26 | return ...; | 2 | +| Switch.cs:82:24:82:27 | true | Switch.cs:82:17:82:28 | return ...; | 2 | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:83:18:83:18 | 2 | 2 | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:84:21:84:25 | ... > ... | 4 | +| Switch.cs:85:21:85:26 | break; | Switch.cs:85:21:85:26 | break; | 1 | +| Switch.cs:86:24:86:27 | true | Switch.cs:86:17:86:28 | return ...; | 2 | | Switch.cs:88:16:88:20 | false | Switch.cs:88:9:88:21 | return ...; | 2 | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:95:18:95:20 | access to type Int32 | 6 | | Switch.cs:91:10:91:11 | exit M8 | Switch.cs:91:10:91:11 | exit M8 | 1 | -| Switch.cs:96:22:96:25 | true | Switch.cs:96:15:96:26 | return ...; | 2 | +| Switch.cs:96:24:96:27 | true | Switch.cs:96:17:96:28 | return ...; | 2 | | Switch.cs:98:16:98:20 | false | Switch.cs:98:9:98:21 | return ...; | 2 | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:103:17:103:17 | access to parameter s | 4 | | Switch.cs:101:9:101:10 | exit M9 | Switch.cs:101:9:101:10 | exit M9 | 1 | | Switch.cs:103:19:103:25 | access to property Length | Switch.cs:103:19:103:25 | access to property Length | 1 | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:18:105:18 | 0 | 2 | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:105:22:105:30 | return ...; | 2 | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:18:106:18 | 1 | 2 | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:106:22:106:30 | return ...; | 2 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:18:105:18 | 0 | 2 | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:105:21:105:29 | return ...; | 2 | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:18:106:18 | 1 | 2 | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:106:21:106:29 | return ...; | 2 | | Switch.cs:108:17:108:17 | 1 | Switch.cs:108:9:108:18 | return ...; | 3 | | Switch.cs:111:17:111:21 | enter Throw | Switch.cs:111:17:111:21 | exit Throw | 4 | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:117:18:117:18 | 3 | 7 | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:113:9:113:11 | exit M10 | 1 | -| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:25:117:32 | ... == ... | 3 | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:117:36:117:44 | return ...; | 2 | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:18:118:18 | 2 | 2 | -| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:25:118:31 | ... == ... | 3 | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:118:35:118:43 | return ...; | 2 | +| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:25:117:34 | ... == ... | 3 | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:117:37:117:45 | return ...; | 2 | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:18:118:18 | 2 | 2 | +| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:25:118:33 | ... == ... | 3 | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:118:36:118:44 | return ...; | 2 | | Switch.cs:120:17:120:17 | 1 | Switch.cs:120:9:120:18 | return ...; | 3 | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:125:24:125:29 | Boolean b | 7 | | Switch.cs:123:10:123:12 | exit M11 | Switch.cs:123:10:123:12 | exit M11 | 1 | @@ -572,10 +848,18 @@ | Switch.cs:149:13:149:20 | default: | Switch.cs:149:22:149:31 | return ...; | 4 | | Switch.cs:150:13:150:19 | case ...: | Switch.cs:150:18:150:18 | 2 | 2 | | Switch.cs:150:28:150:28 | 2 | Switch.cs:150:21:150:29 | return ...; | 2 | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:156:28:156:31 | true | 7 | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:154:10:154:12 | exit M15 | 1 | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:157:13:157:13 | access to parameter b | 3 | +| Switch.cs:156:36:156:38 | "a" | Switch.cs:156:36:156:38 | "a" | 1 | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:41:156:45 | false | 2 | +| Switch.cs:156:50:156:52 | "b" | Switch.cs:156:50:156:52 | "b" | 1 | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:48 | call to method WriteLine | 5 | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:48 | call to method WriteLine | 5 | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:7:13:7:22 | ... is ... | 14 | | TypeAccesses.cs:7:25:7:25 | ; | TypeAccesses.cs:7:25:7:25 | ; | 1 | | TypeAccesses.cs:8:9:8:28 | ... ...; | TypeAccesses.cs:3:10:3:10 | exit M | 4 | -| VarDecls.cs:5:18:5:19 | enter M1 | VarDecls.cs:5:18:5:19 | exit M1 | 16 | +| VarDecls.cs:5:18:5:19 | enter M1 | VarDecls.cs:5:18:5:19 | exit M1 | 18 | | VarDecls.cs:13:12:13:13 | enter M2 | VarDecls.cs:13:12:13:13 | exit M2 | 12 | | VarDecls.cs:19:7:19:8 | enter M3 | VarDecls.cs:25:20:25:20 | access to parameter b | 12 | | VarDecls.cs:25:13:25:29 | return ...; | VarDecls.cs:19:7:19:8 | exit M3 | 2 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Condition.expected b/csharp/ql/test/library-tests/controlflow/graph/Condition.expected index 4937b513ac9f..df9d8425b5f2 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Condition.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Condition.expected @@ -1,4 +1,299 @@ conditionBlock +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:9:24:9:27 | null | true | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:9:31:9:32 | "" | false | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | false | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | true | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:16:24:16:27 | null | true | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:16:31:16:32 | "" | false | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | false | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | true | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:23:24:23:27 | null | true | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:23:31:23:32 | "" | false | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | true | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | false | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:30:24:30:27 | null | true | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:30:31:30:32 | "" | false | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | false | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | true | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:37:24:37:27 | null | true | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:37:31:37:32 | "" | false | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | false | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | true | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:44:24:44:27 | null | true | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:44:31:44:32 | "" | false | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | true | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | false | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:51:24:51:27 | null | true | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:51:31:51:32 | "" | false | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | true | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | false | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:58:24:58:27 | [b (line 56): true] null | true | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:58:31:58:32 | [b (line 56): false] "" | false | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | false | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | true | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | true | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | true | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:65:24:65:27 | [b (line 63): true] null | true | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:65:31:65:32 | [b (line 63): false] "" | false | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | false | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | true | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | false | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | false | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:72:24:72:27 | [b (line 70): true] null | true | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:72:31:72:32 | [b (line 70): false] "" | false | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | false | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | true | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | true | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | true | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:79:24:79:27 | [b (line 77): true] null | true | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:79:31:79:32 | [b (line 77): false] "" | false | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | false | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | true | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | false | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:86:24:86:27 | [b (line 84): true] null | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:86:31:86:32 | [b (line 84): false] "" | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | false | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:119:37:119:38 | [b (line 84): true] !... | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:127:37:127:38 | [b (line 84): true] !... | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | false | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:119:37:119:38 | [b (line 84): true] !... | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:127:37:127:38 | [b (line 84): true] !... | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | false | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | false | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | false | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:119:37:119:38 | [b (line 84): true] !... | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:127:37:127:38 | [b (line 84): true] !... | true | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | true | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | true | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:119:37:119:38 | [b (line 84): true] !... | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:127:37:127:38 | [b (line 84): true] !... | false | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:119:37:119:38 | [b (line 84): true] !... | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:127:37:127:38 | [b (line 84): true] !... | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:119:37:119:38 | [b (line 84): true] !... | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:127:37:127:38 | [b (line 84): true] !... | true | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:119:37:119:38 | [b (line 84): true] !... | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:127:37:127:38 | [b (line 84): true] !... | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:37:119:38 | [b (line 84): true] !... | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:127:37:127:38 | [b (line 84): true] !... | false | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | true | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:37:119:38 | [b (line 84): true] !... | true | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | true | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:127:37:127:38 | [b (line 84): true] !... | true | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:119:37:119:38 | [b (line 84): true] !... | false | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | false | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | false | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:127:37:127:38 | [b (line 84): true] !... | false | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:127:37:127:38 | [b (line 84): true] !... | true | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:127:37:127:38 | [b (line 84): true] !... | false | | BreakInTry.cs:7:13:11:13 | foreach (... ... in ...) ... | BreakInTry.cs:7:26:7:28 | String arg | false | | BreakInTry.cs:7:13:11:13 | foreach (... ... in ...) ... | BreakInTry.cs:10:21:10:26 | break; | false | | BreakInTry.cs:7:26:7:28 | String arg | BreakInTry.cs:10:21:10:26 | break; | true | @@ -53,6 +348,7 @@ conditionBlock | ConditionalAccess.cs:13:25:13:25 | 0 | ConditionalAccess.cs:14:20:14:20 | 0 | true | | ConditionalAccess.cs:13:25:13:25 | 0 | ConditionalAccess.cs:16:20:16:20 | 1 | false | | ConditionalAccess.cs:19:12:19:13 | enter M6 | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | false | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:35:14:35:24 | call to method Out | false | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | true | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:7:9:8:16 | [inc (line 3): false] if (...) ... | false | | Conditions.cs:11:9:11:10 | enter M1 | Conditions.cs:15:13:15:16 | [b (line 11): true] ...; | true | @@ -125,11 +421,11 @@ conditionBlock | Conditions.cs:102:12:102:13 | enter M8 | Conditions.cs:108:13:109:24 | [b (line 102): true] if (...) ... | true | | Conditions.cs:106:13:106:20 | [b (line 102): true] ...; | Conditions.cs:108:13:109:24 | [b (line 102): true] if (...) ... | true | | Conditions.cs:107:9:109:24 | [b (line 102): false] if (...) ... | Conditions.cs:108:13:109:24 | [b (line 102): false] if (...) ... | true | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:113:10:113:11 | exit M9 | false | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:41:116:41 | access to local variable i | true | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:117:9:123:9 | {...} | true | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | true | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | true | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:113:10:113:11 | exit M9 | false | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:42:116:42 | access to local variable i | true | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:117:9:123:9 | {...} | true | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | true | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | true | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | false | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | true | | Conditions.cs:129:10:129:12 | enter M10 | Conditions.cs:131:16:131:19 | [Field1 (line 129): false] true | false | @@ -138,6 +434,8 @@ conditionBlock | Conditions.cs:129:10:129:12 | enter M10 | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | true | | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): false] true | false | | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | true | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | true | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | false | | ExitMethods.cs:43:9:46:9 | [exception: Exception] catch (...) {...} | ExitMethods.cs:47:9:50:9 | [exception: Exception] catch (...) {...} | false | | ExitMethods.cs:65:17:65:26 | enter ErrorMaybe | ExitMethods.cs:68:19:68:33 | object creation of type Exception | true | | ExitMethods.cs:71:17:71:27 | enter ErrorAlways | ExitMethods.cs:74:19:74:33 | object creation of type Exception | true | @@ -146,6 +444,8 @@ conditionBlock | ExitMethods.cs:109:13:109:21 | enter ThrowExpr | ExitMethods.cs:111:69:111:75 | "input" | false | | ExitMethods.cs:114:16:114:34 | enter ExtensionMethodCall | ExitMethods.cs:116:34:116:34 | 0 | true | | ExitMethods.cs:114:16:114:34 | enter ExtensionMethodCall | ExitMethods.cs:116:38:116:38 | 1 | false | +| ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | true | +| ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | false | | Finally.cs:26:9:29:9 | [exception: Exception] catch (...) {...} | Finally.cs:26:38:26:39 | [exception: Exception] IOException ex | true | | Finally.cs:26:9:29:9 | [exception: Exception] catch (...) {...} | Finally.cs:30:9:40:9 | [exception: Exception] catch (...) {...} | false | | Finally.cs:26:9:29:9 | [exception: Exception] catch (...) {...} | Finally.cs:30:41:30:42 | [exception: Exception] ArgumentException ex | false | @@ -423,39 +723,37 @@ conditionBlock | Foreach.cs:38:9:39:11 | foreach (... ... in ...) ... | Foreach.cs:36:10:36:11 | exit M6 | true | | Foreach.cs:38:9:39:11 | foreach (... ... in ...) ... | Foreach.cs:38:26:38:26 | String x | false | | LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:10:13:10:19 | return ...; | true | -| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:11:21:11:23 | String arg | false | -| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:11:28:11:31 | access to parameter args | false | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:21:11:23 | String arg | false | +| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:11:22:11:24 | String arg | false | +| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:11:29:11:32 | access to parameter args | false | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:22:11:24 | String arg | false | | LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:15:10:15:11 | exit M2 | false | -| LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:18:21:18:21 | String x | false | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:15:10:15:11 | exit M2 | true | +| LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:18:22:18:22 | String x | false | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:15:10:15:11 | exit M2 | true | | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | LoopUnrolling.cs:22:10:22:11 | exit M3 | true | | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | LoopUnrolling.cs:24:22:24:24 | Char arg | false | | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | LoopUnrolling.cs:25:26:25:29 | Char arg0 | false | | LoopUnrolling.cs:24:22:24:24 | Char arg | LoopUnrolling.cs:25:26:25:29 | Char arg0 | false | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | exit M4 | true | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:21:32:21 | String x | false | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:36:10:36:11 | exit M5 | false | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | false | -| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:21:40:21 | String x | false | -| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:41:25:41:25 | String y | false | +| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:22:40:22 | String x | false | +| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:41:26:41:26 | String y | false | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | exit M5 | true | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:36:10:36:11 | exit M5 | false | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | false | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:41:25:41:25 | String y | false | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:36:10:36:11 | exit M5 | true | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | true | -| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | false | -| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | true | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:36:10:36:11 | exit M5 | false | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | false | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:41:26:41:26 | String y | false | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:36:10:36:11 | exit M5 | true | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | true | +| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | false | +| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | true | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | true | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | false | -| LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | false | -| LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | false | +| LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | false | +| LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | false | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:70:13:70:19 | return ...; | false | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:71:9:71:21 | ...; | true | -| LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | true | -| LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:72:21:72:23 | String arg | true | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:21:72:23 | String arg | false | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:94:10:94:12 | exit M11 | false | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:97:22:97:22 | String x | false | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:94:10:94:12 | exit M11 | true | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:28:3:28 | 0 | true | | NullCoalescing.cs:5:9:5:10 | enter M2 | NullCoalescing.cs:5:30:5:34 | false | true | | NullCoalescing.cs:5:9:5:10 | enter M2 | NullCoalescing.cs:5:39:5:39 | 0 | true | @@ -568,33 +866,33 @@ conditionBlock | Switch.cs:50:13:50:39 | case ...: | Switch.cs:50:30:50:30 | access to parameter o | true | | Switch.cs:50:13:50:39 | case ...: | Switch.cs:51:17:51:22 | break; | true | | Switch.cs:50:30:50:30 | access to parameter o | Switch.cs:51:17:51:22 | break; | true | -| Switch.cs:66:10:66:11 | enter M6 | Switch.cs:73:15:73:20 | break; | true | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:82:22:82:25 | true | true | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:83:13:83:20 | case ...: | false | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:84:15:85:22 | if (...) ... | false | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:85:17:85:22 | break; | false | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:86:22:86:25 | true | false | +| Switch.cs:66:10:66:11 | enter M6 | Switch.cs:73:17:73:22 | break; | true | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:82:24:82:27 | true | true | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:83:13:83:19 | case ...: | false | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:84:17:85:26 | if (...) ... | false | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:85:21:85:26 | break; | false | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:86:24:86:27 | true | false | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:88:16:88:20 | false | false | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:84:15:85:22 | if (...) ... | true | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:85:17:85:22 | break; | true | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:86:22:86:25 | true | true | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:85:17:85:22 | break; | true | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:86:22:86:25 | true | false | -| Switch.cs:91:10:91:11 | enter M8 | Switch.cs:96:22:96:25 | true | true | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:84:17:85:26 | if (...) ... | true | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:85:21:85:26 | break; | true | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:86:24:86:27 | true | true | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:85:21:85:26 | break; | true | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:86:24:86:27 | true | false | +| Switch.cs:91:10:91:11 | enter M8 | Switch.cs:96:24:96:27 | true | true | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:98:16:98:20 | false | false | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:103:19:103:25 | access to property Length | false | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:29:105:29 | 0 | true | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:106:13:106:20 | case ...: | false | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:106:29:106:29 | 1 | false | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:108:17:108:17 | 1 | false | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:29:106:29 | 1 | true | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:108:17:108:17 | 1 | false | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:28:105:28 | 0 | true | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:106:13:106:19 | case ...: | false | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:106:28:106:28 | 1 | false | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:108:17:108:17 | 1 | false | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:28:106:28 | 1 | true | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:108:17:108:17 | 1 | false | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:117:25:117:25 | access to parameter s | true | -| Switch.cs:113:9:113:11 | enter M10 | Switch.cs:117:43:117:43 | 1 | true | -| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:43:117:43 | 1 | true | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:25:118:25 | access to parameter s | true | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:42:118:42 | 2 | true | -| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:42:118:42 | 2 | true | +| Switch.cs:113:9:113:11 | enter M10 | Switch.cs:117:44:117:44 | 1 | true | +| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:44:117:44 | 1 | true | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:25:118:25 | access to parameter s | true | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:43:118:43 | 2 | true | +| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:43:118:43 | 2 | true | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:125:34:125:34 | access to local variable b | true | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:125:37:125:46 | ... => ... | false | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:126:13:126:19 | return ...; | true | @@ -615,6 +913,12 @@ conditionBlock | Switch.cs:144:9:144:11 | enter M14 | Switch.cs:150:28:150:28 | 2 | false | | Switch.cs:150:13:150:19 | case ...: | Switch.cs:149:13:149:20 | default: | false | | Switch.cs:150:13:150:19 | case ...: | Switch.cs:150:28:150:28 | 2 | true | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:156:36:156:38 | "a" | true | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:156:41:156:52 | ... => ... | false | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:156:50:156:52 | "b" | false | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:158:13:158:49 | ...; | true | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:160:13:160:49 | ...; | false | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:50:156:52 | "b" | true | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:7:25:7:25 | ; | true | | VarDecls.cs:19:7:19:8 | enter M3 | VarDecls.cs:25:24:25:24 | access to local variable x | true | | VarDecls.cs:19:7:19:8 | enter M3 | VarDecls.cs:25:28:25:28 | access to local variable y | false | @@ -793,6 +1097,116 @@ conditionBlock | cflow.cs:264:25:264:25 | access to local variable i | cflow.cs:268:9:276:9 | try {...} ... | false | | cflow.cs:298:10:298:10 | enter M | cflow.cs:300:56:300:56 | access to parameter s | false | conditionFlow +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:24:9:27 | null | true | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:31:9:32 | "" | false | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | false | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | true | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:24:16:27 | null | true | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:31:16:32 | "" | false | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:24:23:27 | null | true | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:31:23:32 | "" | false | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:24:30:27 | null | true | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:31:30:32 | "" | false | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | false | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | true | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:24:37:27 | null | true | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:31:37:32 | "" | false | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | false | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | true | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:24:44:27 | null | true | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:31:44:32 | "" | false | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | true | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | false | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:24:51:27 | null | true | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:31:51:32 | "" | false | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | true | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | false | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:24:58:27 | [b (line 56): true] null | true | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:31:58:32 | [b (line 56): false] "" | false | +| Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | false | +| Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | true | +| Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | false | +| Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | true | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | false | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:59:9:59:37 | [assertion success] call to method IsTrue | true | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:24:65:27 | [b (line 63): true] null | true | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:31:65:32 | [b (line 63): false] "" | false | +| Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | true | +| Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | false | +| Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | true | +| Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | false | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:66:9:66:38 | [assertion success] call to method IsFalse | false | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | true | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:24:72:27 | [b (line 70): true] null | true | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:31:72:32 | [b (line 70): false] "" | false | +| Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | false | +| Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | true | +| Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | false | +| Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | true | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | false | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:73:9:73:37 | [assertion success] call to method IsTrue | true | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:24:79:27 | [b (line 77): true] null | true | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:31:79:32 | [b (line 77): false] "" | false | +| Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | true | +| Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | false | +| Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | true | +| Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | false | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:80:9:80:38 | [assertion success] call to method IsFalse | false | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | true | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:24:86:27 | [b (line 84): true] null | true | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:31:86:32 | [b (line 84): false] "" | false | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | false | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | true | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | false | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | true | +| Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | Assert.cs:90:24:90:25 | [b (line 84): false] "" | false | +| Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | Assert.cs:90:17:90:20 | [b (line 84): true] null | true | +| Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | Assert.cs:94:24:94:25 | [b (line 84): false] "" | false | +| Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | Assert.cs:94:17:94:20 | [b (line 84): true] null | true | +| Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | Assert.cs:98:24:98:25 | [b (line 84): false] "" | false | +| Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | Assert.cs:98:17:98:20 | [b (line 84): true] null | true | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | Assert.cs:102:24:102:25 | [b (line 84): false] "" | false | +| Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | Assert.cs:102:17:102:20 | [b (line 84): true] null | true | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | true | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | Assert.cs:106:24:106:25 | [b (line 84): false] "" | false | +| Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | Assert.cs:106:17:106:20 | [b (line 84): true] null | true | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | false | +| Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | Assert.cs:110:24:110:25 | [b (line 84): false] "" | false | +| Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | Assert.cs:110:17:110:20 | [b (line 84): true] null | true | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | true | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | false | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | false | +| Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | Assert.cs:114:24:114:25 | [b (line 84): false] "" | false | +| Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | Assert.cs:114:17:114:20 | [b (line 84): true] null | true | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | true | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | false | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | Assert.cs:118:17:118:20 | [b (line 84): true] null | true | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | true | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | Assert.cs:119:37:119:38 | [b (line 84): true] !... | false | +| Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | true | +| Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | Assert.cs:122:17:122:20 | [b (line 84): true] null | true | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | false | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | true | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | true | +| Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | Assert.cs:126:17:126:20 | [b (line 84): true] null | true | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | true | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | Assert.cs:127:37:127:38 | [b (line 84): true] !... | false | +| Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | Assert.cs:127:9:127:39 | [assertion success] call to method IsFalse | true | | BreakInTry.cs:9:21:9:31 | ... == ... | BreakInTry.cs:7:13:11:13 | foreach (... ... in ...) ... | false | | BreakInTry.cs:9:21:9:31 | ... == ... | BreakInTry.cs:10:21:10:26 | break; | true | | BreakInTry.cs:15:17:15:28 | ... == ... | BreakInTry.cs:3:10:3:11 | exit M1 | false | @@ -870,8 +1284,8 @@ conditionFlow | Conditions.cs:76:17:76:17 | access to local variable b | Conditions.cs:78:13:79:26 | if (...) ... | false | | Conditions.cs:78:17:78:21 | ... > ... | Conditions.cs:74:9:80:9 | foreach (... ... in ...) ... | false | | Conditions.cs:78:17:78:21 | ... > ... | Conditions.cs:79:17:79:26 | ...; | true | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:82:13:82:16 | ...; | true | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:83:16:83:16 | access to local variable x | false | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:82:13:82:16 | ...; | true | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:83:16:83:16 | access to local variable x | false | | Conditions.cs:92:17:92:17 | access to local variable b | Conditions.cs:93:17:93:20 | ...; | true | | Conditions.cs:92:17:92:17 | access to local variable b | Conditions.cs:94:13:95:26 | if (...) ... | false | | Conditions.cs:94:17:94:21 | ... > ... | Conditions.cs:95:17:95:26 | ...; | true | @@ -886,11 +1300,11 @@ conditionFlow | Conditions.cs:107:13:107:24 | [b (line 102): true] ... > ... | Conditions.cs:110:16:110:16 | access to local variable x | false | | Conditions.cs:108:18:108:18 | [b (line 102): false] access to parameter b | Conditions.cs:109:17:109:24 | ...; | false | | Conditions.cs:108:18:108:18 | [b (line 102): true] access to parameter b | Conditions.cs:110:16:110:16 | access to local variable x | true | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:113:10:113:11 | exit M9 | false | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:117:9:123:9 | {...} | true | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:113:10:113:11 | exit M9 | false | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:117:9:123:9 | {...} | true | | Conditions.cs:119:18:119:21 | access to local variable last | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | false | | Conditions.cs:119:18:119:21 | access to local variable last | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | true | -| Conditions.cs:121:17:121:20 | [last (line 118): false] access to local variable last | Conditions.cs:116:41:116:41 | access to local variable i | false | +| Conditions.cs:121:17:121:20 | [last (line 118): false] access to local variable last | Conditions.cs:116:42:116:42 | access to local variable i | false | | Conditions.cs:121:17:121:20 | [last (line 118): true] access to local variable last | Conditions.cs:122:17:122:25 | ...; | true | | Conditions.cs:131:16:131:19 | [Field1 (line 129): false] true | Conditions.cs:132:9:140:9 | [Field1 (line 129): false] {...} | true | | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): false] true | Conditions.cs:132:9:140:9 | [Field1 (line 129): true, Field2 (line 129): false] {...} | true | @@ -905,6 +1319,10 @@ conditionFlow | Conditions.cs:135:21:135:26 | [Field1 (line 129): true, Field2 (line 129): true] access to field Field2 | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | true | | Conditions.cs:135:21:135:26 | [Field1 (line 129): true] access to field Field2 | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): false] true | false | | Conditions.cs:135:21:135:26 | [Field1 (line 129): true] access to field Field2 | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | true | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | true | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | false | +| Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | Conditions.cs:149:13:149:49 | ...; | false | +| Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | Conditions.cs:147:13:147:49 | ...; | true | | ExitMethods.cs:67:13:67:13 | access to parameter b | ExitMethods.cs:65:17:65:26 | exit ErrorMaybe | false | | ExitMethods.cs:67:13:67:13 | access to parameter b | ExitMethods.cs:68:19:68:33 | object creation of type Exception | true | | ExitMethods.cs:73:13:73:13 | access to parameter b | ExitMethods.cs:74:19:74:33 | object creation of type Exception | true | @@ -913,6 +1331,10 @@ conditionFlow | ExitMethods.cs:111:16:111:25 | ... != ... | ExitMethods.cs:111:69:111:75 | "input" | false | | ExitMethods.cs:116:16:116:30 | call to method Contains | ExitMethods.cs:116:34:116:34 | 0 | true | | ExitMethods.cs:116:16:116:30 | call to method Contains | ExitMethods.cs:116:38:116:38 | 1 | false | +| ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:121:9:121:28 | [assertion failure] call to method IsTrue | false | +| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | true | +| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | false | +| ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:135:9:135:25 | [assertion failure] call to method AssertFalse | true | | Finally.cs:26:48:26:51 | [exception: Exception] true | Finally.cs:27:9:29:9 | {...} | true | | Finally.cs:34:21:34:24 | true | Finally.cs:34:27:34:32 | throw ...; | true | | Finally.cs:61:48:61:51 | [exception: Exception] true | Finally.cs:62:9:64:9 | {...} | true | @@ -1008,7 +1430,7 @@ conditionFlow | Finally.cs:209:21:209:22 | access to parameter b3 | Finally.cs:209:31:209:46 | object creation of type ExceptionC | true | | Finally.cs:209:21:209:22 | access to parameter b3 | Finally.cs:211:13:211:29 | ...; | false | | LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:10:13:10:19 | return ...; | true | -| LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:11:28:11:31 | access to parameter args | false | +| LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:11:29:11:32 | access to parameter args | false | | LoopUnrolling.cs:60:17:60:17 | [b (line 55): false] access to parameter b | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | false | | LoopUnrolling.cs:60:17:60:17 | [b (line 55): true] access to parameter b | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | true | | LoopUnrolling.cs:60:17:60:17 | access to parameter b | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | true | @@ -1044,15 +1466,17 @@ conditionFlow | Switch.cs:24:48:24:55 | ... != ... | Switch.cs:27:13:27:39 | case ...: | false | | Switch.cs:50:30:50:38 | ... != ... | Switch.cs:44:10:44:11 | exit M4 | false | | Switch.cs:50:30:50:38 | ... != ... | Switch.cs:51:17:51:22 | break; | true | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:85:17:85:22 | break; | true | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:86:22:86:25 | true | false | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:117:43:117:43 | 1 | true | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:118:13:118:33 | case ...: | false | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:118:42:118:42 | 2 | true | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:120:17:120:17 | 1 | false | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:85:21:85:26 | break; | true | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:86:24:86:27 | true | false | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:117:44:117:44 | 1 | true | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:118:13:118:34 | case ...: | false | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:118:43:118:43 | 2 | true | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:120:17:120:17 | 1 | false | | Switch.cs:125:34:125:34 | access to local variable b | Switch.cs:123:10:123:12 | exit M11 | false | | Switch.cs:125:34:125:34 | access to local variable b | Switch.cs:126:13:126:19 | return ...; | true | | Switch.cs:125:42:125:46 | false | Switch.cs:123:10:123:12 | exit M11 | false | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:158:13:158:49 | ...; | true | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:160:13:160:49 | ...; | false | | TypeAccesses.cs:7:13:7:22 | ... is ... | TypeAccesses.cs:7:25:7:25 | ; | true | | TypeAccesses.cs:7:13:7:22 | ... is ... | TypeAccesses.cs:8:9:8:28 | ... ...; | false | | VarDecls.cs:25:20:25:20 | access to parameter b | VarDecls.cs:25:24:25:24 | access to local variable x | true | diff --git a/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs b/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs index 00743aba1694..a03564b529f5 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs +++ b/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs @@ -24,6 +24,16 @@ void M7(int i) var s = ((int?)i)?.ToString(); s = ""?.CommaJoinWith(s); } + + ConditionalAccess Prop { get; set; } + + void Out(out int i) => i = 0; + + void M8(bool b, out int i) + { + i = 0; + Prop?.Out(out i); + } } static class Ext diff --git a/csharp/ql/test/library-tests/controlflow/graph/Conditions.cs b/csharp/ql/test/library-tests/controlflow/graph/Conditions.cs index bedcba97884d..761718082ab4 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Conditions.cs +++ b/csharp/ql/test/library-tests/controlflow/graph/Conditions.cs @@ -78,7 +78,7 @@ int M6(string[] ss) if (x > 0) b = false; } - if(b) + if (b) x++; return x; } @@ -113,7 +113,7 @@ string M8(bool b) void M9(string[] args) { string s = null; - for(var i = 0; i < args.Length; i++) + for (var i = 0; i < args.Length; i++) { var last = i == args.Length - 1; if (!last) @@ -139,4 +139,13 @@ void M10() } } } + + void M11(bool b) + { + var s = b ? "a" : "b"; + if (b) + System.Console.WriteLine($"a = {s}"); + else + System.Console.WriteLine($"b = {s}"); + } } diff --git a/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected b/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected index bccab8e85a45..abcb76bffec5 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected @@ -5,3 +5,77 @@ breakInvariant3 breakInvariant4 breakInvariant5 multipleSuccessors +| ConditionalAccess.cs:30:28:30:32 | ... = ... | successor | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | successor | ConditionalAccess.cs:30:10:30:12 | exit Out | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | successor | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | successor | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | successor | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | successor | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | successor | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | successor | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationA.cs:8:16:8:16 | enter M | successor | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:8:16:8:16 | enter M | successor | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | successor | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | successor | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | successor | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | successor | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | successor | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | successor | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | successor | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | successor | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | successor | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | successor | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | successor | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | successor | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | successor | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | successor | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | successor | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | successor | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | successor | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | successor | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | successor | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | successor | MultiImplementationB.cs:32:17:32:17 | 0 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | successor | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | successor | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | successor | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | successor | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | successor | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | successor | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:5:16:5:16 | enter M | successor | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationB.cs:5:16:5:16 | enter M | successor | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | successor | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | successor | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | successor | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | successor | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | successor | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | successor | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | successor | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | successor | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | successor | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | successor | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | successor | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | successor | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | successor | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | successor | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | successor | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | successor | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | successor | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | successor | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | successor | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | successor | MultiImplementationB.cs:32:17:32:17 | 0 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index 577f2f6ccbea..d087f17f601e 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -294,12 +294,15 @@ dominance | ArrayCreation.cs:5:20:5:32 | array creation of type Int32[,] | ArrayCreation.cs:5:12:5:13 | exit M2 | | ArrayCreation.cs:5:28:5:28 | 0 | ArrayCreation.cs:5:31:5:31 | 1 | | ArrayCreation.cs:5:31:5:31 | 1 | ArrayCreation.cs:5:20:5:32 | array creation of type Int32[,] | -| ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | +| ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:19:7:36 | 2 | +| ArrayCreation.cs:7:19:7:36 | 2 | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | ArrayCreation.cs:7:31:7:31 | 0 | | ArrayCreation.cs:7:29:7:36 | { ..., ... } | ArrayCreation.cs:7:11:7:12 | exit M3 | | ArrayCreation.cs:7:31:7:31 | 0 | ArrayCreation.cs:7:34:7:34 | 1 | | ArrayCreation.cs:7:34:7:34 | 1 | ArrayCreation.cs:7:29:7:36 | { ..., ... } | -| ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | +| ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:20:9:52 | 2 | +| ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:20:9:52 | 2 | +| ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | ArrayCreation.cs:9:35:9:35 | 0 | | ArrayCreation.cs:9:31:9:52 | { ..., ... } | ArrayCreation.cs:9:12:9:13 | exit M4 | | ArrayCreation.cs:9:33:9:40 | { ..., ... } | ArrayCreation.cs:9:45:9:45 | 2 | @@ -308,6 +311,496 @@ dominance | ArrayCreation.cs:9:43:9:50 | { ..., ... } | ArrayCreation.cs:9:31:9:52 | { ..., ... } | | ArrayCreation.cs:9:45:9:45 | 2 | ArrayCreation.cs:9:48:9:48 | 3 | | ArrayCreation.cs:9:48:9:48 | 3 | ArrayCreation.cs:9:43:9:50 | { ..., ... } | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:8:5:12:5 | {...} | +| Assert.cs:8:5:12:5 | {...} | Assert.cs:9:9:9:33 | ... ...; | +| Assert.cs:9:9:9:33 | ... ...; | Assert.cs:9:20:9:32 | ... ? ... : ... | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:10:9:10:32 | ...; | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:24:9:27 | null | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:31:9:32 | "" | +| Assert.cs:9:20:9:32 | ... ? ... : ... | Assert.cs:9:20:9:20 | access to parameter b | +| Assert.cs:10:9:10:31 | [assertion success] call to method Assert | Assert.cs:11:9:11:36 | ...; | +| Assert.cs:10:9:10:32 | ...; | Assert.cs:10:22:10:22 | access to local variable s | +| Assert.cs:10:22:10:22 | access to local variable s | Assert.cs:10:27:10:30 | null | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | +| Assert.cs:10:27:10:30 | null | Assert.cs:10:22:10:30 | ... != ... | +| Assert.cs:11:9:11:36 | ...; | Assert.cs:11:27:11:27 | access to local variable s | +| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:11:27:11:34 | access to property Length | +| Assert.cs:11:27:11:34 | access to property Length | Assert.cs:11:9:11:35 | call to method WriteLine | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:15:5:19:5 | {...} | +| Assert.cs:15:5:19:5 | {...} | Assert.cs:16:9:16:33 | ... ...; | +| Assert.cs:16:9:16:33 | ... ...; | Assert.cs:16:20:16:32 | ... ? ... : ... | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:17:9:17:25 | ...; | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:24:16:27 | null | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:31:16:32 | "" | +| Assert.cs:16:20:16:32 | ... ? ... : ... | Assert.cs:16:20:16:20 | access to parameter b | +| Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | Assert.cs:18:9:18:36 | ...; | +| Assert.cs:17:9:17:25 | ...; | Assert.cs:17:23:17:23 | access to local variable s | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | +| Assert.cs:18:9:18:36 | ...; | Assert.cs:18:27:18:27 | access to local variable s | +| Assert.cs:18:27:18:27 | access to local variable s | Assert.cs:18:27:18:34 | access to property Length | +| Assert.cs:18:27:18:34 | access to property Length | Assert.cs:18:9:18:35 | call to method WriteLine | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:22:5:26:5 | {...} | +| Assert.cs:22:5:26:5 | {...} | Assert.cs:23:9:23:33 | ... ...; | +| Assert.cs:23:9:23:33 | ... ...; | Assert.cs:23:20:23:32 | ... ? ... : ... | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:24:9:24:28 | ...; | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:24:23:27 | null | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:31:23:32 | "" | +| Assert.cs:23:20:23:32 | ... ? ... : ... | Assert.cs:23:20:23:20 | access to parameter b | +| Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | Assert.cs:25:9:25:36 | ...; | +| Assert.cs:24:9:24:28 | ...; | Assert.cs:24:26:24:26 | access to local variable s | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | +| Assert.cs:25:9:25:36 | ...; | Assert.cs:25:27:25:27 | access to local variable s | +| Assert.cs:25:27:25:27 | access to local variable s | Assert.cs:25:27:25:34 | access to property Length | +| Assert.cs:25:27:25:34 | access to property Length | Assert.cs:25:9:25:35 | call to method WriteLine | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:29:5:33:5 | {...} | +| Assert.cs:29:5:33:5 | {...} | Assert.cs:30:9:30:33 | ... ...; | +| Assert.cs:30:9:30:33 | ... ...; | Assert.cs:30:20:30:32 | ... ? ... : ... | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:31:9:31:33 | ...; | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:24:30:27 | null | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:31:30:32 | "" | +| Assert.cs:30:20:30:32 | ... ? ... : ... | Assert.cs:30:20:30:20 | access to parameter b | +| Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | Assert.cs:32:9:32:36 | ...; | +| Assert.cs:31:9:31:33 | ...; | Assert.cs:31:23:31:23 | access to local variable s | +| Assert.cs:31:23:31:23 | access to local variable s | Assert.cs:31:28:31:31 | null | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | +| Assert.cs:31:28:31:31 | null | Assert.cs:31:23:31:31 | ... == ... | +| Assert.cs:32:9:32:36 | ...; | Assert.cs:32:27:32:27 | access to local variable s | +| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:32:27:32:34 | access to property Length | +| Assert.cs:32:27:32:34 | access to property Length | Assert.cs:32:9:32:35 | call to method WriteLine | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:36:5:40:5 | {...} | +| Assert.cs:36:5:40:5 | {...} | Assert.cs:37:9:37:33 | ... ...; | +| Assert.cs:37:9:37:33 | ... ...; | Assert.cs:37:20:37:32 | ... ? ... : ... | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:38:9:38:33 | ...; | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:24:37:27 | null | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:31:37:32 | "" | +| Assert.cs:37:20:37:32 | ... ? ... : ... | Assert.cs:37:20:37:20 | access to parameter b | +| Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | Assert.cs:39:9:39:36 | ...; | +| Assert.cs:38:9:38:33 | ...; | Assert.cs:38:23:38:23 | access to local variable s | +| Assert.cs:38:23:38:23 | access to local variable s | Assert.cs:38:28:38:31 | null | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | +| Assert.cs:38:28:38:31 | null | Assert.cs:38:23:38:31 | ... != ... | +| Assert.cs:39:9:39:36 | ...; | Assert.cs:39:27:39:27 | access to local variable s | +| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:39:27:39:34 | access to property Length | +| Assert.cs:39:27:39:34 | access to property Length | Assert.cs:39:9:39:35 | call to method WriteLine | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:43:5:47:5 | {...} | +| Assert.cs:43:5:47:5 | {...} | Assert.cs:44:9:44:33 | ... ...; | +| Assert.cs:44:9:44:33 | ... ...; | Assert.cs:44:20:44:32 | ... ? ... : ... | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:45:9:45:34 | ...; | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:24:44:27 | null | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:31:44:32 | "" | +| Assert.cs:44:20:44:32 | ... ? ... : ... | Assert.cs:44:20:44:20 | access to parameter b | +| Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | Assert.cs:46:9:46:36 | ...; | +| Assert.cs:45:9:45:34 | ...; | Assert.cs:45:24:45:24 | access to local variable s | +| Assert.cs:45:24:45:24 | access to local variable s | Assert.cs:45:29:45:32 | null | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | +| Assert.cs:45:29:45:32 | null | Assert.cs:45:24:45:32 | ... != ... | +| Assert.cs:46:9:46:36 | ...; | Assert.cs:46:27:46:27 | access to local variable s | +| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:46:27:46:34 | access to property Length | +| Assert.cs:46:27:46:34 | access to property Length | Assert.cs:46:9:46:35 | call to method WriteLine | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:50:5:54:5 | {...} | +| Assert.cs:50:5:54:5 | {...} | Assert.cs:51:9:51:33 | ... ...; | +| Assert.cs:51:9:51:33 | ... ...; | Assert.cs:51:20:51:32 | ... ? ... : ... | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:52:9:52:34 | ...; | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:24:51:27 | null | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:31:51:32 | "" | +| Assert.cs:51:20:51:32 | ... ? ... : ... | Assert.cs:51:20:51:20 | access to parameter b | +| Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | Assert.cs:53:9:53:36 | ...; | +| Assert.cs:52:9:52:34 | ...; | Assert.cs:52:24:52:24 | access to local variable s | +| Assert.cs:52:24:52:24 | access to local variable s | Assert.cs:52:29:52:32 | null | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | +| Assert.cs:52:29:52:32 | null | Assert.cs:52:24:52:32 | ... == ... | +| Assert.cs:53:9:53:36 | ...; | Assert.cs:53:27:53:27 | access to local variable s | +| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:53:27:53:34 | access to property Length | +| Assert.cs:53:27:53:34 | access to property Length | Assert.cs:53:9:53:35 | call to method WriteLine | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:57:5:61:5 | {...} | +| Assert.cs:57:5:61:5 | {...} | Assert.cs:58:9:58:33 | ... ...; | +| Assert.cs:58:9:58:33 | ... ...; | Assert.cs:58:20:58:32 | ... ? ... : ... | +| Assert.cs:58:16:58:32 | [b (line 56): false] String s = ... | Assert.cs:59:9:59:38 | [b (line 56): false] ...; | +| Assert.cs:58:16:58:32 | [b (line 56): true] String s = ... | Assert.cs:59:9:59:38 | [b (line 56): true] ...; | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:24:58:27 | [b (line 56): true] null | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:31:58:32 | [b (line 56): false] "" | +| Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:58:16:58:32 | [b (line 56): true] String s = ... | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:58:16:58:32 | [b (line 56): false] String s = ... | +| Assert.cs:59:9:59:37 | [assertion success] call to method IsTrue | Assert.cs:60:9:60:36 | ...; | +| Assert.cs:59:9:59:38 | [b (line 56): false] ...; | Assert.cs:59:23:59:36 | [b (line 56): false] ... && ... | +| Assert.cs:59:9:59:38 | [b (line 56): true] ...; | Assert.cs:59:23:59:36 | [b (line 56): true] ... && ... | +| Assert.cs:59:23:59:23 | [b (line 56): false] access to local variable s | Assert.cs:59:28:59:31 | [b (line 56): false] null | +| Assert.cs:59:23:59:23 | [b (line 56): true] access to local variable s | Assert.cs:59:28:59:31 | [b (line 56): true] null | +| Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | +| Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | +| Assert.cs:59:23:59:36 | [b (line 56): false] ... && ... | Assert.cs:59:23:59:23 | [b (line 56): false] access to local variable s | +| Assert.cs:59:23:59:36 | [b (line 56): true] ... && ... | Assert.cs:59:23:59:23 | [b (line 56): true] access to local variable s | +| Assert.cs:59:28:59:31 | [b (line 56): false] null | Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | +| Assert.cs:59:28:59:31 | [b (line 56): true] null | Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:59:9:59:37 | [assertion success] call to method IsTrue | +| Assert.cs:60:9:60:36 | ...; | Assert.cs:60:27:60:27 | access to local variable s | +| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:60:27:60:34 | access to property Length | +| Assert.cs:60:27:60:34 | access to property Length | Assert.cs:60:9:60:35 | call to method WriteLine | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:64:5:68:5 | {...} | +| Assert.cs:64:5:68:5 | {...} | Assert.cs:65:9:65:33 | ... ...; | +| Assert.cs:65:9:65:33 | ... ...; | Assert.cs:65:20:65:32 | ... ? ... : ... | +| Assert.cs:65:16:65:32 | [b (line 63): false] String s = ... | Assert.cs:66:9:66:39 | [b (line 63): false] ...; | +| Assert.cs:65:16:65:32 | [b (line 63): true] String s = ... | Assert.cs:66:9:66:39 | [b (line 63): true] ...; | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:24:65:27 | [b (line 63): true] null | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:31:65:32 | [b (line 63): false] "" | +| Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:65:16:65:32 | [b (line 63): true] String s = ... | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:65:16:65:32 | [b (line 63): false] String s = ... | +| Assert.cs:66:9:66:38 | [assertion success] call to method IsFalse | Assert.cs:67:9:67:36 | ...; | +| Assert.cs:66:9:66:39 | [b (line 63): false] ...; | Assert.cs:66:24:66:37 | [b (line 63): false] ... \|\| ... | +| Assert.cs:66:9:66:39 | [b (line 63): true] ...; | Assert.cs:66:24:66:37 | [b (line 63): true] ... \|\| ... | +| Assert.cs:66:24:66:24 | [b (line 63): false] access to local variable s | Assert.cs:66:29:66:32 | [b (line 63): false] null | +| Assert.cs:66:24:66:24 | [b (line 63): true] access to local variable s | Assert.cs:66:29:66:32 | [b (line 63): true] null | +| Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | +| Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | +| Assert.cs:66:24:66:37 | [b (line 63): false] ... \|\| ... | Assert.cs:66:24:66:24 | [b (line 63): false] access to local variable s | +| Assert.cs:66:24:66:37 | [b (line 63): true] ... \|\| ... | Assert.cs:66:24:66:24 | [b (line 63): true] access to local variable s | +| Assert.cs:66:29:66:32 | [b (line 63): false] null | Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | +| Assert.cs:66:29:66:32 | [b (line 63): true] null | Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:66:9:66:38 | [assertion success] call to method IsFalse | +| Assert.cs:67:9:67:36 | ...; | Assert.cs:67:27:67:27 | access to local variable s | +| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:67:27:67:34 | access to property Length | +| Assert.cs:67:27:67:34 | access to property Length | Assert.cs:67:9:67:35 | call to method WriteLine | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:71:5:75:5 | {...} | +| Assert.cs:71:5:75:5 | {...} | Assert.cs:72:9:72:33 | ... ...; | +| Assert.cs:72:9:72:33 | ... ...; | Assert.cs:72:20:72:32 | ... ? ... : ... | +| Assert.cs:72:16:72:32 | [b (line 70): false] String s = ... | Assert.cs:73:9:73:38 | [b (line 70): false] ...; | +| Assert.cs:72:16:72:32 | [b (line 70): true] String s = ... | Assert.cs:73:9:73:38 | [b (line 70): true] ...; | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:24:72:27 | [b (line 70): true] null | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:31:72:32 | [b (line 70): false] "" | +| Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:72:16:72:32 | [b (line 70): true] String s = ... | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:72:16:72:32 | [b (line 70): false] String s = ... | +| Assert.cs:73:9:73:37 | [assertion success] call to method IsTrue | Assert.cs:74:9:74:36 | ...; | +| Assert.cs:73:9:73:38 | [b (line 70): false] ...; | Assert.cs:73:23:73:36 | [b (line 70): false] ... && ... | +| Assert.cs:73:9:73:38 | [b (line 70): true] ...; | Assert.cs:73:23:73:36 | [b (line 70): true] ... && ... | +| Assert.cs:73:23:73:23 | [b (line 70): false] access to local variable s | Assert.cs:73:28:73:31 | [b (line 70): false] null | +| Assert.cs:73:23:73:23 | [b (line 70): true] access to local variable s | Assert.cs:73:28:73:31 | [b (line 70): true] null | +| Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | +| Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | +| Assert.cs:73:23:73:36 | [b (line 70): false] ... && ... | Assert.cs:73:23:73:23 | [b (line 70): false] access to local variable s | +| Assert.cs:73:23:73:36 | [b (line 70): true] ... && ... | Assert.cs:73:23:73:23 | [b (line 70): true] access to local variable s | +| Assert.cs:73:28:73:31 | [b (line 70): false] null | Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | +| Assert.cs:73:28:73:31 | [b (line 70): true] null | Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:73:9:73:37 | [assertion success] call to method IsTrue | +| Assert.cs:74:9:74:36 | ...; | Assert.cs:74:27:74:27 | access to local variable s | +| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:74:27:74:34 | access to property Length | +| Assert.cs:74:27:74:34 | access to property Length | Assert.cs:74:9:74:35 | call to method WriteLine | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:78:5:82:5 | {...} | +| Assert.cs:78:5:82:5 | {...} | Assert.cs:79:9:79:33 | ... ...; | +| Assert.cs:79:9:79:33 | ... ...; | Assert.cs:79:20:79:32 | ... ? ... : ... | +| Assert.cs:79:16:79:32 | [b (line 77): false] String s = ... | Assert.cs:80:9:80:39 | [b (line 77): false] ...; | +| Assert.cs:79:16:79:32 | [b (line 77): true] String s = ... | Assert.cs:80:9:80:39 | [b (line 77): true] ...; | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:24:79:27 | [b (line 77): true] null | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:31:79:32 | [b (line 77): false] "" | +| Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:79:16:79:32 | [b (line 77): true] String s = ... | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:79:16:79:32 | [b (line 77): false] String s = ... | +| Assert.cs:80:9:80:38 | [assertion success] call to method IsFalse | Assert.cs:81:9:81:36 | ...; | +| Assert.cs:80:9:80:39 | [b (line 77): false] ...; | Assert.cs:80:24:80:37 | [b (line 77): false] ... \|\| ... | +| Assert.cs:80:9:80:39 | [b (line 77): true] ...; | Assert.cs:80:24:80:37 | [b (line 77): true] ... \|\| ... | +| Assert.cs:80:24:80:24 | [b (line 77): false] access to local variable s | Assert.cs:80:29:80:32 | [b (line 77): false] null | +| Assert.cs:80:24:80:24 | [b (line 77): true] access to local variable s | Assert.cs:80:29:80:32 | [b (line 77): true] null | +| Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | +| Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | +| Assert.cs:80:24:80:37 | [b (line 77): false] ... \|\| ... | Assert.cs:80:24:80:24 | [b (line 77): false] access to local variable s | +| Assert.cs:80:24:80:37 | [b (line 77): true] ... \|\| ... | Assert.cs:80:24:80:24 | [b (line 77): true] access to local variable s | +| Assert.cs:80:29:80:32 | [b (line 77): false] null | Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | +| Assert.cs:80:29:80:32 | [b (line 77): true] null | Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:80:9:80:38 | [assertion success] call to method IsFalse | +| Assert.cs:81:9:81:36 | ...; | Assert.cs:81:27:81:27 | access to local variable s | +| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:81:27:81:34 | access to property Length | +| Assert.cs:81:27:81:34 | access to property Length | Assert.cs:81:9:81:35 | call to method WriteLine | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:85:5:129:5 | {...} | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:86:9:86:33 | ... ...; | +| Assert.cs:86:9:86:33 | ... ...; | Assert.cs:86:20:86:32 | ... ? ... : ... | +| Assert.cs:86:16:86:32 | [b (line 84): false] String s = ... | Assert.cs:87:9:87:32 | [b (line 84): false] ...; | +| Assert.cs:86:16:86:32 | [b (line 84): true] String s = ... | Assert.cs:87:9:87:32 | [b (line 84): true] ...; | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:24:86:27 | [b (line 84): true] null | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:31:86:32 | [b (line 84): false] "" | +| Assert.cs:86:20:86:32 | ... ? ... : ... | Assert.cs:86:20:86:20 | access to parameter b | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:86:16:86:32 | [b (line 84): true] String s = ... | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:86:16:86:32 | [b (line 84): false] String s = ... | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:88:9:88:36 | [b (line 84): false] ...; | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:88:9:88:36 | [b (line 84): true] ...; | +| Assert.cs:87:9:87:32 | [b (line 84): false] ...; | Assert.cs:87:22:87:22 | [b (line 84): false] access to local variable s | +| Assert.cs:87:9:87:32 | [b (line 84): true] ...; | Assert.cs:87:22:87:22 | [b (line 84): true] access to local variable s | +| Assert.cs:87:22:87:22 | [b (line 84): false] access to local variable s | Assert.cs:87:27:87:30 | [b (line 84): false] null | +| Assert.cs:87:22:87:22 | [b (line 84): true] access to local variable s | Assert.cs:87:27:87:30 | [b (line 84): true] null | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | +| Assert.cs:87:27:87:30 | [b (line 84): false] null | Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | +| Assert.cs:87:27:87:30 | [b (line 84): true] null | Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | +| Assert.cs:88:9:88:35 | [b (line 84): false] call to method WriteLine | Assert.cs:90:9:90:26 | [b (line 84): false] ...; | +| Assert.cs:88:9:88:35 | [b (line 84): true] call to method WriteLine | Assert.cs:90:9:90:26 | [b (line 84): true] ...; | +| Assert.cs:88:9:88:36 | [b (line 84): false] ...; | Assert.cs:88:27:88:27 | [b (line 84): false] access to local variable s | +| Assert.cs:88:9:88:36 | [b (line 84): true] ...; | Assert.cs:88:27:88:27 | [b (line 84): true] access to local variable s | +| Assert.cs:88:27:88:27 | [b (line 84): false] access to local variable s | Assert.cs:88:27:88:34 | [b (line 84): false] access to property Length | +| Assert.cs:88:27:88:27 | [b (line 84): true] access to local variable s | Assert.cs:88:27:88:34 | [b (line 84): true] access to property Length | +| Assert.cs:88:27:88:34 | [b (line 84): false] access to property Length | Assert.cs:88:9:88:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:88:27:88:34 | [b (line 84): true] access to property Length | Assert.cs:88:9:88:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:90:9:90:25 | [b (line 84): false] ... = ... | Assert.cs:91:9:91:25 | [b (line 84): false] ...; | +| Assert.cs:90:9:90:25 | [b (line 84): true] ... = ... | Assert.cs:91:9:91:25 | [b (line 84): true] ...; | +| Assert.cs:90:9:90:26 | [b (line 84): false] ...; | Assert.cs:90:13:90:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:90:9:90:26 | [b (line 84): true] ...; | Assert.cs:90:13:90:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | Assert.cs:90:24:90:25 | [b (line 84): false] "" | +| Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | Assert.cs:90:17:90:20 | [b (line 84): true] null | +| Assert.cs:90:13:90:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | +| Assert.cs:90:13:90:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | +| Assert.cs:90:17:90:20 | [b (line 84): true] null | Assert.cs:90:9:90:25 | [b (line 84): true] ... = ... | +| Assert.cs:90:24:90:25 | [b (line 84): false] "" | Assert.cs:90:9:90:25 | [b (line 84): false] ... = ... | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:92:9:92:36 | [b (line 84): false] ...; | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:92:9:92:36 | [b (line 84): true] ...; | +| Assert.cs:91:9:91:25 | [b (line 84): false] ...; | Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | +| Assert.cs:91:9:91:25 | [b (line 84): true] ...; | Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | +| Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | +| Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | +| Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | +| Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | +| Assert.cs:92:9:92:35 | [b (line 84): false] call to method WriteLine | Assert.cs:94:9:94:26 | [b (line 84): false] ...; | +| Assert.cs:92:9:92:35 | [b (line 84): true] call to method WriteLine | Assert.cs:94:9:94:26 | [b (line 84): true] ...; | +| Assert.cs:92:9:92:36 | [b (line 84): false] ...; | Assert.cs:92:27:92:27 | [b (line 84): false] access to local variable s | +| Assert.cs:92:9:92:36 | [b (line 84): true] ...; | Assert.cs:92:27:92:27 | [b (line 84): true] access to local variable s | +| Assert.cs:92:27:92:27 | [b (line 84): false] access to local variable s | Assert.cs:92:27:92:34 | [b (line 84): false] access to property Length | +| Assert.cs:92:27:92:27 | [b (line 84): true] access to local variable s | Assert.cs:92:27:92:34 | [b (line 84): true] access to property Length | +| Assert.cs:92:27:92:34 | [b (line 84): false] access to property Length | Assert.cs:92:9:92:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:92:27:92:34 | [b (line 84): true] access to property Length | Assert.cs:92:9:92:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:94:9:94:25 | [b (line 84): false] ... = ... | Assert.cs:95:9:95:28 | [b (line 84): false] ...; | +| Assert.cs:94:9:94:25 | [b (line 84): true] ... = ... | Assert.cs:95:9:95:28 | [b (line 84): true] ...; | +| Assert.cs:94:9:94:26 | [b (line 84): false] ...; | Assert.cs:94:13:94:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:94:9:94:26 | [b (line 84): true] ...; | Assert.cs:94:13:94:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | Assert.cs:94:24:94:25 | [b (line 84): false] "" | +| Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | Assert.cs:94:17:94:20 | [b (line 84): true] null | +| Assert.cs:94:13:94:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | +| Assert.cs:94:13:94:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | +| Assert.cs:94:17:94:20 | [b (line 84): true] null | Assert.cs:94:9:94:25 | [b (line 84): true] ... = ... | +| Assert.cs:94:24:94:25 | [b (line 84): false] "" | Assert.cs:94:9:94:25 | [b (line 84): false] ... = ... | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:96:9:96:36 | [b (line 84): false] ...; | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:96:9:96:36 | [b (line 84): true] ...; | +| Assert.cs:95:9:95:28 | [b (line 84): false] ...; | Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | +| Assert.cs:95:9:95:28 | [b (line 84): true] ...; | Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | +| Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:96:9:96:35 | [b (line 84): false] call to method WriteLine | Assert.cs:98:9:98:26 | [b (line 84): false] ...; | +| Assert.cs:96:9:96:35 | [b (line 84): true] call to method WriteLine | Assert.cs:98:9:98:26 | [b (line 84): true] ...; | +| Assert.cs:96:9:96:36 | [b (line 84): false] ...; | Assert.cs:96:27:96:27 | [b (line 84): false] access to local variable s | +| Assert.cs:96:9:96:36 | [b (line 84): true] ...; | Assert.cs:96:27:96:27 | [b (line 84): true] access to local variable s | +| Assert.cs:96:27:96:27 | [b (line 84): false] access to local variable s | Assert.cs:96:27:96:34 | [b (line 84): false] access to property Length | +| Assert.cs:96:27:96:27 | [b (line 84): true] access to local variable s | Assert.cs:96:27:96:34 | [b (line 84): true] access to property Length | +| Assert.cs:96:27:96:34 | [b (line 84): false] access to property Length | Assert.cs:96:9:96:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:96:27:96:34 | [b (line 84): true] access to property Length | Assert.cs:96:9:96:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:98:9:98:25 | [b (line 84): false] ... = ... | Assert.cs:99:9:99:33 | [b (line 84): false] ...; | +| Assert.cs:98:9:98:25 | [b (line 84): true] ... = ... | Assert.cs:99:9:99:33 | [b (line 84): true] ...; | +| Assert.cs:98:9:98:26 | [b (line 84): false] ...; | Assert.cs:98:13:98:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:98:9:98:26 | [b (line 84): true] ...; | Assert.cs:98:13:98:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | Assert.cs:98:24:98:25 | [b (line 84): false] "" | +| Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | Assert.cs:98:17:98:20 | [b (line 84): true] null | +| Assert.cs:98:13:98:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | +| Assert.cs:98:13:98:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | +| Assert.cs:98:17:98:20 | [b (line 84): true] null | Assert.cs:98:9:98:25 | [b (line 84): true] ... = ... | +| Assert.cs:98:24:98:25 | [b (line 84): false] "" | Assert.cs:98:9:98:25 | [b (line 84): false] ... = ... | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:100:9:100:36 | [b (line 84): false] ...; | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:100:9:100:36 | [b (line 84): true] ...; | +| Assert.cs:99:9:99:33 | [b (line 84): false] ...; | Assert.cs:99:23:99:23 | [b (line 84): false] access to local variable s | +| Assert.cs:99:9:99:33 | [b (line 84): true] ...; | Assert.cs:99:23:99:23 | [b (line 84): true] access to local variable s | +| Assert.cs:99:23:99:23 | [b (line 84): false] access to local variable s | Assert.cs:99:28:99:31 | [b (line 84): false] null | +| Assert.cs:99:23:99:23 | [b (line 84): true] access to local variable s | Assert.cs:99:28:99:31 | [b (line 84): true] null | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:99:28:99:31 | [b (line 84): false] null | Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | +| Assert.cs:99:28:99:31 | [b (line 84): true] null | Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | +| Assert.cs:100:9:100:35 | [b (line 84): false] call to method WriteLine | Assert.cs:102:9:102:26 | [b (line 84): false] ...; | +| Assert.cs:100:9:100:35 | [b (line 84): true] call to method WriteLine | Assert.cs:102:9:102:26 | [b (line 84): true] ...; | +| Assert.cs:100:9:100:36 | [b (line 84): false] ...; | Assert.cs:100:27:100:27 | [b (line 84): false] access to local variable s | +| Assert.cs:100:9:100:36 | [b (line 84): true] ...; | Assert.cs:100:27:100:27 | [b (line 84): true] access to local variable s | +| Assert.cs:100:27:100:27 | [b (line 84): false] access to local variable s | Assert.cs:100:27:100:34 | [b (line 84): false] access to property Length | +| Assert.cs:100:27:100:27 | [b (line 84): true] access to local variable s | Assert.cs:100:27:100:34 | [b (line 84): true] access to property Length | +| Assert.cs:100:27:100:34 | [b (line 84): false] access to property Length | Assert.cs:100:9:100:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:100:27:100:34 | [b (line 84): true] access to property Length | Assert.cs:100:9:100:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:102:9:102:25 | [b (line 84): false] ... = ... | Assert.cs:103:9:103:33 | [b (line 84): false] ...; | +| Assert.cs:102:9:102:25 | [b (line 84): true] ... = ... | Assert.cs:103:9:103:33 | [b (line 84): true] ...; | +| Assert.cs:102:9:102:26 | [b (line 84): false] ...; | Assert.cs:102:13:102:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:102:9:102:26 | [b (line 84): true] ...; | Assert.cs:102:13:102:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | Assert.cs:102:24:102:25 | [b (line 84): false] "" | +| Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | Assert.cs:102:17:102:20 | [b (line 84): true] null | +| Assert.cs:102:13:102:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | +| Assert.cs:102:13:102:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | +| Assert.cs:102:17:102:20 | [b (line 84): true] null | Assert.cs:102:9:102:25 | [b (line 84): true] ... = ... | +| Assert.cs:102:24:102:25 | [b (line 84): false] "" | Assert.cs:102:9:102:25 | [b (line 84): false] ... = ... | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:104:9:104:36 | [b (line 84): false] ...; | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:104:9:104:36 | [b (line 84): true] ...; | +| Assert.cs:103:9:103:33 | [b (line 84): false] ...; | Assert.cs:103:23:103:23 | [b (line 84): false] access to local variable s | +| Assert.cs:103:9:103:33 | [b (line 84): true] ...; | Assert.cs:103:23:103:23 | [b (line 84): true] access to local variable s | +| Assert.cs:103:23:103:23 | [b (line 84): false] access to local variable s | Assert.cs:103:28:103:31 | [b (line 84): false] null | +| Assert.cs:103:23:103:23 | [b (line 84): true] access to local variable s | Assert.cs:103:28:103:31 | [b (line 84): true] null | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:103:28:103:31 | [b (line 84): false] null | Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | +| Assert.cs:103:28:103:31 | [b (line 84): true] null | Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | +| Assert.cs:104:9:104:35 | [b (line 84): false] call to method WriteLine | Assert.cs:106:9:106:26 | [b (line 84): false] ...; | +| Assert.cs:104:9:104:35 | [b (line 84): true] call to method WriteLine | Assert.cs:106:9:106:26 | [b (line 84): true] ...; | +| Assert.cs:104:9:104:36 | [b (line 84): false] ...; | Assert.cs:104:27:104:27 | [b (line 84): false] access to local variable s | +| Assert.cs:104:9:104:36 | [b (line 84): true] ...; | Assert.cs:104:27:104:27 | [b (line 84): true] access to local variable s | +| Assert.cs:104:27:104:27 | [b (line 84): false] access to local variable s | Assert.cs:104:27:104:34 | [b (line 84): false] access to property Length | +| Assert.cs:104:27:104:27 | [b (line 84): true] access to local variable s | Assert.cs:104:27:104:34 | [b (line 84): true] access to property Length | +| Assert.cs:104:27:104:34 | [b (line 84): false] access to property Length | Assert.cs:104:9:104:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:104:27:104:34 | [b (line 84): true] access to property Length | Assert.cs:104:9:104:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:106:9:106:25 | [b (line 84): false] ... = ... | Assert.cs:107:9:107:34 | [b (line 84): false] ...; | +| Assert.cs:106:9:106:25 | [b (line 84): true] ... = ... | Assert.cs:107:9:107:34 | [b (line 84): true] ...; | +| Assert.cs:106:9:106:26 | [b (line 84): false] ...; | Assert.cs:106:13:106:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:106:9:106:26 | [b (line 84): true] ...; | Assert.cs:106:13:106:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | Assert.cs:106:24:106:25 | [b (line 84): false] "" | +| Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | Assert.cs:106:17:106:20 | [b (line 84): true] null | +| Assert.cs:106:13:106:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | +| Assert.cs:106:13:106:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | +| Assert.cs:106:17:106:20 | [b (line 84): true] null | Assert.cs:106:9:106:25 | [b (line 84): true] ... = ... | +| Assert.cs:106:24:106:25 | [b (line 84): false] "" | Assert.cs:106:9:106:25 | [b (line 84): false] ... = ... | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:108:9:108:36 | [b (line 84): false] ...; | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:108:9:108:36 | [b (line 84): true] ...; | +| Assert.cs:107:9:107:34 | [b (line 84): false] ...; | Assert.cs:107:24:107:24 | [b (line 84): false] access to local variable s | +| Assert.cs:107:9:107:34 | [b (line 84): true] ...; | Assert.cs:107:24:107:24 | [b (line 84): true] access to local variable s | +| Assert.cs:107:24:107:24 | [b (line 84): false] access to local variable s | Assert.cs:107:29:107:32 | [b (line 84): false] null | +| Assert.cs:107:24:107:24 | [b (line 84): true] access to local variable s | Assert.cs:107:29:107:32 | [b (line 84): true] null | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:107:29:107:32 | [b (line 84): false] null | Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | +| Assert.cs:107:29:107:32 | [b (line 84): true] null | Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | +| Assert.cs:108:9:108:35 | [b (line 84): false] call to method WriteLine | Assert.cs:110:9:110:26 | [b (line 84): false] ...; | +| Assert.cs:108:9:108:35 | [b (line 84): true] call to method WriteLine | Assert.cs:110:9:110:26 | [b (line 84): true] ...; | +| Assert.cs:108:9:108:36 | [b (line 84): false] ...; | Assert.cs:108:27:108:27 | [b (line 84): false] access to local variable s | +| Assert.cs:108:9:108:36 | [b (line 84): true] ...; | Assert.cs:108:27:108:27 | [b (line 84): true] access to local variable s | +| Assert.cs:108:27:108:27 | [b (line 84): false] access to local variable s | Assert.cs:108:27:108:34 | [b (line 84): false] access to property Length | +| Assert.cs:108:27:108:27 | [b (line 84): true] access to local variable s | Assert.cs:108:27:108:34 | [b (line 84): true] access to property Length | +| Assert.cs:108:27:108:34 | [b (line 84): false] access to property Length | Assert.cs:108:9:108:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:108:27:108:34 | [b (line 84): true] access to property Length | Assert.cs:108:9:108:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:110:9:110:25 | [b (line 84): false] ... = ... | Assert.cs:111:9:111:34 | [b (line 84): false] ...; | +| Assert.cs:110:9:110:25 | [b (line 84): true] ... = ... | Assert.cs:111:9:111:34 | [b (line 84): true] ...; | +| Assert.cs:110:9:110:26 | [b (line 84): false] ...; | Assert.cs:110:13:110:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:110:9:110:26 | [b (line 84): true] ...; | Assert.cs:110:13:110:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | Assert.cs:110:24:110:25 | [b (line 84): false] "" | +| Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | Assert.cs:110:17:110:20 | [b (line 84): true] null | +| Assert.cs:110:13:110:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | +| Assert.cs:110:13:110:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | +| Assert.cs:110:17:110:20 | [b (line 84): true] null | Assert.cs:110:9:110:25 | [b (line 84): true] ... = ... | +| Assert.cs:110:24:110:25 | [b (line 84): false] "" | Assert.cs:110:9:110:25 | [b (line 84): false] ... = ... | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:112:9:112:36 | [b (line 84): false] ...; | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:112:9:112:36 | [b (line 84): true] ...; | +| Assert.cs:111:9:111:34 | [b (line 84): false] ...; | Assert.cs:111:24:111:24 | [b (line 84): false] access to local variable s | +| Assert.cs:111:9:111:34 | [b (line 84): true] ...; | Assert.cs:111:24:111:24 | [b (line 84): true] access to local variable s | +| Assert.cs:111:24:111:24 | [b (line 84): false] access to local variable s | Assert.cs:111:29:111:32 | [b (line 84): false] null | +| Assert.cs:111:24:111:24 | [b (line 84): true] access to local variable s | Assert.cs:111:29:111:32 | [b (line 84): true] null | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:111:29:111:32 | [b (line 84): false] null | Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | +| Assert.cs:111:29:111:32 | [b (line 84): true] null | Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | +| Assert.cs:112:9:112:35 | [b (line 84): false] call to method WriteLine | Assert.cs:114:9:114:26 | [b (line 84): false] ...; | +| Assert.cs:112:9:112:35 | [b (line 84): true] call to method WriteLine | Assert.cs:114:9:114:26 | [b (line 84): true] ...; | +| Assert.cs:112:9:112:36 | [b (line 84): false] ...; | Assert.cs:112:27:112:27 | [b (line 84): false] access to local variable s | +| Assert.cs:112:9:112:36 | [b (line 84): true] ...; | Assert.cs:112:27:112:27 | [b (line 84): true] access to local variable s | +| Assert.cs:112:27:112:27 | [b (line 84): false] access to local variable s | Assert.cs:112:27:112:34 | [b (line 84): false] access to property Length | +| Assert.cs:112:27:112:27 | [b (line 84): true] access to local variable s | Assert.cs:112:27:112:34 | [b (line 84): true] access to property Length | +| Assert.cs:112:27:112:34 | [b (line 84): false] access to property Length | Assert.cs:112:9:112:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:112:27:112:34 | [b (line 84): true] access to property Length | Assert.cs:112:9:112:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:114:9:114:25 | [b (line 84): false] ... = ... | Assert.cs:115:9:115:38 | [b (line 84): false] ...; | +| Assert.cs:114:9:114:25 | [b (line 84): true] ... = ... | Assert.cs:115:9:115:38 | [b (line 84): true] ...; | +| Assert.cs:114:9:114:26 | [b (line 84): false] ...; | Assert.cs:114:13:114:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:114:9:114:26 | [b (line 84): true] ...; | Assert.cs:114:13:114:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | Assert.cs:114:24:114:25 | [b (line 84): false] "" | +| Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | Assert.cs:114:17:114:20 | [b (line 84): true] null | +| Assert.cs:114:13:114:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | +| Assert.cs:114:13:114:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | +| Assert.cs:114:17:114:20 | [b (line 84): true] null | Assert.cs:114:9:114:25 | [b (line 84): true] ... = ... | +| Assert.cs:114:24:114:25 | [b (line 84): false] "" | Assert.cs:114:9:114:25 | [b (line 84): false] ... = ... | +| Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:116:9:116:36 | [b (line 84): true] ...; | +| Assert.cs:115:9:115:38 | [b (line 84): false] ...; | Assert.cs:115:23:115:36 | [b (line 84): false] ... && ... | +| Assert.cs:115:9:115:38 | [b (line 84): true] ...; | Assert.cs:115:23:115:36 | [b (line 84): true] ... && ... | +| Assert.cs:115:23:115:23 | [b (line 84): false] access to local variable s | Assert.cs:115:28:115:31 | [b (line 84): false] null | +| Assert.cs:115:23:115:23 | [b (line 84): true] access to local variable s | Assert.cs:115:28:115:31 | [b (line 84): true] null | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:115:23:115:36 | [b (line 84): false] ... && ... | Assert.cs:115:23:115:23 | [b (line 84): false] access to local variable s | +| Assert.cs:115:23:115:36 | [b (line 84): true] ... && ... | Assert.cs:115:23:115:23 | [b (line 84): true] access to local variable s | +| Assert.cs:115:28:115:31 | [b (line 84): false] null | Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | +| Assert.cs:115:28:115:31 | [b (line 84): true] null | Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:116:9:116:35 | [b (line 84): true] call to method WriteLine | Assert.cs:118:9:118:26 | [b (line 84): true] ...; | +| Assert.cs:116:9:116:36 | [b (line 84): true] ...; | Assert.cs:116:27:116:27 | [b (line 84): true] access to local variable s | +| Assert.cs:116:27:116:27 | [b (line 84): true] access to local variable s | Assert.cs:116:27:116:34 | [b (line 84): true] access to property Length | +| Assert.cs:116:27:116:34 | [b (line 84): true] access to property Length | Assert.cs:116:9:116:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:118:9:118:25 | [b (line 84): true] ... = ... | Assert.cs:119:9:119:40 | [b (line 84): true] ...; | +| Assert.cs:118:9:118:26 | [b (line 84): true] ...; | Assert.cs:118:13:118:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | Assert.cs:118:17:118:20 | [b (line 84): true] null | +| Assert.cs:118:13:118:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | +| Assert.cs:118:17:118:20 | [b (line 84): true] null | Assert.cs:118:9:118:25 | [b (line 84): true] ... = ... | +| Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:120:9:120:36 | [b (line 84): true] ...; | +| Assert.cs:119:9:119:40 | [b (line 84): true] ...; | Assert.cs:119:24:119:38 | [b (line 84): true] ... \|\| ... | +| Assert.cs:119:24:119:24 | [b (line 84): true] access to local variable s | Assert.cs:119:29:119:32 | [b (line 84): true] null | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:119:24:119:38 | [b (line 84): true] ... \|\| ... | Assert.cs:119:24:119:24 | [b (line 84): true] access to local variable s | +| Assert.cs:119:29:119:32 | [b (line 84): true] null | Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | +| Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:120:9:120:35 | [b (line 84): true] call to method WriteLine | Assert.cs:122:9:122:26 | [b (line 84): true] ...; | +| Assert.cs:120:9:120:36 | [b (line 84): true] ...; | Assert.cs:120:27:120:27 | [b (line 84): true] access to local variable s | +| Assert.cs:120:27:120:27 | [b (line 84): true] access to local variable s | Assert.cs:120:27:120:34 | [b (line 84): true] access to property Length | +| Assert.cs:120:27:120:34 | [b (line 84): true] access to property Length | Assert.cs:120:9:120:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:122:9:122:25 | [b (line 84): true] ... = ... | Assert.cs:123:9:123:38 | [b (line 84): true] ...; | +| Assert.cs:122:9:122:26 | [b (line 84): true] ...; | Assert.cs:122:13:122:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | Assert.cs:122:17:122:20 | [b (line 84): true] null | +| Assert.cs:122:13:122:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | +| Assert.cs:122:17:122:20 | [b (line 84): true] null | Assert.cs:122:9:122:25 | [b (line 84): true] ... = ... | +| Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:124:9:124:36 | [b (line 84): true] ...; | +| Assert.cs:123:9:123:38 | [b (line 84): true] ...; | Assert.cs:123:23:123:36 | [b (line 84): true] ... && ... | +| Assert.cs:123:23:123:23 | [b (line 84): true] access to local variable s | Assert.cs:123:28:123:31 | [b (line 84): true] null | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:123:23:123:36 | [b (line 84): true] ... && ... | Assert.cs:123:23:123:23 | [b (line 84): true] access to local variable s | +| Assert.cs:123:28:123:31 | [b (line 84): true] null | Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:124:9:124:35 | [b (line 84): true] call to method WriteLine | Assert.cs:126:9:126:26 | [b (line 84): true] ...; | +| Assert.cs:124:9:124:36 | [b (line 84): true] ...; | Assert.cs:124:27:124:27 | [b (line 84): true] access to local variable s | +| Assert.cs:124:27:124:27 | [b (line 84): true] access to local variable s | Assert.cs:124:27:124:34 | [b (line 84): true] access to property Length | +| Assert.cs:124:27:124:34 | [b (line 84): true] access to property Length | Assert.cs:124:9:124:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:126:9:126:25 | [b (line 84): true] ... = ... | Assert.cs:127:9:127:40 | [b (line 84): true] ...; | +| Assert.cs:126:9:126:26 | [b (line 84): true] ...; | Assert.cs:126:13:126:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | Assert.cs:126:17:126:20 | [b (line 84): true] null | +| Assert.cs:126:13:126:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | +| Assert.cs:126:17:126:20 | [b (line 84): true] null | Assert.cs:126:9:126:25 | [b (line 84): true] ... = ... | +| Assert.cs:127:9:127:39 | [assertion success] call to method IsFalse | Assert.cs:128:9:128:36 | ...; | +| Assert.cs:127:9:127:40 | [b (line 84): true] ...; | Assert.cs:127:24:127:38 | [b (line 84): true] ... \|\| ... | +| Assert.cs:127:24:127:24 | [b (line 84): true] access to local variable s | Assert.cs:127:29:127:32 | [b (line 84): true] null | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:127:24:127:38 | [b (line 84): true] ... \|\| ... | Assert.cs:127:24:127:24 | [b (line 84): true] access to local variable s | +| Assert.cs:127:29:127:32 | [b (line 84): true] null | Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | +| Assert.cs:127:37:127:38 | [b (line 84): true] !... | Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | +| Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | Assert.cs:127:9:127:39 | [assertion success] call to method IsFalse | +| Assert.cs:128:9:128:36 | ...; | Assert.cs:128:27:128:27 | access to local variable s | +| Assert.cs:128:27:128:27 | access to local variable s | Assert.cs:128:27:128:34 | access to property Length | +| Assert.cs:128:27:128:34 | access to property Length | Assert.cs:128:9:128:35 | call to method WriteLine | | Assignments.cs:3:10:3:10 | enter M | Assignments.cs:4:5:15:5 | {...} | | Assignments.cs:4:5:15:5 | {...} | Assignments.cs:5:9:5:18 | ... ...; | | Assignments.cs:5:9:5:18 | ... ...; | Assignments.cs:5:17:5:17 | 0 | @@ -477,6 +970,7 @@ dominance | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | CompileTimeOperators.cs:28:10:28:10 | exit M | | CompileTimeOperators.cs:40:14:40:38 | ...; | CompileTimeOperators.cs:40:32:40:36 | "End" | | CompileTimeOperators.cs:40:32:40:36 | "End" | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:30:32:30:32 | 0 | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:12:3:13 | exit M1 | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:28:3:38 | call to method ToString | @@ -523,12 +1017,27 @@ dominance | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:31:25:31 | access to local variable s | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:9:25:32 | ... = ... | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:75:31:78 | ", " | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:70:31:78 | ... + ... | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:70:31:83 | ... + ... | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:10:30:12 | exit Out | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:28:30:32 | ... = ... | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:28:30:32 | ... = ... | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:33:5:36:5 | {...} | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:34:9:34:14 | ...; | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:35:9:35:25 | ...; | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:13:34:13 | 0 | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:9:34:13 | ... = ... | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:14:35:24 | call to method Out | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | access to property Prop | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:75:41:78 | ", " | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:70:41:78 | ... + ... | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:70:41:83 | ... + ... | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:4:5:9:5 | {...} | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:5:9:6:16 | if (...) ... | | Conditions.cs:5:9:6:16 | if (...) ... | Conditions.cs:5:13:5:15 | access to parameter inc | @@ -717,9 +1226,9 @@ dominance | Conditions.cs:78:21:78:21 | 0 | Conditions.cs:78:17:78:21 | ... > ... | | Conditions.cs:79:17:79:26 | ...; | Conditions.cs:79:21:79:25 | false | | Conditions.cs:79:21:79:25 | false | Conditions.cs:79:17:79:25 | ... = ... | -| Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:12:81:12 | access to local variable b | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:82:13:82:16 | ...; | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:83:16:83:16 | access to local variable x | +| Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:13:81:13 | access to local variable b | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:82:13:82:16 | ...; | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:83:16:83:16 | access to local variable x | | Conditions.cs:82:13:82:13 | access to local variable x | Conditions.cs:82:13:82:15 | ...++ | | Conditions.cs:82:13:82:16 | ...; | Conditions.cs:82:13:82:13 | access to local variable x | | Conditions.cs:83:9:83:17 | return ...; | Conditions.cs:70:9:70:10 | exit M6 | @@ -798,15 +1307,15 @@ dominance | Conditions.cs:115:9:115:24 | ... ...; | Conditions.cs:115:20:115:23 | null | | Conditions.cs:115:16:115:23 | String s = ... | Conditions.cs:116:9:123:9 | for (...;...;...) ... | | Conditions.cs:115:20:115:23 | null | Conditions.cs:115:16:115:23 | String s = ... | -| Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:116:21:116:21 | 0 | -| Conditions.cs:116:17:116:21 | Int32 i = ... | Conditions.cs:116:24:116:24 | access to local variable i | -| Conditions.cs:116:21:116:21 | 0 | Conditions.cs:116:17:116:21 | Int32 i = ... | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:28:116:31 | access to parameter args | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:113:10:113:11 | exit M9 | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:117:9:123:9 | {...} | -| Conditions.cs:116:28:116:31 | access to parameter args | Conditions.cs:116:28:116:38 | access to property Length | -| Conditions.cs:116:28:116:38 | access to property Length | Conditions.cs:116:24:116:38 | ... < ... | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:116:41:116:43 | ...++ | +| Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:116:22:116:22 | 0 | +| Conditions.cs:116:18:116:22 | Int32 i = ... | Conditions.cs:116:25:116:25 | access to local variable i | +| Conditions.cs:116:22:116:22 | 0 | Conditions.cs:116:18:116:22 | Int32 i = ... | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:29:116:32 | access to parameter args | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:113:10:113:11 | exit M9 | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:117:9:123:9 | {...} | +| Conditions.cs:116:29:116:32 | access to parameter args | Conditions.cs:116:29:116:39 | access to property Length | +| Conditions.cs:116:29:116:39 | access to property Length | Conditions.cs:116:25:116:39 | ... < ... | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:116:42:116:44 | ...++ | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:118:13:118:44 | ... ...; | | Conditions.cs:118:13:118:44 | ... ...; | Conditions.cs:118:24:118:24 | access to local variable i | | Conditions.cs:118:17:118:43 | Boolean last = ... | Conditions.cs:119:13:120:23 | if (...) ... | @@ -867,6 +1376,28 @@ dominance | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] this access | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] access to field Field1 | | Conditions.cs:137:21:137:37 | [Field1 (line 129): true, Field2 (line 129): true] call to method ToString | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): true] true | | Conditions.cs:137:21:137:38 | [Field1 (line 129): true, Field2 (line 129): true] ...; | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] this access | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:144:5:150:5 | {...} | +| Conditions.cs:144:5:150:5 | {...} | Conditions.cs:145:9:145:30 | ... ...; | +| Conditions.cs:145:9:145:30 | ... ...; | Conditions.cs:145:17:145:29 | ... ? ... : ... | +| Conditions.cs:145:13:145:29 | [b (line 143): false] String s = ... | Conditions.cs:146:9:149:49 | [b (line 143): false] if (...) ... | +| Conditions.cs:145:13:145:29 | [b (line 143): true] String s = ... | Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | +| Conditions.cs:145:17:145:29 | ... ? ... : ... | Conditions.cs:145:17:145:17 | access to parameter b | +| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | Conditions.cs:145:13:145:29 | [b (line 143): true] String s = ... | +| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | Conditions.cs:145:13:145:29 | [b (line 143): false] String s = ... | +| Conditions.cs:146:9:149:49 | [b (line 143): false] if (...) ... | Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | +| Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... | Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | +| Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | Conditions.cs:149:13:149:49 | ...; | +| Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | Conditions.cs:147:13:147:49 | ...; | +| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:40:147:43 | "a = " | +| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:13:147:48 | call to method WriteLine | +| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:45:147:45 | access to local variable s | +| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:38:147:47 | $"..." | +| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:40:149:43 | "b = " | +| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:13:149:48 | call to method WriteLine | +| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:45:149:45 | access to local variable s | +| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:38:149:47 | $"..." | | ExitMethods.cs:7:10:7:11 | enter M1 | ExitMethods.cs:8:5:11:5 | {...} | | ExitMethods.cs:8:5:11:5 | {...} | ExitMethods.cs:9:9:9:25 | ...; | | ExitMethods.cs:9:9:9:24 | call to method ErrorMaybe | ExitMethods.cs:10:9:10:15 | return ...; | @@ -975,23 +1506,23 @@ dominance | ExitMethods.cs:116:27:116:29 | - | ExitMethods.cs:116:16:116:30 | call to method Contains | | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | ExitMethods.cs:120:5:123:5 | {...} | | ExitMethods.cs:120:5:123:5 | {...} | ExitMethods.cs:121:9:121:29 | ...; | -| ExitMethods.cs:121:9:121:28 | call to method IsTrue | ExitMethods.cs:119:17:119:32 | exit FailingAssertion | +| ExitMethods.cs:121:9:121:28 | [assertion failure] call to method IsTrue | ExitMethods.cs:119:17:119:32 | exit FailingAssertion | | ExitMethods.cs:121:9:121:29 | ...; | ExitMethods.cs:121:23:121:27 | false | -| ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:121:9:121:28 | call to method IsTrue | +| ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:121:9:121:28 | [assertion failure] call to method IsTrue | | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | ExitMethods.cs:126:5:129:5 | {...} | | ExitMethods.cs:126:5:129:5 | {...} | ExitMethods.cs:127:9:127:27 | ...; | | ExitMethods.cs:127:9:127:26 | call to method FailingAssertion | ExitMethods.cs:125:17:125:33 | exit FailingAssertion2 | | ExitMethods.cs:127:9:127:26 | this access | ExitMethods.cs:127:9:127:26 | call to method FailingAssertion | | ExitMethods.cs:127:9:127:27 | ...; | ExitMethods.cs:127:9:127:26 | this access | | ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:48:131:48 | access to parameter b | -| ExitMethods.cs:131:33:131:49 | call to method IsFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | -| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:33:131:49 | call to method IsFalse | +| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | +| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | ExitMethods.cs:134:5:137:5 | {...} | | ExitMethods.cs:134:5:137:5 | {...} | ExitMethods.cs:135:9:135:26 | ...; | -| ExitMethods.cs:135:9:135:25 | call to method AssertFalse | ExitMethods.cs:133:17:133:33 | exit FailingAssertion3 | +| ExitMethods.cs:135:9:135:25 | [assertion failure] call to method AssertFalse | ExitMethods.cs:133:17:133:33 | exit FailingAssertion3 | | ExitMethods.cs:135:9:135:25 | this access | ExitMethods.cs:135:21:135:24 | true | | ExitMethods.cs:135:9:135:26 | ...; | ExitMethods.cs:135:9:135:25 | this access | -| ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:135:9:135:25 | call to method AssertFalse | +| ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:135:9:135:25 | [assertion failure] call to method AssertFalse | | Extensions.cs:5:23:5:29 | enter ToInt32 | Extensions.cs:6:5:8:5 | {...} | | Extensions.cs:6:5:8:5 | {...} | Extensions.cs:7:28:7:28 | access to parameter s | | Extensions.cs:7:9:7:30 | return ...; | Extensions.cs:5:23:5:29 | exit ToInt32 | @@ -1721,8 +2252,9 @@ dominance | Initializers.cs:14:47:14:47 | access to property G | Initializers.cs:14:47:14:51 | ... = ... | | Initializers.cs:14:47:14:51 | ... = ... | Initializers.cs:14:38:14:53 | { ..., ... } | | Initializers.cs:14:51:14:51 | 1 | Initializers.cs:14:47:14:47 | access to property G | -| Initializers.cs:15:9:15:64 | ... ...; | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | +| Initializers.cs:15:9:15:64 | ... ...; | Initializers.cs:15:18:15:63 | 2 | | Initializers.cs:15:13:15:63 | Initializers[] iz = ... | Initializers.cs:12:10:12:10 | exit M | +| Initializers.cs:15:18:15:63 | 2 | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | Initializers.cs:15:39:15:39 | access to local variable i | | Initializers.cs:15:37:15:63 | { ..., ... } | Initializers.cs:15:13:15:63 | Initializers[] iz = ... | | Initializers.cs:15:39:15:39 | access to local variable i | Initializers.cs:15:59:15:60 | "" | @@ -1879,27 +2411,28 @@ dominance | LoopUnrolling.cs:9:13:9:16 | access to parameter args | LoopUnrolling.cs:9:13:9:23 | access to property Length | | LoopUnrolling.cs:9:13:9:23 | access to property Length | LoopUnrolling.cs:9:28:9:28 | 0 | | LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:10:13:10:19 | return ...; | -| LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:11:28:11:31 | access to parameter args | +| LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:11:29:11:32 | access to parameter args | | LoopUnrolling.cs:9:28:9:28 | 0 | LoopUnrolling.cs:9:13:9:28 | ... == ... | -| LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | LoopUnrolling.cs:11:21:11:23 | String arg | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:12:13:12:35 | ...; | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | LoopUnrolling.cs:11:22:11:24 | String arg | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:12:13:12:35 | ...; | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | | LoopUnrolling.cs:12:13:12:35 | ...; | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | | LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:16:5:20:5 | {...} | -| LoopUnrolling.cs:16:5:20:5 | {...} | LoopUnrolling.cs:17:9:17:47 | ... ...; | -| LoopUnrolling.cs:17:9:17:47 | ... ...; | LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | -| LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | LoopUnrolling.cs:18:26:18:27 | access to local variable xs | -| LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | LoopUnrolling.cs:17:32:17:34 | "a" | -| LoopUnrolling.cs:17:30:17:46 | { ..., ... } | LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | -| LoopUnrolling.cs:17:32:17:34 | "a" | LoopUnrolling.cs:17:37:17:39 | "b" | -| LoopUnrolling.cs:17:37:17:39 | "b" | LoopUnrolling.cs:17:42:17:44 | "c" | -| LoopUnrolling.cs:17:42:17:44 | "c" | LoopUnrolling.cs:17:30:17:46 | { ..., ... } | -| LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | LoopUnrolling.cs:18:21:18:21 | String x | +| LoopUnrolling.cs:16:5:20:5 | {...} | LoopUnrolling.cs:17:9:17:48 | ... ...; | +| LoopUnrolling.cs:17:9:17:48 | ... ...; | LoopUnrolling.cs:17:18:17:47 | 3 | +| LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | LoopUnrolling.cs:18:27:18:28 | access to local variable xs | +| LoopUnrolling.cs:17:18:17:47 | 3 | LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | +| LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | LoopUnrolling.cs:17:33:17:35 | "a" | +| LoopUnrolling.cs:17:31:17:47 | { ..., ... } | LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | +| LoopUnrolling.cs:17:33:17:35 | "a" | LoopUnrolling.cs:17:38:17:40 | "b" | +| LoopUnrolling.cs:17:38:17:40 | "b" | LoopUnrolling.cs:17:43:17:45 | "c" | +| LoopUnrolling.cs:17:43:17:45 | "c" | LoopUnrolling.cs:17:31:17:47 | { ..., ... } | +| LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | LoopUnrolling.cs:18:22:18:22 | String x | | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:15:10:15:11 | exit M2 | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:19:13:19:33 | ...; | -| LoopUnrolling.cs:18:26:18:27 | access to local variable xs | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:19:13:19:33 | ...; | +| LoopUnrolling.cs:18:27:18:28 | access to local variable xs | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | | LoopUnrolling.cs:19:13:19:33 | ...; | LoopUnrolling.cs:19:31:19:31 | access to local variable x | | LoopUnrolling.cs:19:31:19:31 | access to local variable x | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | @@ -1918,77 +2451,77 @@ dominance | LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:30:5:34:5 | {...} | | LoopUnrolling.cs:30:5:34:5 | {...} | LoopUnrolling.cs:31:9:31:31 | ... ...; | | LoopUnrolling.cs:31:9:31:31 | ... ...; | LoopUnrolling.cs:31:29:31:29 | 0 | -| LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | LoopUnrolling.cs:32:26:32:27 | access to local variable xs | +| LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | LoopUnrolling.cs:32:27:32:28 | access to local variable xs | | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | | LoopUnrolling.cs:31:29:31:29 | 0 | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | exit M4 | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:21:32:21 | String x | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:33:13:33:33 | ...; | -| LoopUnrolling.cs:32:26:32:27 | access to local variable xs | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:33:13:33:33 | ...; | LoopUnrolling.cs:33:31:33:31 | access to local variable x | -| LoopUnrolling.cs:33:31:33:31 | access to local variable x | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | +| LoopUnrolling.cs:32:9:33:33 | [skip (line 32)] foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | exit M4 | +| LoopUnrolling.cs:32:27:32:28 | access to local variable xs | LoopUnrolling.cs:32:9:33:33 | [skip (line 32)] foreach (... ... in ...) ... | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:37:5:43:5 | {...} | -| LoopUnrolling.cs:37:5:43:5 | {...} | LoopUnrolling.cs:38:9:38:47 | ... ...; | -| LoopUnrolling.cs:38:9:38:47 | ... ...; | LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | -| LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | LoopUnrolling.cs:39:9:39:47 | ... ...; | -| LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | LoopUnrolling.cs:38:32:38:34 | "a" | -| LoopUnrolling.cs:38:30:38:46 | { ..., ... } | LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | -| LoopUnrolling.cs:38:32:38:34 | "a" | LoopUnrolling.cs:38:37:38:39 | "b" | -| LoopUnrolling.cs:38:37:38:39 | "b" | LoopUnrolling.cs:38:42:38:44 | "c" | -| LoopUnrolling.cs:38:42:38:44 | "c" | LoopUnrolling.cs:38:30:38:46 | { ..., ... } | -| LoopUnrolling.cs:39:9:39:47 | ... ...; | LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | -| LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | LoopUnrolling.cs:40:26:40:27 | access to local variable xs | -| LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | LoopUnrolling.cs:39:32:39:34 | "0" | -| LoopUnrolling.cs:39:30:39:46 | { ..., ... } | LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | -| LoopUnrolling.cs:39:32:39:34 | "0" | LoopUnrolling.cs:39:37:39:39 | "1" | -| LoopUnrolling.cs:39:37:39:39 | "1" | LoopUnrolling.cs:39:42:39:44 | "2" | -| LoopUnrolling.cs:39:42:39:44 | "2" | LoopUnrolling.cs:39:30:39:46 | { ..., ... } | -| LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | LoopUnrolling.cs:40:21:40:21 | String x | +| LoopUnrolling.cs:37:5:43:5 | {...} | LoopUnrolling.cs:38:9:38:48 | ... ...; | +| LoopUnrolling.cs:38:9:38:48 | ... ...; | LoopUnrolling.cs:38:18:38:47 | 3 | +| LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | LoopUnrolling.cs:39:9:39:48 | ... ...; | +| LoopUnrolling.cs:38:18:38:47 | 3 | LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | +| LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | LoopUnrolling.cs:38:33:38:35 | "a" | +| LoopUnrolling.cs:38:31:38:47 | { ..., ... } | LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | +| LoopUnrolling.cs:38:33:38:35 | "a" | LoopUnrolling.cs:38:38:38:40 | "b" | +| LoopUnrolling.cs:38:38:38:40 | "b" | LoopUnrolling.cs:38:43:38:45 | "c" | +| LoopUnrolling.cs:38:43:38:45 | "c" | LoopUnrolling.cs:38:31:38:47 | { ..., ... } | +| LoopUnrolling.cs:39:9:39:48 | ... ...; | LoopUnrolling.cs:39:18:39:47 | 3 | +| LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | LoopUnrolling.cs:40:27:40:28 | access to local variable xs | +| LoopUnrolling.cs:39:18:39:47 | 3 | LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | +| LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | LoopUnrolling.cs:39:33:39:35 | "0" | +| LoopUnrolling.cs:39:31:39:47 | { ..., ... } | LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | +| LoopUnrolling.cs:39:33:39:35 | "0" | LoopUnrolling.cs:39:38:39:40 | "1" | +| LoopUnrolling.cs:39:38:39:40 | "1" | LoopUnrolling.cs:39:43:39:45 | "2" | +| LoopUnrolling.cs:39:43:39:45 | "2" | LoopUnrolling.cs:39:31:39:47 | { ..., ... } | +| LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | LoopUnrolling.cs:40:22:40:22 | String x | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | exit M5 | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:41:30:41:31 | access to local variable ys | -| LoopUnrolling.cs:40:26:40:27 | access to local variable xs | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | -| LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | LoopUnrolling.cs:41:25:41:25 | String y | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:41:31:41:32 | access to local variable ys | +| LoopUnrolling.cs:40:27:40:28 | access to local variable xs | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | LoopUnrolling.cs:41:26:41:26 | String y | | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:42:17:42:41 | ...; | -| LoopUnrolling.cs:41:30:41:31 | access to local variable ys | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:42:17:42:41 | ...; | +| LoopUnrolling.cs:41:31:41:32 | access to local variable ys | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | | LoopUnrolling.cs:42:17:42:41 | ...; | LoopUnrolling.cs:42:35:42:35 | access to local variable x | | LoopUnrolling.cs:42:35:42:35 | access to local variable x | LoopUnrolling.cs:42:39:42:39 | access to local variable y | | LoopUnrolling.cs:42:35:42:39 | ... + ... | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | | LoopUnrolling.cs:42:39:42:39 | access to local variable y | LoopUnrolling.cs:42:35:42:39 | ... + ... | | LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:46:5:53:5 | {...} | -| LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:47:9:47:47 | ... ...; | -| LoopUnrolling.cs:47:9:47:47 | ... ...; | LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | -| LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | LoopUnrolling.cs:48:26:48:27 | access to local variable xs | -| LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | LoopUnrolling.cs:47:32:47:34 | "a" | -| LoopUnrolling.cs:47:30:47:46 | { ..., ... } | LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | -| LoopUnrolling.cs:47:32:47:34 | "a" | LoopUnrolling.cs:47:37:47:39 | "b" | -| LoopUnrolling.cs:47:37:47:39 | "b" | LoopUnrolling.cs:47:42:47:44 | "c" | -| LoopUnrolling.cs:47:42:47:44 | "c" | LoopUnrolling.cs:47:30:47:46 | { ..., ... } | -| LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | LoopUnrolling.cs:48:21:48:21 | String x | -| LoopUnrolling.cs:48:21:48:21 | String x | LoopUnrolling.cs:49:9:52:9 | {...} | -| LoopUnrolling.cs:48:26:48:27 | access to local variable xs | LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | -| LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:50:13:50:17 | Label: | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:50:20:50:40 | ...; | -| LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | LoopUnrolling.cs:51:13:51:23 | goto ...; | -| LoopUnrolling.cs:50:20:50:40 | ...; | LoopUnrolling.cs:50:38:50:38 | access to local variable x | -| LoopUnrolling.cs:50:38:50:38 | access to local variable x | LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | +| LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:47:9:47:48 | ... ...; | +| LoopUnrolling.cs:47:9:47:48 | ... ...; | LoopUnrolling.cs:47:18:47:47 | 3 | +| LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | LoopUnrolling.cs:48:27:48:28 | access to local variable xs | +| LoopUnrolling.cs:47:18:47:47 | 3 | LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | +| LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | LoopUnrolling.cs:47:33:47:35 | "a" | +| LoopUnrolling.cs:47:31:47:47 | { ..., ... } | LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | +| LoopUnrolling.cs:47:33:47:35 | "a" | LoopUnrolling.cs:47:38:47:40 | "b" | +| LoopUnrolling.cs:47:38:47:40 | "b" | LoopUnrolling.cs:47:43:47:45 | "c" | +| LoopUnrolling.cs:47:43:47:45 | "c" | LoopUnrolling.cs:47:31:47:47 | { ..., ... } | +| LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | LoopUnrolling.cs:48:22:48:22 | String x | +| LoopUnrolling.cs:48:22:48:22 | String x | LoopUnrolling.cs:49:9:52:9 | {...} | +| LoopUnrolling.cs:48:27:48:28 | access to local variable xs | LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:50:9:50:13 | Label: | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:50:16:50:36 | ...; | +| LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | LoopUnrolling.cs:51:13:51:23 | goto ...; | +| LoopUnrolling.cs:50:16:50:36 | ...; | LoopUnrolling.cs:50:34:50:34 | access to local variable x | +| LoopUnrolling.cs:50:34:50:34 | access to local variable x | LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:56:5:65:5 | {...} | -| LoopUnrolling.cs:56:5:65:5 | {...} | LoopUnrolling.cs:57:9:57:47 | ... ...; | -| LoopUnrolling.cs:57:9:57:47 | ... ...; | LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | -| LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | LoopUnrolling.cs:58:26:58:27 | access to local variable xs | -| LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | LoopUnrolling.cs:57:32:57:34 | "a" | -| LoopUnrolling.cs:57:30:57:46 | { ..., ... } | LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | -| LoopUnrolling.cs:57:32:57:34 | "a" | LoopUnrolling.cs:57:37:57:39 | "b" | -| LoopUnrolling.cs:57:37:57:39 | "b" | LoopUnrolling.cs:57:42:57:44 | "c" | -| LoopUnrolling.cs:57:42:57:44 | "c" | LoopUnrolling.cs:57:30:57:46 | { ..., ... } | -| LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | -| LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | -| LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | LoopUnrolling.cs:58:21:58:21 | String x | -| LoopUnrolling.cs:58:21:58:21 | String x | LoopUnrolling.cs:59:9:64:9 | {...} | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | -| LoopUnrolling.cs:58:26:58:27 | access to local variable xs | LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:56:5:65:5 | {...} | LoopUnrolling.cs:57:9:57:48 | ... ...; | +| LoopUnrolling.cs:57:9:57:48 | ... ...; | LoopUnrolling.cs:57:18:57:47 | 3 | +| LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | LoopUnrolling.cs:58:27:58:28 | access to local variable xs | +| LoopUnrolling.cs:57:18:57:47 | 3 | LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | +| LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | LoopUnrolling.cs:57:33:57:35 | "a" | +| LoopUnrolling.cs:57:31:57:47 | { ..., ... } | LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | +| LoopUnrolling.cs:57:33:57:35 | "a" | LoopUnrolling.cs:57:38:57:40 | "b" | +| LoopUnrolling.cs:57:38:57:40 | "b" | LoopUnrolling.cs:57:43:57:45 | "c" | +| LoopUnrolling.cs:57:43:57:45 | "c" | LoopUnrolling.cs:57:31:57:47 | { ..., ... } | +| LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | +| LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | +| LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | LoopUnrolling.cs:58:22:58:22 | String x | +| LoopUnrolling.cs:58:22:58:22 | String x | LoopUnrolling.cs:59:9:64:9 | {...} | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | +| LoopUnrolling.cs:58:27:58:28 | access to local variable xs | LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | LoopUnrolling.cs:60:13:61:37 | [b (line 55): false] if (...) ... | | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | LoopUnrolling.cs:60:13:61:37 | [b (line 55): true] if (...) ... | | LoopUnrolling.cs:59:9:64:9 | {...} | LoopUnrolling.cs:60:13:61:37 | if (...) ... | @@ -2015,13 +2548,146 @@ dominance | LoopUnrolling.cs:69:14:69:23 | call to method Any | LoopUnrolling.cs:70:13:70:19 | return ...; | | LoopUnrolling.cs:69:14:69:23 | call to method Any | LoopUnrolling.cs:71:9:71:21 | ...; | | LoopUnrolling.cs:71:9:71:12 | access to parameter args | LoopUnrolling.cs:71:9:71:20 | call to method Clear | -| LoopUnrolling.cs:71:9:71:20 | call to method Clear | LoopUnrolling.cs:72:28:72:31 | access to parameter args | +| LoopUnrolling.cs:71:9:71:20 | call to method Clear | LoopUnrolling.cs:72:29:72:32 | access to parameter args | | LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:71:9:71:12 | access to parameter args | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:21:72:23 | String arg | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:73:13:73:35 | ...; | -| LoopUnrolling.cs:72:28:72:31 | access to parameter args | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:73:13:73:35 | ...; | LoopUnrolling.cs:73:31:73:33 | access to local variable arg | -| LoopUnrolling.cs:73:31:73:33 | access to local variable arg | LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | +| LoopUnrolling.cs:72:29:72:32 | access to parameter args | LoopUnrolling.cs:72:9:73:35 | [skip (line 72)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:76:10:76:11 | enter M9 | LoopUnrolling.cs:77:5:83:5 | {...} | +| LoopUnrolling.cs:77:5:83:5 | {...} | LoopUnrolling.cs:78:9:78:34 | ... ...; | +| LoopUnrolling.cs:78:9:78:34 | ... ...; | LoopUnrolling.cs:78:29:78:29 | 2 | +| LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | LoopUnrolling.cs:79:27:79:28 | access to local variable xs | +| LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | +| LoopUnrolling.cs:78:29:78:29 | 2 | LoopUnrolling.cs:78:32:78:32 | 0 | +| LoopUnrolling.cs:78:32:78:32 | 0 | LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | +| LoopUnrolling.cs:79:9:82:9 | [skip (line 79)] foreach (... ... in ...) ... | LoopUnrolling.cs:76:10:76:11 | exit M9 | +| LoopUnrolling.cs:79:27:79:28 | access to local variable xs | LoopUnrolling.cs:79:9:82:9 | [skip (line 79)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:85:10:85:12 | enter M10 | LoopUnrolling.cs:86:5:92:5 | {...} | +| LoopUnrolling.cs:86:5:92:5 | {...} | LoopUnrolling.cs:87:9:87:34 | ... ...; | +| LoopUnrolling.cs:87:9:87:34 | ... ...; | LoopUnrolling.cs:87:29:87:29 | 0 | +| LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | LoopUnrolling.cs:88:27:88:28 | access to local variable xs | +| LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | +| LoopUnrolling.cs:87:29:87:29 | 0 | LoopUnrolling.cs:87:32:87:32 | 2 | +| LoopUnrolling.cs:87:32:87:32 | 2 | LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | +| LoopUnrolling.cs:88:9:91:9 | [skip (line 88)] foreach (... ... in ...) ... | LoopUnrolling.cs:85:10:85:12 | exit M10 | +| LoopUnrolling.cs:88:27:88:28 | access to local variable xs | LoopUnrolling.cs:88:9:91:9 | [skip (line 88)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:95:5:101:5 | {...} | +| LoopUnrolling.cs:95:5:101:5 | {...} | LoopUnrolling.cs:96:9:96:34 | ... ...; | +| LoopUnrolling.cs:96:9:96:34 | ... ...; | LoopUnrolling.cs:96:29:96:29 | 2 | +| LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | LoopUnrolling.cs:97:27:97:28 | access to local variable xs | +| LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | +| LoopUnrolling.cs:96:29:96:29 | 2 | LoopUnrolling.cs:96:32:96:32 | 2 | +| LoopUnrolling.cs:96:32:96:32 | 2 | LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | +| LoopUnrolling.cs:97:9:100:9 | [unroll (line 97)] foreach (... ... in ...) ... | LoopUnrolling.cs:97:22:97:22 | String x | +| LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:94:10:94:12 | exit M11 | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:98:9:100:9 | {...} | +| LoopUnrolling.cs:97:27:97:28 | access to local variable xs | LoopUnrolling.cs:97:9:100:9 | [unroll (line 97)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:98:9:100:9 | {...} | LoopUnrolling.cs:99:13:99:33 | ...; | +| LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | +| LoopUnrolling.cs:99:13:99:33 | ...; | LoopUnrolling.cs:99:31:99:31 | access to local variable x | +| LoopUnrolling.cs:99:31:99:31 | access to local variable x | LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:22:6:31 | throw ... | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:33:7:36 | null | +| MultiImplementationA.cs:7:33:7:36 | null | MultiImplementationA.cs:7:27:7:37 | throw ...; | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:53:7:56 | null | +| MultiImplementationA.cs:7:53:7:56 | null | MultiImplementationA.cs:7:47:7:57 | throw ...; | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:23:8:32 | throw ... | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:20:13:20 | 0 | +| MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:20 | ... = ... | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:49:15:49 | access to parameter s | +| MultiImplementationA.cs:15:49:15:49 | access to parameter s | MultiImplementationA.cs:15:42:15:50 | return ...; | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:18:9:18:22 | M2(...) | +| MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:21:18:21 | 0 | +| MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | exit M2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:24:20:29 | ...; | +| MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:28:20:28 | access to parameter i | +| MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationA.cs:20:24:20:24 | this access | +| MultiImplementationA.cs:20:28:20:28 | access to parameter i | MultiImplementationA.cs:20:24:20:28 | ... = ... | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:32:24:34 | ... = ... | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:34:24:34 | 0 | +| MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | access to property P | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | +| MultiImplementationA.cs:30:34:30:37 | null | MultiImplementationA.cs:30:28:30:37 | throw ... | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:17:32:17 | 0 | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:22:36:25 | null | +| MultiImplementationA.cs:36:22:36:25 | null | MultiImplementationA.cs:36:16:36:26 | throw ...; | +| MultiImplementationA.cs:37:9:37:10 | enter M2 | MultiImplementationA.cs:37:14:37:28 | {...} | +| MultiImplementationA.cs:37:14:37:28 | {...} | MultiImplementationA.cs:37:22:37:25 | null | +| MultiImplementationA.cs:37:16:37:26 | throw ...; | MultiImplementationA.cs:37:9:37:10 | exit M2 | +| MultiImplementationA.cs:37:22:37:25 | null | MultiImplementationA.cs:37:16:37:26 | throw ...; | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:34:4:34 | 1 | +| MultiImplementationB.cs:4:34:4:34 | 1 | MultiImplementationB.cs:4:27:4:35 | return ...; | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:20:11:20 | 1 | +| MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:20 | ... = ... | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:31:12:40 | throw ... | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:48:13:51 | null | +| MultiImplementationB.cs:13:48:13:51 | null | MultiImplementationB.cs:13:42:13:52 | throw ...; | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:16:9:16:31 | M2(...) | +| MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:27:16:30 | null | +| MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:9:16:31 | exit M2 | +| MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:21:16:30 | throw ... | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:30:18:33 | null | +| MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:24:18:34 | throw ...; | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:19:20:22 | null | +| MultiImplementationB.cs:20:19:20:22 | null | MultiImplementationB.cs:20:13:20:23 | throw ...; | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:50:21:59 | throw ... | +| MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:32:22:34 | ... = ... | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:34:22:34 | 1 | +| MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | access to property P | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:17:32:17 | 0 | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:23:3:28 | ... ?? ... | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:9:3:10 | exit M1 | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:28:3:28 | 0 | @@ -2242,9 +2908,9 @@ dominance | Switch.cs:25:35:25:35 | access to local variable s | Switch.cs:25:17:25:36 | call to method WriteLine | | Switch.cs:27:13:27:39 | case ...: | Switch.cs:27:18:27:25 | Double d | | Switch.cs:27:18:27:25 | Double d | Switch.cs:27:32:27:38 | call to method Throw | -| Switch.cs:28:17:28:21 | Label: | Switch.cs:29:17:29:23 | return ...; | +| Switch.cs:28:13:28:17 | Label: | Switch.cs:29:17:29:23 | return ...; | | Switch.cs:30:13:30:20 | default: | Switch.cs:31:17:31:27 | goto ...; | -| Switch.cs:31:17:31:27 | goto ...; | Switch.cs:28:17:28:21 | Label: | +| Switch.cs:31:17:31:27 | goto ...; | Switch.cs:28:13:28:17 | Label: | | Switch.cs:35:10:35:11 | enter M3 | Switch.cs:36:5:42:5 | {...} | | Switch.cs:36:5:42:5 | {...} | Switch.cs:37:9:41:9 | switch (...) {...} | | Switch.cs:37:9:41:9 | switch (...) {...} | Switch.cs:37:17:37:23 | call to method Throw | @@ -2265,63 +2931,63 @@ dominance | Switch.cs:56:5:64:5 | {...} | Switch.cs:57:9:63:9 | switch (...) {...} | | Switch.cs:57:9:63:9 | switch (...) {...} | Switch.cs:57:17:57:17 | 1 | | Switch.cs:57:17:57:17 | 1 | Switch.cs:57:21:57:21 | 2 | -| Switch.cs:57:17:57:21 | ... + ... | Switch.cs:59:13:59:20 | case ...: | +| Switch.cs:57:17:57:21 | ... + ... | Switch.cs:59:13:59:19 | case ...: | | Switch.cs:57:21:57:21 | 2 | Switch.cs:57:17:57:21 | ... + ... | -| Switch.cs:59:13:59:20 | case ...: | Switch.cs:59:18:59:18 | 2 | -| Switch.cs:59:18:59:18 | 2 | Switch.cs:61:13:61:20 | case ...: | -| Switch.cs:61:13:61:20 | case ...: | Switch.cs:61:18:61:18 | 3 | -| Switch.cs:61:18:61:18 | 3 | Switch.cs:62:15:62:20 | break; | -| Switch.cs:62:15:62:20 | break; | Switch.cs:55:10:55:11 | exit M5 | +| Switch.cs:59:13:59:19 | case ...: | Switch.cs:59:18:59:18 | 2 | +| Switch.cs:59:18:59:18 | 2 | Switch.cs:61:13:61:19 | case ...: | +| Switch.cs:61:13:61:19 | case ...: | Switch.cs:61:18:61:18 | 3 | +| Switch.cs:61:18:61:18 | 3 | Switch.cs:62:17:62:22 | break; | +| Switch.cs:62:17:62:22 | break; | Switch.cs:55:10:55:11 | exit M5 | | Switch.cs:66:10:66:11 | enter M6 | Switch.cs:67:5:75:5 | {...} | | Switch.cs:67:5:75:5 | {...} | Switch.cs:68:9:74:9 | switch (...) {...} | | Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:68:25:68:25 | access to parameter s | -| Switch.cs:68:17:68:25 | (...) ... | Switch.cs:70:13:70:24 | case ...: | +| Switch.cs:68:17:68:25 | (...) ... | Switch.cs:70:13:70:23 | case ...: | | Switch.cs:68:25:68:25 | access to parameter s | Switch.cs:68:17:68:25 | (...) ... | -| Switch.cs:70:13:70:24 | case ...: | Switch.cs:70:18:70:20 | access to type Int32 | -| Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:72:13:72:21 | case ...: | -| Switch.cs:72:13:72:21 | case ...: | Switch.cs:72:18:72:19 | "" | +| Switch.cs:70:13:70:23 | case ...: | Switch.cs:70:18:70:20 | access to type Int32 | +| Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:72:13:72:20 | case ...: | +| Switch.cs:72:13:72:20 | case ...: | Switch.cs:72:18:72:19 | "" | | Switch.cs:72:18:72:19 | "" | Switch.cs:66:10:66:11 | exit M6 | -| Switch.cs:72:18:72:19 | "" | Switch.cs:73:15:73:20 | break; | +| Switch.cs:72:18:72:19 | "" | Switch.cs:73:17:73:22 | break; | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:78:5:89:5 | {...} | | Switch.cs:78:5:89:5 | {...} | Switch.cs:79:9:87:9 | switch (...) {...} | | Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:79:17:79:17 | access to parameter i | -| Switch.cs:79:17:79:17 | access to parameter i | Switch.cs:81:13:81:20 | case ...: | -| Switch.cs:81:13:81:20 | case ...: | Switch.cs:81:18:81:18 | 1 | -| Switch.cs:81:18:81:18 | 1 | Switch.cs:82:22:82:25 | true | -| Switch.cs:81:18:81:18 | 1 | Switch.cs:83:13:83:20 | case ...: | -| Switch.cs:82:22:82:25 | true | Switch.cs:82:15:82:26 | return ...; | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:83:18:83:18 | 2 | -| Switch.cs:83:18:83:18 | 2 | Switch.cs:84:15:85:22 | if (...) ... | +| Switch.cs:79:17:79:17 | access to parameter i | Switch.cs:81:13:81:19 | case ...: | +| Switch.cs:81:13:81:19 | case ...: | Switch.cs:81:18:81:18 | 1 | +| Switch.cs:81:18:81:18 | 1 | Switch.cs:82:24:82:27 | true | +| Switch.cs:81:18:81:18 | 1 | Switch.cs:83:13:83:19 | case ...: | +| Switch.cs:82:24:82:27 | true | Switch.cs:82:17:82:28 | return ...; | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:83:18:83:18 | 2 | +| Switch.cs:83:18:83:18 | 2 | Switch.cs:84:17:85:26 | if (...) ... | | Switch.cs:83:18:83:18 | 2 | Switch.cs:88:16:88:20 | false | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:84:19:84:19 | access to parameter j | -| Switch.cs:84:19:84:19 | access to parameter j | Switch.cs:84:23:84:23 | 2 | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:85:17:85:22 | break; | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:86:22:86:25 | true | -| Switch.cs:84:23:84:23 | 2 | Switch.cs:84:19:84:23 | ... > ... | -| Switch.cs:86:22:86:25 | true | Switch.cs:86:15:86:26 | return ...; | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:84:21:84:21 | access to parameter j | +| Switch.cs:84:21:84:21 | access to parameter j | Switch.cs:84:25:84:25 | 2 | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:85:21:85:26 | break; | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:86:24:86:27 | true | +| Switch.cs:84:25:84:25 | 2 | Switch.cs:84:21:84:25 | ... > ... | +| Switch.cs:86:24:86:27 | true | Switch.cs:86:17:86:28 | return ...; | | Switch.cs:88:16:88:20 | false | Switch.cs:88:9:88:21 | return ...; | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:92:5:99:5 | {...} | | Switch.cs:92:5:99:5 | {...} | Switch.cs:93:9:97:9 | switch (...) {...} | | Switch.cs:93:9:97:9 | switch (...) {...} | Switch.cs:93:17:93:17 | access to parameter o | -| Switch.cs:93:17:93:17 | access to parameter o | Switch.cs:95:13:95:24 | case ...: | -| Switch.cs:95:13:95:24 | case ...: | Switch.cs:95:18:95:20 | access to type Int32 | -| Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:96:22:96:25 | true | +| Switch.cs:93:17:93:17 | access to parameter o | Switch.cs:95:13:95:23 | case ...: | +| Switch.cs:95:13:95:23 | case ...: | Switch.cs:95:18:95:20 | access to type Int32 | +| Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:96:24:96:27 | true | | Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:98:16:98:20 | false | -| Switch.cs:96:22:96:25 | true | Switch.cs:96:15:96:26 | return ...; | +| Switch.cs:96:24:96:27 | true | Switch.cs:96:17:96:28 | return ...; | | Switch.cs:98:16:98:20 | false | Switch.cs:98:9:98:21 | return ...; | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:102:5:109:5 | {...} | | Switch.cs:102:5:109:5 | {...} | Switch.cs:103:9:107:9 | switch (...) {...} | | Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:103:17:103:17 | access to parameter s | | Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:103:19:103:25 | access to property Length | -| Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:105:13:105:20 | case ...: | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:18:105:18 | 0 | -| Switch.cs:105:18:105:18 | 0 | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:105:18:105:18 | 0 | Switch.cs:106:13:106:20 | case ...: | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:105:22:105:30 | return ...; | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:18:106:18 | 1 | -| Switch.cs:106:18:106:18 | 1 | Switch.cs:106:29:106:29 | 1 | +| Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:105:13:105:19 | case ...: | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:18:105:18 | 0 | +| Switch.cs:105:18:105:18 | 0 | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:105:18:105:18 | 0 | Switch.cs:106:13:106:19 | case ...: | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:105:21:105:29 | return ...; | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:18:106:18 | 1 | +| Switch.cs:106:18:106:18 | 1 | Switch.cs:106:28:106:28 | 1 | | Switch.cs:106:18:106:18 | 1 | Switch.cs:108:17:108:17 | 1 | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:106:22:106:30 | return ...; | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:106:21:106:29 | return ...; | | Switch.cs:108:16:108:17 | -... | Switch.cs:108:9:108:18 | return ...; | | Switch.cs:108:17:108:17 | 1 | Switch.cs:108:16:108:17 | -... | | Switch.cs:111:17:111:21 | enter Throw | Switch.cs:111:34:111:48 | object creation of type Exception | @@ -2331,21 +2997,21 @@ dominance | Switch.cs:114:5:121:5 | {...} | Switch.cs:115:9:119:9 | switch (...) {...} | | Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:115:17:115:17 | access to parameter s | | Switch.cs:115:17:115:17 | access to parameter s | Switch.cs:115:17:115:24 | access to property Length | -| Switch.cs:115:17:115:24 | access to property Length | Switch.cs:117:13:117:34 | case ...: | -| Switch.cs:117:13:117:34 | case ...: | Switch.cs:117:18:117:18 | 3 | +| Switch.cs:115:17:115:24 | access to property Length | Switch.cs:117:13:117:35 | case ...: | +| Switch.cs:117:13:117:35 | case ...: | Switch.cs:117:18:117:18 | 3 | | Switch.cs:117:18:117:18 | 3 | Switch.cs:117:25:117:25 | access to parameter s | -| Switch.cs:117:18:117:18 | 3 | Switch.cs:118:13:118:33 | case ...: | -| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:28:117:32 | "foo" | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:117:28:117:32 | "foo" | Switch.cs:117:25:117:32 | ... == ... | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:117:36:117:44 | return ...; | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:18:118:18 | 2 | +| Switch.cs:117:18:117:18 | 3 | Switch.cs:118:13:118:34 | case ...: | +| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:30:117:34 | "foo" | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:117:30:117:34 | "foo" | Switch.cs:117:25:117:34 | ... == ... | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:117:37:117:45 | return ...; | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:18:118:18 | 2 | | Switch.cs:118:18:118:18 | 2 | Switch.cs:118:25:118:25 | access to parameter s | | Switch.cs:118:18:118:18 | 2 | Switch.cs:120:17:120:17 | 1 | -| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:28:118:31 | "fu" | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:118:42:118:42 | 2 | -| Switch.cs:118:28:118:31 | "fu" | Switch.cs:118:25:118:31 | ... == ... | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:118:35:118:43 | return ...; | +| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:30:118:33 | "fu" | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:118:43:118:43 | 2 | +| Switch.cs:118:30:118:33 | "fu" | Switch.cs:118:25:118:33 | ... == ... | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:118:36:118:44 | return ...; | | Switch.cs:120:16:120:17 | -... | Switch.cs:120:9:120:18 | return ...; | | Switch.cs:120:17:120:17 | 1 | Switch.cs:120:16:120:17 | -... | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:124:5:127:5 | {...} | @@ -2400,6 +3066,28 @@ dominance | Switch.cs:150:18:150:18 | 2 | Switch.cs:149:13:149:20 | default: | | Switch.cs:150:18:150:18 | 2 | Switch.cs:150:28:150:28 | 2 | | Switch.cs:150:28:150:28 | 2 | Switch.cs:150:21:150:29 | return ...; | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:155:5:161:5 | {...} | +| Switch.cs:155:5:161:5 | {...} | Switch.cs:156:9:156:55 | ... ...; | +| Switch.cs:156:9:156:55 | ... ...; | Switch.cs:156:17:156:54 | ... switch { ... } | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:157:9:160:49 | if (...) ... | +| Switch.cs:156:17:156:17 | access to parameter b | Switch.cs:156:28:156:38 | ... => ... | +| Switch.cs:156:17:156:54 | ... switch { ... } | Switch.cs:156:17:156:17 | access to parameter b | +| Switch.cs:156:28:156:31 | true | Switch.cs:156:36:156:38 | "a" | +| Switch.cs:156:28:156:31 | true | Switch.cs:156:41:156:52 | ... => ... | +| Switch.cs:156:28:156:38 | ... => ... | Switch.cs:156:28:156:31 | true | +| Switch.cs:156:41:156:45 | false | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:41:156:45 | false | +| Switch.cs:157:9:160:49 | if (...) ... | Switch.cs:157:13:157:13 | access to parameter b | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:158:13:158:49 | ...; | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:160:13:160:49 | ...; | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:40:158:43 | "a = " | +| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:13:158:48 | call to method WriteLine | +| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:45:158:45 | access to local variable s | +| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:38:158:47 | $"..." | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:40:160:43 | "b = " | +| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:13:160:48 | call to method WriteLine | +| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:45:160:45 | access to local variable s | +| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:38:160:47 | $"..." | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:4:5:9:5 | {...} | | TypeAccesses.cs:4:5:9:5 | {...} | TypeAccesses.cs:5:9:5:26 | ... ...; | | TypeAccesses.cs:5:9:5:26 | ... ...; | TypeAccesses.cs:5:25:5:25 | access to parameter o | @@ -2423,11 +3111,13 @@ dominance | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:7:27:7:33 | access to parameter strings | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:35:7:35 | 0 | -| VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:22:7:36 | Char* c1 = ... | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:22:7:36 | Char* c1 = ... | +| VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:27:7:36 | (...) ... | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:27:7:36 | access to array element | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:8:9:10:9 | {...} | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:52:7:52 | 1 | -| VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:39:7:53 | Char* c2 = ... | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:39:7:53 | Char* c2 = ... | +| VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:44:7:53 | (...) ... | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:44:7:53 | access to array element | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:9:27:9:28 | access to local variable c1 | | VarDecls.cs:9:13:9:29 | return ...; | VarDecls.cs:5:18:5:19 | exit M1 | @@ -3356,12 +4046,15 @@ postDominance | ArrayCreation.cs:5:28:5:28 | 0 | ArrayCreation.cs:5:12:5:13 | enter M2 | | ArrayCreation.cs:5:31:5:31 | 1 | ArrayCreation.cs:5:28:5:28 | 0 | | ArrayCreation.cs:7:11:7:12 | exit M3 | ArrayCreation.cs:7:29:7:36 | { ..., ... } | -| ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | ArrayCreation.cs:7:11:7:12 | enter M3 | +| ArrayCreation.cs:7:19:7:36 | 2 | ArrayCreation.cs:7:11:7:12 | enter M3 | +| ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | ArrayCreation.cs:7:19:7:36 | 2 | | ArrayCreation.cs:7:29:7:36 | { ..., ... } | ArrayCreation.cs:7:34:7:34 | 1 | | ArrayCreation.cs:7:31:7:31 | 0 | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | | ArrayCreation.cs:7:34:7:34 | 1 | ArrayCreation.cs:7:31:7:31 | 0 | | ArrayCreation.cs:9:12:9:13 | exit M4 | ArrayCreation.cs:9:31:9:52 | { ..., ... } | -| ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | ArrayCreation.cs:9:12:9:13 | enter M4 | +| ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:12:9:13 | enter M4 | +| ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:20:9:52 | 2 | +| ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | ArrayCreation.cs:9:20:9:52 | 2 | | ArrayCreation.cs:9:31:9:52 | { ..., ... } | ArrayCreation.cs:9:43:9:50 | { ..., ... } | | ArrayCreation.cs:9:33:9:40 | { ..., ... } | ArrayCreation.cs:9:38:9:38 | 1 | | ArrayCreation.cs:9:35:9:35 | 0 | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | @@ -3369,6 +4062,478 @@ postDominance | ArrayCreation.cs:9:43:9:50 | { ..., ... } | ArrayCreation.cs:9:48:9:48 | 3 | | ArrayCreation.cs:9:45:9:45 | 2 | ArrayCreation.cs:9:33:9:40 | { ..., ... } | | ArrayCreation.cs:9:48:9:48 | 3 | ArrayCreation.cs:9:45:9:45 | 2 | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:11:9:11:35 | call to method WriteLine | +| Assert.cs:8:5:12:5 | {...} | Assert.cs:7:10:7:11 | enter M1 | +| Assert.cs:9:9:9:33 | ... ...; | Assert.cs:8:5:12:5 | {...} | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:9:24:9:27 | null | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:9:31:9:32 | "" | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:20:9:32 | ... ? ... : ... | +| Assert.cs:9:20:9:32 | ... ? ... : ... | Assert.cs:9:9:9:33 | ... ...; | +| Assert.cs:10:9:10:32 | ...; | Assert.cs:9:16:9:32 | String s = ... | +| Assert.cs:10:22:10:22 | access to local variable s | Assert.cs:10:9:10:32 | ...; | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:27:10:30 | null | +| Assert.cs:10:27:10:30 | null | Assert.cs:10:22:10:22 | access to local variable s | +| Assert.cs:11:9:11:35 | call to method WriteLine | Assert.cs:11:27:11:34 | access to property Length | +| Assert.cs:11:9:11:36 | ...; | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | +| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:11:9:11:36 | ...; | +| Assert.cs:11:27:11:34 | access to property Length | Assert.cs:11:27:11:27 | access to local variable s | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:18:9:18:35 | call to method WriteLine | +| Assert.cs:15:5:19:5 | {...} | Assert.cs:14:10:14:11 | enter M2 | +| Assert.cs:16:9:16:33 | ... ...; | Assert.cs:15:5:19:5 | {...} | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:16:24:16:27 | null | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:16:31:16:32 | "" | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:20:16:32 | ... ? ... : ... | +| Assert.cs:16:20:16:32 | ... ? ... : ... | Assert.cs:16:9:16:33 | ... ...; | +| Assert.cs:17:9:17:25 | ...; | Assert.cs:16:16:16:32 | String s = ... | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:9:17:25 | ...; | +| Assert.cs:18:9:18:35 | call to method WriteLine | Assert.cs:18:27:18:34 | access to property Length | +| Assert.cs:18:9:18:36 | ...; | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | +| Assert.cs:18:27:18:27 | access to local variable s | Assert.cs:18:9:18:36 | ...; | +| Assert.cs:18:27:18:34 | access to property Length | Assert.cs:18:27:18:27 | access to local variable s | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:25:9:25:35 | call to method WriteLine | +| Assert.cs:22:5:26:5 | {...} | Assert.cs:21:10:21:11 | enter M3 | +| Assert.cs:23:9:23:33 | ... ...; | Assert.cs:22:5:26:5 | {...} | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:23:24:23:27 | null | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:23:31:23:32 | "" | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:20:23:32 | ... ? ... : ... | +| Assert.cs:23:20:23:32 | ... ? ... : ... | Assert.cs:23:9:23:33 | ... ...; | +| Assert.cs:24:9:24:28 | ...; | Assert.cs:23:16:23:32 | String s = ... | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:9:24:28 | ...; | +| Assert.cs:25:9:25:35 | call to method WriteLine | Assert.cs:25:27:25:34 | access to property Length | +| Assert.cs:25:9:25:36 | ...; | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | +| Assert.cs:25:27:25:27 | access to local variable s | Assert.cs:25:9:25:36 | ...; | +| Assert.cs:25:27:25:34 | access to property Length | Assert.cs:25:27:25:27 | access to local variable s | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:32:9:32:35 | call to method WriteLine | +| Assert.cs:29:5:33:5 | {...} | Assert.cs:28:10:28:11 | enter M4 | +| Assert.cs:30:9:30:33 | ... ...; | Assert.cs:29:5:33:5 | {...} | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:30:24:30:27 | null | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:30:31:30:32 | "" | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:20:30:32 | ... ? ... : ... | +| Assert.cs:30:20:30:32 | ... ? ... : ... | Assert.cs:30:9:30:33 | ... ...; | +| Assert.cs:31:9:31:33 | ...; | Assert.cs:30:16:30:32 | String s = ... | +| Assert.cs:31:23:31:23 | access to local variable s | Assert.cs:31:9:31:33 | ...; | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:28:31:31 | null | +| Assert.cs:31:28:31:31 | null | Assert.cs:31:23:31:23 | access to local variable s | +| Assert.cs:32:9:32:35 | call to method WriteLine | Assert.cs:32:27:32:34 | access to property Length | +| Assert.cs:32:9:32:36 | ...; | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | +| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:32:9:32:36 | ...; | +| Assert.cs:32:27:32:34 | access to property Length | Assert.cs:32:27:32:27 | access to local variable s | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:39:9:39:35 | call to method WriteLine | +| Assert.cs:36:5:40:5 | {...} | Assert.cs:35:10:35:11 | enter M5 | +| Assert.cs:37:9:37:33 | ... ...; | Assert.cs:36:5:40:5 | {...} | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:37:24:37:27 | null | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:37:31:37:32 | "" | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:20:37:32 | ... ? ... : ... | +| Assert.cs:37:20:37:32 | ... ? ... : ... | Assert.cs:37:9:37:33 | ... ...; | +| Assert.cs:38:9:38:33 | ...; | Assert.cs:37:16:37:32 | String s = ... | +| Assert.cs:38:23:38:23 | access to local variable s | Assert.cs:38:9:38:33 | ...; | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:28:38:31 | null | +| Assert.cs:38:28:38:31 | null | Assert.cs:38:23:38:23 | access to local variable s | +| Assert.cs:39:9:39:35 | call to method WriteLine | Assert.cs:39:27:39:34 | access to property Length | +| Assert.cs:39:9:39:36 | ...; | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | +| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:39:9:39:36 | ...; | +| Assert.cs:39:27:39:34 | access to property Length | Assert.cs:39:27:39:27 | access to local variable s | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:46:9:46:35 | call to method WriteLine | +| Assert.cs:43:5:47:5 | {...} | Assert.cs:42:10:42:11 | enter M6 | +| Assert.cs:44:9:44:33 | ... ...; | Assert.cs:43:5:47:5 | {...} | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:44:24:44:27 | null | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:44:31:44:32 | "" | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:20:44:32 | ... ? ... : ... | +| Assert.cs:44:20:44:32 | ... ? ... : ... | Assert.cs:44:9:44:33 | ... ...; | +| Assert.cs:45:9:45:34 | ...; | Assert.cs:44:16:44:32 | String s = ... | +| Assert.cs:45:24:45:24 | access to local variable s | Assert.cs:45:9:45:34 | ...; | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:29:45:32 | null | +| Assert.cs:45:29:45:32 | null | Assert.cs:45:24:45:24 | access to local variable s | +| Assert.cs:46:9:46:35 | call to method WriteLine | Assert.cs:46:27:46:34 | access to property Length | +| Assert.cs:46:9:46:36 | ...; | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | +| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:46:9:46:36 | ...; | +| Assert.cs:46:27:46:34 | access to property Length | Assert.cs:46:27:46:27 | access to local variable s | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:53:9:53:35 | call to method WriteLine | +| Assert.cs:50:5:54:5 | {...} | Assert.cs:49:10:49:11 | enter M7 | +| Assert.cs:51:9:51:33 | ... ...; | Assert.cs:50:5:54:5 | {...} | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:51:24:51:27 | null | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:51:31:51:32 | "" | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:20:51:32 | ... ? ... : ... | +| Assert.cs:51:20:51:32 | ... ? ... : ... | Assert.cs:51:9:51:33 | ... ...; | +| Assert.cs:52:9:52:34 | ...; | Assert.cs:51:16:51:32 | String s = ... | +| Assert.cs:52:24:52:24 | access to local variable s | Assert.cs:52:9:52:34 | ...; | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:29:52:32 | null | +| Assert.cs:52:29:52:32 | null | Assert.cs:52:24:52:24 | access to local variable s | +| Assert.cs:53:9:53:35 | call to method WriteLine | Assert.cs:53:27:53:34 | access to property Length | +| Assert.cs:53:9:53:36 | ...; | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | +| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:53:9:53:36 | ...; | +| Assert.cs:53:27:53:34 | access to property Length | Assert.cs:53:27:53:27 | access to local variable s | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:60:9:60:35 | call to method WriteLine | +| Assert.cs:57:5:61:5 | {...} | Assert.cs:56:10:56:11 | enter M8 | +| Assert.cs:58:9:58:33 | ... ...; | Assert.cs:57:5:61:5 | {...} | +| Assert.cs:58:16:58:32 | [b (line 56): false] String s = ... | Assert.cs:58:31:58:32 | [b (line 56): false] "" | +| Assert.cs:58:16:58:32 | [b (line 56): true] String s = ... | Assert.cs:58:24:58:27 | [b (line 56): true] null | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | +| Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:9:58:33 | ... ...; | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | +| Assert.cs:59:9:59:37 | [assertion success] call to method IsTrue | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | +| Assert.cs:59:9:59:38 | [b (line 56): false] ...; | Assert.cs:58:16:58:32 | [b (line 56): false] String s = ... | +| Assert.cs:59:9:59:38 | [b (line 56): true] ...; | Assert.cs:58:16:58:32 | [b (line 56): true] String s = ... | +| Assert.cs:59:23:59:23 | [b (line 56): false] access to local variable s | Assert.cs:59:23:59:36 | [b (line 56): false] ... && ... | +| Assert.cs:59:23:59:23 | [b (line 56): true] access to local variable s | Assert.cs:59:23:59:36 | [b (line 56): true] ... && ... | +| Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | Assert.cs:59:28:59:31 | [b (line 56): false] null | +| Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | Assert.cs:59:28:59:31 | [b (line 56): true] null | +| Assert.cs:59:23:59:36 | [b (line 56): false] ... && ... | Assert.cs:59:9:59:38 | [b (line 56): false] ...; | +| Assert.cs:59:23:59:36 | [b (line 56): true] ... && ... | Assert.cs:59:9:59:38 | [b (line 56): true] ...; | +| Assert.cs:59:28:59:31 | [b (line 56): false] null | Assert.cs:59:23:59:23 | [b (line 56): false] access to local variable s | +| Assert.cs:59:28:59:31 | [b (line 56): true] null | Assert.cs:59:23:59:23 | [b (line 56): true] access to local variable s | +| Assert.cs:60:9:60:35 | call to method WriteLine | Assert.cs:60:27:60:34 | access to property Length | +| Assert.cs:60:9:60:36 | ...; | Assert.cs:59:9:59:37 | [assertion success] call to method IsTrue | +| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:60:9:60:36 | ...; | +| Assert.cs:60:27:60:34 | access to property Length | Assert.cs:60:27:60:27 | access to local variable s | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:67:9:67:35 | call to method WriteLine | +| Assert.cs:64:5:68:5 | {...} | Assert.cs:63:10:63:11 | enter M9 | +| Assert.cs:65:9:65:33 | ... ...; | Assert.cs:64:5:68:5 | {...} | +| Assert.cs:65:16:65:32 | [b (line 63): false] String s = ... | Assert.cs:65:31:65:32 | [b (line 63): false] "" | +| Assert.cs:65:16:65:32 | [b (line 63): true] String s = ... | Assert.cs:65:24:65:27 | [b (line 63): true] null | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | +| Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:9:65:33 | ... ...; | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | +| Assert.cs:66:9:66:38 | [assertion success] call to method IsFalse | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | +| Assert.cs:66:9:66:39 | [b (line 63): false] ...; | Assert.cs:65:16:65:32 | [b (line 63): false] String s = ... | +| Assert.cs:66:9:66:39 | [b (line 63): true] ...; | Assert.cs:65:16:65:32 | [b (line 63): true] String s = ... | +| Assert.cs:66:24:66:24 | [b (line 63): false] access to local variable s | Assert.cs:66:24:66:37 | [b (line 63): false] ... \|\| ... | +| Assert.cs:66:24:66:24 | [b (line 63): true] access to local variable s | Assert.cs:66:24:66:37 | [b (line 63): true] ... \|\| ... | +| Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | Assert.cs:66:29:66:32 | [b (line 63): false] null | +| Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | Assert.cs:66:29:66:32 | [b (line 63): true] null | +| Assert.cs:66:24:66:37 | [b (line 63): false] ... \|\| ... | Assert.cs:66:9:66:39 | [b (line 63): false] ...; | +| Assert.cs:66:24:66:37 | [b (line 63): true] ... \|\| ... | Assert.cs:66:9:66:39 | [b (line 63): true] ...; | +| Assert.cs:66:29:66:32 | [b (line 63): false] null | Assert.cs:66:24:66:24 | [b (line 63): false] access to local variable s | +| Assert.cs:66:29:66:32 | [b (line 63): true] null | Assert.cs:66:24:66:24 | [b (line 63): true] access to local variable s | +| Assert.cs:67:9:67:35 | call to method WriteLine | Assert.cs:67:27:67:34 | access to property Length | +| Assert.cs:67:9:67:36 | ...; | Assert.cs:66:9:66:38 | [assertion success] call to method IsFalse | +| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:67:9:67:36 | ...; | +| Assert.cs:67:27:67:34 | access to property Length | Assert.cs:67:27:67:27 | access to local variable s | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:74:9:74:35 | call to method WriteLine | +| Assert.cs:71:5:75:5 | {...} | Assert.cs:70:10:70:12 | enter M10 | +| Assert.cs:72:9:72:33 | ... ...; | Assert.cs:71:5:75:5 | {...} | +| Assert.cs:72:16:72:32 | [b (line 70): false] String s = ... | Assert.cs:72:31:72:32 | [b (line 70): false] "" | +| Assert.cs:72:16:72:32 | [b (line 70): true] String s = ... | Assert.cs:72:24:72:27 | [b (line 70): true] null | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | +| Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:9:72:33 | ... ...; | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | +| Assert.cs:73:9:73:37 | [assertion success] call to method IsTrue | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | +| Assert.cs:73:9:73:38 | [b (line 70): false] ...; | Assert.cs:72:16:72:32 | [b (line 70): false] String s = ... | +| Assert.cs:73:9:73:38 | [b (line 70): true] ...; | Assert.cs:72:16:72:32 | [b (line 70): true] String s = ... | +| Assert.cs:73:23:73:23 | [b (line 70): false] access to local variable s | Assert.cs:73:23:73:36 | [b (line 70): false] ... && ... | +| Assert.cs:73:23:73:23 | [b (line 70): true] access to local variable s | Assert.cs:73:23:73:36 | [b (line 70): true] ... && ... | +| Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | Assert.cs:73:28:73:31 | [b (line 70): false] null | +| Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | Assert.cs:73:28:73:31 | [b (line 70): true] null | +| Assert.cs:73:23:73:36 | [b (line 70): false] ... && ... | Assert.cs:73:9:73:38 | [b (line 70): false] ...; | +| Assert.cs:73:23:73:36 | [b (line 70): true] ... && ... | Assert.cs:73:9:73:38 | [b (line 70): true] ...; | +| Assert.cs:73:28:73:31 | [b (line 70): false] null | Assert.cs:73:23:73:23 | [b (line 70): false] access to local variable s | +| Assert.cs:73:28:73:31 | [b (line 70): true] null | Assert.cs:73:23:73:23 | [b (line 70): true] access to local variable s | +| Assert.cs:74:9:74:35 | call to method WriteLine | Assert.cs:74:27:74:34 | access to property Length | +| Assert.cs:74:9:74:36 | ...; | Assert.cs:73:9:73:37 | [assertion success] call to method IsTrue | +| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:74:9:74:36 | ...; | +| Assert.cs:74:27:74:34 | access to property Length | Assert.cs:74:27:74:27 | access to local variable s | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:81:9:81:35 | call to method WriteLine | +| Assert.cs:78:5:82:5 | {...} | Assert.cs:77:10:77:12 | enter M11 | +| Assert.cs:79:9:79:33 | ... ...; | Assert.cs:78:5:82:5 | {...} | +| Assert.cs:79:16:79:32 | [b (line 77): false] String s = ... | Assert.cs:79:31:79:32 | [b (line 77): false] "" | +| Assert.cs:79:16:79:32 | [b (line 77): true] String s = ... | Assert.cs:79:24:79:27 | [b (line 77): true] null | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | +| Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:9:79:33 | ... ...; | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | +| Assert.cs:80:9:80:38 | [assertion success] call to method IsFalse | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | +| Assert.cs:80:9:80:39 | [b (line 77): false] ...; | Assert.cs:79:16:79:32 | [b (line 77): false] String s = ... | +| Assert.cs:80:9:80:39 | [b (line 77): true] ...; | Assert.cs:79:16:79:32 | [b (line 77): true] String s = ... | +| Assert.cs:80:24:80:24 | [b (line 77): false] access to local variable s | Assert.cs:80:24:80:37 | [b (line 77): false] ... \|\| ... | +| Assert.cs:80:24:80:24 | [b (line 77): true] access to local variable s | Assert.cs:80:24:80:37 | [b (line 77): true] ... \|\| ... | +| Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | Assert.cs:80:29:80:32 | [b (line 77): false] null | +| Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | Assert.cs:80:29:80:32 | [b (line 77): true] null | +| Assert.cs:80:24:80:37 | [b (line 77): false] ... \|\| ... | Assert.cs:80:9:80:39 | [b (line 77): false] ...; | +| Assert.cs:80:24:80:37 | [b (line 77): true] ... \|\| ... | Assert.cs:80:9:80:39 | [b (line 77): true] ...; | +| Assert.cs:80:29:80:32 | [b (line 77): false] null | Assert.cs:80:24:80:24 | [b (line 77): false] access to local variable s | +| Assert.cs:80:29:80:32 | [b (line 77): true] null | Assert.cs:80:24:80:24 | [b (line 77): true] access to local variable s | +| Assert.cs:81:9:81:35 | call to method WriteLine | Assert.cs:81:27:81:34 | access to property Length | +| Assert.cs:81:9:81:36 | ...; | Assert.cs:80:9:80:38 | [assertion success] call to method IsFalse | +| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:81:9:81:36 | ...; | +| Assert.cs:81:27:81:34 | access to property Length | Assert.cs:81:27:81:27 | access to local variable s | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:128:9:128:35 | call to method WriteLine | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:84:10:84:12 | enter M12 | +| Assert.cs:86:9:86:33 | ... ...; | Assert.cs:85:5:129:5 | {...} | +| Assert.cs:86:16:86:32 | [b (line 84): false] String s = ... | Assert.cs:86:31:86:32 | [b (line 84): false] "" | +| Assert.cs:86:16:86:32 | [b (line 84): true] String s = ... | Assert.cs:86:24:86:27 | [b (line 84): true] null | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:20:86:32 | ... ? ... : ... | +| Assert.cs:86:20:86:32 | ... ? ... : ... | Assert.cs:86:9:86:33 | ... ...; | +| Assert.cs:87:9:87:32 | [b (line 84): false] ...; | Assert.cs:86:16:86:32 | [b (line 84): false] String s = ... | +| Assert.cs:87:9:87:32 | [b (line 84): true] ...; | Assert.cs:86:16:86:32 | [b (line 84): true] String s = ... | +| Assert.cs:87:22:87:22 | [b (line 84): false] access to local variable s | Assert.cs:87:9:87:32 | [b (line 84): false] ...; | +| Assert.cs:87:22:87:22 | [b (line 84): true] access to local variable s | Assert.cs:87:9:87:32 | [b (line 84): true] ...; | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | Assert.cs:87:27:87:30 | [b (line 84): false] null | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | Assert.cs:87:27:87:30 | [b (line 84): true] null | +| Assert.cs:87:27:87:30 | [b (line 84): false] null | Assert.cs:87:22:87:22 | [b (line 84): false] access to local variable s | +| Assert.cs:87:27:87:30 | [b (line 84): true] null | Assert.cs:87:22:87:22 | [b (line 84): true] access to local variable s | +| Assert.cs:88:9:88:35 | [b (line 84): false] call to method WriteLine | Assert.cs:88:27:88:34 | [b (line 84): false] access to property Length | +| Assert.cs:88:9:88:35 | [b (line 84): true] call to method WriteLine | Assert.cs:88:27:88:34 | [b (line 84): true] access to property Length | +| Assert.cs:88:9:88:36 | [b (line 84): false] ...; | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | +| Assert.cs:88:9:88:36 | [b (line 84): true] ...; | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | +| Assert.cs:88:27:88:27 | [b (line 84): false] access to local variable s | Assert.cs:88:9:88:36 | [b (line 84): false] ...; | +| Assert.cs:88:27:88:27 | [b (line 84): true] access to local variable s | Assert.cs:88:9:88:36 | [b (line 84): true] ...; | +| Assert.cs:88:27:88:34 | [b (line 84): false] access to property Length | Assert.cs:88:27:88:27 | [b (line 84): false] access to local variable s | +| Assert.cs:88:27:88:34 | [b (line 84): true] access to property Length | Assert.cs:88:27:88:27 | [b (line 84): true] access to local variable s | +| Assert.cs:90:9:90:25 | [b (line 84): false] ... = ... | Assert.cs:90:24:90:25 | [b (line 84): false] "" | +| Assert.cs:90:9:90:25 | [b (line 84): true] ... = ... | Assert.cs:90:17:90:20 | [b (line 84): true] null | +| Assert.cs:90:9:90:26 | [b (line 84): false] ...; | Assert.cs:88:9:88:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:90:9:90:26 | [b (line 84): true] ...; | Assert.cs:88:9:88:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | Assert.cs:90:13:90:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | Assert.cs:90:13:90:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:90:13:90:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:90:9:90:26 | [b (line 84): false] ...; | +| Assert.cs:90:13:90:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:90:9:90:26 | [b (line 84): true] ...; | +| Assert.cs:90:17:90:20 | [b (line 84): true] null | Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | +| Assert.cs:90:24:90:25 | [b (line 84): false] "" | Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | +| Assert.cs:91:9:91:25 | [b (line 84): false] ...; | Assert.cs:90:9:90:25 | [b (line 84): false] ... = ... | +| Assert.cs:91:9:91:25 | [b (line 84): true] ...; | Assert.cs:90:9:90:25 | [b (line 84): true] ... = ... | +| Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | Assert.cs:91:9:91:25 | [b (line 84): false] ...; | +| Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | Assert.cs:91:9:91:25 | [b (line 84): true] ...; | +| Assert.cs:92:9:92:35 | [b (line 84): false] call to method WriteLine | Assert.cs:92:27:92:34 | [b (line 84): false] access to property Length | +| Assert.cs:92:9:92:35 | [b (line 84): true] call to method WriteLine | Assert.cs:92:27:92:34 | [b (line 84): true] access to property Length | +| Assert.cs:92:9:92:36 | [b (line 84): false] ...; | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | +| Assert.cs:92:9:92:36 | [b (line 84): true] ...; | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | +| Assert.cs:92:27:92:27 | [b (line 84): false] access to local variable s | Assert.cs:92:9:92:36 | [b (line 84): false] ...; | +| Assert.cs:92:27:92:27 | [b (line 84): true] access to local variable s | Assert.cs:92:9:92:36 | [b (line 84): true] ...; | +| Assert.cs:92:27:92:34 | [b (line 84): false] access to property Length | Assert.cs:92:27:92:27 | [b (line 84): false] access to local variable s | +| Assert.cs:92:27:92:34 | [b (line 84): true] access to property Length | Assert.cs:92:27:92:27 | [b (line 84): true] access to local variable s | +| Assert.cs:94:9:94:25 | [b (line 84): false] ... = ... | Assert.cs:94:24:94:25 | [b (line 84): false] "" | +| Assert.cs:94:9:94:25 | [b (line 84): true] ... = ... | Assert.cs:94:17:94:20 | [b (line 84): true] null | +| Assert.cs:94:9:94:26 | [b (line 84): false] ...; | Assert.cs:92:9:92:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:94:9:94:26 | [b (line 84): true] ...; | Assert.cs:92:9:92:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | Assert.cs:94:13:94:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | Assert.cs:94:13:94:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:94:13:94:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:94:9:94:26 | [b (line 84): false] ...; | +| Assert.cs:94:13:94:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:94:9:94:26 | [b (line 84): true] ...; | +| Assert.cs:94:17:94:20 | [b (line 84): true] null | Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | +| Assert.cs:94:24:94:25 | [b (line 84): false] "" | Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | +| Assert.cs:95:9:95:28 | [b (line 84): false] ...; | Assert.cs:94:9:94:25 | [b (line 84): false] ... = ... | +| Assert.cs:95:9:95:28 | [b (line 84): true] ...; | Assert.cs:94:9:94:25 | [b (line 84): true] ... = ... | +| Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | Assert.cs:95:9:95:28 | [b (line 84): false] ...; | +| Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | Assert.cs:95:9:95:28 | [b (line 84): true] ...; | +| Assert.cs:96:9:96:35 | [b (line 84): false] call to method WriteLine | Assert.cs:96:27:96:34 | [b (line 84): false] access to property Length | +| Assert.cs:96:9:96:35 | [b (line 84): true] call to method WriteLine | Assert.cs:96:27:96:34 | [b (line 84): true] access to property Length | +| Assert.cs:96:9:96:36 | [b (line 84): false] ...; | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:96:9:96:36 | [b (line 84): true] ...; | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:96:27:96:27 | [b (line 84): false] access to local variable s | Assert.cs:96:9:96:36 | [b (line 84): false] ...; | +| Assert.cs:96:27:96:27 | [b (line 84): true] access to local variable s | Assert.cs:96:9:96:36 | [b (line 84): true] ...; | +| Assert.cs:96:27:96:34 | [b (line 84): false] access to property Length | Assert.cs:96:27:96:27 | [b (line 84): false] access to local variable s | +| Assert.cs:96:27:96:34 | [b (line 84): true] access to property Length | Assert.cs:96:27:96:27 | [b (line 84): true] access to local variable s | +| Assert.cs:98:9:98:25 | [b (line 84): false] ... = ... | Assert.cs:98:24:98:25 | [b (line 84): false] "" | +| Assert.cs:98:9:98:25 | [b (line 84): true] ... = ... | Assert.cs:98:17:98:20 | [b (line 84): true] null | +| Assert.cs:98:9:98:26 | [b (line 84): false] ...; | Assert.cs:96:9:96:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:98:9:98:26 | [b (line 84): true] ...; | Assert.cs:96:9:96:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | Assert.cs:98:13:98:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | Assert.cs:98:13:98:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:98:13:98:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:98:9:98:26 | [b (line 84): false] ...; | +| Assert.cs:98:13:98:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:98:9:98:26 | [b (line 84): true] ...; | +| Assert.cs:98:17:98:20 | [b (line 84): true] null | Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | +| Assert.cs:98:24:98:25 | [b (line 84): false] "" | Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | +| Assert.cs:99:9:99:33 | [b (line 84): false] ...; | Assert.cs:98:9:98:25 | [b (line 84): false] ... = ... | +| Assert.cs:99:9:99:33 | [b (line 84): true] ...; | Assert.cs:98:9:98:25 | [b (line 84): true] ... = ... | +| Assert.cs:99:23:99:23 | [b (line 84): false] access to local variable s | Assert.cs:99:9:99:33 | [b (line 84): false] ...; | +| Assert.cs:99:23:99:23 | [b (line 84): true] access to local variable s | Assert.cs:99:9:99:33 | [b (line 84): true] ...; | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | Assert.cs:99:28:99:31 | [b (line 84): false] null | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | Assert.cs:99:28:99:31 | [b (line 84): true] null | +| Assert.cs:99:28:99:31 | [b (line 84): false] null | Assert.cs:99:23:99:23 | [b (line 84): false] access to local variable s | +| Assert.cs:99:28:99:31 | [b (line 84): true] null | Assert.cs:99:23:99:23 | [b (line 84): true] access to local variable s | +| Assert.cs:100:9:100:35 | [b (line 84): false] call to method WriteLine | Assert.cs:100:27:100:34 | [b (line 84): false] access to property Length | +| Assert.cs:100:9:100:35 | [b (line 84): true] call to method WriteLine | Assert.cs:100:27:100:34 | [b (line 84): true] access to property Length | +| Assert.cs:100:9:100:36 | [b (line 84): false] ...; | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:100:9:100:36 | [b (line 84): true] ...; | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:100:27:100:27 | [b (line 84): false] access to local variable s | Assert.cs:100:9:100:36 | [b (line 84): false] ...; | +| Assert.cs:100:27:100:27 | [b (line 84): true] access to local variable s | Assert.cs:100:9:100:36 | [b (line 84): true] ...; | +| Assert.cs:100:27:100:34 | [b (line 84): false] access to property Length | Assert.cs:100:27:100:27 | [b (line 84): false] access to local variable s | +| Assert.cs:100:27:100:34 | [b (line 84): true] access to property Length | Assert.cs:100:27:100:27 | [b (line 84): true] access to local variable s | +| Assert.cs:102:9:102:25 | [b (line 84): false] ... = ... | Assert.cs:102:24:102:25 | [b (line 84): false] "" | +| Assert.cs:102:9:102:25 | [b (line 84): true] ... = ... | Assert.cs:102:17:102:20 | [b (line 84): true] null | +| Assert.cs:102:9:102:26 | [b (line 84): false] ...; | Assert.cs:100:9:100:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:102:9:102:26 | [b (line 84): true] ...; | Assert.cs:100:9:100:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | Assert.cs:102:13:102:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | Assert.cs:102:13:102:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:102:13:102:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:102:9:102:26 | [b (line 84): false] ...; | +| Assert.cs:102:13:102:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:102:9:102:26 | [b (line 84): true] ...; | +| Assert.cs:102:17:102:20 | [b (line 84): true] null | Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | +| Assert.cs:102:24:102:25 | [b (line 84): false] "" | Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | +| Assert.cs:103:9:103:33 | [b (line 84): false] ...; | Assert.cs:102:9:102:25 | [b (line 84): false] ... = ... | +| Assert.cs:103:9:103:33 | [b (line 84): true] ...; | Assert.cs:102:9:102:25 | [b (line 84): true] ... = ... | +| Assert.cs:103:23:103:23 | [b (line 84): false] access to local variable s | Assert.cs:103:9:103:33 | [b (line 84): false] ...; | +| Assert.cs:103:23:103:23 | [b (line 84): true] access to local variable s | Assert.cs:103:9:103:33 | [b (line 84): true] ...; | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | Assert.cs:103:28:103:31 | [b (line 84): false] null | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | Assert.cs:103:28:103:31 | [b (line 84): true] null | +| Assert.cs:103:28:103:31 | [b (line 84): false] null | Assert.cs:103:23:103:23 | [b (line 84): false] access to local variable s | +| Assert.cs:103:28:103:31 | [b (line 84): true] null | Assert.cs:103:23:103:23 | [b (line 84): true] access to local variable s | +| Assert.cs:104:9:104:35 | [b (line 84): false] call to method WriteLine | Assert.cs:104:27:104:34 | [b (line 84): false] access to property Length | +| Assert.cs:104:9:104:35 | [b (line 84): true] call to method WriteLine | Assert.cs:104:27:104:34 | [b (line 84): true] access to property Length | +| Assert.cs:104:9:104:36 | [b (line 84): false] ...; | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:104:9:104:36 | [b (line 84): true] ...; | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:104:27:104:27 | [b (line 84): false] access to local variable s | Assert.cs:104:9:104:36 | [b (line 84): false] ...; | +| Assert.cs:104:27:104:27 | [b (line 84): true] access to local variable s | Assert.cs:104:9:104:36 | [b (line 84): true] ...; | +| Assert.cs:104:27:104:34 | [b (line 84): false] access to property Length | Assert.cs:104:27:104:27 | [b (line 84): false] access to local variable s | +| Assert.cs:104:27:104:34 | [b (line 84): true] access to property Length | Assert.cs:104:27:104:27 | [b (line 84): true] access to local variable s | +| Assert.cs:106:9:106:25 | [b (line 84): false] ... = ... | Assert.cs:106:24:106:25 | [b (line 84): false] "" | +| Assert.cs:106:9:106:25 | [b (line 84): true] ... = ... | Assert.cs:106:17:106:20 | [b (line 84): true] null | +| Assert.cs:106:9:106:26 | [b (line 84): false] ...; | Assert.cs:104:9:104:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:106:9:106:26 | [b (line 84): true] ...; | Assert.cs:104:9:104:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | Assert.cs:106:13:106:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | Assert.cs:106:13:106:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:106:13:106:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:106:9:106:26 | [b (line 84): false] ...; | +| Assert.cs:106:13:106:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:106:9:106:26 | [b (line 84): true] ...; | +| Assert.cs:106:17:106:20 | [b (line 84): true] null | Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | +| Assert.cs:106:24:106:25 | [b (line 84): false] "" | Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | +| Assert.cs:107:9:107:34 | [b (line 84): false] ...; | Assert.cs:106:9:106:25 | [b (line 84): false] ... = ... | +| Assert.cs:107:9:107:34 | [b (line 84): true] ...; | Assert.cs:106:9:106:25 | [b (line 84): true] ... = ... | +| Assert.cs:107:24:107:24 | [b (line 84): false] access to local variable s | Assert.cs:107:9:107:34 | [b (line 84): false] ...; | +| Assert.cs:107:24:107:24 | [b (line 84): true] access to local variable s | Assert.cs:107:9:107:34 | [b (line 84): true] ...; | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | Assert.cs:107:29:107:32 | [b (line 84): false] null | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | Assert.cs:107:29:107:32 | [b (line 84): true] null | +| Assert.cs:107:29:107:32 | [b (line 84): false] null | Assert.cs:107:24:107:24 | [b (line 84): false] access to local variable s | +| Assert.cs:107:29:107:32 | [b (line 84): true] null | Assert.cs:107:24:107:24 | [b (line 84): true] access to local variable s | +| Assert.cs:108:9:108:35 | [b (line 84): false] call to method WriteLine | Assert.cs:108:27:108:34 | [b (line 84): false] access to property Length | +| Assert.cs:108:9:108:35 | [b (line 84): true] call to method WriteLine | Assert.cs:108:27:108:34 | [b (line 84): true] access to property Length | +| Assert.cs:108:9:108:36 | [b (line 84): false] ...; | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:108:9:108:36 | [b (line 84): true] ...; | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:108:27:108:27 | [b (line 84): false] access to local variable s | Assert.cs:108:9:108:36 | [b (line 84): false] ...; | +| Assert.cs:108:27:108:27 | [b (line 84): true] access to local variable s | Assert.cs:108:9:108:36 | [b (line 84): true] ...; | +| Assert.cs:108:27:108:34 | [b (line 84): false] access to property Length | Assert.cs:108:27:108:27 | [b (line 84): false] access to local variable s | +| Assert.cs:108:27:108:34 | [b (line 84): true] access to property Length | Assert.cs:108:27:108:27 | [b (line 84): true] access to local variable s | +| Assert.cs:110:9:110:25 | [b (line 84): false] ... = ... | Assert.cs:110:24:110:25 | [b (line 84): false] "" | +| Assert.cs:110:9:110:25 | [b (line 84): true] ... = ... | Assert.cs:110:17:110:20 | [b (line 84): true] null | +| Assert.cs:110:9:110:26 | [b (line 84): false] ...; | Assert.cs:108:9:108:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:110:9:110:26 | [b (line 84): true] ...; | Assert.cs:108:9:108:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | Assert.cs:110:13:110:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | Assert.cs:110:13:110:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:110:13:110:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:110:9:110:26 | [b (line 84): false] ...; | +| Assert.cs:110:13:110:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:110:9:110:26 | [b (line 84): true] ...; | +| Assert.cs:110:17:110:20 | [b (line 84): true] null | Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | +| Assert.cs:110:24:110:25 | [b (line 84): false] "" | Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | +| Assert.cs:111:9:111:34 | [b (line 84): false] ...; | Assert.cs:110:9:110:25 | [b (line 84): false] ... = ... | +| Assert.cs:111:9:111:34 | [b (line 84): true] ...; | Assert.cs:110:9:110:25 | [b (line 84): true] ... = ... | +| Assert.cs:111:24:111:24 | [b (line 84): false] access to local variable s | Assert.cs:111:9:111:34 | [b (line 84): false] ...; | +| Assert.cs:111:24:111:24 | [b (line 84): true] access to local variable s | Assert.cs:111:9:111:34 | [b (line 84): true] ...; | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | Assert.cs:111:29:111:32 | [b (line 84): false] null | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | Assert.cs:111:29:111:32 | [b (line 84): true] null | +| Assert.cs:111:29:111:32 | [b (line 84): false] null | Assert.cs:111:24:111:24 | [b (line 84): false] access to local variable s | +| Assert.cs:111:29:111:32 | [b (line 84): true] null | Assert.cs:111:24:111:24 | [b (line 84): true] access to local variable s | +| Assert.cs:112:9:112:35 | [b (line 84): false] call to method WriteLine | Assert.cs:112:27:112:34 | [b (line 84): false] access to property Length | +| Assert.cs:112:9:112:35 | [b (line 84): true] call to method WriteLine | Assert.cs:112:27:112:34 | [b (line 84): true] access to property Length | +| Assert.cs:112:9:112:36 | [b (line 84): false] ...; | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:112:9:112:36 | [b (line 84): true] ...; | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:112:27:112:27 | [b (line 84): false] access to local variable s | Assert.cs:112:9:112:36 | [b (line 84): false] ...; | +| Assert.cs:112:27:112:27 | [b (line 84): true] access to local variable s | Assert.cs:112:9:112:36 | [b (line 84): true] ...; | +| Assert.cs:112:27:112:34 | [b (line 84): false] access to property Length | Assert.cs:112:27:112:27 | [b (line 84): false] access to local variable s | +| Assert.cs:112:27:112:34 | [b (line 84): true] access to property Length | Assert.cs:112:27:112:27 | [b (line 84): true] access to local variable s | +| Assert.cs:114:9:114:25 | [b (line 84): false] ... = ... | Assert.cs:114:24:114:25 | [b (line 84): false] "" | +| Assert.cs:114:9:114:25 | [b (line 84): true] ... = ... | Assert.cs:114:17:114:20 | [b (line 84): true] null | +| Assert.cs:114:9:114:26 | [b (line 84): false] ...; | Assert.cs:112:9:112:35 | [b (line 84): false] call to method WriteLine | +| Assert.cs:114:9:114:26 | [b (line 84): true] ...; | Assert.cs:112:9:112:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | Assert.cs:114:13:114:25 | [b (line 84): false] ... ? ... : ... | +| Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | Assert.cs:114:13:114:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:114:13:114:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:114:9:114:26 | [b (line 84): false] ...; | +| Assert.cs:114:13:114:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:114:9:114:26 | [b (line 84): true] ...; | +| Assert.cs:114:17:114:20 | [b (line 84): true] null | Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | +| Assert.cs:114:24:114:25 | [b (line 84): false] "" | Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:115:9:115:38 | [b (line 84): false] ...; | Assert.cs:114:9:114:25 | [b (line 84): false] ... = ... | +| Assert.cs:115:9:115:38 | [b (line 84): true] ...; | Assert.cs:114:9:114:25 | [b (line 84): true] ... = ... | +| Assert.cs:115:23:115:23 | [b (line 84): false] access to local variable s | Assert.cs:115:23:115:36 | [b (line 84): false] ... && ... | +| Assert.cs:115:23:115:23 | [b (line 84): true] access to local variable s | Assert.cs:115:23:115:36 | [b (line 84): true] ... && ... | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | Assert.cs:115:28:115:31 | [b (line 84): false] null | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | Assert.cs:115:28:115:31 | [b (line 84): true] null | +| Assert.cs:115:23:115:36 | [b (line 84): false] ... && ... | Assert.cs:115:9:115:38 | [b (line 84): false] ...; | +| Assert.cs:115:23:115:36 | [b (line 84): true] ... && ... | Assert.cs:115:9:115:38 | [b (line 84): true] ...; | +| Assert.cs:115:28:115:31 | [b (line 84): false] null | Assert.cs:115:23:115:23 | [b (line 84): false] access to local variable s | +| Assert.cs:115:28:115:31 | [b (line 84): true] null | Assert.cs:115:23:115:23 | [b (line 84): true] access to local variable s | +| Assert.cs:116:9:116:35 | [b (line 84): true] call to method WriteLine | Assert.cs:116:27:116:34 | [b (line 84): true] access to property Length | +| Assert.cs:116:9:116:36 | [b (line 84): true] ...; | Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:116:27:116:27 | [b (line 84): true] access to local variable s | Assert.cs:116:9:116:36 | [b (line 84): true] ...; | +| Assert.cs:116:27:116:34 | [b (line 84): true] access to property Length | Assert.cs:116:27:116:27 | [b (line 84): true] access to local variable s | +| Assert.cs:118:9:118:25 | [b (line 84): true] ... = ... | Assert.cs:118:17:118:20 | [b (line 84): true] null | +| Assert.cs:118:9:118:26 | [b (line 84): true] ...; | Assert.cs:116:9:116:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | Assert.cs:118:13:118:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:118:13:118:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:118:9:118:26 | [b (line 84): true] ...; | +| Assert.cs:118:17:118:20 | [b (line 84): true] null | Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | +| Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | +| Assert.cs:119:9:119:40 | [b (line 84): true] ...; | Assert.cs:118:9:118:25 | [b (line 84): true] ... = ... | +| Assert.cs:119:24:119:24 | [b (line 84): true] access to local variable s | Assert.cs:119:24:119:38 | [b (line 84): true] ... \|\| ... | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | Assert.cs:119:29:119:32 | [b (line 84): true] null | +| Assert.cs:119:24:119:38 | [b (line 84): true] ... \|\| ... | Assert.cs:119:9:119:40 | [b (line 84): true] ...; | +| Assert.cs:119:29:119:32 | [b (line 84): true] null | Assert.cs:119:24:119:24 | [b (line 84): true] access to local variable s | +| Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:120:9:120:35 | [b (line 84): true] call to method WriteLine | Assert.cs:120:27:120:34 | [b (line 84): true] access to property Length | +| Assert.cs:120:9:120:36 | [b (line 84): true] ...; | Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:120:27:120:27 | [b (line 84): true] access to local variable s | Assert.cs:120:9:120:36 | [b (line 84): true] ...; | +| Assert.cs:120:27:120:34 | [b (line 84): true] access to property Length | Assert.cs:120:27:120:27 | [b (line 84): true] access to local variable s | +| Assert.cs:122:9:122:25 | [b (line 84): true] ... = ... | Assert.cs:122:17:122:20 | [b (line 84): true] null | +| Assert.cs:122:9:122:26 | [b (line 84): true] ...; | Assert.cs:120:9:120:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | Assert.cs:122:13:122:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:122:13:122:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:122:9:122:26 | [b (line 84): true] ...; | +| Assert.cs:122:17:122:20 | [b (line 84): true] null | Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | +| Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:123:9:123:38 | [b (line 84): true] ...; | Assert.cs:122:9:122:25 | [b (line 84): true] ... = ... | +| Assert.cs:123:23:123:23 | [b (line 84): true] access to local variable s | Assert.cs:123:23:123:36 | [b (line 84): true] ... && ... | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | Assert.cs:123:28:123:31 | [b (line 84): true] null | +| Assert.cs:123:23:123:36 | [b (line 84): true] ... && ... | Assert.cs:123:9:123:38 | [b (line 84): true] ...; | +| Assert.cs:123:28:123:31 | [b (line 84): true] null | Assert.cs:123:23:123:23 | [b (line 84): true] access to local variable s | +| Assert.cs:124:9:124:35 | [b (line 84): true] call to method WriteLine | Assert.cs:124:27:124:34 | [b (line 84): true] access to property Length | +| Assert.cs:124:9:124:36 | [b (line 84): true] ...; | Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:124:27:124:27 | [b (line 84): true] access to local variable s | Assert.cs:124:9:124:36 | [b (line 84): true] ...; | +| Assert.cs:124:27:124:34 | [b (line 84): true] access to property Length | Assert.cs:124:27:124:27 | [b (line 84): true] access to local variable s | +| Assert.cs:126:9:126:25 | [b (line 84): true] ... = ... | Assert.cs:126:17:126:20 | [b (line 84): true] null | +| Assert.cs:126:9:126:26 | [b (line 84): true] ...; | Assert.cs:124:9:124:35 | [b (line 84): true] call to method WriteLine | +| Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | Assert.cs:126:13:126:25 | [b (line 84): true] ... ? ... : ... | +| Assert.cs:126:13:126:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:126:9:126:26 | [b (line 84): true] ...; | +| Assert.cs:126:17:126:20 | [b (line 84): true] null | Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | +| Assert.cs:127:9:127:39 | [assertion success] call to method IsFalse | Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | +| Assert.cs:127:9:127:40 | [b (line 84): true] ...; | Assert.cs:126:9:126:25 | [b (line 84): true] ... = ... | +| Assert.cs:127:24:127:24 | [b (line 84): true] access to local variable s | Assert.cs:127:24:127:38 | [b (line 84): true] ... \|\| ... | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | Assert.cs:127:29:127:32 | [b (line 84): true] null | +| Assert.cs:127:24:127:38 | [b (line 84): true] ... \|\| ... | Assert.cs:127:9:127:40 | [b (line 84): true] ...; | +| Assert.cs:127:29:127:32 | [b (line 84): true] null | Assert.cs:127:24:127:24 | [b (line 84): true] access to local variable s | +| Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:128:9:128:35 | call to method WriteLine | Assert.cs:128:27:128:34 | access to property Length | +| Assert.cs:128:9:128:36 | ...; | Assert.cs:127:9:127:39 | [assertion success] call to method IsFalse | +| Assert.cs:128:27:128:27 | access to local variable s | Assert.cs:128:9:128:36 | ...; | +| Assert.cs:128:27:128:34 | access to property Length | Assert.cs:128:27:128:27 | access to local variable s | | Assignments.cs:3:10:3:10 | exit M | Assignments.cs:14:9:14:35 | ... += ... | | Assignments.cs:4:5:15:5 | {...} | Assignments.cs:3:10:3:10 | enter M | | Assignments.cs:5:9:5:18 | ... ...; | Assignments.cs:4:5:15:5 | {...} | @@ -3532,6 +4697,8 @@ postDominance | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | CompileTimeOperators.cs:40:32:40:36 | "End" | | CompileTimeOperators.cs:40:14:40:38 | ...; | CompileTimeOperators.cs:40:9:40:11 | End: | | CompileTimeOperators.cs:40:32:40:36 | "End" | CompileTimeOperators.cs:40:14:40:38 | ...; | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:30:28:30:32 | ... = ... | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:30:28:30:32 | ... = ... | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:28:3:38 | call to method ToString | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:40:3:49 | call to method ToLower | @@ -3578,12 +4745,26 @@ postDominance | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:9:25:33 | ...; | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:31:25:31 | access to local variable s | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:13:25:14 | "" | -| ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | ConditionalAccess.cs:31:70:31:83 | ... + ... | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:75:31:78 | ", " | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:70:31:78 | ... + ... | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:28:30:32 | ... = ... | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:10:30:12 | enter Out | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:35:9:35:12 | access to property Prop | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:35:14:35:24 | call to method Out | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:34:13:34:13 | 0 | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:33:5:36:5 | {...} | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:9:34:14 | ...; | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:25 | ...; | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:34:9:34:13 | ... = ... | +| ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | ConditionalAccess.cs:41:70:41:83 | ... + ... | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:75:41:78 | ", " | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:70:41:78 | ... + ... | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:7:14:7:16 | [inc (line 3): true] access to parameter inc | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:8:13:8:15 | ...-- | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:3:10:3:19 | enter IncrOrDecr | @@ -3772,11 +4953,11 @@ postDominance | Conditions.cs:79:17:79:25 | ... = ... | Conditions.cs:79:21:79:25 | false | | Conditions.cs:79:21:79:25 | false | Conditions.cs:79:17:79:26 | ...; | | Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:74:9:80:9 | foreach (... ... in ...) ... | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:81:9:82:16 | if (...) ... | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:81:9:82:16 | if (...) ... | | Conditions.cs:82:13:82:13 | access to local variable x | Conditions.cs:82:13:82:16 | ...; | | Conditions.cs:82:13:82:15 | ...++ | Conditions.cs:82:13:82:13 | access to local variable x | | Conditions.cs:83:9:83:17 | return ...; | Conditions.cs:83:16:83:16 | access to local variable x | -| Conditions.cs:83:16:83:16 | access to local variable x | Conditions.cs:81:12:81:12 | access to local variable b | +| Conditions.cs:83:16:83:16 | access to local variable x | Conditions.cs:81:13:81:13 | access to local variable b | | Conditions.cs:83:16:83:16 | access to local variable x | Conditions.cs:82:13:82:15 | ...++ | | Conditions.cs:86:9:86:10 | exit M7 | Conditions.cs:99:9:99:17 | return ...; | | Conditions.cs:87:5:100:5 | {...} | Conditions.cs:86:9:86:10 | enter M7 | @@ -3847,22 +5028,22 @@ postDominance | Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:107:13:107:24 | [b (line 102): true] ... > ... | | Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:108:18:108:18 | [b (line 102): true] access to parameter b | | Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:109:17:109:23 | ... = ... | -| Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:116:24:116:38 | ... < ... | +| Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:116:25:116:39 | ... < ... | | Conditions.cs:114:5:124:5 | {...} | Conditions.cs:113:10:113:11 | enter M9 | | Conditions.cs:115:9:115:24 | ... ...; | Conditions.cs:114:5:124:5 | {...} | | Conditions.cs:115:16:115:23 | String s = ... | Conditions.cs:115:20:115:23 | null | | Conditions.cs:115:20:115:23 | null | Conditions.cs:115:9:115:24 | ... ...; | | Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:115:16:115:23 | String s = ... | -| Conditions.cs:116:17:116:21 | Int32 i = ... | Conditions.cs:116:21:116:21 | 0 | -| Conditions.cs:116:21:116:21 | 0 | Conditions.cs:116:9:123:9 | for (...;...;...) ... | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:17:116:21 | Int32 i = ... | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:41:116:43 | ...++ | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:116:28:116:38 | access to property Length | -| Conditions.cs:116:28:116:31 | access to parameter args | Conditions.cs:116:24:116:24 | access to local variable i | -| Conditions.cs:116:28:116:38 | access to property Length | Conditions.cs:116:28:116:31 | access to parameter args | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:121:17:121:20 | [last (line 118): false] access to local variable last | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:122:17:122:24 | ... = ... | -| Conditions.cs:116:41:116:43 | ...++ | Conditions.cs:116:41:116:41 | access to local variable i | +| Conditions.cs:116:18:116:22 | Int32 i = ... | Conditions.cs:116:22:116:22 | 0 | +| Conditions.cs:116:22:116:22 | 0 | Conditions.cs:116:9:123:9 | for (...;...;...) ... | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:18:116:22 | Int32 i = ... | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:42:116:44 | ...++ | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:116:29:116:39 | access to property Length | +| Conditions.cs:116:29:116:32 | access to parameter args | Conditions.cs:116:25:116:25 | access to local variable i | +| Conditions.cs:116:29:116:39 | access to property Length | Conditions.cs:116:29:116:32 | access to parameter args | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:121:17:121:20 | [last (line 118): false] access to local variable last | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:122:17:122:24 | ... = ... | +| Conditions.cs:116:42:116:44 | ...++ | Conditions.cs:116:42:116:42 | access to local variable i | | Conditions.cs:118:13:118:44 | ... ...; | Conditions.cs:117:9:123:9 | {...} | | Conditions.cs:118:17:118:43 | Boolean last = ... | Conditions.cs:118:24:118:43 | ... == ... | | Conditions.cs:118:24:118:24 | access to local variable i | Conditions.cs:118:13:118:44 | ... ...; | @@ -3917,6 +5098,28 @@ postDominance | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] this access | Conditions.cs:137:21:137:38 | [Field1 (line 129): true, Field2 (line 129): true] ...; | | Conditions.cs:137:21:137:37 | [Field1 (line 129): true, Field2 (line 129): true] call to method ToString | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] access to field Field1 | | Conditions.cs:137:21:137:38 | [Field1 (line 129): true, Field2 (line 129): true] ...; | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:147:13:147:48 | call to method WriteLine | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:149:13:149:48 | call to method WriteLine | +| Conditions.cs:144:5:150:5 | {...} | Conditions.cs:143:10:143:12 | enter M11 | +| Conditions.cs:145:9:145:30 | ... ...; | Conditions.cs:144:5:150:5 | {...} | +| Conditions.cs:145:13:145:29 | [b (line 143): false] String s = ... | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | +| Conditions.cs:145:13:145:29 | [b (line 143): true] String s = ... | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:17:145:29 | ... ? ... : ... | +| Conditions.cs:145:17:145:29 | ... ? ... : ... | Conditions.cs:145:9:145:30 | ... ...; | +| Conditions.cs:146:9:149:49 | [b (line 143): false] if (...) ... | Conditions.cs:145:13:145:29 | [b (line 143): false] String s = ... | +| Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... | Conditions.cs:145:13:145:29 | [b (line 143): true] String s = ... | +| Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | Conditions.cs:146:9:149:49 | [b (line 143): false] if (...) ... | +| Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... | +| Conditions.cs:147:13:147:48 | call to method WriteLine | Conditions.cs:147:38:147:47 | $"..." | +| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | +| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:45:147:45 | access to local variable s | +| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:13:147:49 | ...; | +| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:40:147:43 | "a = " | +| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:149:38:149:47 | $"..." | +| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | +| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:45:149:45 | access to local variable s | +| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:13:149:49 | ...; | +| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:40:149:43 | "b = " | | ExitMethods.cs:7:10:7:11 | exit M1 | ExitMethods.cs:10:9:10:15 | return ...; | | ExitMethods.cs:8:5:11:5 | {...} | ExitMethods.cs:7:10:7:11 | enter M1 | | ExitMethods.cs:9:9:9:24 | call to method ErrorMaybe | ExitMethods.cs:9:20:9:23 | true | @@ -4023,9 +5226,9 @@ postDominance | ExitMethods.cs:116:16:116:30 | call to method Contains | ExitMethods.cs:116:27:116:29 | - | | ExitMethods.cs:116:16:116:38 | ... ? ... : ... | ExitMethods.cs:115:5:117:5 | {...} | | ExitMethods.cs:116:27:116:29 | - | ExitMethods.cs:116:16:116:16 | access to parameter s | -| ExitMethods.cs:119:17:119:32 | exit FailingAssertion | ExitMethods.cs:121:9:121:28 | call to method IsTrue | +| ExitMethods.cs:119:17:119:32 | exit FailingAssertion | ExitMethods.cs:121:9:121:28 | [assertion failure] call to method IsTrue | | ExitMethods.cs:120:5:123:5 | {...} | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | -| ExitMethods.cs:121:9:121:28 | call to method IsTrue | ExitMethods.cs:121:23:121:27 | false | +| ExitMethods.cs:121:9:121:28 | [assertion failure] call to method IsTrue | ExitMethods.cs:121:23:121:27 | false | | ExitMethods.cs:121:9:121:29 | ...; | ExitMethods.cs:120:5:123:5 | {...} | | ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:121:9:121:29 | ...; | | ExitMethods.cs:125:17:125:33 | exit FailingAssertion2 | ExitMethods.cs:127:9:127:26 | call to method FailingAssertion | @@ -4033,12 +5236,12 @@ postDominance | ExitMethods.cs:127:9:127:26 | call to method FailingAssertion | ExitMethods.cs:127:9:127:26 | this access | | ExitMethods.cs:127:9:127:26 | this access | ExitMethods.cs:127:9:127:27 | ...; | | ExitMethods.cs:127:9:127:27 | ...; | ExitMethods.cs:126:5:129:5 | {...} | -| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:33:131:49 | call to method IsFalse | -| ExitMethods.cs:131:33:131:49 | call to method IsFalse | ExitMethods.cs:131:48:131:48 | access to parameter b | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | | ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:10:131:20 | enter AssertFalse | -| ExitMethods.cs:133:17:133:33 | exit FailingAssertion3 | ExitMethods.cs:135:9:135:25 | call to method AssertFalse | +| ExitMethods.cs:133:17:133:33 | exit FailingAssertion3 | ExitMethods.cs:135:9:135:25 | [assertion failure] call to method AssertFalse | | ExitMethods.cs:134:5:137:5 | {...} | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | -| ExitMethods.cs:135:9:135:25 | call to method AssertFalse | ExitMethods.cs:135:21:135:24 | true | +| ExitMethods.cs:135:9:135:25 | [assertion failure] call to method AssertFalse | ExitMethods.cs:135:21:135:24 | true | | ExitMethods.cs:135:9:135:25 | this access | ExitMethods.cs:135:9:135:26 | ...; | | ExitMethods.cs:135:9:135:26 | ...; | ExitMethods.cs:134:5:137:5 | {...} | | ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:135:9:135:25 | this access | @@ -4738,7 +5941,8 @@ postDominance | Initializers.cs:14:51:14:51 | 1 | Initializers.cs:14:40:14:44 | ... = ... | | Initializers.cs:15:9:15:64 | ... ...; | Initializers.cs:14:13:14:53 | Initializers i = ... | | Initializers.cs:15:13:15:63 | Initializers[] iz = ... | Initializers.cs:15:37:15:63 | { ..., ... } | -| Initializers.cs:15:18:15:63 | array creation of type Initializers[] | Initializers.cs:15:9:15:64 | ... ...; | +| Initializers.cs:15:18:15:63 | 2 | Initializers.cs:15:9:15:64 | ... ...; | +| Initializers.cs:15:18:15:63 | array creation of type Initializers[] | Initializers.cs:15:18:15:63 | 2 | | Initializers.cs:15:37:15:63 | { ..., ... } | Initializers.cs:15:42:15:61 | object creation of type Initializers | | Initializers.cs:15:39:15:39 | access to local variable i | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | | Initializers.cs:15:42:15:61 | object creation of type Initializers | Initializers.cs:15:59:15:60 | "" | @@ -4896,27 +6100,28 @@ postDominance | LoopUnrolling.cs:9:13:9:23 | access to property Length | LoopUnrolling.cs:9:13:9:16 | access to parameter args | | LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:9:28:9:28 | 0 | | LoopUnrolling.cs:9:28:9:28 | 0 | LoopUnrolling.cs:9:13:9:23 | access to property Length | -| LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | LoopUnrolling.cs:11:28:11:31 | access to parameter args | +| LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | LoopUnrolling.cs:11:29:11:32 | access to parameter args | | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | -| LoopUnrolling.cs:12:13:12:35 | ...; | LoopUnrolling.cs:11:21:11:23 | String arg | +| LoopUnrolling.cs:12:13:12:35 | ...; | LoopUnrolling.cs:11:22:11:24 | String arg | | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | LoopUnrolling.cs:12:13:12:35 | ...; | | LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | | LoopUnrolling.cs:16:5:20:5 | {...} | LoopUnrolling.cs:15:10:15:11 | enter M2 | -| LoopUnrolling.cs:17:9:17:47 | ... ...; | LoopUnrolling.cs:16:5:20:5 | {...} | -| LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | LoopUnrolling.cs:17:30:17:46 | { ..., ... } | -| LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | LoopUnrolling.cs:17:9:17:47 | ... ...; | -| LoopUnrolling.cs:17:30:17:46 | { ..., ... } | LoopUnrolling.cs:17:42:17:44 | "c" | -| LoopUnrolling.cs:17:32:17:34 | "a" | LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | -| LoopUnrolling.cs:17:37:17:39 | "b" | LoopUnrolling.cs:17:32:17:34 | "a" | -| LoopUnrolling.cs:17:42:17:44 | "c" | LoopUnrolling.cs:17:37:17:39 | "b" | -| LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | LoopUnrolling.cs:18:26:18:27 | access to local variable xs | +| LoopUnrolling.cs:17:9:17:48 | ... ...; | LoopUnrolling.cs:16:5:20:5 | {...} | +| LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | LoopUnrolling.cs:17:31:17:47 | { ..., ... } | +| LoopUnrolling.cs:17:18:17:47 | 3 | LoopUnrolling.cs:17:9:17:48 | ... ...; | +| LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | LoopUnrolling.cs:17:18:17:47 | 3 | +| LoopUnrolling.cs:17:31:17:47 | { ..., ... } | LoopUnrolling.cs:17:43:17:45 | "c" | +| LoopUnrolling.cs:17:33:17:35 | "a" | LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | +| LoopUnrolling.cs:17:38:17:40 | "b" | LoopUnrolling.cs:17:33:17:35 | "a" | +| LoopUnrolling.cs:17:43:17:45 | "c" | LoopUnrolling.cs:17:38:17:40 | "b" | +| LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | LoopUnrolling.cs:18:27:18:28 | access to local variable xs | | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | -| LoopUnrolling.cs:18:26:18:27 | access to local variable xs | LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:18:27:18:28 | access to local variable xs | LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | LoopUnrolling.cs:19:31:19:31 | access to local variable x | -| LoopUnrolling.cs:19:13:19:33 | ...; | LoopUnrolling.cs:18:21:18:21 | String x | +| LoopUnrolling.cs:19:13:19:33 | ...; | LoopUnrolling.cs:18:22:18:22 | String x | | LoopUnrolling.cs:19:31:19:31 | access to local variable x | LoopUnrolling.cs:19:13:19:33 | ...; | | LoopUnrolling.cs:22:10:22:11 | exit M3 | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | | LoopUnrolling.cs:23:5:27:5 | {...} | LoopUnrolling.cs:22:10:22:11 | enter M3 | @@ -4930,81 +6135,81 @@ postDominance | LoopUnrolling.cs:26:17:26:39 | call to method WriteLine | LoopUnrolling.cs:26:35:26:38 | access to local variable arg0 | | LoopUnrolling.cs:26:17:26:40 | ...; | LoopUnrolling.cs:25:26:25:29 | Char arg0 | | LoopUnrolling.cs:26:35:26:38 | access to local variable arg0 | LoopUnrolling.cs:26:17:26:40 | ...; | -| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | +| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:32:9:33:33 | [skip (line 32)] foreach (... ... in ...) ... | | LoopUnrolling.cs:30:5:34:5 | {...} | LoopUnrolling.cs:29:10:29:11 | enter M4 | | LoopUnrolling.cs:31:9:31:31 | ... ...; | LoopUnrolling.cs:30:5:34:5 | {...} | | LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | LoopUnrolling.cs:31:29:31:29 | 0 | | LoopUnrolling.cs:31:29:31:29 | 0 | LoopUnrolling.cs:31:9:31:31 | ... ...; | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:26:32:27 | access to local variable xs | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | -| LoopUnrolling.cs:32:26:32:27 | access to local variable xs | LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | -| LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | LoopUnrolling.cs:33:31:33:31 | access to local variable x | -| LoopUnrolling.cs:33:13:33:33 | ...; | LoopUnrolling.cs:32:21:32:21 | String x | -| LoopUnrolling.cs:33:31:33:31 | access to local variable x | LoopUnrolling.cs:33:13:33:33 | ...; | +| LoopUnrolling.cs:32:9:33:33 | [skip (line 32)] foreach (... ... in ...) ... | LoopUnrolling.cs:32:27:32:28 | access to local variable xs | +| LoopUnrolling.cs:32:27:32:28 | access to local variable xs | LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | | LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | | LoopUnrolling.cs:37:5:43:5 | {...} | LoopUnrolling.cs:36:10:36:11 | enter M5 | -| LoopUnrolling.cs:38:9:38:47 | ... ...; | LoopUnrolling.cs:37:5:43:5 | {...} | -| LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | LoopUnrolling.cs:38:30:38:46 | { ..., ... } | -| LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | LoopUnrolling.cs:38:9:38:47 | ... ...; | -| LoopUnrolling.cs:38:30:38:46 | { ..., ... } | LoopUnrolling.cs:38:42:38:44 | "c" | -| LoopUnrolling.cs:38:32:38:34 | "a" | LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | -| LoopUnrolling.cs:38:37:38:39 | "b" | LoopUnrolling.cs:38:32:38:34 | "a" | -| LoopUnrolling.cs:38:42:38:44 | "c" | LoopUnrolling.cs:38:37:38:39 | "b" | -| LoopUnrolling.cs:39:9:39:47 | ... ...; | LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | -| LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | LoopUnrolling.cs:39:30:39:46 | { ..., ... } | -| LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | LoopUnrolling.cs:39:9:39:47 | ... ...; | -| LoopUnrolling.cs:39:30:39:46 | { ..., ... } | LoopUnrolling.cs:39:42:39:44 | "2" | -| LoopUnrolling.cs:39:32:39:34 | "0" | LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | -| LoopUnrolling.cs:39:37:39:39 | "1" | LoopUnrolling.cs:39:32:39:34 | "0" | -| LoopUnrolling.cs:39:42:39:44 | "2" | LoopUnrolling.cs:39:37:39:39 | "1" | -| LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | LoopUnrolling.cs:40:26:40:27 | access to local variable xs | +| LoopUnrolling.cs:38:9:38:48 | ... ...; | LoopUnrolling.cs:37:5:43:5 | {...} | +| LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | LoopUnrolling.cs:38:31:38:47 | { ..., ... } | +| LoopUnrolling.cs:38:18:38:47 | 3 | LoopUnrolling.cs:38:9:38:48 | ... ...; | +| LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | LoopUnrolling.cs:38:18:38:47 | 3 | +| LoopUnrolling.cs:38:31:38:47 | { ..., ... } | LoopUnrolling.cs:38:43:38:45 | "c" | +| LoopUnrolling.cs:38:33:38:35 | "a" | LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | +| LoopUnrolling.cs:38:38:38:40 | "b" | LoopUnrolling.cs:38:33:38:35 | "a" | +| LoopUnrolling.cs:38:43:38:45 | "c" | LoopUnrolling.cs:38:38:38:40 | "b" | +| LoopUnrolling.cs:39:9:39:48 | ... ...; | LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | +| LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | LoopUnrolling.cs:39:31:39:47 | { ..., ... } | +| LoopUnrolling.cs:39:18:39:47 | 3 | LoopUnrolling.cs:39:9:39:48 | ... ...; | +| LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | LoopUnrolling.cs:39:18:39:47 | 3 | +| LoopUnrolling.cs:39:31:39:47 | { ..., ... } | LoopUnrolling.cs:39:43:39:45 | "2" | +| LoopUnrolling.cs:39:33:39:35 | "0" | LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | +| LoopUnrolling.cs:39:38:39:40 | "1" | LoopUnrolling.cs:39:33:39:35 | "0" | +| LoopUnrolling.cs:39:43:39:45 | "2" | LoopUnrolling.cs:39:38:39:40 | "1" | +| LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | LoopUnrolling.cs:40:27:40:28 | access to local variable xs | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | -| LoopUnrolling.cs:40:26:40:27 | access to local variable xs | LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | -| LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | LoopUnrolling.cs:41:30:41:31 | access to local variable ys | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:40:27:40:28 | access to local variable xs | LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | +| LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | LoopUnrolling.cs:41:31:41:32 | access to local variable ys | | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | -| LoopUnrolling.cs:41:30:41:31 | access to local variable ys | LoopUnrolling.cs:40:21:40:21 | String x | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:41:31:41:32 | access to local variable ys | LoopUnrolling.cs:40:22:40:22 | String x | | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | LoopUnrolling.cs:42:35:42:39 | ... + ... | -| LoopUnrolling.cs:42:17:42:41 | ...; | LoopUnrolling.cs:41:25:41:25 | String y | +| LoopUnrolling.cs:42:17:42:41 | ...; | LoopUnrolling.cs:41:26:41:26 | String y | | LoopUnrolling.cs:42:35:42:35 | access to local variable x | LoopUnrolling.cs:42:17:42:41 | ...; | | LoopUnrolling.cs:42:35:42:39 | ... + ... | LoopUnrolling.cs:42:39:42:39 | access to local variable y | | LoopUnrolling.cs:42:39:42:39 | access to local variable y | LoopUnrolling.cs:42:35:42:35 | access to local variable x | | LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:45:10:45:11 | enter M6 | -| LoopUnrolling.cs:47:9:47:47 | ... ...; | LoopUnrolling.cs:46:5:53:5 | {...} | -| LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | LoopUnrolling.cs:47:30:47:46 | { ..., ... } | -| LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | LoopUnrolling.cs:47:9:47:47 | ... ...; | -| LoopUnrolling.cs:47:30:47:46 | { ..., ... } | LoopUnrolling.cs:47:42:47:44 | "c" | -| LoopUnrolling.cs:47:32:47:34 | "a" | LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | -| LoopUnrolling.cs:47:37:47:39 | "b" | LoopUnrolling.cs:47:32:47:34 | "a" | -| LoopUnrolling.cs:47:42:47:44 | "c" | LoopUnrolling.cs:47:37:47:39 | "b" | -| LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | LoopUnrolling.cs:48:26:48:27 | access to local variable xs | -| LoopUnrolling.cs:48:21:48:21 | String x | LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | -| LoopUnrolling.cs:48:26:48:27 | access to local variable xs | LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | -| LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:48:21:48:21 | String x | -| LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | LoopUnrolling.cs:50:38:50:38 | access to local variable x | -| LoopUnrolling.cs:50:20:50:40 | ...; | LoopUnrolling.cs:50:13:50:17 | Label: | -| LoopUnrolling.cs:50:38:50:38 | access to local variable x | LoopUnrolling.cs:50:20:50:40 | ...; | -| LoopUnrolling.cs:51:13:51:23 | goto ...; | LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | +| LoopUnrolling.cs:47:9:47:48 | ... ...; | LoopUnrolling.cs:46:5:53:5 | {...} | +| LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | LoopUnrolling.cs:47:31:47:47 | { ..., ... } | +| LoopUnrolling.cs:47:18:47:47 | 3 | LoopUnrolling.cs:47:9:47:48 | ... ...; | +| LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | LoopUnrolling.cs:47:18:47:47 | 3 | +| LoopUnrolling.cs:47:31:47:47 | { ..., ... } | LoopUnrolling.cs:47:43:47:45 | "c" | +| LoopUnrolling.cs:47:33:47:35 | "a" | LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | +| LoopUnrolling.cs:47:38:47:40 | "b" | LoopUnrolling.cs:47:33:47:35 | "a" | +| LoopUnrolling.cs:47:43:47:45 | "c" | LoopUnrolling.cs:47:38:47:40 | "b" | +| LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | LoopUnrolling.cs:48:27:48:28 | access to local variable xs | +| LoopUnrolling.cs:48:22:48:22 | String x | LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:48:27:48:28 | access to local variable xs | LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | +| LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:48:22:48:22 | String x | +| LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | LoopUnrolling.cs:50:34:50:34 | access to local variable x | +| LoopUnrolling.cs:50:16:50:36 | ...; | LoopUnrolling.cs:50:9:50:13 | Label: | +| LoopUnrolling.cs:50:34:50:34 | access to local variable x | LoopUnrolling.cs:50:16:50:36 | ...; | +| LoopUnrolling.cs:51:13:51:23 | goto ...; | LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | | LoopUnrolling.cs:56:5:65:5 | {...} | LoopUnrolling.cs:55:10:55:11 | enter M7 | -| LoopUnrolling.cs:57:9:57:47 | ... ...; | LoopUnrolling.cs:56:5:65:5 | {...} | -| LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | LoopUnrolling.cs:57:30:57:46 | { ..., ... } | -| LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | LoopUnrolling.cs:57:9:57:47 | ... ...; | -| LoopUnrolling.cs:57:30:57:46 | { ..., ... } | LoopUnrolling.cs:57:42:57:44 | "c" | -| LoopUnrolling.cs:57:32:57:34 | "a" | LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | -| LoopUnrolling.cs:57:37:57:39 | "b" | LoopUnrolling.cs:57:32:57:34 | "a" | -| LoopUnrolling.cs:57:42:57:44 | "c" | LoopUnrolling.cs:57:37:57:39 | "b" | +| LoopUnrolling.cs:57:9:57:48 | ... ...; | LoopUnrolling.cs:56:5:65:5 | {...} | +| LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | LoopUnrolling.cs:57:31:57:47 | { ..., ... } | +| LoopUnrolling.cs:57:18:57:47 | 3 | LoopUnrolling.cs:57:9:57:48 | ... ...; | +| LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | LoopUnrolling.cs:57:18:57:47 | 3 | +| LoopUnrolling.cs:57:31:57:47 | { ..., ... } | LoopUnrolling.cs:57:43:57:45 | "c" | +| LoopUnrolling.cs:57:33:57:35 | "a" | LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | +| LoopUnrolling.cs:57:38:57:40 | "b" | LoopUnrolling.cs:57:33:57:35 | "a" | +| LoopUnrolling.cs:57:43:57:45 | "c" | LoopUnrolling.cs:57:38:57:40 | "b" | | LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | LoopUnrolling.cs:62:17:62:17 | [b (line 55): false] access to parameter b | | LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | LoopUnrolling.cs:63:17:63:36 | [b (line 55): true] call to method WriteLine | -| LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | LoopUnrolling.cs:58:26:58:27 | access to local variable xs | -| LoopUnrolling.cs:58:21:58:21 | String x | LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | -| LoopUnrolling.cs:58:26:58:27 | access to local variable xs | LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | -| LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | -| LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | -| LoopUnrolling.cs:59:9:64:9 | {...} | LoopUnrolling.cs:58:21:58:21 | String x | +| LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | LoopUnrolling.cs:58:27:58:28 | access to local variable xs | +| LoopUnrolling.cs:58:22:58:22 | String x | LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:58:27:58:28 | access to local variable xs | LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | +| LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | +| LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | +| LoopUnrolling.cs:59:9:64:9 | {...} | LoopUnrolling.cs:58:22:58:22 | String x | | LoopUnrolling.cs:60:13:61:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | | LoopUnrolling.cs:60:13:61:37 | [b (line 55): true] if (...) ... | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | | LoopUnrolling.cs:60:13:61:37 | if (...) ... | LoopUnrolling.cs:59:9:64:9 | {...} | @@ -5022,7 +6227,7 @@ postDominance | LoopUnrolling.cs:63:17:63:37 | [b (line 55): true] ...; | LoopUnrolling.cs:62:17:62:17 | [b (line 55): true] access to parameter b | | LoopUnrolling.cs:63:35:63:35 | [b (line 55): true] access to local variable x | LoopUnrolling.cs:63:17:63:37 | [b (line 55): true] ...; | | LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:70:13:70:19 | return ...; | -| LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | +| LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:72:9:73:35 | [skip (line 72)] foreach (... ... in ...) ... | | LoopUnrolling.cs:68:5:74:5 | {...} | LoopUnrolling.cs:67:10:67:11 | enter M8 | | LoopUnrolling.cs:69:9:70:19 | if (...) ... | LoopUnrolling.cs:68:5:74:5 | {...} | | LoopUnrolling.cs:69:13:69:23 | !... | LoopUnrolling.cs:69:9:70:19 | if (...) ... | @@ -5030,12 +6235,145 @@ postDominance | LoopUnrolling.cs:69:14:69:23 | call to method Any | LoopUnrolling.cs:69:14:69:17 | access to parameter args | | LoopUnrolling.cs:71:9:71:12 | access to parameter args | LoopUnrolling.cs:71:9:71:21 | ...; | | LoopUnrolling.cs:71:9:71:20 | call to method Clear | LoopUnrolling.cs:71:9:71:12 | access to parameter args | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:28:72:31 | access to parameter args | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | -| LoopUnrolling.cs:72:28:72:31 | access to parameter args | LoopUnrolling.cs:71:9:71:20 | call to method Clear | -| LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | LoopUnrolling.cs:73:31:73:33 | access to local variable arg | -| LoopUnrolling.cs:73:13:73:35 | ...; | LoopUnrolling.cs:72:21:72:23 | String arg | -| LoopUnrolling.cs:73:31:73:33 | access to local variable arg | LoopUnrolling.cs:73:13:73:35 | ...; | +| LoopUnrolling.cs:72:9:73:35 | [skip (line 72)] foreach (... ... in ...) ... | LoopUnrolling.cs:72:29:72:32 | access to parameter args | +| LoopUnrolling.cs:72:29:72:32 | access to parameter args | LoopUnrolling.cs:71:9:71:20 | call to method Clear | +| LoopUnrolling.cs:76:10:76:11 | exit M9 | LoopUnrolling.cs:79:9:82:9 | [skip (line 79)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:77:5:83:5 | {...} | LoopUnrolling.cs:76:10:76:11 | enter M9 | +| LoopUnrolling.cs:78:9:78:34 | ... ...; | LoopUnrolling.cs:77:5:83:5 | {...} | +| LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | +| LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | LoopUnrolling.cs:78:32:78:32 | 0 | +| LoopUnrolling.cs:78:29:78:29 | 2 | LoopUnrolling.cs:78:9:78:34 | ... ...; | +| LoopUnrolling.cs:78:32:78:32 | 0 | LoopUnrolling.cs:78:29:78:29 | 2 | +| LoopUnrolling.cs:79:9:82:9 | [skip (line 79)] foreach (... ... in ...) ... | LoopUnrolling.cs:79:27:79:28 | access to local variable xs | +| LoopUnrolling.cs:79:27:79:28 | access to local variable xs | LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | +| LoopUnrolling.cs:85:10:85:12 | exit M10 | LoopUnrolling.cs:88:9:91:9 | [skip (line 88)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:86:5:92:5 | {...} | LoopUnrolling.cs:85:10:85:12 | enter M10 | +| LoopUnrolling.cs:87:9:87:34 | ... ...; | LoopUnrolling.cs:86:5:92:5 | {...} | +| LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | +| LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | LoopUnrolling.cs:87:32:87:32 | 2 | +| LoopUnrolling.cs:87:29:87:29 | 0 | LoopUnrolling.cs:87:9:87:34 | ... ...; | +| LoopUnrolling.cs:87:32:87:32 | 2 | LoopUnrolling.cs:87:29:87:29 | 0 | +| LoopUnrolling.cs:88:9:91:9 | [skip (line 88)] foreach (... ... in ...) ... | LoopUnrolling.cs:88:27:88:28 | access to local variable xs | +| LoopUnrolling.cs:88:27:88:28 | access to local variable xs | LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | +| LoopUnrolling.cs:94:10:94:12 | exit M11 | LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | +| LoopUnrolling.cs:95:5:101:5 | {...} | LoopUnrolling.cs:94:10:94:12 | enter M11 | +| LoopUnrolling.cs:96:9:96:34 | ... ...; | LoopUnrolling.cs:95:5:101:5 | {...} | +| LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | +| LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | LoopUnrolling.cs:96:32:96:32 | 2 | +| LoopUnrolling.cs:96:29:96:29 | 2 | LoopUnrolling.cs:96:9:96:34 | ... ...; | +| LoopUnrolling.cs:96:32:96:32 | 2 | LoopUnrolling.cs:96:29:96:29 | 2 | +| LoopUnrolling.cs:97:9:100:9 | [unroll (line 97)] foreach (... ... in ...) ... | LoopUnrolling.cs:97:27:97:28 | access to local variable xs | +| LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:97:9:100:9 | [unroll (line 97)] foreach (... ... in ...) ... | +| LoopUnrolling.cs:97:27:97:28 | access to local variable xs | LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | +| LoopUnrolling.cs:98:9:100:9 | {...} | LoopUnrolling.cs:97:22:97:22 | String x | +| LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | LoopUnrolling.cs:99:31:99:31 | access to local variable x | +| LoopUnrolling.cs:99:13:99:33 | ...; | LoopUnrolling.cs:98:9:100:9 | {...} | +| LoopUnrolling.cs:99:31:99:31 | access to local variable x | LoopUnrolling.cs:99:13:99:33 | ...; | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | throw ... | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationA.cs:6:22:6:31 | throw ... | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationA.cs:7:27:7:37 | throw ...; | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationB.cs:4:27:4:35 | return ...; | +| MultiImplementationA.cs:7:27:7:37 | throw ...; | MultiImplementationA.cs:7:33:7:36 | null | +| MultiImplementationA.cs:7:33:7:36 | null | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationA.cs:7:47:7:57 | throw ...; | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationA.cs:7:47:7:57 | throw ...; | MultiImplementationA.cs:7:53:7:56 | null | +| MultiImplementationA.cs:7:53:7:56 | null | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:23:8:32 | throw ... | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:13:20:13:20 | 0 | +| MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | throw ... | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationA.cs:15:42:15:50 | return ...; | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationB.cs:13:42:13:52 | throw ...; | +| MultiImplementationA.cs:15:42:15:50 | return ...; | MultiImplementationA.cs:15:49:15:49 | access to parameter s | +| MultiImplementationA.cs:15:49:15:49 | access to parameter s | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationA.cs:18:9:18:22 | M2(...) | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationB.cs:16:9:16:31 | M2(...) | +| MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:18:9:18:22 | exit M2 | MultiImplementationA.cs:18:21:18:21 | 0 | +| MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | enter M2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:24:20:28 | ... = ... | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:24:18:34 | throw ...; | +| MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:24:20:29 | ...; | +| MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationA.cs:20:28:20:28 | access to parameter i | +| MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationA.cs:20:28:20:28 | access to parameter i | MultiImplementationA.cs:20:24:20:24 | this access | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationB.cs:20:13:20:23 | throw ...; | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:50:21:59 | throw ... | +| MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:34:24:34 | 0 | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | access to property P | +| MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:30:21:30:23 | exit get_P3 | MultiImplementationA.cs:30:28:30:37 | throw ... | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationA.cs:30:34:30:37 | null | +| MultiImplementationA.cs:30:34:30:37 | null | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | +| MultiImplementationA.cs:30:34:30:37 | null | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationA.cs:36:16:36:26 | throw ...; | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationB.cs:32:17:32:17 | 0 | +| MultiImplementationA.cs:36:16:36:26 | throw ...; | MultiImplementationA.cs:36:22:36:25 | null | +| MultiImplementationA.cs:36:22:36:25 | null | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:37:9:37:10 | exit M2 | MultiImplementationA.cs:37:16:37:26 | throw ...; | +| MultiImplementationA.cs:37:14:37:28 | {...} | MultiImplementationA.cs:37:9:37:10 | enter M2 | +| MultiImplementationA.cs:37:16:37:26 | throw ...; | MultiImplementationA.cs:37:22:37:25 | null | +| MultiImplementationA.cs:37:22:37:25 | null | MultiImplementationA.cs:37:14:37:28 | {...} | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | throw ... | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationA.cs:7:27:7:37 | throw ...; | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationB.cs:4:27:4:35 | return ...; | +| MultiImplementationB.cs:4:27:4:35 | return ...; | MultiImplementationB.cs:4:34:4:34 | 1 | +| MultiImplementationB.cs:4:34:4:34 | 1 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationA.cs:7:47:7:57 | throw ...; | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:23:8:32 | throw ... | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:11:20:11:20 | 1 | +| MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | throw ... | +| MultiImplementationB.cs:12:31:12:40 | throw ... | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationA.cs:15:42:15:50 | return ...; | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationB.cs:13:42:13:52 | throw ...; | +| MultiImplementationB.cs:13:42:13:52 | throw ...; | MultiImplementationB.cs:13:48:13:51 | null | +| MultiImplementationB.cs:13:48:13:51 | null | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationA.cs:18:9:18:22 | M2(...) | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationB.cs:16:9:16:31 | M2(...) | +| MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:16:9:16:31 | exit M2 | MultiImplementationB.cs:16:21:16:30 | throw ... | +| MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:27:16:30 | null | +| MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:9:16:31 | enter M2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:24:20:28 | ... = ... | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:24:18:34 | throw ...; | +| MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:30:18:33 | null | +| MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationB.cs:20:13:20:23 | throw ...; | +| MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationB.cs:20:19:20:22 | null | +| MultiImplementationB.cs:20:19:20:22 | null | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:50:21:59 | throw ... | +| MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:34:22:34 | 1 | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | access to property P | +| MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:27:21:27:23 | exit get_P3 | MultiImplementationA.cs:30:28:30:37 | throw ... | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationA.cs:36:16:36:26 | throw ...; | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationB.cs:32:17:32:17 | 0 | | NullCoalescing.cs:3:9:3:10 | exit M1 | NullCoalescing.cs:3:23:3:23 | access to parameter i | | NullCoalescing.cs:3:9:3:10 | exit M1 | NullCoalescing.cs:3:28:3:28 | 0 | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:23:3:28 | ... ?? ... | @@ -5241,8 +6579,8 @@ postDominance | Switch.cs:25:35:25:35 | access to local variable s | Switch.cs:25:17:25:37 | ...; | | Switch.cs:26:17:26:23 | return ...; | Switch.cs:25:17:25:36 | call to method WriteLine | | Switch.cs:27:18:27:25 | Double d | Switch.cs:27:13:27:39 | case ...: | -| Switch.cs:28:17:28:21 | Label: | Switch.cs:31:17:31:27 | goto ...; | -| Switch.cs:29:17:29:23 | return ...; | Switch.cs:28:17:28:21 | Label: | +| Switch.cs:28:13:28:17 | Label: | Switch.cs:31:17:31:27 | goto ...; | +| Switch.cs:29:17:29:23 | return ...; | Switch.cs:28:13:28:17 | Label: | | Switch.cs:30:13:30:20 | default: | Switch.cs:19:17:19:29 | goto default; | | Switch.cs:31:17:31:27 | goto ...; | Switch.cs:30:13:30:20 | default: | | Switch.cs:35:10:35:11 | exit M3 | Switch.cs:37:17:37:23 | call to method Throw | @@ -5261,85 +6599,85 @@ postDominance | Switch.cs:50:18:50:21 | access to type Boolean | Switch.cs:50:13:50:39 | case ...: | | Switch.cs:50:30:50:38 | ... != ... | Switch.cs:50:35:50:38 | null | | Switch.cs:50:35:50:38 | null | Switch.cs:50:30:50:30 | access to parameter o | -| Switch.cs:55:10:55:11 | exit M5 | Switch.cs:62:15:62:20 | break; | +| Switch.cs:55:10:55:11 | exit M5 | Switch.cs:62:17:62:22 | break; | | Switch.cs:56:5:64:5 | {...} | Switch.cs:55:10:55:11 | enter M5 | | Switch.cs:57:9:63:9 | switch (...) {...} | Switch.cs:56:5:64:5 | {...} | | Switch.cs:57:17:57:17 | 1 | Switch.cs:57:9:63:9 | switch (...) {...} | | Switch.cs:57:17:57:21 | ... + ... | Switch.cs:57:21:57:21 | 2 | | Switch.cs:57:21:57:21 | 2 | Switch.cs:57:17:57:17 | 1 | -| Switch.cs:59:13:59:20 | case ...: | Switch.cs:57:17:57:21 | ... + ... | -| Switch.cs:59:18:59:18 | 2 | Switch.cs:59:13:59:20 | case ...: | -| Switch.cs:61:13:61:20 | case ...: | Switch.cs:59:18:59:18 | 2 | -| Switch.cs:61:18:61:18 | 3 | Switch.cs:61:13:61:20 | case ...: | -| Switch.cs:62:15:62:20 | break; | Switch.cs:61:18:61:18 | 3 | +| Switch.cs:59:13:59:19 | case ...: | Switch.cs:57:17:57:21 | ... + ... | +| Switch.cs:59:18:59:18 | 2 | Switch.cs:59:13:59:19 | case ...: | +| Switch.cs:61:13:61:19 | case ...: | Switch.cs:59:18:59:18 | 2 | +| Switch.cs:61:18:61:18 | 3 | Switch.cs:61:13:61:19 | case ...: | +| Switch.cs:62:17:62:22 | break; | Switch.cs:61:18:61:18 | 3 | | Switch.cs:66:10:66:11 | exit M6 | Switch.cs:72:18:72:19 | "" | -| Switch.cs:66:10:66:11 | exit M6 | Switch.cs:73:15:73:20 | break; | +| Switch.cs:66:10:66:11 | exit M6 | Switch.cs:73:17:73:22 | break; | | Switch.cs:67:5:75:5 | {...} | Switch.cs:66:10:66:11 | enter M6 | | Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:67:5:75:5 | {...} | | Switch.cs:68:17:68:25 | (...) ... | Switch.cs:68:25:68:25 | access to parameter s | | Switch.cs:68:25:68:25 | access to parameter s | Switch.cs:68:9:74:9 | switch (...) {...} | -| Switch.cs:70:13:70:24 | case ...: | Switch.cs:68:17:68:25 | (...) ... | -| Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:70:13:70:24 | case ...: | -| Switch.cs:72:13:72:21 | case ...: | Switch.cs:70:18:70:20 | access to type Int32 | -| Switch.cs:72:18:72:19 | "" | Switch.cs:72:13:72:21 | case ...: | -| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:82:15:82:26 | return ...; | -| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:86:15:86:26 | return ...; | +| Switch.cs:70:13:70:23 | case ...: | Switch.cs:68:17:68:25 | (...) ... | +| Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:70:13:70:23 | case ...: | +| Switch.cs:72:13:72:20 | case ...: | Switch.cs:70:18:70:20 | access to type Int32 | +| Switch.cs:72:18:72:19 | "" | Switch.cs:72:13:72:20 | case ...: | +| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:82:17:82:28 | return ...; | +| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:86:17:86:28 | return ...; | | Switch.cs:77:10:77:11 | exit M7 | Switch.cs:88:9:88:21 | return ...; | | Switch.cs:78:5:89:5 | {...} | Switch.cs:77:10:77:11 | enter M7 | | Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:78:5:89:5 | {...} | | Switch.cs:79:17:79:17 | access to parameter i | Switch.cs:79:9:87:9 | switch (...) {...} | -| Switch.cs:81:13:81:20 | case ...: | Switch.cs:79:17:79:17 | access to parameter i | -| Switch.cs:81:18:81:18 | 1 | Switch.cs:81:13:81:20 | case ...: | -| Switch.cs:82:15:82:26 | return ...; | Switch.cs:82:22:82:25 | true | -| Switch.cs:83:18:83:18 | 2 | Switch.cs:83:13:83:20 | case ...: | -| Switch.cs:84:19:84:19 | access to parameter j | Switch.cs:84:15:85:22 | if (...) ... | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:84:23:84:23 | 2 | -| Switch.cs:84:23:84:23 | 2 | Switch.cs:84:19:84:19 | access to parameter j | -| Switch.cs:86:15:86:26 | return ...; | Switch.cs:86:22:86:25 | true | +| Switch.cs:81:13:81:19 | case ...: | Switch.cs:79:17:79:17 | access to parameter i | +| Switch.cs:81:18:81:18 | 1 | Switch.cs:81:13:81:19 | case ...: | +| Switch.cs:82:17:82:28 | return ...; | Switch.cs:82:24:82:27 | true | +| Switch.cs:83:18:83:18 | 2 | Switch.cs:83:13:83:19 | case ...: | +| Switch.cs:84:21:84:21 | access to parameter j | Switch.cs:84:17:85:26 | if (...) ... | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:84:25:84:25 | 2 | +| Switch.cs:84:25:84:25 | 2 | Switch.cs:84:21:84:21 | access to parameter j | +| Switch.cs:86:17:86:28 | return ...; | Switch.cs:86:24:86:27 | true | | Switch.cs:88:9:88:21 | return ...; | Switch.cs:88:16:88:20 | false | -| Switch.cs:88:16:88:20 | false | Switch.cs:85:17:85:22 | break; | -| Switch.cs:91:10:91:11 | exit M8 | Switch.cs:96:15:96:26 | return ...; | +| Switch.cs:88:16:88:20 | false | Switch.cs:85:21:85:26 | break; | +| Switch.cs:91:10:91:11 | exit M8 | Switch.cs:96:17:96:28 | return ...; | | Switch.cs:91:10:91:11 | exit M8 | Switch.cs:98:9:98:21 | return ...; | | Switch.cs:92:5:99:5 | {...} | Switch.cs:91:10:91:11 | enter M8 | | Switch.cs:93:9:97:9 | switch (...) {...} | Switch.cs:92:5:99:5 | {...} | | Switch.cs:93:17:93:17 | access to parameter o | Switch.cs:93:9:97:9 | switch (...) {...} | -| Switch.cs:95:13:95:24 | case ...: | Switch.cs:93:17:93:17 | access to parameter o | -| Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:95:13:95:24 | case ...: | -| Switch.cs:96:15:96:26 | return ...; | Switch.cs:96:22:96:25 | true | +| Switch.cs:95:13:95:23 | case ...: | Switch.cs:93:17:93:17 | access to parameter o | +| Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:95:13:95:23 | case ...: | +| Switch.cs:96:17:96:28 | return ...; | Switch.cs:96:24:96:27 | true | | Switch.cs:98:9:98:21 | return ...; | Switch.cs:98:16:98:20 | false | -| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:105:22:105:30 | return ...; | -| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:106:22:106:30 | return ...; | +| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:105:21:105:29 | return ...; | +| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:106:21:106:29 | return ...; | | Switch.cs:101:9:101:10 | exit M9 | Switch.cs:108:9:108:18 | return ...; | | Switch.cs:102:5:109:5 | {...} | Switch.cs:101:9:101:10 | enter M9 | | Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:102:5:109:5 | {...} | | Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:103:9:107:9 | switch (...) {...} | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:103:17:103:17 | access to parameter s | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:103:19:103:25 | access to property Length | -| Switch.cs:105:18:105:18 | 0 | Switch.cs:105:13:105:20 | case ...: | -| Switch.cs:105:22:105:30 | return ...; | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:106:18:106:18 | 1 | Switch.cs:106:13:106:20 | case ...: | -| Switch.cs:106:22:106:30 | return ...; | Switch.cs:106:29:106:29 | 1 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:103:17:103:17 | access to parameter s | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:103:19:103:25 | access to property Length | +| Switch.cs:105:18:105:18 | 0 | Switch.cs:105:13:105:19 | case ...: | +| Switch.cs:105:21:105:29 | return ...; | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:106:18:106:18 | 1 | Switch.cs:106:13:106:19 | case ...: | +| Switch.cs:106:21:106:29 | return ...; | Switch.cs:106:28:106:28 | 1 | | Switch.cs:108:9:108:18 | return ...; | Switch.cs:108:16:108:17 | -... | | Switch.cs:108:16:108:17 | -... | Switch.cs:108:17:108:17 | 1 | | Switch.cs:111:17:111:21 | exit Throw | Switch.cs:111:28:111:48 | throw ... | | Switch.cs:111:28:111:48 | throw ... | Switch.cs:111:34:111:48 | object creation of type Exception | | Switch.cs:111:34:111:48 | object creation of type Exception | Switch.cs:111:17:111:21 | enter Throw | -| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:117:36:117:44 | return ...; | -| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:118:35:118:43 | return ...; | +| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:117:37:117:45 | return ...; | +| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:118:36:118:44 | return ...; | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:120:9:120:18 | return ...; | | Switch.cs:114:5:121:5 | {...} | Switch.cs:113:9:113:11 | enter M10 | | Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:114:5:121:5 | {...} | | Switch.cs:115:17:115:17 | access to parameter s | Switch.cs:115:9:119:9 | switch (...) {...} | | Switch.cs:115:17:115:24 | access to property Length | Switch.cs:115:17:115:17 | access to parameter s | -| Switch.cs:117:13:117:34 | case ...: | Switch.cs:115:17:115:24 | access to property Length | -| Switch.cs:117:18:117:18 | 3 | Switch.cs:117:13:117:34 | case ...: | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:117:28:117:32 | "foo" | -| Switch.cs:117:28:117:32 | "foo" | Switch.cs:117:25:117:25 | access to parameter s | -| Switch.cs:117:36:117:44 | return ...; | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:118:18:118:18 | 2 | Switch.cs:118:13:118:33 | case ...: | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:118:28:118:31 | "fu" | -| Switch.cs:118:28:118:31 | "fu" | Switch.cs:118:25:118:25 | access to parameter s | -| Switch.cs:118:35:118:43 | return ...; | Switch.cs:118:42:118:42 | 2 | +| Switch.cs:117:13:117:35 | case ...: | Switch.cs:115:17:115:24 | access to property Length | +| Switch.cs:117:18:117:18 | 3 | Switch.cs:117:13:117:35 | case ...: | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:117:30:117:34 | "foo" | +| Switch.cs:117:30:117:34 | "foo" | Switch.cs:117:25:117:25 | access to parameter s | +| Switch.cs:117:37:117:45 | return ...; | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:118:18:118:18 | 2 | Switch.cs:118:13:118:34 | case ...: | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:118:30:118:33 | "fu" | +| Switch.cs:118:30:118:33 | "fu" | Switch.cs:118:25:118:25 | access to parameter s | +| Switch.cs:118:36:118:44 | return ...; | Switch.cs:118:43:118:43 | 2 | | Switch.cs:120:9:120:18 | return ...; | Switch.cs:120:16:120:17 | -... | | Switch.cs:120:16:120:17 | -... | Switch.cs:120:17:120:17 | 1 | | Switch.cs:123:10:123:12 | exit M11 | Switch.cs:125:34:125:34 | access to local variable b | @@ -5392,6 +6730,28 @@ postDominance | Switch.cs:149:30:149:30 | 1 | Switch.cs:149:13:149:20 | default: | | Switch.cs:150:18:150:18 | 2 | Switch.cs:150:13:150:19 | case ...: | | Switch.cs:150:21:150:29 | return ...; | Switch.cs:150:28:150:28 | 2 | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:156:41:156:45 | false | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:158:13:158:48 | call to method WriteLine | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:160:13:160:48 | call to method WriteLine | +| Switch.cs:155:5:161:5 | {...} | Switch.cs:154:10:154:12 | enter M15 | +| Switch.cs:156:9:156:55 | ... ...; | Switch.cs:155:5:161:5 | {...} | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:36:156:38 | "a" | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:156:17:156:17 | access to parameter b | Switch.cs:156:17:156:54 | ... switch { ... } | +| Switch.cs:156:17:156:54 | ... switch { ... } | Switch.cs:156:9:156:55 | ... ...; | +| Switch.cs:156:28:156:31 | true | Switch.cs:156:28:156:38 | ... => ... | +| Switch.cs:156:28:156:38 | ... => ... | Switch.cs:156:17:156:17 | access to parameter b | +| Switch.cs:156:41:156:45 | false | Switch.cs:156:41:156:52 | ... => ... | +| Switch.cs:157:9:160:49 | if (...) ... | Switch.cs:156:13:156:54 | String s = ... | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:157:9:160:49 | if (...) ... | +| Switch.cs:158:13:158:48 | call to method WriteLine | Switch.cs:158:38:158:47 | $"..." | +| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:45:158:45 | access to local variable s | +| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:13:158:49 | ...; | +| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:40:158:43 | "a = " | +| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:160:38:160:47 | $"..." | +| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:45:160:45 | access to local variable s | +| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:13:160:49 | ...; | +| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:40:160:43 | "b = " | | TypeAccesses.cs:3:10:3:10 | exit M | TypeAccesses.cs:8:13:8:27 | Type t = ... | | TypeAccesses.cs:4:5:9:5 | {...} | TypeAccesses.cs:3:10:3:10 | enter M | | TypeAccesses.cs:5:9:5:26 | ... ...; | TypeAccesses.cs:4:5:9:5 | {...} | @@ -5413,12 +6773,14 @@ postDominance | VarDecls.cs:5:18:5:19 | exit M1 | VarDecls.cs:9:13:9:29 | return ...; | | VarDecls.cs:6:5:11:5 | {...} | VarDecls.cs:5:18:5:19 | enter M1 | | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:6:5:11:5 | {...} | -| VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:27:7:36 | access to array element | +| VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:27:7:36 | (...) ... | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:27:7:36 | access to array element | | VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:35:7:35 | 0 | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:27:7:33 | access to parameter strings | -| VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:7:44:7:53 | access to array element | +| VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:7:44:7:53 | (...) ... | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:22:7:36 | Char* c1 = ... | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:44:7:53 | access to array element | | VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:52:7:52 | 1 | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:7:39:7:53 | Char* c2 = ... | @@ -6059,6 +7421,465 @@ blockDominance | ArrayCreation.cs:5:12:5:13 | enter M2 | ArrayCreation.cs:5:12:5:13 | enter M2 | | ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:11:7:12 | enter M3 | | ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:12:9:13 | enter M4 | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:7:10:7:11 | enter M1 | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:7:10:7:11 | exit M1 | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:9:16:9:32 | String s = ... | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:9:24:9:27 | null | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:9:31:9:32 | "" | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:7:10:7:11 | exit M1 | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:7:10:7:11 | exit M1 | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:9:16:9:32 | String s = ... | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | +| Assert.cs:9:24:9:27 | null | Assert.cs:9:24:9:27 | null | +| Assert.cs:9:31:9:32 | "" | Assert.cs:9:31:9:32 | "" | +| Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | +| Assert.cs:10:9:10:31 | [assertion success] call to method Assert | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:14:10:14:11 | enter M2 | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:14:10:14:11 | exit M2 | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:16:16:16:32 | String s = ... | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:16:24:16:27 | null | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:16:31:16:32 | "" | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:14:10:14:11 | exit M2 | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:14:10:14:11 | exit M2 | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:16:16:16:32 | String s = ... | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | +| Assert.cs:16:24:16:27 | null | Assert.cs:16:24:16:27 | null | +| Assert.cs:16:31:16:32 | "" | Assert.cs:16:31:16:32 | "" | +| Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | +| Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:21:10:21:11 | enter M3 | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:21:10:21:11 | exit M3 | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:23:16:23:32 | String s = ... | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:23:24:23:27 | null | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:23:31:23:32 | "" | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:21:10:21:11 | exit M3 | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:21:10:21:11 | exit M3 | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:23:16:23:32 | String s = ... | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | +| Assert.cs:23:24:23:27 | null | Assert.cs:23:24:23:27 | null | +| Assert.cs:23:31:23:32 | "" | Assert.cs:23:31:23:32 | "" | +| Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | +| Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:28:10:28:11 | enter M4 | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:28:10:28:11 | exit M4 | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:30:16:30:32 | String s = ... | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:30:24:30:27 | null | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:30:31:30:32 | "" | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:28:10:28:11 | exit M4 | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:28:10:28:11 | exit M4 | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:30:16:30:32 | String s = ... | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | +| Assert.cs:30:24:30:27 | null | Assert.cs:30:24:30:27 | null | +| Assert.cs:30:31:30:32 | "" | Assert.cs:30:31:30:32 | "" | +| Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | +| Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:35:10:35:11 | enter M5 | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:35:10:35:11 | exit M5 | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:37:16:37:32 | String s = ... | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:37:24:37:27 | null | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:37:31:37:32 | "" | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:35:10:35:11 | exit M5 | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:35:10:35:11 | exit M5 | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:37:16:37:32 | String s = ... | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | +| Assert.cs:37:24:37:27 | null | Assert.cs:37:24:37:27 | null | +| Assert.cs:37:31:37:32 | "" | Assert.cs:37:31:37:32 | "" | +| Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | +| Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:42:10:42:11 | enter M6 | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:42:10:42:11 | exit M6 | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:44:16:44:32 | String s = ... | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:44:24:44:27 | null | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:44:31:44:32 | "" | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:42:10:42:11 | exit M6 | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:42:10:42:11 | exit M6 | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:44:16:44:32 | String s = ... | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | +| Assert.cs:44:24:44:27 | null | Assert.cs:44:24:44:27 | null | +| Assert.cs:44:31:44:32 | "" | Assert.cs:44:31:44:32 | "" | +| Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | +| Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:49:10:49:11 | enter M7 | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:49:10:49:11 | exit M7 | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:51:16:51:32 | String s = ... | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:51:24:51:27 | null | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:51:31:51:32 | "" | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:49:10:49:11 | exit M7 | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:49:10:49:11 | exit M7 | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:51:16:51:32 | String s = ... | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | +| Assert.cs:51:24:51:27 | null | Assert.cs:51:24:51:27 | null | +| Assert.cs:51:31:51:32 | "" | Assert.cs:51:31:51:32 | "" | +| Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | +| Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:56:10:56:11 | enter M8 | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:56:10:56:11 | exit M8 | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:58:24:58:27 | [b (line 56): true] null | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:58:31:58:32 | [b (line 56): false] "" | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:56:10:56:11 | exit M8 | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:58:24:58:27 | [b (line 56): true] null | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:58:31:58:32 | [b (line 56): false] "" | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:63:10:63:11 | enter M9 | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:63:10:63:11 | exit M9 | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:65:24:65:27 | [b (line 63): true] null | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:65:31:65:32 | [b (line 63): false] "" | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:63:10:63:11 | exit M9 | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:65:24:65:27 | [b (line 63): true] null | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:65:31:65:32 | [b (line 63): false] "" | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:70:10:70:12 | enter M10 | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:70:10:70:12 | exit M10 | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:72:24:72:27 | [b (line 70): true] null | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:72:31:72:32 | [b (line 70): false] "" | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:70:10:70:12 | exit M10 | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:72:24:72:27 | [b (line 70): true] null | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:72:31:72:32 | [b (line 70): false] "" | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:77:10:77:12 | enter M11 | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:77:10:77:12 | exit M11 | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:79:24:79:27 | [b (line 77): true] null | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:79:31:79:32 | [b (line 77): false] "" | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:77:10:77:12 | exit M11 | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:79:24:79:27 | [b (line 77): true] null | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:79:31:79:32 | [b (line 77): false] "" | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:84:10:84:12 | enter M12 | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:84:10:84:12 | exit M12 | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:86:24:86:27 | [b (line 84): true] null | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:86:31:86:32 | [b (line 84): false] "" | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:84:10:84:12 | exit M12 | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:86:24:86:27 | [b (line 84): true] null | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:86:31:86:32 | [b (line 84): false] "" | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:127:37:127:38 | [b (line 84): true] !... | Assert.cs:127:37:127:38 | [b (line 84): true] !... | | Assignments.cs:3:10:3:10 | enter M | Assignments.cs:3:10:3:10 | enter M | | Assignments.cs:14:18:14:35 | enter (...) => ... | Assignments.cs:14:18:14:35 | enter (...) => ... | | Assignments.cs:17:40:17:40 | enter + | Assignments.cs:17:40:17:40 | enter + | @@ -6181,6 +8002,9 @@ blockDominance | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | | CompileTimeOperators.cs:28:10:28:10 | enter M | CompileTimeOperators.cs:28:10:28:10 | enter M | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | enter M1 | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | exit M1 | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:28:3:38 | call to method ToString | @@ -6228,7 +8052,16 @@ blockDominance | ConditionalAccess.cs:19:12:19:13 | exit M6 | ConditionalAccess.cs:19:12:19:13 | exit M6 | | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | enter M7 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | enter Out | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | exit Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:35:14:35:24 | call to method Out | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:14:35:24 | call to method Out | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | exit IncrOrDecr | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | @@ -6406,20 +8239,20 @@ blockDominance | Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:110:16:110:16 | access to local variable x | | Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:113:10:113:11 | enter M9 | | Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:113:10:113:11 | exit M9 | -| Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:116:24:116:24 | access to local variable i | -| Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:116:41:116:41 | access to local variable i | +| Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:116:25:116:25 | access to local variable i | +| Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:116:42:116:42 | access to local variable i | | Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:117:9:123:9 | {...} | | Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | | Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | | Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:113:10:113:11 | exit M9 | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:113:10:113:11 | exit M9 | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:24:116:24 | access to local variable i | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:41:116:41 | access to local variable i | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:117:9:123:9 | {...} | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:116:41:116:41 | access to local variable i | -| Conditions.cs:117:9:123:9 | {...} | Conditions.cs:116:41:116:41 | access to local variable i | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:113:10:113:11 | exit M9 | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:25:116:25 | access to local variable i | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:42:116:42 | access to local variable i | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:117:9:123:9 | {...} | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:116:42:116:42 | access to local variable i | +| Conditions.cs:117:9:123:9 | {...} | Conditions.cs:116:42:116:42 | access to local variable i | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:117:9:123:9 | {...} | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | @@ -6436,6 +8269,13 @@ blockDominance | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:143:10:143:12 | enter M11 | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:143:10:143:12 | exit M11 | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:143:10:143:12 | exit M11 | +| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | +| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | | ExitMethods.cs:7:10:7:11 | enter M1 | ExitMethods.cs:7:10:7:11 | enter M1 | | ExitMethods.cs:13:10:13:11 | enter M2 | ExitMethods.cs:13:10:13:11 | enter M2 | | ExitMethods.cs:19:10:19:11 | enter M3 | ExitMethods.cs:19:10:19:11 | enter M3 | @@ -6489,6 +8329,12 @@ blockDominance | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | | ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:10:131:20 | enter AssertFalse | +| ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | +| ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | +| ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | +| ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | +| ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | | Extensions.cs:5:23:5:29 | enter ToInt32 | Extensions.cs:5:23:5:29 | enter ToInt32 | | Extensions.cs:10:24:10:29 | enter ToBool | Extensions.cs:10:24:10:29 | enter ToBool | @@ -7262,19 +9108,19 @@ blockDominance | LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:7:10:7:11 | enter M1 | | LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:7:10:7:11 | exit M1 | | LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:10:13:10:19 | return ...; | -| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:11:21:11:23 | String arg | -| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:11:28:11:31 | access to parameter args | +| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:11:22:11:24 | String arg | +| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:11:29:11:32 | access to parameter args | | LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:7:10:7:11 | exit M1 | | LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:10:13:10:19 | return ...; | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:11:21:11:23 | String arg | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:21:11:23 | String arg | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:28:11:31 | access to parameter args | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:11:22:11:24 | String arg | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:22:11:24 | String arg | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:29:11:32 | access to parameter args | | LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:15:10:15:11 | enter M2 | | LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:15:10:15:11 | exit M2 | -| LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:18:21:18:21 | String x | +| LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:18:22:18:22 | String x | | LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:15:10:15:11 | exit M2 | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:15:10:15:11 | exit M2 | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:18:21:18:21 | String x | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:15:10:15:11 | exit M2 | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:18:22:18:22 | String x | | LoopUnrolling.cs:22:10:22:11 | enter M3 | LoopUnrolling.cs:22:10:22:11 | enter M3 | | LoopUnrolling.cs:22:10:22:11 | enter M3 | LoopUnrolling.cs:22:10:22:11 | exit M3 | | LoopUnrolling.cs:22:10:22:11 | enter M3 | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | @@ -7289,59 +9135,311 @@ blockDominance | LoopUnrolling.cs:24:22:24:24 | Char arg | LoopUnrolling.cs:25:26:25:29 | Char arg0 | | LoopUnrolling.cs:25:26:25:29 | Char arg0 | LoopUnrolling.cs:25:26:25:29 | Char arg0 | | LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:29:10:29:11 | enter M4 | -| LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:29:10:29:11 | exit M4 | -| LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:32:21:32:21 | String x | -| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:29:10:29:11 | exit M4 | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | exit M4 | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:21:32:21 | String x | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:32:21:32:21 | String x | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:36:10:36:11 | enter M5 | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:36:10:36:11 | exit M5 | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:21:40:21 | String x | -| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:41:25:41:25 | String y | +| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:22:40:22 | String x | +| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:41:26:41:26 | String y | | LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:36:10:36:11 | exit M5 | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | exit M5 | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:36:10:36:11 | exit M5 | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:40:21:40:21 | String x | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:41:25:41:25 | String y | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:36:10:36:11 | exit M5 | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:41:25:41:25 | String y | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:36:10:36:11 | exit M5 | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:40:22:40:22 | String x | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:41:26:41:26 | String y | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:36:10:36:11 | exit M5 | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:41:26:41:26 | String y | | LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:45:10:45:11 | enter M6 | -| LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:50:13:50:17 | Label: | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:50:13:50:17 | Label: | +| LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:50:9:50:13 | Label: | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:50:9:50:13 | Label: | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:55:10:55:11 | enter M7 | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:55:10:55:11 | exit M7 | -| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | -| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | +| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | +| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:55:10:55:11 | exit M7 | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | -| LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | +| LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | -| LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | +| LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:67:10:67:11 | enter M8 | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:67:10:67:11 | exit M8 | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:70:13:70:19 | return ...; | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:71:9:71:21 | ...; | -| LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:72:21:72:23 | String arg | | LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:67:10:67:11 | exit M8 | | LoopUnrolling.cs:70:13:70:19 | return ...; | LoopUnrolling.cs:70:13:70:19 | return ...; | | LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:71:9:71:21 | ...; | -| LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:72:21:72:23 | String arg | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:21:72:23 | String arg | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:72:21:72:23 | String arg | +| LoopUnrolling.cs:76:10:76:11 | enter M9 | LoopUnrolling.cs:76:10:76:11 | enter M9 | +| LoopUnrolling.cs:85:10:85:12 | enter M10 | LoopUnrolling.cs:85:10:85:12 | enter M10 | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:94:10:94:12 | enter M11 | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:94:10:94:12 | exit M11 | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:97:22:97:22 | String x | +| LoopUnrolling.cs:94:10:94:12 | exit M11 | LoopUnrolling.cs:94:10:94:12 | exit M11 | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:94:10:94:12 | exit M11 | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:97:22:97:22 | String x | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | enter get_P1 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | enter get_P1 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | enter get_P2 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | enter get_P2 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | enter set_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | enter set_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:16:8:16 | enter M | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:16:8:16 | exit M | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:16:5:16 | enter M | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:16:5:16 | exit M | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | exit get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | exit get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | enter get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | exit get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | enter get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | exit get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | exit get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | exit get_Item | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | enter set_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | exit set_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | enter set_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | exit set_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | exit set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | exit set_Item | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | enter M1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | exit M1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | enter M1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | exit M1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | exit M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | exit M1 | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | enter M2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | exit M1 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | enter M1 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | exit M1 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:17:32:17 | 0 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | exit M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | exit M1 | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:37:9:37:10 | enter M2 | MultiImplementationA.cs:37:9:37:10 | enter M2 | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | enter get_P1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | enter get_P1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | enter get_P2 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | enter get_P2 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | enter set_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | enter set_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:16:8:16 | enter M | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:16:8:16 | exit M | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:16:5:16 | enter M | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:16:5:16 | exit M | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | exit get_Item | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | exit get_Item | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | enter get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | exit get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | enter get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | exit get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | exit get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | exit get_Item | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | enter set_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | exit set_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | enter set_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | exit set_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | exit set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | exit set_Item | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | enter M1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | exit M1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | enter M1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | exit M1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | exit M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | exit M1 | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | enter M2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | exit M1 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | enter M1 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | exit M1 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:17:32:17 | 0 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | exit M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | exit M1 | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationB.cs:32:17:32:17 | 0 | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:9:3:10 | enter M1 | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:9:3:10 | exit M1 | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:28:3:28 | 0 | @@ -7564,79 +9662,79 @@ blockDominance | Switch.cs:55:10:55:11 | enter M5 | Switch.cs:55:10:55:11 | enter M5 | | Switch.cs:66:10:66:11 | enter M6 | Switch.cs:66:10:66:11 | enter M6 | | Switch.cs:66:10:66:11 | enter M6 | Switch.cs:66:10:66:11 | exit M6 | -| Switch.cs:66:10:66:11 | enter M6 | Switch.cs:73:15:73:20 | break; | +| Switch.cs:66:10:66:11 | enter M6 | Switch.cs:73:17:73:22 | break; | | Switch.cs:66:10:66:11 | exit M6 | Switch.cs:66:10:66:11 | exit M6 | -| Switch.cs:73:15:73:20 | break; | Switch.cs:73:15:73:20 | break; | +| Switch.cs:73:17:73:22 | break; | Switch.cs:73:17:73:22 | break; | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:77:10:77:11 | enter M7 | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:77:10:77:11 | exit M7 | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:82:22:82:25 | true | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:83:13:83:20 | case ...: | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:84:15:85:22 | if (...) ... | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:85:17:85:22 | break; | -| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:86:22:86:25 | true | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:82:24:82:27 | true | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:83:13:83:19 | case ...: | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:84:17:85:26 | if (...) ... | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:85:21:85:26 | break; | +| Switch.cs:77:10:77:11 | enter M7 | Switch.cs:86:24:86:27 | true | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:88:16:88:20 | false | | Switch.cs:77:10:77:11 | exit M7 | Switch.cs:77:10:77:11 | exit M7 | -| Switch.cs:82:22:82:25 | true | Switch.cs:82:22:82:25 | true | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:83:13:83:20 | case ...: | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:84:15:85:22 | if (...) ... | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:85:17:85:22 | break; | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:86:22:86:25 | true | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:88:16:88:20 | false | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:84:15:85:22 | if (...) ... | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:85:17:85:22 | break; | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:86:22:86:25 | true | -| Switch.cs:85:17:85:22 | break; | Switch.cs:85:17:85:22 | break; | -| Switch.cs:86:22:86:25 | true | Switch.cs:86:22:86:25 | true | +| Switch.cs:82:24:82:27 | true | Switch.cs:82:24:82:27 | true | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:83:13:83:19 | case ...: | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:84:17:85:26 | if (...) ... | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:85:21:85:26 | break; | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:86:24:86:27 | true | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:88:16:88:20 | false | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:84:17:85:26 | if (...) ... | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:85:21:85:26 | break; | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:86:24:86:27 | true | +| Switch.cs:85:21:85:26 | break; | Switch.cs:85:21:85:26 | break; | +| Switch.cs:86:24:86:27 | true | Switch.cs:86:24:86:27 | true | | Switch.cs:88:16:88:20 | false | Switch.cs:88:16:88:20 | false | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:91:10:91:11 | enter M8 | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:91:10:91:11 | exit M8 | -| Switch.cs:91:10:91:11 | enter M8 | Switch.cs:96:22:96:25 | true | +| Switch.cs:91:10:91:11 | enter M8 | Switch.cs:96:24:96:27 | true | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:98:16:98:20 | false | | Switch.cs:91:10:91:11 | exit M8 | Switch.cs:91:10:91:11 | exit M8 | -| Switch.cs:96:22:96:25 | true | Switch.cs:96:22:96:25 | true | +| Switch.cs:96:24:96:27 | true | Switch.cs:96:24:96:27 | true | | Switch.cs:98:16:98:20 | false | Switch.cs:98:16:98:20 | false | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:101:9:101:10 | enter M9 | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:101:9:101:10 | exit M9 | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:103:19:103:25 | access to property Length | -| Switch.cs:101:9:101:10 | enter M9 | Switch.cs:105:13:105:20 | case ...: | -| Switch.cs:101:9:101:10 | enter M9 | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:101:9:101:10 | enter M9 | Switch.cs:106:13:106:20 | case ...: | -| Switch.cs:101:9:101:10 | enter M9 | Switch.cs:106:29:106:29 | 1 | +| Switch.cs:101:9:101:10 | enter M9 | Switch.cs:105:13:105:19 | case ...: | +| Switch.cs:101:9:101:10 | enter M9 | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:101:9:101:10 | enter M9 | Switch.cs:106:13:106:19 | case ...: | +| Switch.cs:101:9:101:10 | enter M9 | Switch.cs:106:28:106:28 | 1 | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:108:17:108:17 | 1 | | Switch.cs:101:9:101:10 | exit M9 | Switch.cs:101:9:101:10 | exit M9 | | Switch.cs:103:19:103:25 | access to property Length | Switch.cs:103:19:103:25 | access to property Length | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:101:9:101:10 | exit M9 | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:13:105:20 | case ...: | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:106:13:106:20 | case ...: | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:106:29:106:29 | 1 | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:108:17:108:17 | 1 | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:13:106:20 | case ...: | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:29:106:29 | 1 | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:108:17:108:17 | 1 | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:106:29:106:29 | 1 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:101:9:101:10 | exit M9 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:13:105:19 | case ...: | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:106:13:106:19 | case ...: | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:106:28:106:28 | 1 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:108:17:108:17 | 1 | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:13:106:19 | case ...: | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:28:106:28 | 1 | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:108:17:108:17 | 1 | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:106:28:106:28 | 1 | | Switch.cs:108:17:108:17 | 1 | Switch.cs:108:17:108:17 | 1 | | Switch.cs:111:17:111:21 | enter Throw | Switch.cs:111:17:111:21 | enter Throw | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:113:9:113:11 | enter M10 | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:113:9:113:11 | exit M10 | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:117:25:117:25 | access to parameter s | -| Switch.cs:113:9:113:11 | enter M10 | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:113:9:113:11 | enter M10 | Switch.cs:118:13:118:33 | case ...: | +| Switch.cs:113:9:113:11 | enter M10 | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:113:9:113:11 | enter M10 | Switch.cs:118:13:118:34 | case ...: | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:118:25:118:25 | access to parameter s | -| Switch.cs:113:9:113:11 | enter M10 | Switch.cs:118:42:118:42 | 2 | +| Switch.cs:113:9:113:11 | enter M10 | Switch.cs:118:43:118:43 | 2 | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:120:17:120:17 | 1 | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:113:9:113:11 | exit M10 | | Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:25:117:25 | access to parameter s | -| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:13:118:33 | case ...: | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:25:118:25 | access to parameter s | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:42:118:42 | 2 | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:120:17:120:17 | 1 | +| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:13:118:34 | case ...: | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:25:118:25 | access to parameter s | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:43:118:43 | 2 | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:120:17:120:17 | 1 | | Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:25:118:25 | access to parameter s | -| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:42:118:42 | 2 | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:118:42:118:42 | 2 | +| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:43:118:43 | 2 | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:118:43:118:43 | 2 | | Switch.cs:120:17:120:17 | 1 | Switch.cs:120:17:120:17 | 1 | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:123:10:123:12 | enter M11 | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:123:10:123:12 | exit M11 | @@ -7684,6 +9782,24 @@ blockDominance | Switch.cs:150:13:150:19 | case ...: | Switch.cs:150:13:150:19 | case ...: | | Switch.cs:150:13:150:19 | case ...: | Switch.cs:150:28:150:28 | 2 | | Switch.cs:150:28:150:28 | 2 | Switch.cs:150:28:150:28 | 2 | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:154:10:154:12 | enter M15 | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:154:10:154:12 | exit M15 | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:156:13:156:54 | String s = ... | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:156:36:156:38 | "a" | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:156:41:156:52 | ... => ... | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:158:13:158:49 | ...; | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:160:13:160:49 | ...; | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:154:10:154:12 | exit M15 | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:13:156:54 | String s = ... | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:158:13:158:49 | ...; | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:160:13:160:49 | ...; | +| Switch.cs:156:36:156:38 | "a" | Switch.cs:156:36:156:38 | "a" | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:41:156:52 | ... => ... | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:156:50:156:52 | "b" | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:49 | ...; | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:49 | ...; | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:3:10:3:10 | enter M | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:7:25:7:25 | ; | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:8:9:8:28 | ... ...; | @@ -8255,6 +10371,263 @@ postBlockDominance | ArrayCreation.cs:5:12:5:13 | enter M2 | ArrayCreation.cs:5:12:5:13 | enter M2 | | ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:11:7:12 | enter M3 | | ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:12:9:13 | enter M4 | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:7:10:7:11 | enter M1 | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:7:10:7:11 | enter M1 | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:7:10:7:11 | exit M1 | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:9:16:9:32 | String s = ... | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:9:24:9:27 | null | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:9:31:9:32 | "" | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:7:10:7:11 | enter M1 | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:9:16:9:32 | String s = ... | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:9:24:9:27 | null | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:9:31:9:32 | "" | +| Assert.cs:9:24:9:27 | null | Assert.cs:9:24:9:27 | null | +| Assert.cs:9:31:9:32 | "" | Assert.cs:9:31:9:32 | "" | +| Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | +| Assert.cs:10:9:10:31 | [assertion success] call to method Assert | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:14:10:14:11 | enter M2 | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:14:10:14:11 | enter M2 | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:14:10:14:11 | exit M2 | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:16:16:16:32 | String s = ... | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:16:24:16:27 | null | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:16:31:16:32 | "" | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:14:10:14:11 | enter M2 | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:16:16:16:32 | String s = ... | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:16:24:16:27 | null | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:16:31:16:32 | "" | +| Assert.cs:16:24:16:27 | null | Assert.cs:16:24:16:27 | null | +| Assert.cs:16:31:16:32 | "" | Assert.cs:16:31:16:32 | "" | +| Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | +| Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:21:10:21:11 | enter M3 | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:21:10:21:11 | enter M3 | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:21:10:21:11 | exit M3 | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:23:16:23:32 | String s = ... | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:23:24:23:27 | null | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:23:31:23:32 | "" | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:21:10:21:11 | enter M3 | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:23:16:23:32 | String s = ... | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:23:24:23:27 | null | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:23:31:23:32 | "" | +| Assert.cs:23:24:23:27 | null | Assert.cs:23:24:23:27 | null | +| Assert.cs:23:31:23:32 | "" | Assert.cs:23:31:23:32 | "" | +| Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | +| Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:28:10:28:11 | enter M4 | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:28:10:28:11 | enter M4 | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:28:10:28:11 | exit M4 | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:30:16:30:32 | String s = ... | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:30:24:30:27 | null | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:30:31:30:32 | "" | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:28:10:28:11 | enter M4 | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:30:16:30:32 | String s = ... | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:30:24:30:27 | null | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:30:31:30:32 | "" | +| Assert.cs:30:24:30:27 | null | Assert.cs:30:24:30:27 | null | +| Assert.cs:30:31:30:32 | "" | Assert.cs:30:31:30:32 | "" | +| Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | +| Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:35:10:35:11 | enter M5 | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:35:10:35:11 | enter M5 | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:35:10:35:11 | exit M5 | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:37:16:37:32 | String s = ... | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:37:24:37:27 | null | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:37:31:37:32 | "" | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:35:10:35:11 | enter M5 | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:37:16:37:32 | String s = ... | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:37:24:37:27 | null | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:37:31:37:32 | "" | +| Assert.cs:37:24:37:27 | null | Assert.cs:37:24:37:27 | null | +| Assert.cs:37:31:37:32 | "" | Assert.cs:37:31:37:32 | "" | +| Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | +| Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:42:10:42:11 | enter M6 | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:42:10:42:11 | enter M6 | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:42:10:42:11 | exit M6 | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:44:16:44:32 | String s = ... | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:44:24:44:27 | null | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:44:31:44:32 | "" | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:42:10:42:11 | enter M6 | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:44:16:44:32 | String s = ... | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:44:24:44:27 | null | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:44:31:44:32 | "" | +| Assert.cs:44:24:44:27 | null | Assert.cs:44:24:44:27 | null | +| Assert.cs:44:31:44:32 | "" | Assert.cs:44:31:44:32 | "" | +| Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | +| Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:49:10:49:11 | enter M7 | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:49:10:49:11 | enter M7 | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:49:10:49:11 | exit M7 | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:51:16:51:32 | String s = ... | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:51:24:51:27 | null | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:51:31:51:32 | "" | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:49:10:49:11 | enter M7 | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:51:16:51:32 | String s = ... | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:51:24:51:27 | null | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:51:31:51:32 | "" | +| Assert.cs:51:24:51:27 | null | Assert.cs:51:24:51:27 | null | +| Assert.cs:51:31:51:32 | "" | Assert.cs:51:31:51:32 | "" | +| Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | +| Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:56:10:56:11 | enter M8 | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:56:10:56:11 | enter M8 | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:56:10:56:11 | exit M8 | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:58:24:58:27 | [b (line 56): true] null | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:58:31:58:32 | [b (line 56): false] "" | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:58:24:58:27 | [b (line 56): true] null | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:58:31:58:32 | [b (line 56): false] "" | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:58:31:58:32 | [b (line 56): false] "" | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:63:10:63:11 | enter M9 | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:63:10:63:11 | enter M9 | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:63:10:63:11 | exit M9 | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:65:24:65:27 | [b (line 63): true] null | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:65:31:65:32 | [b (line 63): false] "" | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:65:24:65:27 | [b (line 63): true] null | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:65:31:65:32 | [b (line 63): false] "" | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:65:24:65:27 | [b (line 63): true] null | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:70:10:70:12 | enter M10 | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:70:10:70:12 | enter M10 | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:70:10:70:12 | exit M10 | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:72:24:72:27 | [b (line 70): true] null | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:72:31:72:32 | [b (line 70): false] "" | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:72:24:72:27 | [b (line 70): true] null | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:72:31:72:32 | [b (line 70): false] "" | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:72:31:72:32 | [b (line 70): false] "" | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:77:10:77:12 | enter M11 | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:77:10:77:12 | enter M11 | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:77:10:77:12 | exit M11 | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:79:24:79:27 | [b (line 77): true] null | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:79:31:79:32 | [b (line 77): false] "" | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:79:24:79:27 | [b (line 77): true] null | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:79:31:79:32 | [b (line 77): false] "" | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:79:24:79:27 | [b (line 77): true] null | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:84:10:84:12 | enter M12 | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:84:10:84:12 | enter M12 | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:84:10:84:12 | exit M12 | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:86:24:86:27 | [b (line 84): true] null | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:86:31:86:32 | [b (line 84): false] "" | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:127:37:127:38 | [b (line 84): true] !... | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:86:24:86:27 | [b (line 84): true] null | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:86:31:86:32 | [b (line 84): false] "" | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | +| Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:119:37:119:38 | [b (line 84): true] !... | +| Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | +| Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | +| Assert.cs:127:37:127:38 | [b (line 84): true] !... | Assert.cs:127:37:127:38 | [b (line 84): true] !... | | Assignments.cs:3:10:3:10 | enter M | Assignments.cs:3:10:3:10 | enter M | | Assignments.cs:14:18:14:35 | enter (...) => ... | Assignments.cs:14:18:14:35 | enter (...) => ... | | Assignments.cs:17:40:17:40 | enter + | Assignments.cs:17:40:17:40 | enter + | @@ -8347,6 +10720,10 @@ postBlockDominance | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | | CompileTimeOperators.cs:28:10:28:10 | enter M | CompileTimeOperators.cs:28:10:28:10 | enter M | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:30:10:30:12 | enter Out | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | enter M1 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | enter M1 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | exit M1 | @@ -8392,7 +10769,15 @@ postBlockDominance | ConditionalAccess.cs:19:12:19:13 | exit M6 | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | enter M7 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | enter Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | enter Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | exit Out | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | enter M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | exit M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:35:14:35:24 | call to method Out | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:14:35:24 | call to method Out | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | enter IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | exit IncrOrDecr | @@ -8557,21 +10942,21 @@ postBlockDominance | Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:113:10:113:11 | enter M9 | | Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:113:10:113:11 | enter M9 | | Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:113:10:113:11 | exit M9 | -| Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:116:24:116:24 | access to local variable i | -| Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:116:41:116:41 | access to local variable i | +| Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:116:25:116:25 | access to local variable i | +| Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:116:42:116:42 | access to local variable i | | Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:117:9:123:9 | {...} | | Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | | Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:113:10:113:11 | enter M9 | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:24:116:24 | access to local variable i | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:41:116:41 | access to local variable i | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:117:9:123:9 | {...} | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:116:41:116:41 | access to local variable i | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:117:9:123:9 | {...} | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:113:10:113:11 | enter M9 | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:25:116:25 | access to local variable i | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:42:116:42 | access to local variable i | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:117:9:123:9 | {...} | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:116:42:116:42 | access to local variable i | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:117:9:123:9 | {...} | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:117:9:123:9 | {...} | | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | @@ -8580,6 +10965,13 @@ postBlockDominance | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): false] true | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): false] true | | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:143:10:143:12 | enter M11 | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:143:10:143:12 | enter M11 | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:143:10:143:12 | exit M11 | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | +| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | +| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | | ExitMethods.cs:7:10:7:11 | enter M1 | ExitMethods.cs:7:10:7:11 | enter M1 | | ExitMethods.cs:13:10:13:11 | enter M2 | ExitMethods.cs:13:10:13:11 | enter M2 | | ExitMethods.cs:19:10:19:11 | enter M3 | ExitMethods.cs:19:10:19:11 | enter M3 | @@ -8633,6 +11025,12 @@ postBlockDominance | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | | ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:10:131:20 | enter AssertFalse | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:10:131:20 | enter AssertFalse | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | +| ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | +| ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | | Extensions.cs:5:23:5:29 | enter ToInt32 | Extensions.cs:5:23:5:29 | enter ToInt32 | | Extensions.cs:10:24:10:29 | enter ToBool | Extensions.cs:10:24:10:29 | enter ToBool | @@ -9077,18 +11475,18 @@ postBlockDominance | LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:7:10:7:11 | enter M1 | | LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:7:10:7:11 | exit M1 | | LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:10:13:10:19 | return ...; | -| LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:11:21:11:23 | String arg | -| LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:11:28:11:31 | access to parameter args | +| LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:11:22:11:24 | String arg | +| LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:11:29:11:32 | access to parameter args | | LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:10:13:10:19 | return ...; | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:11:21:11:23 | String arg | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:11:28:11:31 | access to parameter args | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:28:11:31 | access to parameter args | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:11:22:11:24 | String arg | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:11:29:11:32 | access to parameter args | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:29:11:32 | access to parameter args | | LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:15:10:15:11 | enter M2 | | LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:15:10:15:11 | enter M2 | | LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:15:10:15:11 | exit M2 | -| LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:18:21:18:21 | String x | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:15:10:15:11 | enter M2 | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:18:21:18:21 | String x | +| LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:18:22:18:22 | String x | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:15:10:15:11 | enter M2 | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:18:22:18:22 | String x | | LoopUnrolling.cs:22:10:22:11 | enter M3 | LoopUnrolling.cs:22:10:22:11 | enter M3 | | LoopUnrolling.cs:22:10:22:11 | exit M3 | LoopUnrolling.cs:22:10:22:11 | enter M3 | | LoopUnrolling.cs:22:10:22:11 | exit M3 | LoopUnrolling.cs:22:10:22:11 | exit M3 | @@ -9103,57 +11501,310 @@ postBlockDominance | LoopUnrolling.cs:25:26:25:29 | Char arg0 | LoopUnrolling.cs:24:22:24:24 | Char arg | | LoopUnrolling.cs:25:26:25:29 | Char arg0 | LoopUnrolling.cs:25:26:25:29 | Char arg0 | | LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:29:10:29:11 | enter M4 | -| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:29:10:29:11 | enter M4 | -| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:29:10:29:11 | exit M4 | -| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:32:21:32:21 | String x | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | enter M4 | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:21:32:21 | String x | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:32:21:32:21 | String x | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:36:10:36:11 | enter M5 | | LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:36:10:36:11 | enter M5 | | LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:36:10:36:11 | exit M5 | | LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:40:21:40:21 | String x | -| LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:41:25:41:25 | String y | +| LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:40:22:40:22 | String x | +| LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:41:26:41:26 | String y | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | enter M5 | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:21:40:21 | String x | -| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:25:41:25 | String y | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:36:10:36:11 | enter M5 | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:40:21:40:21 | String x | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:36:10:36:11 | enter M5 | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:40:21:40:21 | String x | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:41:25:41:25 | String y | +| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:22:40:22 | String x | +| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:26:41:26 | String y | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:36:10:36:11 | enter M5 | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:40:22:40:22 | String x | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:36:10:36:11 | enter M5 | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:40:22:40:22 | String x | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:41:26:41:26 | String y | | LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:45:10:45:11 | enter M6 | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:50:13:50:17 | Label: | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:50:9:50:13 | Label: | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:55:10:55:11 | enter M7 | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:55:10:55:11 | enter M7 | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:55:10:55:11 | exit M7 | -| LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | -| LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | +| LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | +| LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | -| LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | +| LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | -| LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | +| LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:67:10:67:11 | enter M8 | | LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:67:10:67:11 | enter M8 | | LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:67:10:67:11 | exit M8 | | LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:70:13:70:19 | return ...; | | LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:71:9:71:21 | ...; | -| LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:72:21:72:23 | String arg | | LoopUnrolling.cs:70:13:70:19 | return ...; | LoopUnrolling.cs:70:13:70:19 | return ...; | | LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:71:9:71:21 | ...; | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:71:9:71:21 | ...; | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:21:72:23 | String arg | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:72:21:72:23 | String arg | +| LoopUnrolling.cs:76:10:76:11 | enter M9 | LoopUnrolling.cs:76:10:76:11 | enter M9 | +| LoopUnrolling.cs:85:10:85:12 | enter M10 | LoopUnrolling.cs:85:10:85:12 | enter M10 | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:94:10:94:12 | enter M11 | +| LoopUnrolling.cs:94:10:94:12 | exit M11 | LoopUnrolling.cs:94:10:94:12 | enter M11 | +| LoopUnrolling.cs:94:10:94:12 | exit M11 | LoopUnrolling.cs:94:10:94:12 | exit M11 | +| LoopUnrolling.cs:94:10:94:12 | exit M11 | LoopUnrolling.cs:97:22:97:22 | String x | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:94:10:94:12 | enter M11 | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:97:22:97:22 | String x | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | enter get_P1 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | enter get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | enter get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | enter get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | enter get_P2 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | enter get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | enter get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | enter get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | enter set_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | enter set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | enter set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | enter set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:16:8:16 | enter M | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:16:5:16 | enter M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | enter M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | enter M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | exit get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | enter get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | enter get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | enter get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | exit get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | enter get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | exit get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | enter set_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | enter set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | enter set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | exit set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | enter set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | exit set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | enter M1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | enter M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | enter M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | exit M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | enter M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | exit M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | enter M2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | enter M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | exit M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | enter M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | exit M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationB.cs:32:17:32:17 | 0 | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:37:9:37:10 | enter M2 | MultiImplementationA.cs:37:9:37:10 | enter M2 | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | enter get_P1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | enter get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | enter get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | enter get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | enter get_P2 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | enter get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | enter get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | enter get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | enter set_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | enter set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | enter set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | enter set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:16:8:16 | enter M | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:16:5:16 | enter M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:16:8:16 | enter M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | enter M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | exit get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | enter get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | enter get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | enter get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | exit get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | enter get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | exit get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | enter set_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | enter set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | enter set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | exit set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | enter set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | exit set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | enter M1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | enter M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | enter M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | exit M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | enter M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | exit M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | enter M2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | enter M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | exit M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | enter M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | exit M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationB.cs:32:17:32:17 | 0 | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationB.cs:32:17:32:17 | 0 | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:9:3:10 | enter M1 | | NullCoalescing.cs:3:9:3:10 | exit M1 | NullCoalescing.cs:3:9:3:10 | enter M1 | | NullCoalescing.cs:3:9:3:10 | exit M1 | NullCoalescing.cs:3:9:3:10 | exit M1 | @@ -9297,63 +11948,63 @@ postBlockDominance | Switch.cs:66:10:66:11 | enter M6 | Switch.cs:66:10:66:11 | enter M6 | | Switch.cs:66:10:66:11 | exit M6 | Switch.cs:66:10:66:11 | enter M6 | | Switch.cs:66:10:66:11 | exit M6 | Switch.cs:66:10:66:11 | exit M6 | -| Switch.cs:66:10:66:11 | exit M6 | Switch.cs:73:15:73:20 | break; | -| Switch.cs:73:15:73:20 | break; | Switch.cs:73:15:73:20 | break; | +| Switch.cs:66:10:66:11 | exit M6 | Switch.cs:73:17:73:22 | break; | +| Switch.cs:73:17:73:22 | break; | Switch.cs:73:17:73:22 | break; | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:77:10:77:11 | enter M7 | | Switch.cs:77:10:77:11 | exit M7 | Switch.cs:77:10:77:11 | enter M7 | | Switch.cs:77:10:77:11 | exit M7 | Switch.cs:77:10:77:11 | exit M7 | -| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:82:22:82:25 | true | -| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:83:13:83:20 | case ...: | -| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:84:15:85:22 | if (...) ... | -| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:85:17:85:22 | break; | -| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:86:22:86:25 | true | +| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:82:24:82:27 | true | +| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:83:13:83:19 | case ...: | +| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:84:17:85:26 | if (...) ... | +| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:85:21:85:26 | break; | +| Switch.cs:77:10:77:11 | exit M7 | Switch.cs:86:24:86:27 | true | | Switch.cs:77:10:77:11 | exit M7 | Switch.cs:88:16:88:20 | false | -| Switch.cs:82:22:82:25 | true | Switch.cs:82:22:82:25 | true | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:83:13:83:20 | case ...: | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:84:15:85:22 | if (...) ... | -| Switch.cs:85:17:85:22 | break; | Switch.cs:85:17:85:22 | break; | -| Switch.cs:86:22:86:25 | true | Switch.cs:86:22:86:25 | true | -| Switch.cs:88:16:88:20 | false | Switch.cs:85:17:85:22 | break; | +| Switch.cs:82:24:82:27 | true | Switch.cs:82:24:82:27 | true | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:83:13:83:19 | case ...: | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:84:17:85:26 | if (...) ... | +| Switch.cs:85:21:85:26 | break; | Switch.cs:85:21:85:26 | break; | +| Switch.cs:86:24:86:27 | true | Switch.cs:86:24:86:27 | true | +| Switch.cs:88:16:88:20 | false | Switch.cs:85:21:85:26 | break; | | Switch.cs:88:16:88:20 | false | Switch.cs:88:16:88:20 | false | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:91:10:91:11 | enter M8 | | Switch.cs:91:10:91:11 | exit M8 | Switch.cs:91:10:91:11 | enter M8 | | Switch.cs:91:10:91:11 | exit M8 | Switch.cs:91:10:91:11 | exit M8 | -| Switch.cs:91:10:91:11 | exit M8 | Switch.cs:96:22:96:25 | true | +| Switch.cs:91:10:91:11 | exit M8 | Switch.cs:96:24:96:27 | true | | Switch.cs:91:10:91:11 | exit M8 | Switch.cs:98:16:98:20 | false | -| Switch.cs:96:22:96:25 | true | Switch.cs:96:22:96:25 | true | +| Switch.cs:96:24:96:27 | true | Switch.cs:96:24:96:27 | true | | Switch.cs:98:16:98:20 | false | Switch.cs:98:16:98:20 | false | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:101:9:101:10 | enter M9 | | Switch.cs:101:9:101:10 | exit M9 | Switch.cs:101:9:101:10 | enter M9 | | Switch.cs:101:9:101:10 | exit M9 | Switch.cs:101:9:101:10 | exit M9 | | Switch.cs:101:9:101:10 | exit M9 | Switch.cs:103:19:103:25 | access to property Length | -| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:105:13:105:20 | case ...: | -| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:106:13:106:20 | case ...: | -| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:106:29:106:29 | 1 | +| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:105:13:105:19 | case ...: | +| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:106:13:106:19 | case ...: | +| Switch.cs:101:9:101:10 | exit M9 | Switch.cs:106:28:106:28 | 1 | | Switch.cs:101:9:101:10 | exit M9 | Switch.cs:108:17:108:17 | 1 | | Switch.cs:103:19:103:25 | access to property Length | Switch.cs:103:19:103:25 | access to property Length | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:101:9:101:10 | enter M9 | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:103:19:103:25 | access to property Length | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:13:105:20 | case ...: | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:13:106:20 | case ...: | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:106:29:106:29 | 1 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:101:9:101:10 | enter M9 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:103:19:103:25 | access to property Length | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:13:105:19 | case ...: | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:13:106:19 | case ...: | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:106:28:106:28 | 1 | | Switch.cs:108:17:108:17 | 1 | Switch.cs:108:17:108:17 | 1 | | Switch.cs:111:17:111:21 | enter Throw | Switch.cs:111:17:111:21 | enter Throw | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:113:9:113:11 | enter M10 | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:113:9:113:11 | enter M10 | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:113:9:113:11 | exit M10 | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:117:25:117:25 | access to parameter s | -| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:118:13:118:33 | case ...: | +| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:118:13:118:34 | case ...: | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:118:25:118:25 | access to parameter s | -| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:118:42:118:42 | 2 | +| Switch.cs:113:9:113:11 | exit M10 | Switch.cs:118:43:118:43 | 2 | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:120:17:120:17 | 1 | | Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:25:117:25 | access to parameter s | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:13:118:33 | case ...: | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:13:118:34 | case ...: | | Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:25:118:25 | access to parameter s | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:118:42:118:42 | 2 | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:118:43:118:43 | 2 | | Switch.cs:120:17:120:17 | 1 | Switch.cs:120:17:120:17 | 1 | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:123:10:123:12 | enter M11 | | Switch.cs:123:10:123:12 | exit M11 | Switch.cs:123:10:123:12 | enter M11 | @@ -9395,6 +12046,23 @@ postBlockDominance | Switch.cs:149:13:149:20 | default: | Switch.cs:149:13:149:20 | default: | | Switch.cs:150:13:150:19 | case ...: | Switch.cs:150:13:150:19 | case ...: | | Switch.cs:150:28:150:28 | 2 | Switch.cs:150:28:150:28 | 2 | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:154:10:154:12 | enter M15 | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:154:10:154:12 | enter M15 | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:154:10:154:12 | exit M15 | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:156:13:156:54 | String s = ... | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:156:36:156:38 | "a" | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:156:41:156:52 | ... => ... | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:158:13:158:49 | ...; | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:160:13:160:49 | ...; | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:13:156:54 | String s = ... | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:36:156:38 | "a" | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:156:36:156:38 | "a" | Switch.cs:156:36:156:38 | "a" | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:41:156:52 | ... => ... | +| Switch.cs:156:50:156:52 | "b" | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:49 | ...; | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:49 | ...; | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:3:10:3:10 | enter M | | TypeAccesses.cs:7:25:7:25 | ; | TypeAccesses.cs:7:25:7:25 | ; | | TypeAccesses.cs:8:9:8:28 | ... ...; | TypeAccesses.cs:3:10:3:10 | enter M | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index bb34142d5506..e73626a3c95b 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -311,12 +311,15 @@ nodeEnclosing | ArrayCreation.cs:5:31:5:31 | 1 | ArrayCreation.cs:5:12:5:13 | M2 | | ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:11:7:12 | M3 | | ArrayCreation.cs:7:11:7:12 | exit M3 | ArrayCreation.cs:7:11:7:12 | M3 | +| ArrayCreation.cs:7:19:7:36 | 2 | ArrayCreation.cs:7:11:7:12 | M3 | | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | ArrayCreation.cs:7:11:7:12 | M3 | | ArrayCreation.cs:7:29:7:36 | { ..., ... } | ArrayCreation.cs:7:11:7:12 | M3 | | ArrayCreation.cs:7:31:7:31 | 0 | ArrayCreation.cs:7:11:7:12 | M3 | | ArrayCreation.cs:7:34:7:34 | 1 | ArrayCreation.cs:7:11:7:12 | M3 | | ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:12:9:13 | M4 | | ArrayCreation.cs:9:12:9:13 | exit M4 | ArrayCreation.cs:9:12:9:13 | M4 | +| ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:12:9:13 | M4 | +| ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:12:9:13 | M4 | | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | ArrayCreation.cs:9:12:9:13 | M4 | | ArrayCreation.cs:9:31:9:52 | { ..., ... } | ArrayCreation.cs:9:12:9:13 | M4 | | ArrayCreation.cs:9:33:9:40 | { ..., ... } | ArrayCreation.cs:9:12:9:13 | M4 | @@ -325,6 +328,531 @@ nodeEnclosing | ArrayCreation.cs:9:43:9:50 | { ..., ... } | ArrayCreation.cs:9:12:9:13 | M4 | | ArrayCreation.cs:9:45:9:45 | 2 | ArrayCreation.cs:9:12:9:13 | M4 | | ArrayCreation.cs:9:48:9:48 | 3 | ArrayCreation.cs:9:12:9:13 | M4 | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:8:5:12:5 | {...} | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:9:9:33 | ... ...; | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:20:9:32 | ... ? ... : ... | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:24:9:27 | null | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:31:9:32 | "" | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:10:9:10:31 | [assertion success] call to method Assert | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:10:9:10:32 | ...; | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:10:22:10:22 | access to local variable s | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:10:27:10:30 | null | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:11:9:11:35 | call to method WriteLine | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:11:9:11:36 | ...; | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:11:27:11:34 | access to property Length | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:15:5:19:5 | {...} | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:9:16:33 | ... ...; | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:20:16:32 | ... ? ... : ... | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:24:16:27 | null | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:31:16:32 | "" | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:17:9:17:25 | ...; | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:18:9:18:35 | call to method WriteLine | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:18:9:18:36 | ...; | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:18:27:18:27 | access to local variable s | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:18:27:18:34 | access to property Length | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:22:5:26:5 | {...} | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:9:23:33 | ... ...; | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:20:23:32 | ... ? ... : ... | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:24:23:27 | null | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:31:23:32 | "" | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:24:9:24:28 | ...; | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:25:9:25:35 | call to method WriteLine | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:25:9:25:36 | ...; | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:25:27:25:27 | access to local variable s | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:25:27:25:34 | access to property Length | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:29:5:33:5 | {...} | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:9:30:33 | ... ...; | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:20:30:32 | ... ? ... : ... | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:24:30:27 | null | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:31:30:32 | "" | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:31:9:31:33 | ...; | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:31:23:31:23 | access to local variable s | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:31:28:31:31 | null | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:32:9:32:35 | call to method WriteLine | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:32:9:32:36 | ...; | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:32:27:32:34 | access to property Length | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:36:5:40:5 | {...} | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:9:37:33 | ... ...; | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:20:37:32 | ... ? ... : ... | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:24:37:27 | null | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:31:37:32 | "" | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:38:9:38:33 | ...; | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:38:23:38:23 | access to local variable s | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:38:28:38:31 | null | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:39:9:39:35 | call to method WriteLine | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:39:9:39:36 | ...; | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:39:27:39:34 | access to property Length | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:43:5:47:5 | {...} | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:9:44:33 | ... ...; | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:20:44:32 | ... ? ... : ... | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:24:44:27 | null | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:31:44:32 | "" | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:45:9:45:34 | ...; | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:45:24:45:24 | access to local variable s | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:45:29:45:32 | null | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:46:9:46:35 | call to method WriteLine | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:46:9:46:36 | ...; | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:46:27:46:34 | access to property Length | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:50:5:54:5 | {...} | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:9:51:33 | ... ...; | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:20:51:32 | ... ? ... : ... | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:24:51:27 | null | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:31:51:32 | "" | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:52:9:52:34 | ...; | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:52:24:52:24 | access to local variable s | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:52:29:52:32 | null | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:53:9:53:35 | call to method WriteLine | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:53:9:53:36 | ...; | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:53:27:53:34 | access to property Length | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:57:5:61:5 | {...} | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:9:58:33 | ... ...; | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:16:58:32 | [b (line 56): false] String s = ... | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:16:58:32 | [b (line 56): true] String s = ... | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:9:59:37 | [assertion success] call to method IsTrue | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:9:59:38 | [b (line 56): false] ...; | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:9:59:38 | [b (line 56): true] ...; | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:23:59:23 | [b (line 56): false] access to local variable s | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:23:59:23 | [b (line 56): true] access to local variable s | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:23:59:36 | [b (line 56): false] ... && ... | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:23:59:36 | [b (line 56): true] ... && ... | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:28:59:31 | [b (line 56): false] null | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:28:59:31 | [b (line 56): true] null | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:60:9:60:35 | call to method WriteLine | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:60:9:60:36 | ...; | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:60:27:60:34 | access to property Length | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:64:5:68:5 | {...} | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:9:65:33 | ... ...; | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:16:65:32 | [b (line 63): false] String s = ... | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:16:65:32 | [b (line 63): true] String s = ... | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:9:66:38 | [assertion success] call to method IsFalse | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:9:66:39 | [b (line 63): false] ...; | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:9:66:39 | [b (line 63): true] ...; | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:24:66:24 | [b (line 63): false] access to local variable s | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:24:66:24 | [b (line 63): true] access to local variable s | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:24:66:37 | [b (line 63): false] ... \|\| ... | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:24:66:37 | [b (line 63): true] ... \|\| ... | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:29:66:32 | [b (line 63): false] null | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:29:66:32 | [b (line 63): true] null | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:67:9:67:35 | call to method WriteLine | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:67:9:67:36 | ...; | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:67:27:67:34 | access to property Length | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:71:5:75:5 | {...} | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:9:72:33 | ... ...; | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:16:72:32 | [b (line 70): false] String s = ... | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:16:72:32 | [b (line 70): true] String s = ... | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:9:73:37 | [assertion success] call to method IsTrue | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:9:73:38 | [b (line 70): false] ...; | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:9:73:38 | [b (line 70): true] ...; | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:23:73:23 | [b (line 70): false] access to local variable s | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:23:73:23 | [b (line 70): true] access to local variable s | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:23:73:36 | [b (line 70): false] ... && ... | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:23:73:36 | [b (line 70): true] ... && ... | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:28:73:31 | [b (line 70): false] null | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:28:73:31 | [b (line 70): true] null | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:74:9:74:35 | call to method WriteLine | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:74:9:74:36 | ...; | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:74:27:74:34 | access to property Length | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:78:5:82:5 | {...} | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:9:79:33 | ... ...; | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:16:79:32 | [b (line 77): false] String s = ... | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:16:79:32 | [b (line 77): true] String s = ... | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:9:80:38 | [assertion success] call to method IsFalse | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:9:80:39 | [b (line 77): false] ...; | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:9:80:39 | [b (line 77): true] ...; | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:24:80:24 | [b (line 77): false] access to local variable s | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:24:80:24 | [b (line 77): true] access to local variable s | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:24:80:37 | [b (line 77): false] ... \|\| ... | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:24:80:37 | [b (line 77): true] ... \|\| ... | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:29:80:32 | [b (line 77): false] null | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:29:80:32 | [b (line 77): true] null | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:81:9:81:35 | call to method WriteLine | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:81:9:81:36 | ...; | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:81:27:81:34 | access to property Length | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:9:86:33 | ... ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:16:86:32 | [b (line 84): false] String s = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:16:86:32 | [b (line 84): true] String s = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:20:86:32 | ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:32 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:32 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:22:87:22 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:22:87:22 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:27:87:30 | [b (line 84): false] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:27:87:30 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:88:9:88:35 | [b (line 84): false] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:88:9:88:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:88:9:88:36 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:88:9:88:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:88:27:88:27 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:88:27:88:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:88:27:88:34 | [b (line 84): false] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:88:27:88:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:9:90:25 | [b (line 84): false] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:9:90:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:9:90:26 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:9:90:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:13:90:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:13:90:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:17:90:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:90:24:90:25 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:25 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:25 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:92:9:92:35 | [b (line 84): false] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:92:9:92:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:92:9:92:36 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:92:9:92:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:92:27:92:27 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:92:27:92:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:92:27:92:34 | [b (line 84): false] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:92:27:92:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:9:94:25 | [b (line 84): false] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:9:94:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:9:94:26 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:9:94:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:13:94:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:13:94:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:17:94:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:94:24:94:25 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:28 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:28 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:96:9:96:35 | [b (line 84): false] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:96:9:96:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:96:9:96:36 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:96:9:96:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:96:27:96:27 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:96:27:96:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:96:27:96:34 | [b (line 84): false] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:96:27:96:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:9:98:25 | [b (line 84): false] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:9:98:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:9:98:26 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:9:98:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:13:98:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:13:98:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:17:98:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:98:24:98:25 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:33 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:33 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:23:99:23 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:23:99:23 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:28:99:31 | [b (line 84): false] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:28:99:31 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:100:9:100:35 | [b (line 84): false] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:100:9:100:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:100:9:100:36 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:100:9:100:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:100:27:100:27 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:100:27:100:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:100:27:100:34 | [b (line 84): false] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:100:27:100:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:9:102:25 | [b (line 84): false] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:9:102:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:9:102:26 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:9:102:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:13:102:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:13:102:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:17:102:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:102:24:102:25 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:33 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:33 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:23:103:23 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:23:103:23 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:28:103:31 | [b (line 84): false] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:28:103:31 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:104:9:104:35 | [b (line 84): false] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:104:9:104:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:104:9:104:36 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:104:9:104:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:104:27:104:27 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:104:27:104:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:104:27:104:34 | [b (line 84): false] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:104:27:104:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:9:106:25 | [b (line 84): false] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:9:106:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:9:106:26 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:9:106:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:13:106:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:13:106:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:17:106:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:106:24:106:25 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:34 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:34 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:24:107:24 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:24:107:24 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:29:107:32 | [b (line 84): false] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:29:107:32 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:108:9:108:35 | [b (line 84): false] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:108:9:108:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:108:9:108:36 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:108:9:108:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:108:27:108:27 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:108:27:108:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:108:27:108:34 | [b (line 84): false] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:108:27:108:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:9:110:25 | [b (line 84): false] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:9:110:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:9:110:26 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:9:110:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:13:110:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:13:110:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:17:110:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:110:24:110:25 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:34 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:34 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:24:111:24 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:24:111:24 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:29:111:32 | [b (line 84): false] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:29:111:32 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:112:9:112:35 | [b (line 84): false] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:112:9:112:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:112:9:112:36 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:112:9:112:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:112:27:112:27 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:112:27:112:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:112:27:112:34 | [b (line 84): false] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:112:27:112:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:9:114:25 | [b (line 84): false] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:9:114:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:9:114:26 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:9:114:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:13:114:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:13:114:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:17:114:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:114:24:114:25 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:9:115:38 | [b (line 84): false] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:9:115:38 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:23:115:23 | [b (line 84): false] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:23:115:23 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:23:115:36 | [b (line 84): false] ... && ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:23:115:36 | [b (line 84): true] ... && ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:28:115:31 | [b (line 84): false] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:28:115:31 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:116:9:116:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:116:9:116:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:116:27:116:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:116:27:116:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:118:9:118:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:118:9:118:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:118:13:118:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:118:17:118:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:9:119:40 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:24:119:24 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:24:119:38 | [b (line 84): true] ... \|\| ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:29:119:32 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:120:9:120:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:120:9:120:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:120:27:120:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:120:27:120:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:122:9:122:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:122:9:122:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:122:13:122:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:122:17:122:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:9:123:38 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:23:123:23 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:23:123:36 | [b (line 84): true] ... && ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:28:123:31 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:124:9:124:35 | [b (line 84): true] call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:124:9:124:36 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:124:27:124:27 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:124:27:124:34 | [b (line 84): true] access to property Length | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:126:9:126:25 | [b (line 84): true] ... = ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:126:9:126:26 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:126:13:126:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:126:17:126:20 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:9:127:39 | [assertion success] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:9:127:40 | [b (line 84): true] ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:24:127:24 | [b (line 84): true] access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:24:127:38 | [b (line 84): true] ... \|\| ... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:29:127:32 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:37:127:38 | [b (line 84): true] !... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:128:9:128:35 | call to method WriteLine | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:128:9:128:36 | ...; | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:128:27:128:27 | access to local variable s | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:128:27:128:34 | access to property Length | Assert.cs:84:10:84:12 | M12 | | Assignments.cs:3:10:3:10 | enter M | Assignments.cs:3:10:3:10 | M | | Assignments.cs:3:10:3:10 | exit M | Assignments.cs:3:10:3:10 | M | | Assignments.cs:4:5:15:5 | {...} | Assignments.cs:3:10:3:10 | M | @@ -508,6 +1036,8 @@ nodeEnclosing | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | CompileTimeOperators.cs:28:10:28:10 | M | | CompileTimeOperators.cs:40:14:40:38 | ...; | CompileTimeOperators.cs:28:10:28:10 | M | | CompileTimeOperators.cs:40:32:40:36 | "End" | CompileTimeOperators.cs:28:10:28:10 | M | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | M1 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | M1 | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:12:3:13 | M1 | @@ -564,13 +1094,31 @@ nodeEnclosing | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:21:10:21:11 | M7 | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:21:10:21:11 | M7 | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:21:10:21:11 | M7 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:3:10:3:19 | IncrOrDecr | @@ -774,7 +1322,7 @@ nodeEnclosing | Conditions.cs:79:17:79:26 | ...; | Conditions.cs:70:9:70:10 | M6 | | Conditions.cs:79:21:79:25 | false | Conditions.cs:70:9:70:10 | M6 | | Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:70:9:70:10 | M6 | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:70:9:70:10 | M6 | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:70:9:70:10 | M6 | | Conditions.cs:82:13:82:13 | access to local variable x | Conditions.cs:70:9:70:10 | M6 | | Conditions.cs:82:13:82:15 | ...++ | Conditions.cs:70:9:70:10 | M6 | | Conditions.cs:82:13:82:16 | ...; | Conditions.cs:70:9:70:10 | M6 | @@ -859,14 +1407,14 @@ nodeEnclosing | Conditions.cs:115:16:115:23 | String s = ... | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:115:20:115:23 | null | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:17:116:21 | Int32 i = ... | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:21:116:21 | 0 | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:28:116:31 | access to parameter args | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:28:116:38 | access to property Length | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:41:116:43 | ...++ | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:18:116:22 | Int32 i = ... | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:22:116:22 | 0 | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:29:116:32 | access to parameter args | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:29:116:39 | access to property Length | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:42:116:44 | ...++ | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:118:13:118:44 | ... ...; | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:118:17:118:43 | Boolean last = ... | Conditions.cs:113:10:113:11 | M9 | @@ -929,6 +1477,30 @@ nodeEnclosing | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] this access | Conditions.cs:129:10:129:12 | M10 | | Conditions.cs:137:21:137:37 | [Field1 (line 129): true, Field2 (line 129): true] call to method ToString | Conditions.cs:129:10:129:12 | M10 | | Conditions.cs:137:21:137:38 | [Field1 (line 129): true, Field2 (line 129): true] ...; | Conditions.cs:129:10:129:12 | M10 | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:144:5:150:5 | {...} | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:9:145:30 | ... ...; | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:13:145:29 | [b (line 143): false] String s = ... | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:13:145:29 | [b (line 143): true] String s = ... | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:17:145:29 | ... ? ... : ... | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:146:9:149:49 | [b (line 143): false] if (...) ... | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:147:13:147:48 | call to method WriteLine | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:143:10:143:12 | M11 | | ExitMethods.cs:7:10:7:11 | enter M1 | ExitMethods.cs:7:10:7:11 | M1 | | ExitMethods.cs:7:10:7:11 | exit M1 | ExitMethods.cs:7:10:7:11 | M1 | | ExitMethods.cs:8:5:11:5 | {...} | ExitMethods.cs:7:10:7:11 | M1 | @@ -1060,7 +1632,7 @@ nodeEnclosing | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | ExitMethods.cs:119:17:119:32 | FailingAssertion | | ExitMethods.cs:119:17:119:32 | exit FailingAssertion | ExitMethods.cs:119:17:119:32 | FailingAssertion | | ExitMethods.cs:120:5:123:5 | {...} | ExitMethods.cs:119:17:119:32 | FailingAssertion | -| ExitMethods.cs:121:9:121:28 | call to method IsTrue | ExitMethods.cs:119:17:119:32 | FailingAssertion | +| ExitMethods.cs:121:9:121:28 | [assertion failure] call to method IsTrue | ExitMethods.cs:119:17:119:32 | FailingAssertion | | ExitMethods.cs:121:9:121:29 | ...; | ExitMethods.cs:119:17:119:32 | FailingAssertion | | ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:119:17:119:32 | FailingAssertion | | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | ExitMethods.cs:125:17:125:33 | FailingAssertion2 | @@ -1071,12 +1643,13 @@ nodeEnclosing | ExitMethods.cs:127:9:127:27 | ...; | ExitMethods.cs:125:17:125:33 | FailingAssertion2 | | ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | | ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | -| ExitMethods.cs:131:33:131:49 | call to method IsFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | +| ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | +| ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | | ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:10:131:20 | AssertFalse | | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | | ExitMethods.cs:133:17:133:33 | exit FailingAssertion3 | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | | ExitMethods.cs:134:5:137:5 | {...} | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | -| ExitMethods.cs:135:9:135:25 | call to method AssertFalse | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | +| ExitMethods.cs:135:9:135:25 | [assertion failure] call to method AssertFalse | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | | ExitMethods.cs:135:9:135:25 | this access | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | | ExitMethods.cs:135:9:135:26 | ...; | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | | ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | @@ -1856,6 +2429,7 @@ nodeEnclosing | Initializers.cs:14:51:14:51 | 1 | Initializers.cs:12:10:12:10 | M | | Initializers.cs:15:9:15:64 | ... ...; | Initializers.cs:12:10:12:10 | M | | Initializers.cs:15:13:15:63 | Initializers[] iz = ... | Initializers.cs:12:10:12:10 | M | +| Initializers.cs:15:18:15:63 | 2 | Initializers.cs:12:10:12:10 | M | | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | Initializers.cs:12:10:12:10 | M | | Initializers.cs:15:37:15:63 | { ..., ... } | Initializers.cs:12:10:12:10 | M | | Initializers.cs:15:39:15:39 | access to local variable i | Initializers.cs:12:10:12:10 | M | @@ -2021,25 +2595,26 @@ nodeEnclosing | LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:7:10:7:11 | M1 | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:7:10:7:11 | M1 | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:7:10:7:11 | M1 | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:7:10:7:11 | M1 | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:12:13:12:35 | ...; | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:16:5:20:5 | {...} | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:17:9:17:47 | ... ...; | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:17:30:17:46 | { ..., ... } | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:17:32:17:34 | "a" | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:17:37:17:39 | "b" | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:17:42:17:44 | "c" | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:17:9:17:48 | ... ...; | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:17:18:17:47 | 3 | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:17:31:17:47 | { ..., ... } | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:17:33:17:35 | "a" | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:17:38:17:40 | "b" | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:17:43:17:45 | "c" | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:18:26:18:27 | access to local variable xs | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:18:27:18:28 | access to local variable xs | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:19:13:19:33 | ...; | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:19:31:19:31 | access to local variable x | LoopUnrolling.cs:15:10:15:11 | M2 | @@ -2063,37 +2638,35 @@ nodeEnclosing | LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | LoopUnrolling.cs:29:10:29:11 | M4 | | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | LoopUnrolling.cs:29:10:29:11 | M4 | | LoopUnrolling.cs:31:29:31:29 | 0 | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:32:26:32:27 | access to local variable xs | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:33:13:33:33 | ...; | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:33:31:33:31 | access to local variable x | LoopUnrolling.cs:29:10:29:11 | M4 | +| LoopUnrolling.cs:32:9:33:33 | [skip (line 32)] foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | M4 | +| LoopUnrolling.cs:32:27:32:28 | access to local variable xs | LoopUnrolling.cs:29:10:29:11 | M4 | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:37:5:43:5 | {...} | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:38:9:38:47 | ... ...; | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:38:30:38:46 | { ..., ... } | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:38:32:38:34 | "a" | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:38:37:38:39 | "b" | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:38:42:38:44 | "c" | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:39:9:39:47 | ... ...; | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:39:30:39:46 | { ..., ... } | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:39:32:39:34 | "0" | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:39:37:39:39 | "1" | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:39:42:39:44 | "2" | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:38:9:38:48 | ... ...; | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:38:18:38:47 | 3 | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:38:31:38:47 | { ..., ... } | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:38:33:38:35 | "a" | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:38:38:38:40 | "b" | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:38:43:38:45 | "c" | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:39:9:39:48 | ... ...; | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:39:18:39:47 | 3 | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:39:31:39:47 | { ..., ... } | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:39:33:39:35 | "0" | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:39:38:39:40 | "1" | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:39:43:39:45 | "2" | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:40:26:40:27 | access to local variable xs | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:40:27:40:28 | access to local variable xs | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:41:30:41:31 | access to local variable ys | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:41:31:41:32 | access to local variable ys | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:42:17:42:41 | ...; | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:42:35:42:35 | access to local variable x | LoopUnrolling.cs:36:10:36:11 | M5 | @@ -2101,39 +2674,41 @@ nodeEnclosing | LoopUnrolling.cs:42:39:42:39 | access to local variable y | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:45:10:45:11 | M6 | | LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:47:9:47:47 | ... ...; | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:47:30:47:46 | { ..., ... } | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:47:32:47:34 | "a" | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:47:37:47:39 | "b" | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:47:42:47:44 | "c" | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:47:9:47:48 | ... ...; | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:47:18:47:47 | 3 | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:47:31:47:47 | { ..., ... } | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:47:33:47:35 | "a" | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:47:38:47:40 | "b" | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:47:43:47:45 | "c" | LoopUnrolling.cs:45:10:45:11 | M6 | | LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:48:21:48:21 | String x | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:48:26:48:27 | access to local variable xs | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:48:22:48:22 | String x | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:48:27:48:28 | access to local variable xs | LoopUnrolling.cs:45:10:45:11 | M6 | | LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:50:20:50:40 | ...; | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:50:38:50:38 | access to local variable x | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:50:16:50:36 | ...; | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:50:34:50:34 | access to local variable x | LoopUnrolling.cs:45:10:45:11 | M6 | | LoopUnrolling.cs:51:13:51:23 | goto ...; | LoopUnrolling.cs:45:10:45:11 | M6 | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:56:5:65:5 | {...} | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:57:9:57:47 | ... ...; | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:57:30:57:46 | { ..., ... } | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:57:32:57:34 | "a" | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:57:37:57:39 | "b" | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:57:42:57:44 | "c" | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:57:9:57:48 | ... ...; | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:57:18:57:47 | 3 | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:57:31:57:47 | { ..., ... } | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:57:33:57:35 | "a" | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:57:38:57:40 | "b" | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:57:43:57:45 | "c" | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:58:21:58:21 | String x | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:58:26:58:27 | access to local variable xs | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:58:22:58:22 | String x | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:58:27:58:28 | access to local variable xs | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:59:9:64:9 | {...} | LoopUnrolling.cs:55:10:55:11 | M7 | @@ -2164,12 +2739,312 @@ nodeEnclosing | LoopUnrolling.cs:71:9:71:12 | access to parameter args | LoopUnrolling.cs:67:10:67:11 | M8 | | LoopUnrolling.cs:71:9:71:20 | call to method Clear | LoopUnrolling.cs:67:10:67:11 | M8 | | LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:67:10:67:11 | M8 | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:67:10:67:11 | M8 | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:67:10:67:11 | M8 | -| LoopUnrolling.cs:72:28:72:31 | access to parameter args | LoopUnrolling.cs:67:10:67:11 | M8 | -| LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | LoopUnrolling.cs:67:10:67:11 | M8 | -| LoopUnrolling.cs:73:13:73:35 | ...; | LoopUnrolling.cs:67:10:67:11 | M8 | -| LoopUnrolling.cs:73:31:73:33 | access to local variable arg | LoopUnrolling.cs:67:10:67:11 | M8 | +| LoopUnrolling.cs:72:9:73:35 | [skip (line 72)] foreach (... ... in ...) ... | LoopUnrolling.cs:67:10:67:11 | M8 | +| LoopUnrolling.cs:72:29:72:32 | access to parameter args | LoopUnrolling.cs:67:10:67:11 | M8 | +| LoopUnrolling.cs:76:10:76:11 | enter M9 | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:76:10:76:11 | exit M9 | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:77:5:83:5 | {...} | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:78:9:78:34 | ... ...; | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:78:29:78:29 | 2 | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:78:32:78:32 | 0 | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:79:9:82:9 | [skip (line 79)] foreach (... ... in ...) ... | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:79:27:79:28 | access to local variable xs | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:85:10:85:12 | enter M10 | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:85:10:85:12 | exit M10 | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:86:5:92:5 | {...} | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:87:9:87:34 | ... ...; | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:87:29:87:29 | 0 | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:87:32:87:32 | 2 | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:88:9:91:9 | [skip (line 88)] foreach (... ... in ...) ... | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:88:27:88:28 | access to local variable xs | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:94:10:94:12 | exit M11 | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:95:5:101:5 | {...} | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:96:9:96:34 | ... ...; | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:96:29:96:29 | 2 | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:96:32:96:32 | 2 | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:97:9:100:9 | [unroll (line 97)] foreach (... ... in ...) ... | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:97:27:97:28 | access to local variable xs | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:98:9:100:9 | {...} | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:99:13:99:33 | ...; | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:99:31:99:31 | access to local variable x | LoopUnrolling.cs:94:10:94:12 | M11 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationA.cs:6:22:6:31 | throw ... | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationA.cs:6:22:6:31 | throw ... | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationA.cs:7:27:7:37 | throw ...; | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationA.cs:7:27:7:37 | throw ...; | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationA.cs:7:33:7:36 | null | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationA.cs:7:33:7:36 | null | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationA.cs:7:47:7:57 | throw ...; | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationA.cs:7:47:7:57 | throw ...; | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationA.cs:7:53:7:56 | null | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationA.cs:7:53:7:56 | null | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationA.cs:15:42:15:50 | return ...; | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationA.cs:15:42:15:50 | return ...; | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationA.cs:15:49:15:49 | access to parameter s | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationA.cs:15:49:15:49 | access to parameter s | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | M2 | +| MultiImplementationA.cs:18:9:18:22 | exit M2 | MultiImplementationA.cs:18:9:18:22 | M2 | +| MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | M2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:20:28:20:28 | access to parameter i | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:28:20:28 | access to parameter i | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | +| MultiImplementationA.cs:30:21:30:23 | exit get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | +| MultiImplementationA.cs:30:21:30:23 | exit get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationA.cs:30:21:30:23 | get_P3 | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationB.cs:27:21:27:23 | get_P3 | +| MultiImplementationA.cs:30:34:30:37 | null | MultiImplementationA.cs:30:21:30:23 | get_P3 | +| MultiImplementationA.cs:30:34:30:37 | null | MultiImplementationB.cs:27:21:27:23 | get_P3 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationA.cs:36:16:36:26 | throw ...; | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationA.cs:36:16:36:26 | throw ...; | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationA.cs:36:22:36:25 | null | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationA.cs:36:22:36:25 | null | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationA.cs:37:9:37:10 | enter M2 | MultiImplementationA.cs:37:9:37:10 | M2 | +| MultiImplementationA.cs:37:9:37:10 | exit M2 | MultiImplementationA.cs:37:9:37:10 | M2 | +| MultiImplementationA.cs:37:14:37:28 | {...} | MultiImplementationA.cs:37:9:37:10 | M2 | +| MultiImplementationA.cs:37:16:37:26 | throw ...; | MultiImplementationA.cs:37:9:37:10 | M2 | +| MultiImplementationA.cs:37:22:37:25 | null | MultiImplementationA.cs:37:9:37:10 | M2 | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationB.cs:4:27:4:35 | return ...; | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationB.cs:4:27:4:35 | return ...; | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationB.cs:4:34:4:34 | 1 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationB.cs:4:34:4:34 | 1 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationB.cs:12:31:12:40 | throw ... | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationB.cs:12:31:12:40 | throw ... | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationB.cs:13:42:13:52 | throw ...; | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationB.cs:13:42:13:52 | throw ...; | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationB.cs:13:48:13:51 | null | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationB.cs:13:48:13:51 | null | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | M2 | +| MultiImplementationB.cs:16:9:16:31 | exit M2 | MultiImplementationB.cs:16:9:16:31 | M2 | +| MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:9:16:31 | M2 | +| MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:9:16:31 | M2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationB.cs:20:19:20:22 | null | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationB.cs:20:19:20:22 | null | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | +| MultiImplementationB.cs:27:21:27:23 | exit get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | +| MultiImplementationB.cs:27:21:27:23 | exit get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationB.cs:32:9:32:10 | M1 | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:9:3:10 | M1 | | NullCoalescing.cs:3:9:3:10 | exit M1 | NullCoalescing.cs:3:9:3:10 | M1 | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:9:3:10 | M1 | @@ -2409,7 +3284,7 @@ nodeEnclosing | Switch.cs:27:13:27:39 | case ...: | Switch.cs:10:10:10:11 | M2 | | Switch.cs:27:18:27:25 | Double d | Switch.cs:10:10:10:11 | M2 | | Switch.cs:27:32:27:38 | call to method Throw | Switch.cs:10:10:10:11 | M2 | -| Switch.cs:28:17:28:21 | Label: | Switch.cs:10:10:10:11 | M2 | +| Switch.cs:28:13:28:17 | Label: | Switch.cs:10:10:10:11 | M2 | | Switch.cs:29:17:29:23 | return ...; | Switch.cs:10:10:10:11 | M2 | | Switch.cs:30:13:30:20 | default: | Switch.cs:10:10:10:11 | M2 | | Switch.cs:31:17:31:27 | goto ...; | Switch.cs:10:10:10:11 | M2 | @@ -2439,40 +3314,40 @@ nodeEnclosing | Switch.cs:57:17:57:17 | 1 | Switch.cs:55:10:55:11 | M5 | | Switch.cs:57:17:57:21 | ... + ... | Switch.cs:55:10:55:11 | M5 | | Switch.cs:57:21:57:21 | 2 | Switch.cs:55:10:55:11 | M5 | -| Switch.cs:59:13:59:20 | case ...: | Switch.cs:55:10:55:11 | M5 | +| Switch.cs:59:13:59:19 | case ...: | Switch.cs:55:10:55:11 | M5 | | Switch.cs:59:18:59:18 | 2 | Switch.cs:55:10:55:11 | M5 | -| Switch.cs:61:13:61:20 | case ...: | Switch.cs:55:10:55:11 | M5 | +| Switch.cs:61:13:61:19 | case ...: | Switch.cs:55:10:55:11 | M5 | | Switch.cs:61:18:61:18 | 3 | Switch.cs:55:10:55:11 | M5 | -| Switch.cs:62:15:62:20 | break; | Switch.cs:55:10:55:11 | M5 | +| Switch.cs:62:17:62:22 | break; | Switch.cs:55:10:55:11 | M5 | | Switch.cs:66:10:66:11 | enter M6 | Switch.cs:66:10:66:11 | M6 | | Switch.cs:66:10:66:11 | exit M6 | Switch.cs:66:10:66:11 | M6 | | Switch.cs:67:5:75:5 | {...} | Switch.cs:66:10:66:11 | M6 | | Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:66:10:66:11 | M6 | | Switch.cs:68:17:68:25 | (...) ... | Switch.cs:66:10:66:11 | M6 | | Switch.cs:68:25:68:25 | access to parameter s | Switch.cs:66:10:66:11 | M6 | -| Switch.cs:70:13:70:24 | case ...: | Switch.cs:66:10:66:11 | M6 | +| Switch.cs:70:13:70:23 | case ...: | Switch.cs:66:10:66:11 | M6 | | Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:66:10:66:11 | M6 | -| Switch.cs:72:13:72:21 | case ...: | Switch.cs:66:10:66:11 | M6 | +| Switch.cs:72:13:72:20 | case ...: | Switch.cs:66:10:66:11 | M6 | | Switch.cs:72:18:72:19 | "" | Switch.cs:66:10:66:11 | M6 | -| Switch.cs:73:15:73:20 | break; | Switch.cs:66:10:66:11 | M6 | +| Switch.cs:73:17:73:22 | break; | Switch.cs:66:10:66:11 | M6 | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:77:10:77:11 | M7 | | Switch.cs:77:10:77:11 | exit M7 | Switch.cs:77:10:77:11 | M7 | | Switch.cs:78:5:89:5 | {...} | Switch.cs:77:10:77:11 | M7 | | Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:77:10:77:11 | M7 | | Switch.cs:79:17:79:17 | access to parameter i | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:81:13:81:20 | case ...: | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:81:13:81:19 | case ...: | Switch.cs:77:10:77:11 | M7 | | Switch.cs:81:18:81:18 | 1 | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:82:15:82:26 | return ...; | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:82:22:82:25 | true | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:82:17:82:28 | return ...; | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:82:24:82:27 | true | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:77:10:77:11 | M7 | | Switch.cs:83:18:83:18 | 2 | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:84:19:84:19 | access to parameter j | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:84:23:84:23 | 2 | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:85:17:85:22 | break; | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:86:15:86:26 | return ...; | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:86:22:86:25 | true | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:84:21:84:21 | access to parameter j | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:84:25:84:25 | 2 | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:85:21:85:26 | break; | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:86:17:86:28 | return ...; | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:86:24:86:27 | true | Switch.cs:77:10:77:11 | M7 | | Switch.cs:88:9:88:21 | return ...; | Switch.cs:77:10:77:11 | M7 | | Switch.cs:88:16:88:20 | false | Switch.cs:77:10:77:11 | M7 | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:91:10:91:11 | M8 | @@ -2480,10 +3355,10 @@ nodeEnclosing | Switch.cs:92:5:99:5 | {...} | Switch.cs:91:10:91:11 | M8 | | Switch.cs:93:9:97:9 | switch (...) {...} | Switch.cs:91:10:91:11 | M8 | | Switch.cs:93:17:93:17 | access to parameter o | Switch.cs:91:10:91:11 | M8 | -| Switch.cs:95:13:95:24 | case ...: | Switch.cs:91:10:91:11 | M8 | +| Switch.cs:95:13:95:23 | case ...: | Switch.cs:91:10:91:11 | M8 | | Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:91:10:91:11 | M8 | -| Switch.cs:96:15:96:26 | return ...; | Switch.cs:91:10:91:11 | M8 | -| Switch.cs:96:22:96:25 | true | Switch.cs:91:10:91:11 | M8 | +| Switch.cs:96:17:96:28 | return ...; | Switch.cs:91:10:91:11 | M8 | +| Switch.cs:96:24:96:27 | true | Switch.cs:91:10:91:11 | M8 | | Switch.cs:98:9:98:21 | return ...; | Switch.cs:91:10:91:11 | M8 | | Switch.cs:98:16:98:20 | false | Switch.cs:91:10:91:11 | M8 | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:101:9:101:10 | M9 | @@ -2492,14 +3367,14 @@ nodeEnclosing | Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:101:9:101:10 | M9 | | Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:101:9:101:10 | M9 | | Switch.cs:103:19:103:25 | access to property Length | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:101:9:101:10 | M9 | | Switch.cs:105:18:105:18 | 0 | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:105:22:105:30 | return ...; | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:105:21:105:29 | return ...; | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:101:9:101:10 | M9 | | Switch.cs:106:18:106:18 | 1 | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:106:22:106:30 | return ...; | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:106:21:106:29 | return ...; | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:101:9:101:10 | M9 | | Switch.cs:108:9:108:18 | return ...; | Switch.cs:101:9:101:10 | M9 | | Switch.cs:108:16:108:17 | -... | Switch.cs:101:9:101:10 | M9 | | Switch.cs:108:17:108:17 | 1 | Switch.cs:101:9:101:10 | M9 | @@ -2513,20 +3388,20 @@ nodeEnclosing | Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:113:9:113:11 | M10 | | Switch.cs:115:17:115:17 | access to parameter s | Switch.cs:113:9:113:11 | M10 | | Switch.cs:115:17:115:24 | access to property Length | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:117:13:117:34 | case ...: | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:117:13:117:35 | case ...: | Switch.cs:113:9:113:11 | M10 | | Switch.cs:117:18:117:18 | 3 | Switch.cs:113:9:113:11 | M10 | | Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:117:28:117:32 | "foo" | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:117:36:117:44 | return ...; | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:117:30:117:34 | "foo" | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:117:37:117:45 | return ...; | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:113:9:113:11 | M10 | | Switch.cs:118:18:118:18 | 2 | Switch.cs:113:9:113:11 | M10 | | Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:118:28:118:31 | "fu" | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:118:35:118:43 | return ...; | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:118:30:118:33 | "fu" | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:118:36:118:44 | return ...; | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:113:9:113:11 | M10 | | Switch.cs:120:9:120:18 | return ...; | Switch.cs:113:9:113:11 | M10 | | Switch.cs:120:16:120:17 | -... | Switch.cs:113:9:113:11 | M10 | | Switch.cs:120:17:120:17 | 1 | Switch.cs:113:9:113:11 | M10 | @@ -2590,6 +3465,31 @@ nodeEnclosing | Switch.cs:150:18:150:18 | 2 | Switch.cs:144:9:144:11 | M14 | | Switch.cs:150:21:150:29 | return ...; | Switch.cs:144:9:144:11 | M14 | | Switch.cs:150:28:150:28 | 2 | Switch.cs:144:9:144:11 | M14 | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:155:5:161:5 | {...} | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:9:156:55 | ... ...; | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:17:156:17 | access to parameter b | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:17:156:54 | ... switch { ... } | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:28:156:31 | true | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:28:156:38 | ... => ... | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:36:156:38 | "a" | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:41:156:45 | false | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:50:156:52 | "b" | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:157:9:160:49 | if (...) ... | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:158:13:158:48 | call to method WriteLine | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:158:38:158:47 | $"..." | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:158:40:158:43 | "a = " | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:160:38:160:47 | $"..." | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:160:40:160:43 | "b = " | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:154:10:154:12 | M15 | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:3:10:3:10 | M | | TypeAccesses.cs:3:10:3:10 | exit M | TypeAccesses.cs:3:10:3:10 | M | | TypeAccesses.cs:4:5:9:5 | {...} | TypeAccesses.cs:3:10:3:10 | M | @@ -2615,10 +3515,12 @@ nodeEnclosing | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:5:18:5:19 | M1 | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:5:18:5:19 | M1 | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:5:18:5:19 | M1 | @@ -3317,6 +4219,125 @@ blockEnclosing | ArrayCreation.cs:5:12:5:13 | enter M2 | ArrayCreation.cs:5:12:5:13 | M2 | | ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:11:7:12 | M3 | | ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:12:9:13 | M4 | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:7:10:7:11 | exit M1 | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:24:9:27 | null | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:9:31:9:32 | "" | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:10:9:10:31 | [assertion success] call to method Assert | Assert.cs:7:10:7:11 | M1 | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:14:10:14:11 | exit M2 | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:24:16:27 | null | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:16:31:16:32 | "" | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | Assert.cs:14:10:14:11 | M2 | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:21:10:21:11 | exit M3 | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:24:23:27 | null | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:23:31:23:32 | "" | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | Assert.cs:21:10:21:11 | M3 | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:28:10:28:11 | exit M4 | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:24:30:27 | null | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:30:31:30:32 | "" | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | Assert.cs:28:10:28:11 | M4 | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:35:10:35:11 | exit M5 | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:24:37:27 | null | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:37:31:37:32 | "" | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | Assert.cs:35:10:35:11 | M5 | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:42:10:42:11 | exit M6 | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:24:44:27 | null | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:44:31:44:32 | "" | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | Assert.cs:42:10:42:11 | M6 | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:49:10:49:11 | exit M7 | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:24:51:27 | null | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:51:31:51:32 | "" | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | Assert.cs:49:10:49:11 | M7 | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:56:10:56:11 | exit M8 | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:56:10:56:11 | M8 | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:63:10:63:11 | exit M9 | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:63:10:63:11 | M9 | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:70:10:70:12 | exit M10 | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:70:10:70:12 | M10 | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:77:10:77:12 | exit M11 | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:77:10:77:12 | M11 | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:84:10:84:12 | exit M12 | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | Assert.cs:84:10:84:12 | M12 | +| Assert.cs:127:37:127:38 | [b (line 84): true] !... | Assert.cs:84:10:84:12 | M12 | | Assignments.cs:3:10:3:10 | enter M | Assignments.cs:3:10:3:10 | M | | Assignments.cs:14:18:14:35 | enter (...) => ... | Assignments.cs:14:18:14:35 | (...) => ... | | Assignments.cs:17:40:17:40 | enter + | Assignments.cs:17:40:17:40 | + | @@ -3361,6 +4382,8 @@ blockEnclosing | CompileTimeOperators.cs:15:10:15:15 | enter Typeof | CompileTimeOperators.cs:15:10:15:15 | Typeof | | CompileTimeOperators.cs:20:12:20:17 | enter Nameof | CompileTimeOperators.cs:20:12:20:17 | Nameof | | CompileTimeOperators.cs:28:10:28:10 | enter M | CompileTimeOperators.cs:28:10:28:10 | M | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | +| ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:12:3:13 | M1 | | ConditionalAccess.cs:3:12:3:13 | exit M1 | ConditionalAccess.cs:3:12:3:13 | M1 | | ConditionalAccess.cs:3:28:3:38 | call to method ToString | ConditionalAccess.cs:3:12:3:13 | M1 | @@ -3386,7 +4409,12 @@ blockEnclosing | ConditionalAccess.cs:19:12:19:13 | exit M6 | ConditionalAccess.cs:19:12:19:13 | M6 | | ConditionalAccess.cs:19:58:19:59 | access to parameter s2 | ConditionalAccess.cs:19:12:19:13 | M6 | | ConditionalAccess.cs:21:10:21:11 | enter M7 | ConditionalAccess.cs:21:10:21:11 | M7 | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:30:10:30:12 | exit Out | ConditionalAccess.cs:30:10:30:12 | Out | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:32:10:32:11 | exit M8 | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:32:10:32:11 | M8 | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:3:10:3:19 | exit IncrOrDecr | Conditions.cs:3:10:3:19 | IncrOrDecr | | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | Conditions.cs:3:10:3:19 | IncrOrDecr | @@ -3454,8 +4482,8 @@ blockEnclosing | Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:102:12:102:13 | M8 | | Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:113:10:113:11 | exit M9 | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:113:10:113:11 | M9 | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:113:10:113:11 | M9 | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:120:17:120:23 | [last (line 118): false] ...; | Conditions.cs:113:10:113:11 | M9 | | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | Conditions.cs:113:10:113:11 | M9 | @@ -3464,6 +4492,10 @@ blockEnclosing | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): false] true | Conditions.cs:129:10:129:12 | M10 | | Conditions.cs:134:13:139:13 | [Field1 (line 129): true] {...} | Conditions.cs:129:10:129:12 | M10 | | Conditions.cs:136:17:138:17 | [Field1 (line 129): true, Field2 (line 129): true] {...} | Conditions.cs:129:10:129:12 | M10 | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:143:10:143:12 | exit M11 | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | Conditions.cs:143:10:143:12 | M11 | +| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | Conditions.cs:143:10:143:12 | M11 | | ExitMethods.cs:7:10:7:11 | enter M1 | ExitMethods.cs:7:10:7:11 | M1 | | ExitMethods.cs:13:10:13:11 | enter M2 | ExitMethods.cs:13:10:13:11 | M2 | | ExitMethods.cs:19:10:19:11 | enter M3 | ExitMethods.cs:19:10:19:11 | M3 | @@ -3500,6 +4532,9 @@ blockEnclosing | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | ExitMethods.cs:119:17:119:32 | FailingAssertion | | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | ExitMethods.cs:125:17:125:33 | FailingAssertion2 | | ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | +| ExitMethods.cs:131:10:131:20 | exit AssertFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | +| ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | +| ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | ExitMethods.cs:131:10:131:20 | AssertFalse | | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | ExitMethods.cs:133:17:133:33 | FailingAssertion3 | | Extensions.cs:5:23:5:29 | enter ToInt32 | Extensions.cs:5:23:5:29 | ToInt32 | | Extensions.cs:10:24:10:29 | enter ToBool | Extensions.cs:10:24:10:29 | ToBool | @@ -3713,39 +4748,214 @@ blockEnclosing | LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:7:10:7:11 | M1 | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:7:10:7:11 | M1 | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:7:10:7:11 | M1 | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:7:10:7:11 | M1 | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:7:10:7:11 | M1 | | LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:15:10:15:11 | M2 | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:15:10:15:11 | M2 | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:15:10:15:11 | M2 | | LoopUnrolling.cs:22:10:22:11 | enter M3 | LoopUnrolling.cs:22:10:22:11 | M3 | | LoopUnrolling.cs:22:10:22:11 | exit M3 | LoopUnrolling.cs:22:10:22:11 | M3 | | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | LoopUnrolling.cs:22:10:22:11 | M3 | | LoopUnrolling.cs:24:22:24:24 | Char arg | LoopUnrolling.cs:22:10:22:11 | M3 | | LoopUnrolling.cs:25:26:25:29 | Char arg0 | LoopUnrolling.cs:22:10:22:11 | M3 | | LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | M4 | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:29:10:29:11 | M4 | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:36:10:36:11 | M5 | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:36:10:36:11 | M5 | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:36:10:36:11 | M5 | | LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:45:10:45:11 | M6 | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:45:10:45:11 | M6 | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:45:10:45:11 | M6 | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:55:10:55:11 | M7 | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | LoopUnrolling.cs:55:10:55:11 | M7 | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:55:10:55:11 | M7 | | LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:67:10:67:11 | M8 | | LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:67:10:67:11 | M8 | | LoopUnrolling.cs:70:13:70:19 | return ...; | LoopUnrolling.cs:67:10:67:11 | M8 | | LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:67:10:67:11 | M8 | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:67:10:67:11 | M8 | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:67:10:67:11 | M8 | +| LoopUnrolling.cs:76:10:76:11 | enter M9 | LoopUnrolling.cs:76:10:76:11 | M9 | +| LoopUnrolling.cs:85:10:85:12 | enter M10 | LoopUnrolling.cs:85:10:85:12 | M10 | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:94:10:94:12 | exit M11 | LoopUnrolling.cs:94:10:94:12 | M11 | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:94:10:94:12 | M11 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationA.cs:6:22:6:31 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationA.cs:7:21:7:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationA.cs:7:41:7:43 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationA.cs:14:31:14:31 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationA.cs:15:36:15:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationA.cs:15:54:15:56 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationA.cs:16:17:16:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | M2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:12:21:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationA.cs:36:9:36:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationA.cs:37:9:37:10 | enter M2 | MultiImplementationA.cs:37:9:37:10 | M2 | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationA.cs:6:22:6:31 | get_P1 | +| MultiImplementationB.cs:3:22:3:22 | exit get_P1 | MultiImplementationB.cs:3:22:3:22 | get_P1 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationB.cs:4:21:4:23 | exit get_P2 | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationA.cs:7:21:7:23 | get_P2 | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:21:4:23 | get_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationB.cs:4:39:4:41 | exit set_P2 | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationA.cs:7:41:7:43 | set_P2 | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationB.cs:4:39:4:41 | set_P2 | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | M | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | M | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationA.cs:14:31:14:31 | get_Item | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:31:12:40 | get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationB.cs:13:36:13:38 | exit get_Item | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationA.cs:15:36:15:38 | get_Item | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:36:13:38 | get_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationB.cs:13:56:13:58 | exit set_Item | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationA.cs:15:54:15:56 | set_Item | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationB.cs:13:56:13:58 | set_Item | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationB.cs:14:17:14:18 | exit M1 | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationA.cs:16:17:16:18 | M1 | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:14:17:14:18 | M1 | +| MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | M2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationA.cs:21:12:21:13 | C2 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:12:19:13 | C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationA.cs:22:6:22:7 | ~C2 | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:6:20:7 | ~C2 | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationA.cs:23:28:23:35 | implicit conversion | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:28:21:35 | implicit conversion | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationB.cs:32:9:32:10 | exit M1 | MultiImplementationB.cs:32:9:32:10 | M1 | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationA.cs:36:9:36:10 | M1 | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationB.cs:32:9:32:10 | M1 | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:9:3:10 | M1 | | NullCoalescing.cs:3:9:3:10 | exit M1 | NullCoalescing.cs:3:9:3:10 | M1 | | NullCoalescing.cs:3:28:3:28 | 0 | NullCoalescing.cs:3:9:3:10 | M1 | @@ -3821,35 +5031,35 @@ blockEnclosing | Switch.cs:55:10:55:11 | enter M5 | Switch.cs:55:10:55:11 | M5 | | Switch.cs:66:10:66:11 | enter M6 | Switch.cs:66:10:66:11 | M6 | | Switch.cs:66:10:66:11 | exit M6 | Switch.cs:66:10:66:11 | M6 | -| Switch.cs:73:15:73:20 | break; | Switch.cs:66:10:66:11 | M6 | +| Switch.cs:73:17:73:22 | break; | Switch.cs:66:10:66:11 | M6 | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:77:10:77:11 | M7 | | Switch.cs:77:10:77:11 | exit M7 | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:82:22:82:25 | true | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:85:17:85:22 | break; | Switch.cs:77:10:77:11 | M7 | -| Switch.cs:86:22:86:25 | true | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:82:24:82:27 | true | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:85:21:85:26 | break; | Switch.cs:77:10:77:11 | M7 | +| Switch.cs:86:24:86:27 | true | Switch.cs:77:10:77:11 | M7 | | Switch.cs:88:16:88:20 | false | Switch.cs:77:10:77:11 | M7 | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:91:10:91:11 | M8 | | Switch.cs:91:10:91:11 | exit M8 | Switch.cs:91:10:91:11 | M8 | -| Switch.cs:96:22:96:25 | true | Switch.cs:91:10:91:11 | M8 | +| Switch.cs:96:24:96:27 | true | Switch.cs:91:10:91:11 | M8 | | Switch.cs:98:16:98:20 | false | Switch.cs:91:10:91:11 | M8 | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:101:9:101:10 | M9 | | Switch.cs:101:9:101:10 | exit M9 | Switch.cs:101:9:101:10 | M9 | | Switch.cs:103:19:103:25 | access to property Length | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:101:9:101:10 | M9 | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:101:9:101:10 | M9 | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:101:9:101:10 | M9 | | Switch.cs:108:17:108:17 | 1 | Switch.cs:101:9:101:10 | M9 | | Switch.cs:111:17:111:21 | enter Throw | Switch.cs:111:17:111:21 | Throw | | Switch.cs:113:9:113:11 | enter M10 | Switch.cs:113:9:113:11 | M10 | | Switch.cs:113:9:113:11 | exit M10 | Switch.cs:113:9:113:11 | M10 | | Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:113:9:113:11 | M10 | | Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:113:9:113:11 | M10 | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:113:9:113:11 | M10 | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:113:9:113:11 | M10 | | Switch.cs:120:17:120:17 | 1 | Switch.cs:113:9:113:11 | M10 | | Switch.cs:123:10:123:12 | enter M11 | Switch.cs:123:10:123:12 | M11 | | Switch.cs:123:10:123:12 | exit M11 | Switch.cs:123:10:123:12 | M11 | @@ -3873,6 +5083,14 @@ blockEnclosing | Switch.cs:149:13:149:20 | default: | Switch.cs:144:9:144:11 | M14 | | Switch.cs:150:13:150:19 | case ...: | Switch.cs:144:9:144:11 | M14 | | Switch.cs:150:28:150:28 | 2 | Switch.cs:144:9:144:11 | M14 | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:154:10:154:12 | exit M15 | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:36:156:38 | "a" | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:156:50:156:52 | "b" | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:154:10:154:12 | M15 | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:154:10:154:12 | M15 | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:3:10:3:10 | M | | TypeAccesses.cs:7:25:7:25 | ; | TypeAccesses.cs:3:10:3:10 | M | | TypeAccesses.cs:8:9:8:28 | ... ...; | TypeAccesses.cs:3:10:3:10 | M | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected index 4cc60894d75b..a001b9b54178 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected @@ -293,13 +293,13 @@ | ArrayCreation.cs:5:28:5:28 | 0 | ArrayCreation.cs:5:28:5:28 | 0 | | ArrayCreation.cs:5:31:5:31 | 1 | ArrayCreation.cs:5:31:5:31 | 1 | | ArrayCreation.cs:7:19:7:36 | 2 | ArrayCreation.cs:7:19:7:36 | 2 | -| ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | +| ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | ArrayCreation.cs:7:19:7:36 | 2 | | ArrayCreation.cs:7:29:7:36 | { ..., ... } | ArrayCreation.cs:7:31:7:31 | 0 | | ArrayCreation.cs:7:31:7:31 | 0 | ArrayCreation.cs:7:31:7:31 | 0 | | ArrayCreation.cs:7:34:7:34 | 1 | ArrayCreation.cs:7:34:7:34 | 1 | | ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:20:9:52 | 2 | | ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:20:9:52 | 2 | -| ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | +| ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | ArrayCreation.cs:9:20:9:52 | 2 | | ArrayCreation.cs:9:31:9:52 | { ..., ... } | ArrayCreation.cs:9:35:9:35 | 0 | | ArrayCreation.cs:9:33:9:40 | { ..., ... } | ArrayCreation.cs:9:35:9:35 | 0 | | ArrayCreation.cs:9:35:9:35 | 0 | ArrayCreation.cs:9:35:9:35 | 0 | @@ -307,6 +307,358 @@ | ArrayCreation.cs:9:43:9:50 | { ..., ... } | ArrayCreation.cs:9:45:9:45 | 2 | | ArrayCreation.cs:9:45:9:45 | 2 | ArrayCreation.cs:9:45:9:45 | 2 | | ArrayCreation.cs:9:48:9:48 | 3 | ArrayCreation.cs:9:48:9:48 | 3 | +| Assert.cs:8:5:12:5 | {...} | Assert.cs:8:5:12:5 | {...} | +| Assert.cs:9:9:9:33 | ... ...; | Assert.cs:9:9:9:33 | ... ...; | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:9:20:9:32 | ... ? ... : ... | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:20:9:20 | access to parameter b | +| Assert.cs:9:20:9:32 | ... ? ... : ... | Assert.cs:9:20:9:32 | ... ? ... : ... | +| Assert.cs:9:24:9:27 | null | Assert.cs:9:24:9:27 | null | +| Assert.cs:9:31:9:32 | "" | Assert.cs:9:31:9:32 | "" | +| Assert.cs:10:9:10:31 | call to method Assert | Assert.cs:10:22:10:22 | access to local variable s | +| Assert.cs:10:9:10:32 | ...; | Assert.cs:10:9:10:32 | ...; | +| Assert.cs:10:22:10:22 | access to local variable s | Assert.cs:10:22:10:22 | access to local variable s | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:22:10:22 | access to local variable s | +| Assert.cs:10:27:10:30 | null | Assert.cs:10:27:10:30 | null | +| Assert.cs:11:9:11:35 | call to method WriteLine | Assert.cs:11:27:11:27 | access to local variable s | +| Assert.cs:11:9:11:36 | ...; | Assert.cs:11:9:11:36 | ...; | +| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:11:27:11:27 | access to local variable s | +| Assert.cs:11:27:11:34 | access to property Length | Assert.cs:11:27:11:27 | access to local variable s | +| Assert.cs:15:5:19:5 | {...} | Assert.cs:15:5:19:5 | {...} | +| Assert.cs:16:9:16:33 | ... ...; | Assert.cs:16:9:16:33 | ... ...; | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:16:20:16:32 | ... ? ... : ... | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:20:16:20 | access to parameter b | +| Assert.cs:16:20:16:32 | ... ? ... : ... | Assert.cs:16:20:16:32 | ... ? ... : ... | +| Assert.cs:16:24:16:27 | null | Assert.cs:16:24:16:27 | null | +| Assert.cs:16:31:16:32 | "" | Assert.cs:16:31:16:32 | "" | +| Assert.cs:17:9:17:24 | call to method IsNull | Assert.cs:17:23:17:23 | access to local variable s | +| Assert.cs:17:9:17:25 | ...; | Assert.cs:17:9:17:25 | ...; | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:23:17:23 | access to local variable s | +| Assert.cs:18:9:18:35 | call to method WriteLine | Assert.cs:18:27:18:27 | access to local variable s | +| Assert.cs:18:9:18:36 | ...; | Assert.cs:18:9:18:36 | ...; | +| Assert.cs:18:27:18:27 | access to local variable s | Assert.cs:18:27:18:27 | access to local variable s | +| Assert.cs:18:27:18:34 | access to property Length | Assert.cs:18:27:18:27 | access to local variable s | +| Assert.cs:22:5:26:5 | {...} | Assert.cs:22:5:26:5 | {...} | +| Assert.cs:23:9:23:33 | ... ...; | Assert.cs:23:9:23:33 | ... ...; | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:23:20:23:32 | ... ? ... : ... | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:20:23:20 | access to parameter b | +| Assert.cs:23:20:23:32 | ... ? ... : ... | Assert.cs:23:20:23:32 | ... ? ... : ... | +| Assert.cs:23:24:23:27 | null | Assert.cs:23:24:23:27 | null | +| Assert.cs:23:31:23:32 | "" | Assert.cs:23:31:23:32 | "" | +| Assert.cs:24:9:24:27 | call to method IsNotNull | Assert.cs:24:26:24:26 | access to local variable s | +| Assert.cs:24:9:24:28 | ...; | Assert.cs:24:9:24:28 | ...; | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:26:24:26 | access to local variable s | +| Assert.cs:25:9:25:35 | call to method WriteLine | Assert.cs:25:27:25:27 | access to local variable s | +| Assert.cs:25:9:25:36 | ...; | Assert.cs:25:9:25:36 | ...; | +| Assert.cs:25:27:25:27 | access to local variable s | Assert.cs:25:27:25:27 | access to local variable s | +| Assert.cs:25:27:25:34 | access to property Length | Assert.cs:25:27:25:27 | access to local variable s | +| Assert.cs:29:5:33:5 | {...} | Assert.cs:29:5:33:5 | {...} | +| Assert.cs:30:9:30:33 | ... ...; | Assert.cs:30:9:30:33 | ... ...; | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:30:20:30:32 | ... ? ... : ... | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:20:30:20 | access to parameter b | +| Assert.cs:30:20:30:32 | ... ? ... : ... | Assert.cs:30:20:30:32 | ... ? ... : ... | +| Assert.cs:30:24:30:27 | null | Assert.cs:30:24:30:27 | null | +| Assert.cs:30:31:30:32 | "" | Assert.cs:30:31:30:32 | "" | +| Assert.cs:31:9:31:32 | call to method IsTrue | Assert.cs:31:23:31:23 | access to local variable s | +| Assert.cs:31:9:31:33 | ...; | Assert.cs:31:9:31:33 | ...; | +| Assert.cs:31:23:31:23 | access to local variable s | Assert.cs:31:23:31:23 | access to local variable s | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:23:31:23 | access to local variable s | +| Assert.cs:31:28:31:31 | null | Assert.cs:31:28:31:31 | null | +| Assert.cs:32:9:32:35 | call to method WriteLine | Assert.cs:32:27:32:27 | access to local variable s | +| Assert.cs:32:9:32:36 | ...; | Assert.cs:32:9:32:36 | ...; | +| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:32:27:32:27 | access to local variable s | +| Assert.cs:32:27:32:34 | access to property Length | Assert.cs:32:27:32:27 | access to local variable s | +| Assert.cs:36:5:40:5 | {...} | Assert.cs:36:5:40:5 | {...} | +| Assert.cs:37:9:37:33 | ... ...; | Assert.cs:37:9:37:33 | ... ...; | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:37:20:37:32 | ... ? ... : ... | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:20:37:20 | access to parameter b | +| Assert.cs:37:20:37:32 | ... ? ... : ... | Assert.cs:37:20:37:32 | ... ? ... : ... | +| Assert.cs:37:24:37:27 | null | Assert.cs:37:24:37:27 | null | +| Assert.cs:37:31:37:32 | "" | Assert.cs:37:31:37:32 | "" | +| Assert.cs:38:9:38:32 | call to method IsTrue | Assert.cs:38:23:38:23 | access to local variable s | +| Assert.cs:38:9:38:33 | ...; | Assert.cs:38:9:38:33 | ...; | +| Assert.cs:38:23:38:23 | access to local variable s | Assert.cs:38:23:38:23 | access to local variable s | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:23:38:23 | access to local variable s | +| Assert.cs:38:28:38:31 | null | Assert.cs:38:28:38:31 | null | +| Assert.cs:39:9:39:35 | call to method WriteLine | Assert.cs:39:27:39:27 | access to local variable s | +| Assert.cs:39:9:39:36 | ...; | Assert.cs:39:9:39:36 | ...; | +| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:39:27:39:27 | access to local variable s | +| Assert.cs:39:27:39:34 | access to property Length | Assert.cs:39:27:39:27 | access to local variable s | +| Assert.cs:43:5:47:5 | {...} | Assert.cs:43:5:47:5 | {...} | +| Assert.cs:44:9:44:33 | ... ...; | Assert.cs:44:9:44:33 | ... ...; | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:44:20:44:32 | ... ? ... : ... | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:20:44:20 | access to parameter b | +| Assert.cs:44:20:44:32 | ... ? ... : ... | Assert.cs:44:20:44:32 | ... ? ... : ... | +| Assert.cs:44:24:44:27 | null | Assert.cs:44:24:44:27 | null | +| Assert.cs:44:31:44:32 | "" | Assert.cs:44:31:44:32 | "" | +| Assert.cs:45:9:45:33 | call to method IsFalse | Assert.cs:45:24:45:24 | access to local variable s | +| Assert.cs:45:9:45:34 | ...; | Assert.cs:45:9:45:34 | ...; | +| Assert.cs:45:24:45:24 | access to local variable s | Assert.cs:45:24:45:24 | access to local variable s | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:24:45:24 | access to local variable s | +| Assert.cs:45:29:45:32 | null | Assert.cs:45:29:45:32 | null | +| Assert.cs:46:9:46:35 | call to method WriteLine | Assert.cs:46:27:46:27 | access to local variable s | +| Assert.cs:46:9:46:36 | ...; | Assert.cs:46:9:46:36 | ...; | +| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:46:27:46:27 | access to local variable s | +| Assert.cs:46:27:46:34 | access to property Length | Assert.cs:46:27:46:27 | access to local variable s | +| Assert.cs:50:5:54:5 | {...} | Assert.cs:50:5:54:5 | {...} | +| Assert.cs:51:9:51:33 | ... ...; | Assert.cs:51:9:51:33 | ... ...; | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:51:20:51:32 | ... ? ... : ... | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:20:51:20 | access to parameter b | +| Assert.cs:51:20:51:32 | ... ? ... : ... | Assert.cs:51:20:51:32 | ... ? ... : ... | +| Assert.cs:51:24:51:27 | null | Assert.cs:51:24:51:27 | null | +| Assert.cs:51:31:51:32 | "" | Assert.cs:51:31:51:32 | "" | +| Assert.cs:52:9:52:33 | call to method IsFalse | Assert.cs:52:24:52:24 | access to local variable s | +| Assert.cs:52:9:52:34 | ...; | Assert.cs:52:9:52:34 | ...; | +| Assert.cs:52:24:52:24 | access to local variable s | Assert.cs:52:24:52:24 | access to local variable s | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:24 | access to local variable s | +| Assert.cs:52:29:52:32 | null | Assert.cs:52:29:52:32 | null | +| Assert.cs:53:9:53:35 | call to method WriteLine | Assert.cs:53:27:53:27 | access to local variable s | +| Assert.cs:53:9:53:36 | ...; | Assert.cs:53:9:53:36 | ...; | +| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:53:27:53:27 | access to local variable s | +| Assert.cs:53:27:53:34 | access to property Length | Assert.cs:53:27:53:27 | access to local variable s | +| Assert.cs:57:5:61:5 | {...} | Assert.cs:57:5:61:5 | {...} | +| Assert.cs:58:9:58:33 | ... ...; | Assert.cs:58:9:58:33 | ... ...; | +| Assert.cs:58:16:58:32 | String s = ... | Assert.cs:58:20:58:32 | ... ? ... : ... | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | +| Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:32 | ... ? ... : ... | +| Assert.cs:58:24:58:27 | null | Assert.cs:58:24:58:27 | null | +| Assert.cs:58:31:58:32 | "" | Assert.cs:58:31:58:32 | "" | +| Assert.cs:59:9:59:37 | call to method IsTrue | Assert.cs:59:23:59:36 | ... && ... | +| Assert.cs:59:9:59:38 | ...; | Assert.cs:59:9:59:38 | ...; | +| Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | +| Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | +| Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:36 | ... && ... | +| Assert.cs:59:28:59:31 | null | Assert.cs:59:28:59:31 | null | +| Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:59:36:59:36 | access to parameter b | +| Assert.cs:60:9:60:35 | call to method WriteLine | Assert.cs:60:27:60:27 | access to local variable s | +| Assert.cs:60:9:60:36 | ...; | Assert.cs:60:9:60:36 | ...; | +| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:60:27:60:27 | access to local variable s | +| Assert.cs:60:27:60:34 | access to property Length | Assert.cs:60:27:60:27 | access to local variable s | +| Assert.cs:64:5:68:5 | {...} | Assert.cs:64:5:68:5 | {...} | +| Assert.cs:65:9:65:33 | ... ...; | Assert.cs:65:9:65:33 | ... ...; | +| Assert.cs:65:16:65:32 | String s = ... | Assert.cs:65:20:65:32 | ... ? ... : ... | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | +| Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:32 | ... ? ... : ... | +| Assert.cs:65:24:65:27 | null | Assert.cs:65:24:65:27 | null | +| Assert.cs:65:31:65:32 | "" | Assert.cs:65:31:65:32 | "" | +| Assert.cs:66:9:66:38 | call to method IsFalse | Assert.cs:66:24:66:37 | ... \|\| ... | +| Assert.cs:66:9:66:39 | ...; | Assert.cs:66:9:66:39 | ...; | +| Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | +| Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | +| Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:37 | ... \|\| ... | +| Assert.cs:66:29:66:32 | null | Assert.cs:66:29:66:32 | null | +| Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:66:37:66:37 | access to parameter b | +| Assert.cs:67:9:67:35 | call to method WriteLine | Assert.cs:67:27:67:27 | access to local variable s | +| Assert.cs:67:9:67:36 | ...; | Assert.cs:67:9:67:36 | ...; | +| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:67:27:67:27 | access to local variable s | +| Assert.cs:67:27:67:34 | access to property Length | Assert.cs:67:27:67:27 | access to local variable s | +| Assert.cs:71:5:75:5 | {...} | Assert.cs:71:5:75:5 | {...} | +| Assert.cs:72:9:72:33 | ... ...; | Assert.cs:72:9:72:33 | ... ...; | +| Assert.cs:72:16:72:32 | String s = ... | Assert.cs:72:20:72:32 | ... ? ... : ... | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | +| Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:32 | ... ? ... : ... | +| Assert.cs:72:24:72:27 | null | Assert.cs:72:24:72:27 | null | +| Assert.cs:72:31:72:32 | "" | Assert.cs:72:31:72:32 | "" | +| Assert.cs:73:9:73:37 | call to method IsTrue | Assert.cs:73:23:73:36 | ... && ... | +| Assert.cs:73:9:73:38 | ...; | Assert.cs:73:9:73:38 | ...; | +| Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | +| Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | +| Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:36 | ... && ... | +| Assert.cs:73:28:73:31 | null | Assert.cs:73:28:73:31 | null | +| Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:73:36:73:36 | access to parameter b | +| Assert.cs:74:9:74:35 | call to method WriteLine | Assert.cs:74:27:74:27 | access to local variable s | +| Assert.cs:74:9:74:36 | ...; | Assert.cs:74:9:74:36 | ...; | +| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:74:27:74:27 | access to local variable s | +| Assert.cs:74:27:74:34 | access to property Length | Assert.cs:74:27:74:27 | access to local variable s | +| Assert.cs:78:5:82:5 | {...} | Assert.cs:78:5:82:5 | {...} | +| Assert.cs:79:9:79:33 | ... ...; | Assert.cs:79:9:79:33 | ... ...; | +| Assert.cs:79:16:79:32 | String s = ... | Assert.cs:79:20:79:32 | ... ? ... : ... | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | +| Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:32 | ... ? ... : ... | +| Assert.cs:79:24:79:27 | null | Assert.cs:79:24:79:27 | null | +| Assert.cs:79:31:79:32 | "" | Assert.cs:79:31:79:32 | "" | +| Assert.cs:80:9:80:38 | call to method IsFalse | Assert.cs:80:24:80:37 | ... \|\| ... | +| Assert.cs:80:9:80:39 | ...; | Assert.cs:80:9:80:39 | ...; | +| Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | +| Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | +| Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:37 | ... \|\| ... | +| Assert.cs:80:29:80:32 | null | Assert.cs:80:29:80:32 | null | +| Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:80:37:80:37 | access to parameter b | +| Assert.cs:81:9:81:35 | call to method WriteLine | Assert.cs:81:27:81:27 | access to local variable s | +| Assert.cs:81:9:81:36 | ...; | Assert.cs:81:9:81:36 | ...; | +| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:81:27:81:27 | access to local variable s | +| Assert.cs:81:27:81:34 | access to property Length | Assert.cs:81:27:81:27 | access to local variable s | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:85:5:129:5 | {...} | +| Assert.cs:86:9:86:33 | ... ...; | Assert.cs:86:9:86:33 | ... ...; | +| Assert.cs:86:16:86:32 | String s = ... | Assert.cs:86:20:86:32 | ... ? ... : ... | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:20:86:20 | access to parameter b | +| Assert.cs:86:20:86:32 | ... ? ... : ... | Assert.cs:86:20:86:32 | ... ? ... : ... | +| Assert.cs:86:24:86:27 | null | Assert.cs:86:24:86:27 | null | +| Assert.cs:86:31:86:32 | "" | Assert.cs:86:31:86:32 | "" | +| Assert.cs:87:9:87:31 | call to method Assert | Assert.cs:87:22:87:22 | access to local variable s | +| Assert.cs:87:9:87:32 | ...; | Assert.cs:87:9:87:32 | ...; | +| Assert.cs:87:22:87:22 | access to local variable s | Assert.cs:87:22:87:22 | access to local variable s | +| Assert.cs:87:22:87:30 | ... != ... | Assert.cs:87:22:87:22 | access to local variable s | +| Assert.cs:87:27:87:30 | null | Assert.cs:87:27:87:30 | null | +| Assert.cs:88:9:88:35 | call to method WriteLine | Assert.cs:88:27:88:27 | access to local variable s | +| Assert.cs:88:9:88:36 | ...; | Assert.cs:88:9:88:36 | ...; | +| Assert.cs:88:27:88:27 | access to local variable s | Assert.cs:88:27:88:27 | access to local variable s | +| Assert.cs:88:27:88:34 | access to property Length | Assert.cs:88:27:88:27 | access to local variable s | +| Assert.cs:90:9:90:25 | ... = ... | Assert.cs:90:13:90:25 | ... ? ... : ... | +| Assert.cs:90:9:90:26 | ...; | Assert.cs:90:9:90:26 | ...; | +| Assert.cs:90:13:90:13 | access to parameter b | Assert.cs:90:13:90:13 | access to parameter b | +| Assert.cs:90:13:90:25 | ... ? ... : ... | Assert.cs:90:13:90:25 | ... ? ... : ... | +| Assert.cs:90:17:90:20 | null | Assert.cs:90:17:90:20 | null | +| Assert.cs:90:24:90:25 | "" | Assert.cs:90:24:90:25 | "" | +| Assert.cs:91:9:91:24 | call to method IsNull | Assert.cs:91:23:91:23 | access to local variable s | +| Assert.cs:91:9:91:25 | ...; | Assert.cs:91:9:91:25 | ...; | +| Assert.cs:91:23:91:23 | access to local variable s | Assert.cs:91:23:91:23 | access to local variable s | +| Assert.cs:92:9:92:35 | call to method WriteLine | Assert.cs:92:27:92:27 | access to local variable s | +| Assert.cs:92:9:92:36 | ...; | Assert.cs:92:9:92:36 | ...; | +| Assert.cs:92:27:92:27 | access to local variable s | Assert.cs:92:27:92:27 | access to local variable s | +| Assert.cs:92:27:92:34 | access to property Length | Assert.cs:92:27:92:27 | access to local variable s | +| Assert.cs:94:9:94:25 | ... = ... | Assert.cs:94:13:94:25 | ... ? ... : ... | +| Assert.cs:94:9:94:26 | ...; | Assert.cs:94:9:94:26 | ...; | +| Assert.cs:94:13:94:13 | access to parameter b | Assert.cs:94:13:94:13 | access to parameter b | +| Assert.cs:94:13:94:25 | ... ? ... : ... | Assert.cs:94:13:94:25 | ... ? ... : ... | +| Assert.cs:94:17:94:20 | null | Assert.cs:94:17:94:20 | null | +| Assert.cs:94:24:94:25 | "" | Assert.cs:94:24:94:25 | "" | +| Assert.cs:95:9:95:27 | call to method IsNotNull | Assert.cs:95:26:95:26 | access to local variable s | +| Assert.cs:95:9:95:28 | ...; | Assert.cs:95:9:95:28 | ...; | +| Assert.cs:95:26:95:26 | access to local variable s | Assert.cs:95:26:95:26 | access to local variable s | +| Assert.cs:96:9:96:35 | call to method WriteLine | Assert.cs:96:27:96:27 | access to local variable s | +| Assert.cs:96:9:96:36 | ...; | Assert.cs:96:9:96:36 | ...; | +| Assert.cs:96:27:96:27 | access to local variable s | Assert.cs:96:27:96:27 | access to local variable s | +| Assert.cs:96:27:96:34 | access to property Length | Assert.cs:96:27:96:27 | access to local variable s | +| Assert.cs:98:9:98:25 | ... = ... | Assert.cs:98:13:98:25 | ... ? ... : ... | +| Assert.cs:98:9:98:26 | ...; | Assert.cs:98:9:98:26 | ...; | +| Assert.cs:98:13:98:13 | access to parameter b | Assert.cs:98:13:98:13 | access to parameter b | +| Assert.cs:98:13:98:25 | ... ? ... : ... | Assert.cs:98:13:98:25 | ... ? ... : ... | +| Assert.cs:98:17:98:20 | null | Assert.cs:98:17:98:20 | null | +| Assert.cs:98:24:98:25 | "" | Assert.cs:98:24:98:25 | "" | +| Assert.cs:99:9:99:32 | call to method IsTrue | Assert.cs:99:23:99:23 | access to local variable s | +| Assert.cs:99:9:99:33 | ...; | Assert.cs:99:9:99:33 | ...; | +| Assert.cs:99:23:99:23 | access to local variable s | Assert.cs:99:23:99:23 | access to local variable s | +| Assert.cs:99:23:99:31 | ... == ... | Assert.cs:99:23:99:23 | access to local variable s | +| Assert.cs:99:28:99:31 | null | Assert.cs:99:28:99:31 | null | +| Assert.cs:100:9:100:35 | call to method WriteLine | Assert.cs:100:27:100:27 | access to local variable s | +| Assert.cs:100:9:100:36 | ...; | Assert.cs:100:9:100:36 | ...; | +| Assert.cs:100:27:100:27 | access to local variable s | Assert.cs:100:27:100:27 | access to local variable s | +| Assert.cs:100:27:100:34 | access to property Length | Assert.cs:100:27:100:27 | access to local variable s | +| Assert.cs:102:9:102:25 | ... = ... | Assert.cs:102:13:102:25 | ... ? ... : ... | +| Assert.cs:102:9:102:26 | ...; | Assert.cs:102:9:102:26 | ...; | +| Assert.cs:102:13:102:13 | access to parameter b | Assert.cs:102:13:102:13 | access to parameter b | +| Assert.cs:102:13:102:25 | ... ? ... : ... | Assert.cs:102:13:102:25 | ... ? ... : ... | +| Assert.cs:102:17:102:20 | null | Assert.cs:102:17:102:20 | null | +| Assert.cs:102:24:102:25 | "" | Assert.cs:102:24:102:25 | "" | +| Assert.cs:103:9:103:32 | call to method IsTrue | Assert.cs:103:23:103:23 | access to local variable s | +| Assert.cs:103:9:103:33 | ...; | Assert.cs:103:9:103:33 | ...; | +| Assert.cs:103:23:103:23 | access to local variable s | Assert.cs:103:23:103:23 | access to local variable s | +| Assert.cs:103:23:103:31 | ... != ... | Assert.cs:103:23:103:23 | access to local variable s | +| Assert.cs:103:28:103:31 | null | Assert.cs:103:28:103:31 | null | +| Assert.cs:104:9:104:35 | call to method WriteLine | Assert.cs:104:27:104:27 | access to local variable s | +| Assert.cs:104:9:104:36 | ...; | Assert.cs:104:9:104:36 | ...; | +| Assert.cs:104:27:104:27 | access to local variable s | Assert.cs:104:27:104:27 | access to local variable s | +| Assert.cs:104:27:104:34 | access to property Length | Assert.cs:104:27:104:27 | access to local variable s | +| Assert.cs:106:9:106:25 | ... = ... | Assert.cs:106:13:106:25 | ... ? ... : ... | +| Assert.cs:106:9:106:26 | ...; | Assert.cs:106:9:106:26 | ...; | +| Assert.cs:106:13:106:13 | access to parameter b | Assert.cs:106:13:106:13 | access to parameter b | +| Assert.cs:106:13:106:25 | ... ? ... : ... | Assert.cs:106:13:106:25 | ... ? ... : ... | +| Assert.cs:106:17:106:20 | null | Assert.cs:106:17:106:20 | null | +| Assert.cs:106:24:106:25 | "" | Assert.cs:106:24:106:25 | "" | +| Assert.cs:107:9:107:33 | call to method IsFalse | Assert.cs:107:24:107:24 | access to local variable s | +| Assert.cs:107:9:107:34 | ...; | Assert.cs:107:9:107:34 | ...; | +| Assert.cs:107:24:107:24 | access to local variable s | Assert.cs:107:24:107:24 | access to local variable s | +| Assert.cs:107:24:107:32 | ... != ... | Assert.cs:107:24:107:24 | access to local variable s | +| Assert.cs:107:29:107:32 | null | Assert.cs:107:29:107:32 | null | +| Assert.cs:108:9:108:35 | call to method WriteLine | Assert.cs:108:27:108:27 | access to local variable s | +| Assert.cs:108:9:108:36 | ...; | Assert.cs:108:9:108:36 | ...; | +| Assert.cs:108:27:108:27 | access to local variable s | Assert.cs:108:27:108:27 | access to local variable s | +| Assert.cs:108:27:108:34 | access to property Length | Assert.cs:108:27:108:27 | access to local variable s | +| Assert.cs:110:9:110:25 | ... = ... | Assert.cs:110:13:110:25 | ... ? ... : ... | +| Assert.cs:110:9:110:26 | ...; | Assert.cs:110:9:110:26 | ...; | +| Assert.cs:110:13:110:13 | access to parameter b | Assert.cs:110:13:110:13 | access to parameter b | +| Assert.cs:110:13:110:25 | ... ? ... : ... | Assert.cs:110:13:110:25 | ... ? ... : ... | +| Assert.cs:110:17:110:20 | null | Assert.cs:110:17:110:20 | null | +| Assert.cs:110:24:110:25 | "" | Assert.cs:110:24:110:25 | "" | +| Assert.cs:111:9:111:33 | call to method IsFalse | Assert.cs:111:24:111:24 | access to local variable s | +| Assert.cs:111:9:111:34 | ...; | Assert.cs:111:9:111:34 | ...; | +| Assert.cs:111:24:111:24 | access to local variable s | Assert.cs:111:24:111:24 | access to local variable s | +| Assert.cs:111:24:111:32 | ... == ... | Assert.cs:111:24:111:24 | access to local variable s | +| Assert.cs:111:29:111:32 | null | Assert.cs:111:29:111:32 | null | +| Assert.cs:112:9:112:35 | call to method WriteLine | Assert.cs:112:27:112:27 | access to local variable s | +| Assert.cs:112:9:112:36 | ...; | Assert.cs:112:9:112:36 | ...; | +| Assert.cs:112:27:112:27 | access to local variable s | Assert.cs:112:27:112:27 | access to local variable s | +| Assert.cs:112:27:112:34 | access to property Length | Assert.cs:112:27:112:27 | access to local variable s | +| Assert.cs:114:9:114:25 | ... = ... | Assert.cs:114:13:114:25 | ... ? ... : ... | +| Assert.cs:114:9:114:26 | ...; | Assert.cs:114:9:114:26 | ...; | +| Assert.cs:114:13:114:13 | access to parameter b | Assert.cs:114:13:114:13 | access to parameter b | +| Assert.cs:114:13:114:25 | ... ? ... : ... | Assert.cs:114:13:114:25 | ... ? ... : ... | +| Assert.cs:114:17:114:20 | null | Assert.cs:114:17:114:20 | null | +| Assert.cs:114:24:114:25 | "" | Assert.cs:114:24:114:25 | "" | +| Assert.cs:115:9:115:37 | call to method IsTrue | Assert.cs:115:23:115:36 | ... && ... | +| Assert.cs:115:9:115:38 | ...; | Assert.cs:115:9:115:38 | ...; | +| Assert.cs:115:23:115:23 | access to local variable s | Assert.cs:115:23:115:23 | access to local variable s | +| Assert.cs:115:23:115:31 | ... != ... | Assert.cs:115:23:115:23 | access to local variable s | +| Assert.cs:115:23:115:36 | ... && ... | Assert.cs:115:23:115:36 | ... && ... | +| Assert.cs:115:28:115:31 | null | Assert.cs:115:28:115:31 | null | +| Assert.cs:115:36:115:36 | access to parameter b | Assert.cs:115:36:115:36 | access to parameter b | +| Assert.cs:116:9:116:35 | call to method WriteLine | Assert.cs:116:27:116:27 | access to local variable s | +| Assert.cs:116:9:116:36 | ...; | Assert.cs:116:9:116:36 | ...; | +| Assert.cs:116:27:116:27 | access to local variable s | Assert.cs:116:27:116:27 | access to local variable s | +| Assert.cs:116:27:116:34 | access to property Length | Assert.cs:116:27:116:27 | access to local variable s | +| Assert.cs:118:9:118:25 | ... = ... | Assert.cs:118:13:118:25 | ... ? ... : ... | +| Assert.cs:118:9:118:26 | ...; | Assert.cs:118:9:118:26 | ...; | +| Assert.cs:118:13:118:13 | access to parameter b | Assert.cs:118:13:118:13 | access to parameter b | +| Assert.cs:118:13:118:25 | ... ? ... : ... | Assert.cs:118:13:118:25 | ... ? ... : ... | +| Assert.cs:118:17:118:20 | null | Assert.cs:118:17:118:20 | null | +| Assert.cs:118:24:118:25 | "" | Assert.cs:118:24:118:25 | "" | +| Assert.cs:119:9:119:39 | call to method IsFalse | Assert.cs:119:24:119:38 | ... \|\| ... | +| Assert.cs:119:9:119:40 | ...; | Assert.cs:119:9:119:40 | ...; | +| Assert.cs:119:24:119:24 | access to local variable s | Assert.cs:119:24:119:24 | access to local variable s | +| Assert.cs:119:24:119:32 | ... == ... | Assert.cs:119:24:119:24 | access to local variable s | +| Assert.cs:119:24:119:38 | ... \|\| ... | Assert.cs:119:24:119:38 | ... \|\| ... | +| Assert.cs:119:29:119:32 | null | Assert.cs:119:29:119:32 | null | +| Assert.cs:119:37:119:38 | !... | Assert.cs:119:37:119:38 | !... | +| Assert.cs:119:38:119:38 | access to parameter b | Assert.cs:119:38:119:38 | access to parameter b | +| Assert.cs:120:9:120:35 | call to method WriteLine | Assert.cs:120:27:120:27 | access to local variable s | +| Assert.cs:120:9:120:36 | ...; | Assert.cs:120:9:120:36 | ...; | +| Assert.cs:120:27:120:27 | access to local variable s | Assert.cs:120:27:120:27 | access to local variable s | +| Assert.cs:120:27:120:34 | access to property Length | Assert.cs:120:27:120:27 | access to local variable s | +| Assert.cs:122:9:122:25 | ... = ... | Assert.cs:122:13:122:25 | ... ? ... : ... | +| Assert.cs:122:9:122:26 | ...; | Assert.cs:122:9:122:26 | ...; | +| Assert.cs:122:13:122:13 | access to parameter b | Assert.cs:122:13:122:13 | access to parameter b | +| Assert.cs:122:13:122:25 | ... ? ... : ... | Assert.cs:122:13:122:25 | ... ? ... : ... | +| Assert.cs:122:17:122:20 | null | Assert.cs:122:17:122:20 | null | +| Assert.cs:122:24:122:25 | "" | Assert.cs:122:24:122:25 | "" | +| Assert.cs:123:9:123:37 | call to method IsTrue | Assert.cs:123:23:123:36 | ... && ... | +| Assert.cs:123:9:123:38 | ...; | Assert.cs:123:9:123:38 | ...; | +| Assert.cs:123:23:123:23 | access to local variable s | Assert.cs:123:23:123:23 | access to local variable s | +| Assert.cs:123:23:123:31 | ... == ... | Assert.cs:123:23:123:23 | access to local variable s | +| Assert.cs:123:23:123:36 | ... && ... | Assert.cs:123:23:123:36 | ... && ... | +| Assert.cs:123:28:123:31 | null | Assert.cs:123:28:123:31 | null | +| Assert.cs:123:36:123:36 | access to parameter b | Assert.cs:123:36:123:36 | access to parameter b | +| Assert.cs:124:9:124:35 | call to method WriteLine | Assert.cs:124:27:124:27 | access to local variable s | +| Assert.cs:124:9:124:36 | ...; | Assert.cs:124:9:124:36 | ...; | +| Assert.cs:124:27:124:27 | access to local variable s | Assert.cs:124:27:124:27 | access to local variable s | +| Assert.cs:124:27:124:34 | access to property Length | Assert.cs:124:27:124:27 | access to local variable s | +| Assert.cs:126:9:126:25 | ... = ... | Assert.cs:126:13:126:25 | ... ? ... : ... | +| Assert.cs:126:9:126:26 | ...; | Assert.cs:126:9:126:26 | ...; | +| Assert.cs:126:13:126:13 | access to parameter b | Assert.cs:126:13:126:13 | access to parameter b | +| Assert.cs:126:13:126:25 | ... ? ... : ... | Assert.cs:126:13:126:25 | ... ? ... : ... | +| Assert.cs:126:17:126:20 | null | Assert.cs:126:17:126:20 | null | +| Assert.cs:126:24:126:25 | "" | Assert.cs:126:24:126:25 | "" | +| Assert.cs:127:9:127:39 | call to method IsFalse | Assert.cs:127:24:127:38 | ... \|\| ... | +| Assert.cs:127:9:127:40 | ...; | Assert.cs:127:9:127:40 | ...; | +| Assert.cs:127:24:127:24 | access to local variable s | Assert.cs:127:24:127:24 | access to local variable s | +| Assert.cs:127:24:127:32 | ... != ... | Assert.cs:127:24:127:24 | access to local variable s | +| Assert.cs:127:24:127:38 | ... \|\| ... | Assert.cs:127:24:127:38 | ... \|\| ... | +| Assert.cs:127:29:127:32 | null | Assert.cs:127:29:127:32 | null | +| Assert.cs:127:37:127:38 | !... | Assert.cs:127:37:127:38 | !... | +| Assert.cs:127:38:127:38 | access to parameter b | Assert.cs:127:38:127:38 | access to parameter b | +| Assert.cs:128:9:128:35 | call to method WriteLine | Assert.cs:128:27:128:27 | access to local variable s | +| Assert.cs:128:9:128:36 | ...; | Assert.cs:128:9:128:36 | ...; | +| Assert.cs:128:27:128:27 | access to local variable s | Assert.cs:128:27:128:27 | access to local variable s | +| Assert.cs:128:27:128:34 | access to property Length | Assert.cs:128:27:128:27 | access to local variable s | | Assignments.cs:4:5:15:5 | {...} | Assignments.cs:4:5:15:5 | {...} | | Assignments.cs:5:9:5:18 | ... ...; | Assignments.cs:5:9:5:18 | ... ...; | | Assignments.cs:5:13:5:17 | Int32 x = ... | Assignments.cs:5:17:5:17 | 0 | @@ -493,11 +845,21 @@ | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:13:25:14 | "" | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:13:25:14 | "" | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:31:25:31 | access to local variable s | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:75:31:78 | ", " | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:33:5:36:5 | {...} | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:34:13:34:13 | 0 | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:9:34:14 | ...; | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:13:34:13 | 0 | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:25 | ...; | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:9:35:12 | this access | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:75:41:78 | ", " | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:4:5:9:5 | {...} | | Conditions.cs:5:9:6:16 | if (...) ... | Conditions.cs:5:9:6:16 | if (...) ... | | Conditions.cs:5:13:5:15 | access to parameter inc | Conditions.cs:5:13:5:15 | access to parameter inc | @@ -639,7 +1001,7 @@ | Conditions.cs:79:17:79:26 | ...; | Conditions.cs:79:17:79:26 | ...; | | Conditions.cs:79:21:79:25 | false | Conditions.cs:79:21:79:25 | false | | Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:9:82:16 | if (...) ... | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:81:12:81:12 | access to local variable b | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:81:13:81:13 | access to local variable b | | Conditions.cs:82:13:82:13 | access to local variable x | Conditions.cs:82:13:82:13 | access to local variable x | | Conditions.cs:82:13:82:15 | ...++ | Conditions.cs:82:13:82:13 | access to local variable x | | Conditions.cs:82:13:82:16 | ...; | Conditions.cs:82:13:82:16 | ...; | @@ -712,14 +1074,14 @@ | Conditions.cs:115:16:115:23 | String s = ... | Conditions.cs:115:20:115:23 | null | | Conditions.cs:115:20:115:23 | null | Conditions.cs:115:20:115:23 | null | | Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:116:9:123:9 | for (...;...;...) ... | -| Conditions.cs:116:17:116:21 | Int32 i = ... | Conditions.cs:116:21:116:21 | 0 | -| Conditions.cs:116:21:116:21 | 0 | Conditions.cs:116:21:116:21 | 0 | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:24:116:24 | access to local variable i | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:116:24:116:24 | access to local variable i | -| Conditions.cs:116:28:116:31 | access to parameter args | Conditions.cs:116:28:116:31 | access to parameter args | -| Conditions.cs:116:28:116:38 | access to property Length | Conditions.cs:116:28:116:31 | access to parameter args | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:116:41:116:41 | access to local variable i | -| Conditions.cs:116:41:116:43 | ...++ | Conditions.cs:116:41:116:41 | access to local variable i | +| Conditions.cs:116:18:116:22 | Int32 i = ... | Conditions.cs:116:22:116:22 | 0 | +| Conditions.cs:116:22:116:22 | 0 | Conditions.cs:116:22:116:22 | 0 | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:25:116:25 | access to local variable i | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:116:25:116:25 | access to local variable i | +| Conditions.cs:116:29:116:32 | access to parameter args | Conditions.cs:116:29:116:32 | access to parameter args | +| Conditions.cs:116:29:116:39 | access to property Length | Conditions.cs:116:29:116:32 | access to parameter args | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:116:42:116:42 | access to local variable i | +| Conditions.cs:116:42:116:44 | ...++ | Conditions.cs:116:42:116:42 | access to local variable i | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:117:9:123:9 | {...} | | Conditions.cs:118:13:118:44 | ... ...; | Conditions.cs:118:13:118:44 | ... ...; | | Conditions.cs:118:17:118:43 | Boolean last = ... | Conditions.cs:118:24:118:24 | access to local variable i | @@ -756,6 +1118,25 @@ | Conditions.cs:137:21:137:26 | this access | Conditions.cs:137:21:137:26 | this access | | Conditions.cs:137:21:137:37 | call to method ToString | Conditions.cs:137:21:137:26 | this access | | Conditions.cs:137:21:137:38 | ...; | Conditions.cs:137:21:137:38 | ...; | +| Conditions.cs:144:5:150:5 | {...} | Conditions.cs:144:5:150:5 | {...} | +| Conditions.cs:145:9:145:30 | ... ...; | Conditions.cs:145:9:145:30 | ... ...; | +| Conditions.cs:145:13:145:29 | String s = ... | Conditions.cs:145:17:145:29 | ... ? ... : ... | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:17:145:17 | access to parameter b | +| Conditions.cs:145:17:145:29 | ... ? ... : ... | Conditions.cs:145:17:145:29 | ... ? ... : ... | +| Conditions.cs:145:21:145:23 | "a" | Conditions.cs:145:21:145:23 | "a" | +| Conditions.cs:145:27:145:29 | "b" | Conditions.cs:145:27:145:29 | "b" | +| Conditions.cs:146:9:149:49 | if (...) ... | Conditions.cs:146:9:149:49 | if (...) ... | +| Conditions.cs:146:13:146:13 | access to parameter b | Conditions.cs:146:13:146:13 | access to parameter b | +| Conditions.cs:147:13:147:48 | call to method WriteLine | Conditions.cs:147:40:147:43 | "a = " | +| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:13:147:49 | ...; | +| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:40:147:43 | "a = " | +| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:40:147:43 | "a = " | +| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:45:147:45 | access to local variable s | +| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:149:40:149:43 | "b = " | +| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:13:149:49 | ...; | +| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:40:149:43 | "b = " | +| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:40:149:43 | "b = " | +| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:45:149:45 | access to local variable s | | ExitMethods.cs:8:5:11:5 | {...} | ExitMethods.cs:8:5:11:5 | {...} | | ExitMethods.cs:9:9:9:24 | call to method ErrorMaybe | ExitMethods.cs:9:20:9:23 | true | | ExitMethods.cs:9:9:9:25 | ...; | ExitMethods.cs:9:9:9:25 | ...; | @@ -1251,9 +1632,9 @@ | Initializers.cs:14:47:14:51 | ... = ... | Initializers.cs:14:51:14:51 | 1 | | Initializers.cs:14:51:14:51 | 1 | Initializers.cs:14:51:14:51 | 1 | | Initializers.cs:15:9:15:64 | ... ...; | Initializers.cs:15:9:15:64 | ... ...; | -| Initializers.cs:15:13:15:63 | Initializers[] iz = ... | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | +| Initializers.cs:15:13:15:63 | Initializers[] iz = ... | Initializers.cs:15:18:15:63 | 2 | | Initializers.cs:15:18:15:63 | 2 | Initializers.cs:15:18:15:63 | 2 | -| Initializers.cs:15:18:15:63 | array creation of type Initializers[] | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | +| Initializers.cs:15:18:15:63 | array creation of type Initializers[] | Initializers.cs:15:18:15:63 | 2 | | Initializers.cs:15:37:15:63 | { ..., ... } | Initializers.cs:15:39:15:39 | access to local variable i | | Initializers.cs:15:39:15:39 | access to local variable i | Initializers.cs:15:39:15:39 | access to local variable i | | Initializers.cs:15:42:15:61 | object creation of type Initializers | Initializers.cs:15:59:15:60 | "" | @@ -1408,24 +1789,24 @@ | LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:9:13:9:16 | access to parameter args | | LoopUnrolling.cs:9:28:9:28 | 0 | LoopUnrolling.cs:9:28:9:28 | 0 | | LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:10:13:10:19 | return ...; | -| LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:11:28:11:31 | access to parameter args | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:11:21:11:23 | String arg | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:28:11:31 | access to parameter args | +| LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:11:29:11:32 | access to parameter args | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:11:22:11:24 | String arg | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:29:11:32 | access to parameter args | | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | | LoopUnrolling.cs:12:13:12:35 | ...; | LoopUnrolling.cs:12:13:12:35 | ...; | | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | | LoopUnrolling.cs:16:5:20:5 | {...} | LoopUnrolling.cs:16:5:20:5 | {...} | -| LoopUnrolling.cs:17:9:17:47 | ... ...; | LoopUnrolling.cs:17:9:17:47 | ... ...; | -| LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | -| LoopUnrolling.cs:17:18:17:46 | 3 | LoopUnrolling.cs:17:18:17:46 | 3 | -| LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | -| LoopUnrolling.cs:17:30:17:46 | { ..., ... } | LoopUnrolling.cs:17:32:17:34 | "a" | -| LoopUnrolling.cs:17:32:17:34 | "a" | LoopUnrolling.cs:17:32:17:34 | "a" | -| LoopUnrolling.cs:17:37:17:39 | "b" | LoopUnrolling.cs:17:37:17:39 | "b" | -| LoopUnrolling.cs:17:42:17:44 | "c" | LoopUnrolling.cs:17:42:17:44 | "c" | -| LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:18:26:18:27 | access to local variable xs | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:18:21:18:21 | String x | -| LoopUnrolling.cs:18:26:18:27 | access to local variable xs | LoopUnrolling.cs:18:26:18:27 | access to local variable xs | +| LoopUnrolling.cs:17:9:17:48 | ... ...; | LoopUnrolling.cs:17:9:17:48 | ... ...; | +| LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | LoopUnrolling.cs:17:18:17:47 | 3 | +| LoopUnrolling.cs:17:18:17:47 | 3 | LoopUnrolling.cs:17:18:17:47 | 3 | +| LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | LoopUnrolling.cs:17:18:17:47 | 3 | +| LoopUnrolling.cs:17:31:17:47 | { ..., ... } | LoopUnrolling.cs:17:33:17:35 | "a" | +| LoopUnrolling.cs:17:33:17:35 | "a" | LoopUnrolling.cs:17:33:17:35 | "a" | +| LoopUnrolling.cs:17:38:17:40 | "b" | LoopUnrolling.cs:17:38:17:40 | "b" | +| LoopUnrolling.cs:17:43:17:45 | "c" | LoopUnrolling.cs:17:43:17:45 | "c" | +| LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:18:27:18:28 | access to local variable xs | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:18:22:18:22 | String x | +| LoopUnrolling.cs:18:27:18:28 | access to local variable xs | LoopUnrolling.cs:18:27:18:28 | access to local variable xs | | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | LoopUnrolling.cs:19:31:19:31 | access to local variable x | | LoopUnrolling.cs:19:13:19:33 | ...; | LoopUnrolling.cs:19:13:19:33 | ...; | | LoopUnrolling.cs:19:31:19:31 | access to local variable x | LoopUnrolling.cs:19:31:19:31 | access to local variable x | @@ -1444,70 +1825,70 @@ | LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | LoopUnrolling.cs:31:29:31:29 | 0 | | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | LoopUnrolling.cs:31:29:31:29 | 0 | | LoopUnrolling.cs:31:29:31:29 | 0 | LoopUnrolling.cs:31:29:31:29 | 0 | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:26:32:27 | access to local variable xs | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:32:21:32:21 | String x | -| LoopUnrolling.cs:32:26:32:27 | access to local variable xs | LoopUnrolling.cs:32:26:32:27 | access to local variable xs | +| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:27:32:28 | access to local variable xs | +| LoopUnrolling.cs:32:22:32:22 | String x | LoopUnrolling.cs:32:22:32:22 | String x | +| LoopUnrolling.cs:32:27:32:28 | access to local variable xs | LoopUnrolling.cs:32:27:32:28 | access to local variable xs | | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | LoopUnrolling.cs:33:31:33:31 | access to local variable x | | LoopUnrolling.cs:33:13:33:33 | ...; | LoopUnrolling.cs:33:13:33:33 | ...; | | LoopUnrolling.cs:33:31:33:31 | access to local variable x | LoopUnrolling.cs:33:31:33:31 | access to local variable x | | LoopUnrolling.cs:37:5:43:5 | {...} | LoopUnrolling.cs:37:5:43:5 | {...} | -| LoopUnrolling.cs:38:9:38:47 | ... ...; | LoopUnrolling.cs:38:9:38:47 | ... ...; | -| LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | -| LoopUnrolling.cs:38:18:38:46 | 3 | LoopUnrolling.cs:38:18:38:46 | 3 | -| LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | -| LoopUnrolling.cs:38:30:38:46 | { ..., ... } | LoopUnrolling.cs:38:32:38:34 | "a" | -| LoopUnrolling.cs:38:32:38:34 | "a" | LoopUnrolling.cs:38:32:38:34 | "a" | -| LoopUnrolling.cs:38:37:38:39 | "b" | LoopUnrolling.cs:38:37:38:39 | "b" | -| LoopUnrolling.cs:38:42:38:44 | "c" | LoopUnrolling.cs:38:42:38:44 | "c" | -| LoopUnrolling.cs:39:9:39:47 | ... ...; | LoopUnrolling.cs:39:9:39:47 | ... ...; | -| LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | -| LoopUnrolling.cs:39:18:39:46 | 3 | LoopUnrolling.cs:39:18:39:46 | 3 | -| LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | -| LoopUnrolling.cs:39:30:39:46 | { ..., ... } | LoopUnrolling.cs:39:32:39:34 | "0" | -| LoopUnrolling.cs:39:32:39:34 | "0" | LoopUnrolling.cs:39:32:39:34 | "0" | -| LoopUnrolling.cs:39:37:39:39 | "1" | LoopUnrolling.cs:39:37:39:39 | "1" | -| LoopUnrolling.cs:39:42:39:44 | "2" | LoopUnrolling.cs:39:42:39:44 | "2" | -| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:26:40:27 | access to local variable xs | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:40:21:40:21 | String x | -| LoopUnrolling.cs:40:26:40:27 | access to local variable xs | LoopUnrolling.cs:40:26:40:27 | access to local variable xs | -| LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:30:41:31 | access to local variable ys | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:41:25:41:25 | String y | -| LoopUnrolling.cs:41:30:41:31 | access to local variable ys | LoopUnrolling.cs:41:30:41:31 | access to local variable ys | +| LoopUnrolling.cs:38:9:38:48 | ... ...; | LoopUnrolling.cs:38:9:38:48 | ... ...; | +| LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | LoopUnrolling.cs:38:18:38:47 | 3 | +| LoopUnrolling.cs:38:18:38:47 | 3 | LoopUnrolling.cs:38:18:38:47 | 3 | +| LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | LoopUnrolling.cs:38:18:38:47 | 3 | +| LoopUnrolling.cs:38:31:38:47 | { ..., ... } | LoopUnrolling.cs:38:33:38:35 | "a" | +| LoopUnrolling.cs:38:33:38:35 | "a" | LoopUnrolling.cs:38:33:38:35 | "a" | +| LoopUnrolling.cs:38:38:38:40 | "b" | LoopUnrolling.cs:38:38:38:40 | "b" | +| LoopUnrolling.cs:38:43:38:45 | "c" | LoopUnrolling.cs:38:43:38:45 | "c" | +| LoopUnrolling.cs:39:9:39:48 | ... ...; | LoopUnrolling.cs:39:9:39:48 | ... ...; | +| LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | LoopUnrolling.cs:39:18:39:47 | 3 | +| LoopUnrolling.cs:39:18:39:47 | 3 | LoopUnrolling.cs:39:18:39:47 | 3 | +| LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | LoopUnrolling.cs:39:18:39:47 | 3 | +| LoopUnrolling.cs:39:31:39:47 | { ..., ... } | LoopUnrolling.cs:39:33:39:35 | "0" | +| LoopUnrolling.cs:39:33:39:35 | "0" | LoopUnrolling.cs:39:33:39:35 | "0" | +| LoopUnrolling.cs:39:38:39:40 | "1" | LoopUnrolling.cs:39:38:39:40 | "1" | +| LoopUnrolling.cs:39:43:39:45 | "2" | LoopUnrolling.cs:39:43:39:45 | "2" | +| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:27:40:28 | access to local variable xs | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:40:22:40:22 | String x | +| LoopUnrolling.cs:40:27:40:28 | access to local variable xs | LoopUnrolling.cs:40:27:40:28 | access to local variable xs | +| LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:31:41:32 | access to local variable ys | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:41:26:41:26 | String y | +| LoopUnrolling.cs:41:31:41:32 | access to local variable ys | LoopUnrolling.cs:41:31:41:32 | access to local variable ys | | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | LoopUnrolling.cs:42:35:42:35 | access to local variable x | | LoopUnrolling.cs:42:17:42:41 | ...; | LoopUnrolling.cs:42:17:42:41 | ...; | | LoopUnrolling.cs:42:35:42:35 | access to local variable x | LoopUnrolling.cs:42:35:42:35 | access to local variable x | | LoopUnrolling.cs:42:35:42:39 | ... + ... | LoopUnrolling.cs:42:35:42:35 | access to local variable x | | LoopUnrolling.cs:42:39:42:39 | access to local variable y | LoopUnrolling.cs:42:39:42:39 | access to local variable y | | LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:46:5:53:5 | {...} | -| LoopUnrolling.cs:47:9:47:47 | ... ...; | LoopUnrolling.cs:47:9:47:47 | ... ...; | -| LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | -| LoopUnrolling.cs:47:18:47:46 | 3 | LoopUnrolling.cs:47:18:47:46 | 3 | -| LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | -| LoopUnrolling.cs:47:30:47:46 | { ..., ... } | LoopUnrolling.cs:47:32:47:34 | "a" | -| LoopUnrolling.cs:47:32:47:34 | "a" | LoopUnrolling.cs:47:32:47:34 | "a" | -| LoopUnrolling.cs:47:37:47:39 | "b" | LoopUnrolling.cs:47:37:47:39 | "b" | -| LoopUnrolling.cs:47:42:47:44 | "c" | LoopUnrolling.cs:47:42:47:44 | "c" | -| LoopUnrolling.cs:48:9:52:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:48:26:48:27 | access to local variable xs | -| LoopUnrolling.cs:48:21:48:21 | String x | LoopUnrolling.cs:48:21:48:21 | String x | -| LoopUnrolling.cs:48:26:48:27 | access to local variable xs | LoopUnrolling.cs:48:26:48:27 | access to local variable xs | +| LoopUnrolling.cs:47:9:47:48 | ... ...; | LoopUnrolling.cs:47:9:47:48 | ... ...; | +| LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | LoopUnrolling.cs:47:18:47:47 | 3 | +| LoopUnrolling.cs:47:18:47:47 | 3 | LoopUnrolling.cs:47:18:47:47 | 3 | +| LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | LoopUnrolling.cs:47:18:47:47 | 3 | +| LoopUnrolling.cs:47:31:47:47 | { ..., ... } | LoopUnrolling.cs:47:33:47:35 | "a" | +| LoopUnrolling.cs:47:33:47:35 | "a" | LoopUnrolling.cs:47:33:47:35 | "a" | +| LoopUnrolling.cs:47:38:47:40 | "b" | LoopUnrolling.cs:47:38:47:40 | "b" | +| LoopUnrolling.cs:47:43:47:45 | "c" | LoopUnrolling.cs:47:43:47:45 | "c" | +| LoopUnrolling.cs:48:9:52:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:48:27:48:28 | access to local variable xs | +| LoopUnrolling.cs:48:22:48:22 | String x | LoopUnrolling.cs:48:22:48:22 | String x | +| LoopUnrolling.cs:48:27:48:28 | access to local variable xs | LoopUnrolling.cs:48:27:48:28 | access to local variable xs | | LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:49:9:52:9 | {...} | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:50:13:50:17 | Label: | -| LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | LoopUnrolling.cs:50:38:50:38 | access to local variable x | -| LoopUnrolling.cs:50:20:50:40 | ...; | LoopUnrolling.cs:50:20:50:40 | ...; | -| LoopUnrolling.cs:50:38:50:38 | access to local variable x | LoopUnrolling.cs:50:38:50:38 | access to local variable x | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:50:9:50:13 | Label: | +| LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | LoopUnrolling.cs:50:34:50:34 | access to local variable x | +| LoopUnrolling.cs:50:16:50:36 | ...; | LoopUnrolling.cs:50:16:50:36 | ...; | +| LoopUnrolling.cs:50:34:50:34 | access to local variable x | LoopUnrolling.cs:50:34:50:34 | access to local variable x | | LoopUnrolling.cs:51:13:51:23 | goto ...; | LoopUnrolling.cs:51:13:51:23 | goto ...; | | LoopUnrolling.cs:56:5:65:5 | {...} | LoopUnrolling.cs:56:5:65:5 | {...} | -| LoopUnrolling.cs:57:9:57:47 | ... ...; | LoopUnrolling.cs:57:9:57:47 | ... ...; | -| LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | -| LoopUnrolling.cs:57:18:57:46 | 3 | LoopUnrolling.cs:57:18:57:46 | 3 | -| LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | -| LoopUnrolling.cs:57:30:57:46 | { ..., ... } | LoopUnrolling.cs:57:32:57:34 | "a" | -| LoopUnrolling.cs:57:32:57:34 | "a" | LoopUnrolling.cs:57:32:57:34 | "a" | -| LoopUnrolling.cs:57:37:57:39 | "b" | LoopUnrolling.cs:57:37:57:39 | "b" | -| LoopUnrolling.cs:57:42:57:44 | "c" | LoopUnrolling.cs:57:42:57:44 | "c" | -| LoopUnrolling.cs:58:9:64:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:58:26:58:27 | access to local variable xs | -| LoopUnrolling.cs:58:21:58:21 | String x | LoopUnrolling.cs:58:21:58:21 | String x | -| LoopUnrolling.cs:58:26:58:27 | access to local variable xs | LoopUnrolling.cs:58:26:58:27 | access to local variable xs | +| LoopUnrolling.cs:57:9:57:48 | ... ...; | LoopUnrolling.cs:57:9:57:48 | ... ...; | +| LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | LoopUnrolling.cs:57:18:57:47 | 3 | +| LoopUnrolling.cs:57:18:57:47 | 3 | LoopUnrolling.cs:57:18:57:47 | 3 | +| LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | LoopUnrolling.cs:57:18:57:47 | 3 | +| LoopUnrolling.cs:57:31:57:47 | { ..., ... } | LoopUnrolling.cs:57:33:57:35 | "a" | +| LoopUnrolling.cs:57:33:57:35 | "a" | LoopUnrolling.cs:57:33:57:35 | "a" | +| LoopUnrolling.cs:57:38:57:40 | "b" | LoopUnrolling.cs:57:38:57:40 | "b" | +| LoopUnrolling.cs:57:43:57:45 | "c" | LoopUnrolling.cs:57:43:57:45 | "c" | +| LoopUnrolling.cs:58:9:64:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:58:27:58:28 | access to local variable xs | +| LoopUnrolling.cs:58:22:58:22 | String x | LoopUnrolling.cs:58:22:58:22 | String x | +| LoopUnrolling.cs:58:27:58:28 | access to local variable xs | LoopUnrolling.cs:58:27:58:28 | access to local variable xs | | LoopUnrolling.cs:59:9:64:9 | {...} | LoopUnrolling.cs:59:9:64:9 | {...} | | LoopUnrolling.cs:60:13:61:37 | if (...) ... | LoopUnrolling.cs:60:13:61:37 | if (...) ... | | LoopUnrolling.cs:60:17:60:17 | access to parameter b | LoopUnrolling.cs:60:17:60:17 | access to parameter b | @@ -1528,12 +1909,134 @@ | LoopUnrolling.cs:71:9:71:12 | access to parameter args | LoopUnrolling.cs:71:9:71:12 | access to parameter args | | LoopUnrolling.cs:71:9:71:20 | call to method Clear | LoopUnrolling.cs:71:9:71:12 | access to parameter args | | LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:71:9:71:21 | ...; | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:28:72:31 | access to parameter args | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:72:21:72:23 | String arg | -| LoopUnrolling.cs:72:28:72:31 | access to parameter args | LoopUnrolling.cs:72:28:72:31 | access to parameter args | +| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:29:72:32 | access to parameter args | +| LoopUnrolling.cs:72:22:72:24 | String arg | LoopUnrolling.cs:72:22:72:24 | String arg | +| LoopUnrolling.cs:72:29:72:32 | access to parameter args | LoopUnrolling.cs:72:29:72:32 | access to parameter args | | LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | LoopUnrolling.cs:73:31:73:33 | access to local variable arg | | LoopUnrolling.cs:73:13:73:35 | ...; | LoopUnrolling.cs:73:13:73:35 | ...; | | LoopUnrolling.cs:73:31:73:33 | access to local variable arg | LoopUnrolling.cs:73:31:73:33 | access to local variable arg | +| LoopUnrolling.cs:77:5:83:5 | {...} | LoopUnrolling.cs:77:5:83:5 | {...} | +| LoopUnrolling.cs:78:9:78:34 | ... ...; | LoopUnrolling.cs:78:9:78:34 | ... ...; | +| LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | LoopUnrolling.cs:78:29:78:29 | 2 | +| LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | LoopUnrolling.cs:78:29:78:29 | 2 | +| LoopUnrolling.cs:78:29:78:29 | 2 | LoopUnrolling.cs:78:29:78:29 | 2 | +| LoopUnrolling.cs:78:32:78:32 | 0 | LoopUnrolling.cs:78:32:78:32 | 0 | +| LoopUnrolling.cs:79:9:82:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:79:27:79:28 | access to local variable xs | +| LoopUnrolling.cs:79:22:79:22 | String x | LoopUnrolling.cs:79:22:79:22 | String x | +| LoopUnrolling.cs:79:27:79:28 | access to local variable xs | LoopUnrolling.cs:79:27:79:28 | access to local variable xs | +| LoopUnrolling.cs:80:9:82:9 | {...} | LoopUnrolling.cs:80:9:82:9 | {...} | +| LoopUnrolling.cs:81:13:81:32 | call to method WriteLine | LoopUnrolling.cs:81:31:81:31 | access to local variable x | +| LoopUnrolling.cs:81:13:81:33 | ...; | LoopUnrolling.cs:81:13:81:33 | ...; | +| LoopUnrolling.cs:81:31:81:31 | access to local variable x | LoopUnrolling.cs:81:31:81:31 | access to local variable x | +| LoopUnrolling.cs:86:5:92:5 | {...} | LoopUnrolling.cs:86:5:92:5 | {...} | +| LoopUnrolling.cs:87:9:87:34 | ... ...; | LoopUnrolling.cs:87:9:87:34 | ... ...; | +| LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | LoopUnrolling.cs:87:29:87:29 | 0 | +| LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | LoopUnrolling.cs:87:29:87:29 | 0 | +| LoopUnrolling.cs:87:29:87:29 | 0 | LoopUnrolling.cs:87:29:87:29 | 0 | +| LoopUnrolling.cs:87:32:87:32 | 2 | LoopUnrolling.cs:87:32:87:32 | 2 | +| LoopUnrolling.cs:88:9:91:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:88:27:88:28 | access to local variable xs | +| LoopUnrolling.cs:88:22:88:22 | String x | LoopUnrolling.cs:88:22:88:22 | String x | +| LoopUnrolling.cs:88:27:88:28 | access to local variable xs | LoopUnrolling.cs:88:27:88:28 | access to local variable xs | +| LoopUnrolling.cs:89:9:91:9 | {...} | LoopUnrolling.cs:89:9:91:9 | {...} | +| LoopUnrolling.cs:90:13:90:32 | call to method WriteLine | LoopUnrolling.cs:90:31:90:31 | access to local variable x | +| LoopUnrolling.cs:90:13:90:33 | ...; | LoopUnrolling.cs:90:13:90:33 | ...; | +| LoopUnrolling.cs:90:31:90:31 | access to local variable x | LoopUnrolling.cs:90:31:90:31 | access to local variable x | +| LoopUnrolling.cs:95:5:101:5 | {...} | LoopUnrolling.cs:95:5:101:5 | {...} | +| LoopUnrolling.cs:96:9:96:34 | ... ...; | LoopUnrolling.cs:96:9:96:34 | ... ...; | +| LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | LoopUnrolling.cs:96:29:96:29 | 2 | +| LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | LoopUnrolling.cs:96:29:96:29 | 2 | +| LoopUnrolling.cs:96:29:96:29 | 2 | LoopUnrolling.cs:96:29:96:29 | 2 | +| LoopUnrolling.cs:96:32:96:32 | 2 | LoopUnrolling.cs:96:32:96:32 | 2 | +| LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:97:27:97:28 | access to local variable xs | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:97:22:97:22 | String x | +| LoopUnrolling.cs:97:27:97:28 | access to local variable xs | LoopUnrolling.cs:97:27:97:28 | access to local variable xs | +| LoopUnrolling.cs:98:9:100:9 | {...} | LoopUnrolling.cs:98:9:100:9 | {...} | +| LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | LoopUnrolling.cs:99:31:99:31 | access to local variable x | +| LoopUnrolling.cs:99:13:99:33 | ...; | LoopUnrolling.cs:99:13:99:33 | ...; | +| LoopUnrolling.cs:99:31:99:31 | access to local variable x | LoopUnrolling.cs:99:31:99:31 | access to local variable x | +| MultiImplementationA.cs:6:22:6:31 | throw ... | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:27:7:37 | throw ...; | MultiImplementationA.cs:7:33:7:36 | null | +| MultiImplementationA.cs:7:33:7:36 | null | MultiImplementationA.cs:7:33:7:36 | null | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:7:47:7:57 | throw ...; | MultiImplementationA.cs:7:53:7:56 | null | +| MultiImplementationA.cs:7:53:7:56 | null | MultiImplementationA.cs:7:53:7:56 | null | +| MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:13:16:13:16 | access to field F | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:20:13:20 | 0 | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:42:15:50 | return ...; | MultiImplementationA.cs:15:49:15:49 | access to parameter s | +| MultiImplementationA.cs:15:49:15:49 | access to parameter s | MultiImplementationA.cs:15:49:15:49 | access to parameter s | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:16:28:16:28 | 0 | MultiImplementationA.cs:16:28:16:28 | 0 | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationA.cs:18:9:18:22 | M2(...) | +| MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:21:18:21 | 0 | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:22:20:31 | {...} | +| MultiImplementationA.cs:20:24:20:24 | access to field F | MultiImplementationA.cs:20:24:20:24 | this access | +| MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:24:20:24 | this access | +| MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationA.cs:20:24:20:24 | this access | +| MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationA.cs:20:24:20:29 | ...; | +| MultiImplementationA.cs:20:28:20:28 | access to parameter i | MultiImplementationA.cs:20:28:20:28 | access to parameter i | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | +| MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:34:24:34 | 0 | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationA.cs:30:34:30:37 | null | +| MultiImplementationA.cs:30:34:30:37 | null | MultiImplementationA.cs:30:34:30:37 | null | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:36:16:36:26 | throw ...; | MultiImplementationA.cs:36:22:36:25 | null | +| MultiImplementationA.cs:36:22:36:25 | null | MultiImplementationA.cs:36:22:36:25 | null | +| MultiImplementationA.cs:37:14:37:28 | {...} | MultiImplementationA.cs:37:14:37:28 | {...} | +| MultiImplementationA.cs:37:16:37:26 | throw ...; | MultiImplementationA.cs:37:22:37:25 | null | +| MultiImplementationA.cs:37:22:37:25 | null | MultiImplementationA.cs:37:22:37:25 | null | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:27:4:35 | return ...; | MultiImplementationB.cs:4:34:4:34 | 1 | +| MultiImplementationB.cs:4:34:4:34 | 1 | MultiImplementationB.cs:4:34:4:34 | 1 | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:11:16:11:16 | access to field F | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:20:11:20 | 1 | +| MultiImplementationB.cs:12:31:12:40 | throw ... | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:42:13:52 | throw ...; | MultiImplementationB.cs:13:48:13:51 | null | +| MultiImplementationB.cs:13:48:13:51 | null | MultiImplementationB.cs:13:48:13:51 | null | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:14:28:14:28 | 1 | MultiImplementationB.cs:14:28:14:28 | 1 | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationB.cs:16:9:16:31 | M2(...) | +| MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:27:16:30 | null | +| MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:27:16:30 | null | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:22:18:36 | {...} | +| MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:30:18:33 | null | +| MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:30:18:33 | null | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationB.cs:20:19:20:22 | null | +| MultiImplementationB.cs:20:19:20:22 | null | MultiImplementationB.cs:20:19:20:22 | null | +| MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | +| MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:34:22:34 | 1 | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationB.cs:32:17:32:17 | 0 | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:23:3:23 | access to parameter i | | NullCoalescing.cs:3:23:3:28 | ... ?? ... | NullCoalescing.cs:3:23:3:28 | ... ?? ... | | NullCoalescing.cs:3:28:3:28 | 0 | NullCoalescing.cs:3:28:3:28 | 0 | @@ -1752,7 +2255,7 @@ | Switch.cs:27:13:27:39 | case ...: | Switch.cs:27:13:27:39 | case ...: | | Switch.cs:27:18:27:25 | Double d | Switch.cs:27:18:27:25 | Double d | | Switch.cs:27:32:27:38 | call to method Throw | Switch.cs:27:32:27:38 | call to method Throw | -| Switch.cs:28:17:28:21 | Label: | Switch.cs:28:17:28:21 | Label: | +| Switch.cs:28:13:28:17 | Label: | Switch.cs:28:13:28:17 | Label: | | Switch.cs:29:17:29:23 | return ...; | Switch.cs:29:17:29:23 | return ...; | | Switch.cs:30:13:30:20 | default: | Switch.cs:30:13:30:20 | default: | | Switch.cs:31:17:31:27 | goto ...; | Switch.cs:31:17:31:27 | goto ...; | @@ -1778,61 +2281,61 @@ | Switch.cs:57:17:57:17 | 1 | Switch.cs:57:17:57:17 | 1 | | Switch.cs:57:17:57:21 | ... + ... | Switch.cs:57:17:57:17 | 1 | | Switch.cs:57:21:57:21 | 2 | Switch.cs:57:21:57:21 | 2 | -| Switch.cs:59:13:59:20 | case ...: | Switch.cs:59:13:59:20 | case ...: | +| Switch.cs:59:13:59:19 | case ...: | Switch.cs:59:13:59:19 | case ...: | | Switch.cs:59:18:59:18 | 2 | Switch.cs:59:18:59:18 | 2 | -| Switch.cs:60:15:60:20 | break; | Switch.cs:60:15:60:20 | break; | -| Switch.cs:61:13:61:20 | case ...: | Switch.cs:61:13:61:20 | case ...: | +| Switch.cs:60:17:60:22 | break; | Switch.cs:60:17:60:22 | break; | +| Switch.cs:61:13:61:19 | case ...: | Switch.cs:61:13:61:19 | case ...: | | Switch.cs:61:18:61:18 | 3 | Switch.cs:61:18:61:18 | 3 | -| Switch.cs:62:15:62:20 | break; | Switch.cs:62:15:62:20 | break; | +| Switch.cs:62:17:62:22 | break; | Switch.cs:62:17:62:22 | break; | | Switch.cs:67:5:75:5 | {...} | Switch.cs:67:5:75:5 | {...} | | Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:68:9:74:9 | switch (...) {...} | | Switch.cs:68:17:68:25 | (...) ... | Switch.cs:68:25:68:25 | access to parameter s | | Switch.cs:68:25:68:25 | access to parameter s | Switch.cs:68:25:68:25 | access to parameter s | -| Switch.cs:70:13:70:24 | case ...: | Switch.cs:70:13:70:24 | case ...: | +| Switch.cs:70:13:70:23 | case ...: | Switch.cs:70:13:70:23 | case ...: | | Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:70:18:70:20 | access to type Int32 | -| Switch.cs:71:15:71:20 | break; | Switch.cs:71:15:71:20 | break; | -| Switch.cs:72:13:72:21 | case ...: | Switch.cs:72:13:72:21 | case ...: | +| Switch.cs:71:17:71:22 | break; | Switch.cs:71:17:71:22 | break; | +| Switch.cs:72:13:72:20 | case ...: | Switch.cs:72:13:72:20 | case ...: | | Switch.cs:72:18:72:19 | "" | Switch.cs:72:18:72:19 | "" | -| Switch.cs:73:15:73:20 | break; | Switch.cs:73:15:73:20 | break; | +| Switch.cs:73:17:73:22 | break; | Switch.cs:73:17:73:22 | break; | | Switch.cs:78:5:89:5 | {...} | Switch.cs:78:5:89:5 | {...} | | Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:79:9:87:9 | switch (...) {...} | | Switch.cs:79:17:79:17 | access to parameter i | Switch.cs:79:17:79:17 | access to parameter i | -| Switch.cs:81:13:81:20 | case ...: | Switch.cs:81:13:81:20 | case ...: | +| Switch.cs:81:13:81:19 | case ...: | Switch.cs:81:13:81:19 | case ...: | | Switch.cs:81:18:81:18 | 1 | Switch.cs:81:18:81:18 | 1 | -| Switch.cs:82:15:82:26 | return ...; | Switch.cs:82:22:82:25 | true | -| Switch.cs:82:22:82:25 | true | Switch.cs:82:22:82:25 | true | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:83:13:83:20 | case ...: | +| Switch.cs:82:17:82:28 | return ...; | Switch.cs:82:24:82:27 | true | +| Switch.cs:82:24:82:27 | true | Switch.cs:82:24:82:27 | true | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:83:13:83:19 | case ...: | | Switch.cs:83:18:83:18 | 2 | Switch.cs:83:18:83:18 | 2 | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:84:15:85:22 | if (...) ... | -| Switch.cs:84:19:84:19 | access to parameter j | Switch.cs:84:19:84:19 | access to parameter j | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:84:19:84:19 | access to parameter j | -| Switch.cs:84:23:84:23 | 2 | Switch.cs:84:23:84:23 | 2 | -| Switch.cs:85:17:85:22 | break; | Switch.cs:85:17:85:22 | break; | -| Switch.cs:86:15:86:26 | return ...; | Switch.cs:86:22:86:25 | true | -| Switch.cs:86:22:86:25 | true | Switch.cs:86:22:86:25 | true | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:84:17:85:26 | if (...) ... | +| Switch.cs:84:21:84:21 | access to parameter j | Switch.cs:84:21:84:21 | access to parameter j | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:84:21:84:21 | access to parameter j | +| Switch.cs:84:25:84:25 | 2 | Switch.cs:84:25:84:25 | 2 | +| Switch.cs:85:21:85:26 | break; | Switch.cs:85:21:85:26 | break; | +| Switch.cs:86:17:86:28 | return ...; | Switch.cs:86:24:86:27 | true | +| Switch.cs:86:24:86:27 | true | Switch.cs:86:24:86:27 | true | | Switch.cs:88:9:88:21 | return ...; | Switch.cs:88:16:88:20 | false | | Switch.cs:88:16:88:20 | false | Switch.cs:88:16:88:20 | false | | Switch.cs:92:5:99:5 | {...} | Switch.cs:92:5:99:5 | {...} | | Switch.cs:93:9:97:9 | switch (...) {...} | Switch.cs:93:9:97:9 | switch (...) {...} | | Switch.cs:93:17:93:17 | access to parameter o | Switch.cs:93:17:93:17 | access to parameter o | -| Switch.cs:95:13:95:24 | case ...: | Switch.cs:95:13:95:24 | case ...: | +| Switch.cs:95:13:95:23 | case ...: | Switch.cs:95:13:95:23 | case ...: | | Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:95:18:95:20 | access to type Int32 | -| Switch.cs:96:15:96:26 | return ...; | Switch.cs:96:22:96:25 | true | -| Switch.cs:96:22:96:25 | true | Switch.cs:96:22:96:25 | true | +| Switch.cs:96:17:96:28 | return ...; | Switch.cs:96:24:96:27 | true | +| Switch.cs:96:24:96:27 | true | Switch.cs:96:24:96:27 | true | | Switch.cs:98:9:98:21 | return ...; | Switch.cs:98:16:98:20 | false | | Switch.cs:98:16:98:20 | false | Switch.cs:98:16:98:20 | false | | Switch.cs:102:5:109:5 | {...} | Switch.cs:102:5:109:5 | {...} | | Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:103:9:107:9 | switch (...) {...} | | Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:103:17:103:17 | access to parameter s | | Switch.cs:103:19:103:25 | access to property Length | Switch.cs:103:17:103:17 | access to parameter s | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:13:105:20 | case ...: | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:13:105:19 | case ...: | | Switch.cs:105:18:105:18 | 0 | Switch.cs:105:18:105:18 | 0 | -| Switch.cs:105:22:105:30 | return ...; | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:105:29:105:29 | 0 | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:13:106:20 | case ...: | +| Switch.cs:105:21:105:29 | return ...; | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:105:28:105:28 | 0 | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:13:106:19 | case ...: | | Switch.cs:106:18:106:18 | 1 | Switch.cs:106:18:106:18 | 1 | -| Switch.cs:106:22:106:30 | return ...; | Switch.cs:106:29:106:29 | 1 | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:106:29:106:29 | 1 | +| Switch.cs:106:21:106:29 | return ...; | Switch.cs:106:28:106:28 | 1 | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:106:28:106:28 | 1 | | Switch.cs:108:9:108:18 | return ...; | Switch.cs:108:17:108:17 | 1 | | Switch.cs:108:16:108:17 | -... | Switch.cs:108:17:108:17 | 1 | | Switch.cs:108:17:108:17 | 1 | Switch.cs:108:17:108:17 | 1 | @@ -1842,20 +2345,20 @@ | Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:115:9:119:9 | switch (...) {...} | | Switch.cs:115:17:115:17 | access to parameter s | Switch.cs:115:17:115:17 | access to parameter s | | Switch.cs:115:17:115:24 | access to property Length | Switch.cs:115:17:115:17 | access to parameter s | -| Switch.cs:117:13:117:34 | case ...: | Switch.cs:117:13:117:34 | case ...: | +| Switch.cs:117:13:117:35 | case ...: | Switch.cs:117:13:117:35 | case ...: | | Switch.cs:117:18:117:18 | 3 | Switch.cs:117:18:117:18 | 3 | | Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:25:117:25 | access to parameter s | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:117:25:117:25 | access to parameter s | -| Switch.cs:117:28:117:32 | "foo" | Switch.cs:117:28:117:32 | "foo" | -| Switch.cs:117:36:117:44 | return ...; | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:117:43:117:43 | 1 | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:13:118:33 | case ...: | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:117:25:117:25 | access to parameter s | +| Switch.cs:117:30:117:34 | "foo" | Switch.cs:117:30:117:34 | "foo" | +| Switch.cs:117:37:117:45 | return ...; | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:117:44:117:44 | 1 | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:13:118:34 | case ...: | | Switch.cs:118:18:118:18 | 2 | Switch.cs:118:18:118:18 | 2 | | Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:25:118:25 | access to parameter s | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:118:25:118:25 | access to parameter s | -| Switch.cs:118:28:118:31 | "fu" | Switch.cs:118:28:118:31 | "fu" | -| Switch.cs:118:35:118:43 | return ...; | Switch.cs:118:42:118:42 | 2 | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:118:42:118:42 | 2 | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:118:25:118:25 | access to parameter s | +| Switch.cs:118:30:118:33 | "fu" | Switch.cs:118:30:118:33 | "fu" | +| Switch.cs:118:36:118:44 | return ...; | Switch.cs:118:43:118:43 | 2 | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:118:43:118:43 | 2 | | Switch.cs:120:9:120:18 | return ...; | Switch.cs:120:17:120:17 | 1 | | Switch.cs:120:16:120:17 | -... | Switch.cs:120:17:120:17 | 1 | | Switch.cs:120:17:120:17 | 1 | Switch.cs:120:17:120:17 | 1 | @@ -1911,6 +2414,29 @@ | Switch.cs:150:18:150:18 | 2 | Switch.cs:150:18:150:18 | 2 | | Switch.cs:150:21:150:29 | return ...; | Switch.cs:150:28:150:28 | 2 | | Switch.cs:150:28:150:28 | 2 | Switch.cs:150:28:150:28 | 2 | +| Switch.cs:155:5:161:5 | {...} | Switch.cs:155:5:161:5 | {...} | +| Switch.cs:156:9:156:55 | ... ...; | Switch.cs:156:9:156:55 | ... ...; | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:17:156:54 | ... switch { ... } | +| Switch.cs:156:17:156:17 | access to parameter b | Switch.cs:156:17:156:17 | access to parameter b | +| Switch.cs:156:17:156:54 | ... switch { ... } | Switch.cs:156:17:156:54 | ... switch { ... } | +| Switch.cs:156:28:156:31 | true | Switch.cs:156:28:156:31 | true | +| Switch.cs:156:28:156:38 | ... => ... | Switch.cs:156:28:156:38 | ... => ... | +| Switch.cs:156:36:156:38 | "a" | Switch.cs:156:36:156:38 | "a" | +| Switch.cs:156:41:156:45 | false | Switch.cs:156:41:156:45 | false | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:41:156:52 | ... => ... | +| Switch.cs:156:50:156:52 | "b" | Switch.cs:156:50:156:52 | "b" | +| Switch.cs:157:9:160:49 | if (...) ... | Switch.cs:157:9:160:49 | if (...) ... | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:157:13:157:13 | access to parameter b | +| Switch.cs:158:13:158:48 | call to method WriteLine | Switch.cs:158:40:158:43 | "a = " | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:49 | ...; | +| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:40:158:43 | "a = " | +| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:40:158:43 | "a = " | +| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:45:158:45 | access to local variable s | +| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:160:40:160:43 | "b = " | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:49 | ...; | +| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:40:160:43 | "b = " | +| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:40:160:43 | "b = " | +| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:45:160:45 | access to local variable s | | TypeAccesses.cs:4:5:9:5 | {...} | TypeAccesses.cs:4:5:9:5 | {...} | | TypeAccesses.cs:5:9:5:26 | ... ...; | TypeAccesses.cs:5:9:5:26 | ... ...; | | TypeAccesses.cs:5:13:5:25 | String s = ... | TypeAccesses.cs:5:25:5:25 | access to parameter o | @@ -1932,10 +2458,12 @@ | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:27:7:33 | access to parameter strings | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:27:7:33 | access to parameter strings | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:27:7:33 | access to parameter strings | | VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:27:7:33 | access to parameter strings | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:35:7:35 | 0 | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:44:7:50 | access to parameter strings | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:52:7:52 | 1 | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:8:9:10:9 | {...} | diff --git a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected index fb1a799cb55d..0f67af1f9c7a 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected @@ -307,6 +307,516 @@ | ArrayCreation.cs:9:43:9:50 | { ..., ... } | ArrayCreation.cs:9:43:9:50 | { ..., ... } | normal | | ArrayCreation.cs:9:45:9:45 | 2 | ArrayCreation.cs:9:45:9:45 | 2 | normal | | ArrayCreation.cs:9:48:9:48 | 3 | ArrayCreation.cs:9:48:9:48 | 3 | normal | +| Assert.cs:8:5:12:5 | {...} | Assert.cs:10:9:10:31 | call to method Assert | exit | +| Assert.cs:8:5:12:5 | {...} | Assert.cs:11:9:11:35 | call to method WriteLine | normal | +| Assert.cs:9:9:9:33 | ... ...; | Assert.cs:9:16:9:32 | String s = ... | normal | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:9:16:9:32 | String s = ... | normal | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:20:9:20 | access to parameter b | false | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:20:9:20 | access to parameter b | true | +| Assert.cs:9:20:9:32 | ... ? ... : ... | Assert.cs:9:24:9:27 | null | normal | +| Assert.cs:9:20:9:32 | ... ? ... : ... | Assert.cs:9:31:9:32 | "" | normal | +| Assert.cs:9:24:9:27 | null | Assert.cs:9:24:9:27 | null | normal | +| Assert.cs:9:31:9:32 | "" | Assert.cs:9:31:9:32 | "" | normal | +| Assert.cs:10:9:10:31 | call to method Assert | Assert.cs:10:9:10:31 | call to method Assert | exit | +| Assert.cs:10:9:10:31 | call to method Assert | Assert.cs:10:9:10:31 | call to method Assert | normal | +| Assert.cs:10:9:10:32 | ...; | Assert.cs:10:9:10:31 | call to method Assert | exit | +| Assert.cs:10:9:10:32 | ...; | Assert.cs:10:9:10:31 | call to method Assert | normal | +| Assert.cs:10:22:10:22 | access to local variable s | Assert.cs:10:22:10:22 | access to local variable s | normal | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:22:10:30 | ... != ... | false | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:22:10:30 | ... != ... | true | +| Assert.cs:10:27:10:30 | null | Assert.cs:10:27:10:30 | null | normal | +| Assert.cs:11:9:11:35 | call to method WriteLine | Assert.cs:11:9:11:35 | call to method WriteLine | normal | +| Assert.cs:11:9:11:36 | ...; | Assert.cs:11:9:11:35 | call to method WriteLine | normal | +| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:11:27:11:27 | access to local variable s | normal | +| Assert.cs:11:27:11:34 | access to property Length | Assert.cs:11:27:11:34 | access to property Length | normal | +| Assert.cs:15:5:19:5 | {...} | Assert.cs:17:9:17:24 | call to method IsNull | throw(AssertFailedException) | +| Assert.cs:15:5:19:5 | {...} | Assert.cs:18:9:18:35 | call to method WriteLine | normal | +| Assert.cs:16:9:16:33 | ... ...; | Assert.cs:16:16:16:32 | String s = ... | normal | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:16:16:16:32 | String s = ... | normal | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:20:16:20 | access to parameter b | false | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:20:16:20 | access to parameter b | true | +| Assert.cs:16:20:16:32 | ... ? ... : ... | Assert.cs:16:24:16:27 | null | normal | +| Assert.cs:16:20:16:32 | ... ? ... : ... | Assert.cs:16:31:16:32 | "" | normal | +| Assert.cs:16:24:16:27 | null | Assert.cs:16:24:16:27 | null | normal | +| Assert.cs:16:31:16:32 | "" | Assert.cs:16:31:16:32 | "" | normal | +| Assert.cs:17:9:17:24 | call to method IsNull | Assert.cs:17:9:17:24 | call to method IsNull | normal | +| Assert.cs:17:9:17:24 | call to method IsNull | Assert.cs:17:9:17:24 | call to method IsNull | throw(AssertFailedException) | +| Assert.cs:17:9:17:25 | ...; | Assert.cs:17:9:17:24 | call to method IsNull | normal | +| Assert.cs:17:9:17:25 | ...; | Assert.cs:17:9:17:24 | call to method IsNull | throw(AssertFailedException) | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:23:17:23 | access to local variable s | non-null | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:23:17:23 | access to local variable s | null | +| Assert.cs:18:9:18:35 | call to method WriteLine | Assert.cs:18:9:18:35 | call to method WriteLine | normal | +| Assert.cs:18:9:18:36 | ...; | Assert.cs:18:9:18:35 | call to method WriteLine | normal | +| Assert.cs:18:27:18:27 | access to local variable s | Assert.cs:18:27:18:27 | access to local variable s | normal | +| Assert.cs:18:27:18:34 | access to property Length | Assert.cs:18:27:18:34 | access to property Length | normal | +| Assert.cs:22:5:26:5 | {...} | Assert.cs:24:9:24:27 | call to method IsNotNull | throw(AssertFailedException) | +| Assert.cs:22:5:26:5 | {...} | Assert.cs:25:9:25:35 | call to method WriteLine | normal | +| Assert.cs:23:9:23:33 | ... ...; | Assert.cs:23:16:23:32 | String s = ... | normal | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:23:16:23:32 | String s = ... | normal | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:20:23:20 | access to parameter b | false | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:20:23:20 | access to parameter b | true | +| Assert.cs:23:20:23:32 | ... ? ... : ... | Assert.cs:23:24:23:27 | null | normal | +| Assert.cs:23:20:23:32 | ... ? ... : ... | Assert.cs:23:31:23:32 | "" | normal | +| Assert.cs:23:24:23:27 | null | Assert.cs:23:24:23:27 | null | normal | +| Assert.cs:23:31:23:32 | "" | Assert.cs:23:31:23:32 | "" | normal | +| Assert.cs:24:9:24:27 | call to method IsNotNull | Assert.cs:24:9:24:27 | call to method IsNotNull | normal | +| Assert.cs:24:9:24:27 | call to method IsNotNull | Assert.cs:24:9:24:27 | call to method IsNotNull | throw(AssertFailedException) | +| Assert.cs:24:9:24:28 | ...; | Assert.cs:24:9:24:27 | call to method IsNotNull | normal | +| Assert.cs:24:9:24:28 | ...; | Assert.cs:24:9:24:27 | call to method IsNotNull | throw(AssertFailedException) | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:26:24:26 | access to local variable s | non-null | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:26:24:26 | access to local variable s | null | +| Assert.cs:25:9:25:35 | call to method WriteLine | Assert.cs:25:9:25:35 | call to method WriteLine | normal | +| Assert.cs:25:9:25:36 | ...; | Assert.cs:25:9:25:35 | call to method WriteLine | normal | +| Assert.cs:25:27:25:27 | access to local variable s | Assert.cs:25:27:25:27 | access to local variable s | normal | +| Assert.cs:25:27:25:34 | access to property Length | Assert.cs:25:27:25:34 | access to property Length | normal | +| Assert.cs:29:5:33:5 | {...} | Assert.cs:31:9:31:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:29:5:33:5 | {...} | Assert.cs:32:9:32:35 | call to method WriteLine | normal | +| Assert.cs:30:9:30:33 | ... ...; | Assert.cs:30:16:30:32 | String s = ... | normal | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:30:16:30:32 | String s = ... | normal | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:20:30:20 | access to parameter b | false | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:20:30:20 | access to parameter b | true | +| Assert.cs:30:20:30:32 | ... ? ... : ... | Assert.cs:30:24:30:27 | null | normal | +| Assert.cs:30:20:30:32 | ... ? ... : ... | Assert.cs:30:31:30:32 | "" | normal | +| Assert.cs:30:24:30:27 | null | Assert.cs:30:24:30:27 | null | normal | +| Assert.cs:30:31:30:32 | "" | Assert.cs:30:31:30:32 | "" | normal | +| Assert.cs:31:9:31:32 | call to method IsTrue | Assert.cs:31:9:31:32 | call to method IsTrue | normal | +| Assert.cs:31:9:31:32 | call to method IsTrue | Assert.cs:31:9:31:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:31:9:31:33 | ...; | Assert.cs:31:9:31:32 | call to method IsTrue | normal | +| Assert.cs:31:9:31:33 | ...; | Assert.cs:31:9:31:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:31:23:31:23 | access to local variable s | Assert.cs:31:23:31:23 | access to local variable s | normal | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:23:31:31 | ... == ... | false | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:23:31:31 | ... == ... | true | +| Assert.cs:31:28:31:31 | null | Assert.cs:31:28:31:31 | null | normal | +| Assert.cs:32:9:32:35 | call to method WriteLine | Assert.cs:32:9:32:35 | call to method WriteLine | normal | +| Assert.cs:32:9:32:36 | ...; | Assert.cs:32:9:32:35 | call to method WriteLine | normal | +| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:32:27:32:27 | access to local variable s | normal | +| Assert.cs:32:27:32:34 | access to property Length | Assert.cs:32:27:32:34 | access to property Length | normal | +| Assert.cs:36:5:40:5 | {...} | Assert.cs:38:9:38:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:36:5:40:5 | {...} | Assert.cs:39:9:39:35 | call to method WriteLine | normal | +| Assert.cs:37:9:37:33 | ... ...; | Assert.cs:37:16:37:32 | String s = ... | normal | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:37:16:37:32 | String s = ... | normal | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:20:37:20 | access to parameter b | false | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:20:37:20 | access to parameter b | true | +| Assert.cs:37:20:37:32 | ... ? ... : ... | Assert.cs:37:24:37:27 | null | normal | +| Assert.cs:37:20:37:32 | ... ? ... : ... | Assert.cs:37:31:37:32 | "" | normal | +| Assert.cs:37:24:37:27 | null | Assert.cs:37:24:37:27 | null | normal | +| Assert.cs:37:31:37:32 | "" | Assert.cs:37:31:37:32 | "" | normal | +| Assert.cs:38:9:38:32 | call to method IsTrue | Assert.cs:38:9:38:32 | call to method IsTrue | normal | +| Assert.cs:38:9:38:32 | call to method IsTrue | Assert.cs:38:9:38:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:38:9:38:33 | ...; | Assert.cs:38:9:38:32 | call to method IsTrue | normal | +| Assert.cs:38:9:38:33 | ...; | Assert.cs:38:9:38:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:38:23:38:23 | access to local variable s | Assert.cs:38:23:38:23 | access to local variable s | normal | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:23:38:31 | ... != ... | false | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:23:38:31 | ... != ... | true | +| Assert.cs:38:28:38:31 | null | Assert.cs:38:28:38:31 | null | normal | +| Assert.cs:39:9:39:35 | call to method WriteLine | Assert.cs:39:9:39:35 | call to method WriteLine | normal | +| Assert.cs:39:9:39:36 | ...; | Assert.cs:39:9:39:35 | call to method WriteLine | normal | +| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:39:27:39:27 | access to local variable s | normal | +| Assert.cs:39:27:39:34 | access to property Length | Assert.cs:39:27:39:34 | access to property Length | normal | +| Assert.cs:43:5:47:5 | {...} | Assert.cs:45:9:45:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:43:5:47:5 | {...} | Assert.cs:46:9:46:35 | call to method WriteLine | normal | +| Assert.cs:44:9:44:33 | ... ...; | Assert.cs:44:16:44:32 | String s = ... | normal | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:44:16:44:32 | String s = ... | normal | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:20:44:20 | access to parameter b | false | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:20:44:20 | access to parameter b | true | +| Assert.cs:44:20:44:32 | ... ? ... : ... | Assert.cs:44:24:44:27 | null | normal | +| Assert.cs:44:20:44:32 | ... ? ... : ... | Assert.cs:44:31:44:32 | "" | normal | +| Assert.cs:44:24:44:27 | null | Assert.cs:44:24:44:27 | null | normal | +| Assert.cs:44:31:44:32 | "" | Assert.cs:44:31:44:32 | "" | normal | +| Assert.cs:45:9:45:33 | call to method IsFalse | Assert.cs:45:9:45:33 | call to method IsFalse | normal | +| Assert.cs:45:9:45:33 | call to method IsFalse | Assert.cs:45:9:45:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:45:9:45:34 | ...; | Assert.cs:45:9:45:33 | call to method IsFalse | normal | +| Assert.cs:45:9:45:34 | ...; | Assert.cs:45:9:45:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:45:24:45:24 | access to local variable s | Assert.cs:45:24:45:24 | access to local variable s | normal | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:24:45:32 | ... != ... | false | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:24:45:32 | ... != ... | true | +| Assert.cs:45:29:45:32 | null | Assert.cs:45:29:45:32 | null | normal | +| Assert.cs:46:9:46:35 | call to method WriteLine | Assert.cs:46:9:46:35 | call to method WriteLine | normal | +| Assert.cs:46:9:46:36 | ...; | Assert.cs:46:9:46:35 | call to method WriteLine | normal | +| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:46:27:46:27 | access to local variable s | normal | +| Assert.cs:46:27:46:34 | access to property Length | Assert.cs:46:27:46:34 | access to property Length | normal | +| Assert.cs:50:5:54:5 | {...} | Assert.cs:52:9:52:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:50:5:54:5 | {...} | Assert.cs:53:9:53:35 | call to method WriteLine | normal | +| Assert.cs:51:9:51:33 | ... ...; | Assert.cs:51:16:51:32 | String s = ... | normal | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:51:16:51:32 | String s = ... | normal | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:20:51:20 | access to parameter b | false | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:20:51:20 | access to parameter b | true | +| Assert.cs:51:20:51:32 | ... ? ... : ... | Assert.cs:51:24:51:27 | null | normal | +| Assert.cs:51:20:51:32 | ... ? ... : ... | Assert.cs:51:31:51:32 | "" | normal | +| Assert.cs:51:24:51:27 | null | Assert.cs:51:24:51:27 | null | normal | +| Assert.cs:51:31:51:32 | "" | Assert.cs:51:31:51:32 | "" | normal | +| Assert.cs:52:9:52:33 | call to method IsFalse | Assert.cs:52:9:52:33 | call to method IsFalse | normal | +| Assert.cs:52:9:52:33 | call to method IsFalse | Assert.cs:52:9:52:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:52:9:52:34 | ...; | Assert.cs:52:9:52:33 | call to method IsFalse | normal | +| Assert.cs:52:9:52:34 | ...; | Assert.cs:52:9:52:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:52:24:52:24 | access to local variable s | Assert.cs:52:24:52:24 | access to local variable s | normal | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:32 | ... == ... | false | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:32 | ... == ... | true | +| Assert.cs:52:29:52:32 | null | Assert.cs:52:29:52:32 | null | normal | +| Assert.cs:53:9:53:35 | call to method WriteLine | Assert.cs:53:9:53:35 | call to method WriteLine | normal | +| Assert.cs:53:9:53:36 | ...; | Assert.cs:53:9:53:35 | call to method WriteLine | normal | +| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:53:27:53:27 | access to local variable s | normal | +| Assert.cs:53:27:53:34 | access to property Length | Assert.cs:53:27:53:34 | access to property Length | normal | +| Assert.cs:57:5:61:5 | {...} | Assert.cs:59:9:59:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:57:5:61:5 | {...} | Assert.cs:60:9:60:35 | call to method WriteLine | normal | +| Assert.cs:58:9:58:33 | ... ...; | Assert.cs:58:16:58:32 | String s = ... | normal | +| Assert.cs:58:16:58:32 | String s = ... | Assert.cs:58:16:58:32 | String s = ... | normal | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | false | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | true | +| Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:24:58:27 | null | normal | +| Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:31:58:32 | "" | normal | +| Assert.cs:58:24:58:27 | null | Assert.cs:58:24:58:27 | null | normal | +| Assert.cs:58:31:58:32 | "" | Assert.cs:58:31:58:32 | "" | normal | +| Assert.cs:59:9:59:37 | call to method IsTrue | Assert.cs:59:9:59:37 | call to method IsTrue | normal | +| Assert.cs:59:9:59:37 | call to method IsTrue | Assert.cs:59:9:59:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:59:9:59:38 | ...; | Assert.cs:59:9:59:37 | call to method IsTrue | normal | +| Assert.cs:59:9:59:38 | ...; | Assert.cs:59:9:59:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | normal | +| Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:31 | ... != ... | false | +| Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:31 | ... != ... | true | +| Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:31 | ... != ... | false | +| Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:36:59:36 | access to parameter b | false | +| Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:36:59:36 | access to parameter b | true | +| Assert.cs:59:28:59:31 | null | Assert.cs:59:28:59:31 | null | normal | +| Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:59:36:59:36 | access to parameter b | false | +| Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:59:36:59:36 | access to parameter b | true | +| Assert.cs:60:9:60:35 | call to method WriteLine | Assert.cs:60:9:60:35 | call to method WriteLine | normal | +| Assert.cs:60:9:60:36 | ...; | Assert.cs:60:9:60:35 | call to method WriteLine | normal | +| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:60:27:60:27 | access to local variable s | normal | +| Assert.cs:60:27:60:34 | access to property Length | Assert.cs:60:27:60:34 | access to property Length | normal | +| Assert.cs:64:5:68:5 | {...} | Assert.cs:66:9:66:38 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:64:5:68:5 | {...} | Assert.cs:67:9:67:35 | call to method WriteLine | normal | +| Assert.cs:65:9:65:33 | ... ...; | Assert.cs:65:16:65:32 | String s = ... | normal | +| Assert.cs:65:16:65:32 | String s = ... | Assert.cs:65:16:65:32 | String s = ... | normal | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | true | +| Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:24:65:27 | null | normal | +| Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:31:65:32 | "" | normal | +| Assert.cs:65:24:65:27 | null | Assert.cs:65:24:65:27 | null | normal | +| Assert.cs:65:31:65:32 | "" | Assert.cs:65:31:65:32 | "" | normal | +| Assert.cs:66:9:66:38 | call to method IsFalse | Assert.cs:66:9:66:38 | call to method IsFalse | normal | +| Assert.cs:66:9:66:38 | call to method IsFalse | Assert.cs:66:9:66:38 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:66:9:66:39 | ...; | Assert.cs:66:9:66:38 | call to method IsFalse | normal | +| Assert.cs:66:9:66:39 | ...; | Assert.cs:66:9:66:38 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | normal | +| Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:32 | ... == ... | false | +| Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:32 | ... == ... | true | +| Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:32 | ... == ... | true | +| Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:37:66:37 | access to parameter b | false | +| Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:37:66:37 | access to parameter b | true | +| Assert.cs:66:29:66:32 | null | Assert.cs:66:29:66:32 | null | normal | +| Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:66:37:66:37 | access to parameter b | false | +| Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:66:37:66:37 | access to parameter b | true | +| Assert.cs:67:9:67:35 | call to method WriteLine | Assert.cs:67:9:67:35 | call to method WriteLine | normal | +| Assert.cs:67:9:67:36 | ...; | Assert.cs:67:9:67:35 | call to method WriteLine | normal | +| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:67:27:67:27 | access to local variable s | normal | +| Assert.cs:67:27:67:34 | access to property Length | Assert.cs:67:27:67:34 | access to property Length | normal | +| Assert.cs:71:5:75:5 | {...} | Assert.cs:73:9:73:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:71:5:75:5 | {...} | Assert.cs:74:9:74:35 | call to method WriteLine | normal | +| Assert.cs:72:9:72:33 | ... ...; | Assert.cs:72:16:72:32 | String s = ... | normal | +| Assert.cs:72:16:72:32 | String s = ... | Assert.cs:72:16:72:32 | String s = ... | normal | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | false | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | +| Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:24:72:27 | null | normal | +| Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:31:72:32 | "" | normal | +| Assert.cs:72:24:72:27 | null | Assert.cs:72:24:72:27 | null | normal | +| Assert.cs:72:31:72:32 | "" | Assert.cs:72:31:72:32 | "" | normal | +| Assert.cs:73:9:73:37 | call to method IsTrue | Assert.cs:73:9:73:37 | call to method IsTrue | normal | +| Assert.cs:73:9:73:37 | call to method IsTrue | Assert.cs:73:9:73:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:73:9:73:38 | ...; | Assert.cs:73:9:73:37 | call to method IsTrue | normal | +| Assert.cs:73:9:73:38 | ...; | Assert.cs:73:9:73:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | normal | +| Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:31 | ... == ... | false | +| Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:31 | ... == ... | true | +| Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:31 | ... == ... | false | +| Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:36:73:36 | access to parameter b | false | +| Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:36:73:36 | access to parameter b | true | +| Assert.cs:73:28:73:31 | null | Assert.cs:73:28:73:31 | null | normal | +| Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:73:36:73:36 | access to parameter b | false | +| Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:73:36:73:36 | access to parameter b | true | +| Assert.cs:74:9:74:35 | call to method WriteLine | Assert.cs:74:9:74:35 | call to method WriteLine | normal | +| Assert.cs:74:9:74:36 | ...; | Assert.cs:74:9:74:35 | call to method WriteLine | normal | +| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:74:27:74:27 | access to local variable s | normal | +| Assert.cs:74:27:74:34 | access to property Length | Assert.cs:74:27:74:34 | access to property Length | normal | +| Assert.cs:78:5:82:5 | {...} | Assert.cs:80:9:80:38 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:78:5:82:5 | {...} | Assert.cs:81:9:81:35 | call to method WriteLine | normal | +| Assert.cs:79:9:79:33 | ... ...; | Assert.cs:79:16:79:32 | String s = ... | normal | +| Assert.cs:79:16:79:32 | String s = ... | Assert.cs:79:16:79:32 | String s = ... | normal | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | false | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | +| Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:24:79:27 | null | normal | +| Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:31:79:32 | "" | normal | +| Assert.cs:79:24:79:27 | null | Assert.cs:79:24:79:27 | null | normal | +| Assert.cs:79:31:79:32 | "" | Assert.cs:79:31:79:32 | "" | normal | +| Assert.cs:80:9:80:38 | call to method IsFalse | Assert.cs:80:9:80:38 | call to method IsFalse | normal | +| Assert.cs:80:9:80:38 | call to method IsFalse | Assert.cs:80:9:80:38 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:80:9:80:39 | ...; | Assert.cs:80:9:80:38 | call to method IsFalse | normal | +| Assert.cs:80:9:80:39 | ...; | Assert.cs:80:9:80:38 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | normal | +| Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:32 | ... != ... | false | +| Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:32 | ... != ... | true | +| Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:32 | ... != ... | true | +| Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:37:80:37 | access to parameter b | false | +| Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:37:80:37 | access to parameter b | true | +| Assert.cs:80:29:80:32 | null | Assert.cs:80:29:80:32 | null | normal | +| Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:80:37:80:37 | access to parameter b | false | +| Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:80:37:80:37 | access to parameter b | true | +| Assert.cs:81:9:81:35 | call to method WriteLine | Assert.cs:81:9:81:35 | call to method WriteLine | normal | +| Assert.cs:81:9:81:36 | ...; | Assert.cs:81:9:81:35 | call to method WriteLine | normal | +| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:81:27:81:27 | access to local variable s | normal | +| Assert.cs:81:27:81:34 | access to property Length | Assert.cs:81:27:81:34 | access to property Length | normal | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:87:9:87:31 | call to method Assert | exit | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:91:9:91:24 | call to method IsNull | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:95:9:95:27 | call to method IsNotNull | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:99:9:99:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:103:9:103:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:107:9:107:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:111:9:111:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:115:9:115:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:119:9:119:39 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:123:9:123:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:127:9:127:39 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:128:9:128:35 | call to method WriteLine | normal | +| Assert.cs:86:9:86:33 | ... ...; | Assert.cs:86:16:86:32 | String s = ... | normal | +| Assert.cs:86:16:86:32 | String s = ... | Assert.cs:86:16:86:32 | String s = ... | normal | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:20:86:20 | access to parameter b | false | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:20:86:20 | access to parameter b | true | +| Assert.cs:86:20:86:32 | ... ? ... : ... | Assert.cs:86:24:86:27 | null | normal | +| Assert.cs:86:20:86:32 | ... ? ... : ... | Assert.cs:86:31:86:32 | "" | normal | +| Assert.cs:86:24:86:27 | null | Assert.cs:86:24:86:27 | null | normal | +| Assert.cs:86:31:86:32 | "" | Assert.cs:86:31:86:32 | "" | normal | +| Assert.cs:87:9:87:31 | call to method Assert | Assert.cs:87:9:87:31 | call to method Assert | exit | +| Assert.cs:87:9:87:31 | call to method Assert | Assert.cs:87:9:87:31 | call to method Assert | normal | +| Assert.cs:87:9:87:32 | ...; | Assert.cs:87:9:87:31 | call to method Assert | exit | +| Assert.cs:87:9:87:32 | ...; | Assert.cs:87:9:87:31 | call to method Assert | normal | +| Assert.cs:87:22:87:22 | access to local variable s | Assert.cs:87:22:87:22 | access to local variable s | normal | +| Assert.cs:87:22:87:30 | ... != ... | Assert.cs:87:22:87:30 | ... != ... | false | +| Assert.cs:87:22:87:30 | ... != ... | Assert.cs:87:22:87:30 | ... != ... | true | +| Assert.cs:87:27:87:30 | null | Assert.cs:87:27:87:30 | null | normal | +| Assert.cs:88:9:88:35 | call to method WriteLine | Assert.cs:88:9:88:35 | call to method WriteLine | normal | +| Assert.cs:88:9:88:36 | ...; | Assert.cs:88:9:88:35 | call to method WriteLine | normal | +| Assert.cs:88:27:88:27 | access to local variable s | Assert.cs:88:27:88:27 | access to local variable s | normal | +| Assert.cs:88:27:88:34 | access to property Length | Assert.cs:88:27:88:34 | access to property Length | normal | +| Assert.cs:90:9:90:25 | ... = ... | Assert.cs:90:9:90:25 | ... = ... | normal | +| Assert.cs:90:9:90:26 | ...; | Assert.cs:90:9:90:25 | ... = ... | normal | +| Assert.cs:90:13:90:13 | access to parameter b | Assert.cs:90:13:90:13 | access to parameter b | false | +| Assert.cs:90:13:90:13 | access to parameter b | Assert.cs:90:13:90:13 | access to parameter b | true | +| Assert.cs:90:13:90:25 | ... ? ... : ... | Assert.cs:90:17:90:20 | null | normal | +| Assert.cs:90:13:90:25 | ... ? ... : ... | Assert.cs:90:24:90:25 | "" | normal | +| Assert.cs:90:17:90:20 | null | Assert.cs:90:17:90:20 | null | normal | +| Assert.cs:90:24:90:25 | "" | Assert.cs:90:24:90:25 | "" | normal | +| Assert.cs:91:9:91:24 | call to method IsNull | Assert.cs:91:9:91:24 | call to method IsNull | normal | +| Assert.cs:91:9:91:24 | call to method IsNull | Assert.cs:91:9:91:24 | call to method IsNull | throw(AssertFailedException) | +| Assert.cs:91:9:91:25 | ...; | Assert.cs:91:9:91:24 | call to method IsNull | normal | +| Assert.cs:91:9:91:25 | ...; | Assert.cs:91:9:91:24 | call to method IsNull | throw(AssertFailedException) | +| Assert.cs:91:23:91:23 | access to local variable s | Assert.cs:91:23:91:23 | access to local variable s | non-null | +| Assert.cs:91:23:91:23 | access to local variable s | Assert.cs:91:23:91:23 | access to local variable s | null | +| Assert.cs:92:9:92:35 | call to method WriteLine | Assert.cs:92:9:92:35 | call to method WriteLine | normal | +| Assert.cs:92:9:92:36 | ...; | Assert.cs:92:9:92:35 | call to method WriteLine | normal | +| Assert.cs:92:27:92:27 | access to local variable s | Assert.cs:92:27:92:27 | access to local variable s | normal | +| Assert.cs:92:27:92:34 | access to property Length | Assert.cs:92:27:92:34 | access to property Length | normal | +| Assert.cs:94:9:94:25 | ... = ... | Assert.cs:94:9:94:25 | ... = ... | normal | +| Assert.cs:94:9:94:26 | ...; | Assert.cs:94:9:94:25 | ... = ... | normal | +| Assert.cs:94:13:94:13 | access to parameter b | Assert.cs:94:13:94:13 | access to parameter b | false | +| Assert.cs:94:13:94:13 | access to parameter b | Assert.cs:94:13:94:13 | access to parameter b | true | +| Assert.cs:94:13:94:25 | ... ? ... : ... | Assert.cs:94:17:94:20 | null | normal | +| Assert.cs:94:13:94:25 | ... ? ... : ... | Assert.cs:94:24:94:25 | "" | normal | +| Assert.cs:94:17:94:20 | null | Assert.cs:94:17:94:20 | null | normal | +| Assert.cs:94:24:94:25 | "" | Assert.cs:94:24:94:25 | "" | normal | +| Assert.cs:95:9:95:27 | call to method IsNotNull | Assert.cs:95:9:95:27 | call to method IsNotNull | normal | +| Assert.cs:95:9:95:27 | call to method IsNotNull | Assert.cs:95:9:95:27 | call to method IsNotNull | throw(AssertFailedException) | +| Assert.cs:95:9:95:28 | ...; | Assert.cs:95:9:95:27 | call to method IsNotNull | normal | +| Assert.cs:95:9:95:28 | ...; | Assert.cs:95:9:95:27 | call to method IsNotNull | throw(AssertFailedException) | +| Assert.cs:95:26:95:26 | access to local variable s | Assert.cs:95:26:95:26 | access to local variable s | non-null | +| Assert.cs:95:26:95:26 | access to local variable s | Assert.cs:95:26:95:26 | access to local variable s | null | +| Assert.cs:96:9:96:35 | call to method WriteLine | Assert.cs:96:9:96:35 | call to method WriteLine | normal | +| Assert.cs:96:9:96:36 | ...; | Assert.cs:96:9:96:35 | call to method WriteLine | normal | +| Assert.cs:96:27:96:27 | access to local variable s | Assert.cs:96:27:96:27 | access to local variable s | normal | +| Assert.cs:96:27:96:34 | access to property Length | Assert.cs:96:27:96:34 | access to property Length | normal | +| Assert.cs:98:9:98:25 | ... = ... | Assert.cs:98:9:98:25 | ... = ... | normal | +| Assert.cs:98:9:98:26 | ...; | Assert.cs:98:9:98:25 | ... = ... | normal | +| Assert.cs:98:13:98:13 | access to parameter b | Assert.cs:98:13:98:13 | access to parameter b | false | +| Assert.cs:98:13:98:13 | access to parameter b | Assert.cs:98:13:98:13 | access to parameter b | true | +| Assert.cs:98:13:98:25 | ... ? ... : ... | Assert.cs:98:17:98:20 | null | normal | +| Assert.cs:98:13:98:25 | ... ? ... : ... | Assert.cs:98:24:98:25 | "" | normal | +| Assert.cs:98:17:98:20 | null | Assert.cs:98:17:98:20 | null | normal | +| Assert.cs:98:24:98:25 | "" | Assert.cs:98:24:98:25 | "" | normal | +| Assert.cs:99:9:99:32 | call to method IsTrue | Assert.cs:99:9:99:32 | call to method IsTrue | normal | +| Assert.cs:99:9:99:32 | call to method IsTrue | Assert.cs:99:9:99:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:99:9:99:33 | ...; | Assert.cs:99:9:99:32 | call to method IsTrue | normal | +| Assert.cs:99:9:99:33 | ...; | Assert.cs:99:9:99:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:99:23:99:23 | access to local variable s | Assert.cs:99:23:99:23 | access to local variable s | normal | +| Assert.cs:99:23:99:31 | ... == ... | Assert.cs:99:23:99:31 | ... == ... | false | +| Assert.cs:99:23:99:31 | ... == ... | Assert.cs:99:23:99:31 | ... == ... | true | +| Assert.cs:99:28:99:31 | null | Assert.cs:99:28:99:31 | null | normal | +| Assert.cs:100:9:100:35 | call to method WriteLine | Assert.cs:100:9:100:35 | call to method WriteLine | normal | +| Assert.cs:100:9:100:36 | ...; | Assert.cs:100:9:100:35 | call to method WriteLine | normal | +| Assert.cs:100:27:100:27 | access to local variable s | Assert.cs:100:27:100:27 | access to local variable s | normal | +| Assert.cs:100:27:100:34 | access to property Length | Assert.cs:100:27:100:34 | access to property Length | normal | +| Assert.cs:102:9:102:25 | ... = ... | Assert.cs:102:9:102:25 | ... = ... | normal | +| Assert.cs:102:9:102:26 | ...; | Assert.cs:102:9:102:25 | ... = ... | normal | +| Assert.cs:102:13:102:13 | access to parameter b | Assert.cs:102:13:102:13 | access to parameter b | false | +| Assert.cs:102:13:102:13 | access to parameter b | Assert.cs:102:13:102:13 | access to parameter b | true | +| Assert.cs:102:13:102:25 | ... ? ... : ... | Assert.cs:102:17:102:20 | null | normal | +| Assert.cs:102:13:102:25 | ... ? ... : ... | Assert.cs:102:24:102:25 | "" | normal | +| Assert.cs:102:17:102:20 | null | Assert.cs:102:17:102:20 | null | normal | +| Assert.cs:102:24:102:25 | "" | Assert.cs:102:24:102:25 | "" | normal | +| Assert.cs:103:9:103:32 | call to method IsTrue | Assert.cs:103:9:103:32 | call to method IsTrue | normal | +| Assert.cs:103:9:103:32 | call to method IsTrue | Assert.cs:103:9:103:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:103:9:103:33 | ...; | Assert.cs:103:9:103:32 | call to method IsTrue | normal | +| Assert.cs:103:9:103:33 | ...; | Assert.cs:103:9:103:32 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:103:23:103:23 | access to local variable s | Assert.cs:103:23:103:23 | access to local variable s | normal | +| Assert.cs:103:23:103:31 | ... != ... | Assert.cs:103:23:103:31 | ... != ... | false | +| Assert.cs:103:23:103:31 | ... != ... | Assert.cs:103:23:103:31 | ... != ... | true | +| Assert.cs:103:28:103:31 | null | Assert.cs:103:28:103:31 | null | normal | +| Assert.cs:104:9:104:35 | call to method WriteLine | Assert.cs:104:9:104:35 | call to method WriteLine | normal | +| Assert.cs:104:9:104:36 | ...; | Assert.cs:104:9:104:35 | call to method WriteLine | normal | +| Assert.cs:104:27:104:27 | access to local variable s | Assert.cs:104:27:104:27 | access to local variable s | normal | +| Assert.cs:104:27:104:34 | access to property Length | Assert.cs:104:27:104:34 | access to property Length | normal | +| Assert.cs:106:9:106:25 | ... = ... | Assert.cs:106:9:106:25 | ... = ... | normal | +| Assert.cs:106:9:106:26 | ...; | Assert.cs:106:9:106:25 | ... = ... | normal | +| Assert.cs:106:13:106:13 | access to parameter b | Assert.cs:106:13:106:13 | access to parameter b | false | +| Assert.cs:106:13:106:13 | access to parameter b | Assert.cs:106:13:106:13 | access to parameter b | true | +| Assert.cs:106:13:106:25 | ... ? ... : ... | Assert.cs:106:17:106:20 | null | normal | +| Assert.cs:106:13:106:25 | ... ? ... : ... | Assert.cs:106:24:106:25 | "" | normal | +| Assert.cs:106:17:106:20 | null | Assert.cs:106:17:106:20 | null | normal | +| Assert.cs:106:24:106:25 | "" | Assert.cs:106:24:106:25 | "" | normal | +| Assert.cs:107:9:107:33 | call to method IsFalse | Assert.cs:107:9:107:33 | call to method IsFalse | normal | +| Assert.cs:107:9:107:33 | call to method IsFalse | Assert.cs:107:9:107:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:107:9:107:34 | ...; | Assert.cs:107:9:107:33 | call to method IsFalse | normal | +| Assert.cs:107:9:107:34 | ...; | Assert.cs:107:9:107:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:107:24:107:24 | access to local variable s | Assert.cs:107:24:107:24 | access to local variable s | normal | +| Assert.cs:107:24:107:32 | ... != ... | Assert.cs:107:24:107:32 | ... != ... | false | +| Assert.cs:107:24:107:32 | ... != ... | Assert.cs:107:24:107:32 | ... != ... | true | +| Assert.cs:107:29:107:32 | null | Assert.cs:107:29:107:32 | null | normal | +| Assert.cs:108:9:108:35 | call to method WriteLine | Assert.cs:108:9:108:35 | call to method WriteLine | normal | +| Assert.cs:108:9:108:36 | ...; | Assert.cs:108:9:108:35 | call to method WriteLine | normal | +| Assert.cs:108:27:108:27 | access to local variable s | Assert.cs:108:27:108:27 | access to local variable s | normal | +| Assert.cs:108:27:108:34 | access to property Length | Assert.cs:108:27:108:34 | access to property Length | normal | +| Assert.cs:110:9:110:25 | ... = ... | Assert.cs:110:9:110:25 | ... = ... | normal | +| Assert.cs:110:9:110:26 | ...; | Assert.cs:110:9:110:25 | ... = ... | normal | +| Assert.cs:110:13:110:13 | access to parameter b | Assert.cs:110:13:110:13 | access to parameter b | false | +| Assert.cs:110:13:110:13 | access to parameter b | Assert.cs:110:13:110:13 | access to parameter b | true | +| Assert.cs:110:13:110:25 | ... ? ... : ... | Assert.cs:110:17:110:20 | null | normal | +| Assert.cs:110:13:110:25 | ... ? ... : ... | Assert.cs:110:24:110:25 | "" | normal | +| Assert.cs:110:17:110:20 | null | Assert.cs:110:17:110:20 | null | normal | +| Assert.cs:110:24:110:25 | "" | Assert.cs:110:24:110:25 | "" | normal | +| Assert.cs:111:9:111:33 | call to method IsFalse | Assert.cs:111:9:111:33 | call to method IsFalse | normal | +| Assert.cs:111:9:111:33 | call to method IsFalse | Assert.cs:111:9:111:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:111:9:111:34 | ...; | Assert.cs:111:9:111:33 | call to method IsFalse | normal | +| Assert.cs:111:9:111:34 | ...; | Assert.cs:111:9:111:33 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:111:24:111:24 | access to local variable s | Assert.cs:111:24:111:24 | access to local variable s | normal | +| Assert.cs:111:24:111:32 | ... == ... | Assert.cs:111:24:111:32 | ... == ... | false | +| Assert.cs:111:24:111:32 | ... == ... | Assert.cs:111:24:111:32 | ... == ... | true | +| Assert.cs:111:29:111:32 | null | Assert.cs:111:29:111:32 | null | normal | +| Assert.cs:112:9:112:35 | call to method WriteLine | Assert.cs:112:9:112:35 | call to method WriteLine | normal | +| Assert.cs:112:9:112:36 | ...; | Assert.cs:112:9:112:35 | call to method WriteLine | normal | +| Assert.cs:112:27:112:27 | access to local variable s | Assert.cs:112:27:112:27 | access to local variable s | normal | +| Assert.cs:112:27:112:34 | access to property Length | Assert.cs:112:27:112:34 | access to property Length | normal | +| Assert.cs:114:9:114:25 | ... = ... | Assert.cs:114:9:114:25 | ... = ... | normal | +| Assert.cs:114:9:114:26 | ...; | Assert.cs:114:9:114:25 | ... = ... | normal | +| Assert.cs:114:13:114:13 | access to parameter b | Assert.cs:114:13:114:13 | access to parameter b | false | +| Assert.cs:114:13:114:13 | access to parameter b | Assert.cs:114:13:114:13 | access to parameter b | true | +| Assert.cs:114:13:114:25 | ... ? ... : ... | Assert.cs:114:17:114:20 | null | normal | +| Assert.cs:114:13:114:25 | ... ? ... : ... | Assert.cs:114:24:114:25 | "" | normal | +| Assert.cs:114:17:114:20 | null | Assert.cs:114:17:114:20 | null | normal | +| Assert.cs:114:24:114:25 | "" | Assert.cs:114:24:114:25 | "" | normal | +| Assert.cs:115:9:115:37 | call to method IsTrue | Assert.cs:115:9:115:37 | call to method IsTrue | normal | +| Assert.cs:115:9:115:37 | call to method IsTrue | Assert.cs:115:9:115:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:115:9:115:38 | ...; | Assert.cs:115:9:115:37 | call to method IsTrue | normal | +| Assert.cs:115:9:115:38 | ...; | Assert.cs:115:9:115:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:115:23:115:23 | access to local variable s | Assert.cs:115:23:115:23 | access to local variable s | normal | +| Assert.cs:115:23:115:31 | ... != ... | Assert.cs:115:23:115:31 | ... != ... | false | +| Assert.cs:115:23:115:31 | ... != ... | Assert.cs:115:23:115:31 | ... != ... | true | +| Assert.cs:115:23:115:36 | ... && ... | Assert.cs:115:23:115:31 | ... != ... | false | +| Assert.cs:115:23:115:36 | ... && ... | Assert.cs:115:36:115:36 | access to parameter b | false | +| Assert.cs:115:23:115:36 | ... && ... | Assert.cs:115:36:115:36 | access to parameter b | true | +| Assert.cs:115:28:115:31 | null | Assert.cs:115:28:115:31 | null | normal | +| Assert.cs:115:36:115:36 | access to parameter b | Assert.cs:115:36:115:36 | access to parameter b | false | +| Assert.cs:115:36:115:36 | access to parameter b | Assert.cs:115:36:115:36 | access to parameter b | true | +| Assert.cs:116:9:116:35 | call to method WriteLine | Assert.cs:116:9:116:35 | call to method WriteLine | normal | +| Assert.cs:116:9:116:36 | ...; | Assert.cs:116:9:116:35 | call to method WriteLine | normal | +| Assert.cs:116:27:116:27 | access to local variable s | Assert.cs:116:27:116:27 | access to local variable s | normal | +| Assert.cs:116:27:116:34 | access to property Length | Assert.cs:116:27:116:34 | access to property Length | normal | +| Assert.cs:118:9:118:25 | ... = ... | Assert.cs:118:9:118:25 | ... = ... | normal | +| Assert.cs:118:9:118:26 | ...; | Assert.cs:118:9:118:25 | ... = ... | normal | +| Assert.cs:118:13:118:13 | access to parameter b | Assert.cs:118:13:118:13 | access to parameter b | false | +| Assert.cs:118:13:118:13 | access to parameter b | Assert.cs:118:13:118:13 | access to parameter b | true | +| Assert.cs:118:13:118:25 | ... ? ... : ... | Assert.cs:118:17:118:20 | null | normal | +| Assert.cs:118:13:118:25 | ... ? ... : ... | Assert.cs:118:24:118:25 | "" | normal | +| Assert.cs:118:17:118:20 | null | Assert.cs:118:17:118:20 | null | normal | +| Assert.cs:118:24:118:25 | "" | Assert.cs:118:24:118:25 | "" | normal | +| Assert.cs:119:9:119:39 | call to method IsFalse | Assert.cs:119:9:119:39 | call to method IsFalse | normal | +| Assert.cs:119:9:119:39 | call to method IsFalse | Assert.cs:119:9:119:39 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:119:9:119:40 | ...; | Assert.cs:119:9:119:39 | call to method IsFalse | normal | +| Assert.cs:119:9:119:40 | ...; | Assert.cs:119:9:119:39 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:119:24:119:24 | access to local variable s | Assert.cs:119:24:119:24 | access to local variable s | normal | +| Assert.cs:119:24:119:32 | ... == ... | Assert.cs:119:24:119:32 | ... == ... | false | +| Assert.cs:119:24:119:32 | ... == ... | Assert.cs:119:24:119:32 | ... == ... | true | +| Assert.cs:119:24:119:38 | ... \|\| ... | Assert.cs:119:24:119:32 | ... == ... | true | +| Assert.cs:119:24:119:38 | ... \|\| ... | Assert.cs:119:38:119:38 | access to parameter b | false [true] | +| Assert.cs:119:24:119:38 | ... \|\| ... | Assert.cs:119:38:119:38 | access to parameter b | true [false] | +| Assert.cs:119:29:119:32 | null | Assert.cs:119:29:119:32 | null | normal | +| Assert.cs:119:37:119:38 | !... | Assert.cs:119:38:119:38 | access to parameter b | false [true] | +| Assert.cs:119:37:119:38 | !... | Assert.cs:119:38:119:38 | access to parameter b | true [false] | +| Assert.cs:119:38:119:38 | access to parameter b | Assert.cs:119:38:119:38 | access to parameter b | false | +| Assert.cs:119:38:119:38 | access to parameter b | Assert.cs:119:38:119:38 | access to parameter b | true | +| Assert.cs:120:9:120:35 | call to method WriteLine | Assert.cs:120:9:120:35 | call to method WriteLine | normal | +| Assert.cs:120:9:120:36 | ...; | Assert.cs:120:9:120:35 | call to method WriteLine | normal | +| Assert.cs:120:27:120:27 | access to local variable s | Assert.cs:120:27:120:27 | access to local variable s | normal | +| Assert.cs:120:27:120:34 | access to property Length | Assert.cs:120:27:120:34 | access to property Length | normal | +| Assert.cs:122:9:122:25 | ... = ... | Assert.cs:122:9:122:25 | ... = ... | normal | +| Assert.cs:122:9:122:26 | ...; | Assert.cs:122:9:122:25 | ... = ... | normal | +| Assert.cs:122:13:122:13 | access to parameter b | Assert.cs:122:13:122:13 | access to parameter b | false | +| Assert.cs:122:13:122:13 | access to parameter b | Assert.cs:122:13:122:13 | access to parameter b | true | +| Assert.cs:122:13:122:25 | ... ? ... : ... | Assert.cs:122:17:122:20 | null | normal | +| Assert.cs:122:13:122:25 | ... ? ... : ... | Assert.cs:122:24:122:25 | "" | normal | +| Assert.cs:122:17:122:20 | null | Assert.cs:122:17:122:20 | null | normal | +| Assert.cs:122:24:122:25 | "" | Assert.cs:122:24:122:25 | "" | normal | +| Assert.cs:123:9:123:37 | call to method IsTrue | Assert.cs:123:9:123:37 | call to method IsTrue | normal | +| Assert.cs:123:9:123:37 | call to method IsTrue | Assert.cs:123:9:123:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:123:9:123:38 | ...; | Assert.cs:123:9:123:37 | call to method IsTrue | normal | +| Assert.cs:123:9:123:38 | ...; | Assert.cs:123:9:123:37 | call to method IsTrue | throw(AssertFailedException) | +| Assert.cs:123:23:123:23 | access to local variable s | Assert.cs:123:23:123:23 | access to local variable s | normal | +| Assert.cs:123:23:123:31 | ... == ... | Assert.cs:123:23:123:31 | ... == ... | false | +| Assert.cs:123:23:123:31 | ... == ... | Assert.cs:123:23:123:31 | ... == ... | true | +| Assert.cs:123:23:123:36 | ... && ... | Assert.cs:123:23:123:31 | ... == ... | false | +| Assert.cs:123:23:123:36 | ... && ... | Assert.cs:123:36:123:36 | access to parameter b | false | +| Assert.cs:123:23:123:36 | ... && ... | Assert.cs:123:36:123:36 | access to parameter b | true | +| Assert.cs:123:28:123:31 | null | Assert.cs:123:28:123:31 | null | normal | +| Assert.cs:123:36:123:36 | access to parameter b | Assert.cs:123:36:123:36 | access to parameter b | false | +| Assert.cs:123:36:123:36 | access to parameter b | Assert.cs:123:36:123:36 | access to parameter b | true | +| Assert.cs:124:9:124:35 | call to method WriteLine | Assert.cs:124:9:124:35 | call to method WriteLine | normal | +| Assert.cs:124:9:124:36 | ...; | Assert.cs:124:9:124:35 | call to method WriteLine | normal | +| Assert.cs:124:27:124:27 | access to local variable s | Assert.cs:124:27:124:27 | access to local variable s | normal | +| Assert.cs:124:27:124:34 | access to property Length | Assert.cs:124:27:124:34 | access to property Length | normal | +| Assert.cs:126:9:126:25 | ... = ... | Assert.cs:126:9:126:25 | ... = ... | normal | +| Assert.cs:126:9:126:26 | ...; | Assert.cs:126:9:126:25 | ... = ... | normal | +| Assert.cs:126:13:126:13 | access to parameter b | Assert.cs:126:13:126:13 | access to parameter b | false | +| Assert.cs:126:13:126:13 | access to parameter b | Assert.cs:126:13:126:13 | access to parameter b | true | +| Assert.cs:126:13:126:25 | ... ? ... : ... | Assert.cs:126:17:126:20 | null | normal | +| Assert.cs:126:13:126:25 | ... ? ... : ... | Assert.cs:126:24:126:25 | "" | normal | +| Assert.cs:126:17:126:20 | null | Assert.cs:126:17:126:20 | null | normal | +| Assert.cs:126:24:126:25 | "" | Assert.cs:126:24:126:25 | "" | normal | +| Assert.cs:127:9:127:39 | call to method IsFalse | Assert.cs:127:9:127:39 | call to method IsFalse | normal | +| Assert.cs:127:9:127:39 | call to method IsFalse | Assert.cs:127:9:127:39 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:127:9:127:40 | ...; | Assert.cs:127:9:127:39 | call to method IsFalse | normal | +| Assert.cs:127:9:127:40 | ...; | Assert.cs:127:9:127:39 | call to method IsFalse | throw(AssertFailedException) | +| Assert.cs:127:24:127:24 | access to local variable s | Assert.cs:127:24:127:24 | access to local variable s | normal | +| Assert.cs:127:24:127:32 | ... != ... | Assert.cs:127:24:127:32 | ... != ... | false | +| Assert.cs:127:24:127:32 | ... != ... | Assert.cs:127:24:127:32 | ... != ... | true | +| Assert.cs:127:24:127:38 | ... \|\| ... | Assert.cs:127:24:127:32 | ... != ... | true | +| Assert.cs:127:24:127:38 | ... \|\| ... | Assert.cs:127:38:127:38 | access to parameter b | false [true] | +| Assert.cs:127:24:127:38 | ... \|\| ... | Assert.cs:127:38:127:38 | access to parameter b | true [false] | +| Assert.cs:127:29:127:32 | null | Assert.cs:127:29:127:32 | null | normal | +| Assert.cs:127:37:127:38 | !... | Assert.cs:127:38:127:38 | access to parameter b | false [true] | +| Assert.cs:127:37:127:38 | !... | Assert.cs:127:38:127:38 | access to parameter b | true [false] | +| Assert.cs:127:38:127:38 | access to parameter b | Assert.cs:127:38:127:38 | access to parameter b | false | +| Assert.cs:127:38:127:38 | access to parameter b | Assert.cs:127:38:127:38 | access to parameter b | true | +| Assert.cs:128:9:128:35 | call to method WriteLine | Assert.cs:128:9:128:35 | call to method WriteLine | normal | +| Assert.cs:128:9:128:36 | ...; | Assert.cs:128:9:128:35 | call to method WriteLine | normal | +| Assert.cs:128:27:128:27 | access to local variable s | Assert.cs:128:27:128:27 | access to local variable s | normal | +| Assert.cs:128:27:128:34 | access to property Length | Assert.cs:128:27:128:34 | access to property Length | normal | | Assignments.cs:4:5:15:5 | {...} | Assignments.cs:14:9:14:35 | ... += ... | normal | | Assignments.cs:5:9:5:18 | ... ...; | Assignments.cs:5:13:5:17 | Int32 x = ... | normal | | Assignments.cs:5:13:5:17 | Int32 x = ... | Assignments.cs:5:13:5:17 | Int32 x = ... | normal | @@ -582,11 +1092,25 @@ | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:13:25:14 | "" | non-null | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | normal | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:31:25:31 | access to local variable s | normal | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | normal | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:70:31:78 | ... + ... | normal | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:70:31:83 | ... + ... | normal | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:75:31:78 | ", " | normal | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | normal | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:28:30:32 | ... = ... | normal | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:32:30:32 | 0 | normal | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:35:9:35:12 | access to property Prop | null | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:35:14:35:24 | call to method Out | normal | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:34:9:34:13 | ... = ... | normal | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:9:34:13 | ... = ... | normal | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:13:34:13 | 0 | normal | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:9:35:12 | access to property Prop | non-null | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:9:35:12 | access to property Prop | null | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | this access | normal | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:12 | access to property Prop | null | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:14:35:24 | call to method Out | normal | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:9:35:12 | access to property Prop | null | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:35:14:35:24 | call to method Out | normal | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | normal | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:70:41:78 | ... + ... | normal | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:70:41:83 | ... + ... | normal | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:75:41:78 | ", " | normal | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | normal | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:7:14:7:16 | access to parameter inc | false [true] | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:8:13:8:15 | ...-- | normal | | Conditions.cs:5:9:6:16 | if (...) ... | Conditions.cs:5:13:5:15 | access to parameter inc | false | @@ -769,10 +1293,10 @@ | Conditions.cs:79:17:79:25 | ... = ... | Conditions.cs:79:17:79:25 | ... = ... | normal | | Conditions.cs:79:17:79:26 | ...; | Conditions.cs:79:17:79:25 | ... = ... | normal | | Conditions.cs:79:21:79:25 | false | Conditions.cs:79:21:79:25 | false | normal | -| Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:12:81:12 | access to local variable b | false | +| Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:13:81:13 | access to local variable b | false | | Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:82:13:82:15 | ...++ | normal | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:81:12:81:12 | access to local variable b | false | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:81:12:81:12 | access to local variable b | true | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:81:13:81:13 | access to local variable b | false | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:81:13:81:13 | access to local variable b | true | | Conditions.cs:82:13:82:13 | access to local variable x | Conditions.cs:82:13:82:13 | access to local variable x | normal | | Conditions.cs:82:13:82:15 | ...++ | Conditions.cs:82:13:82:15 | ...++ | normal | | Conditions.cs:82:13:82:16 | ...; | Conditions.cs:82:13:82:15 | ...++ | normal | @@ -855,20 +1379,20 @@ | Conditions.cs:109:22:109:23 | "" | Conditions.cs:109:22:109:23 | "" | normal | | Conditions.cs:110:9:110:17 | return ...; | Conditions.cs:110:9:110:17 | return ...; | return | | Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:110:16:110:16 | access to local variable x | normal | -| Conditions.cs:114:5:124:5 | {...} | Conditions.cs:116:24:116:38 | ... < ... | false | +| Conditions.cs:114:5:124:5 | {...} | Conditions.cs:116:25:116:39 | ... < ... | false | | Conditions.cs:115:9:115:24 | ... ...; | Conditions.cs:115:16:115:23 | String s = ... | normal | | Conditions.cs:115:16:115:23 | String s = ... | Conditions.cs:115:16:115:23 | String s = ... | normal | | Conditions.cs:115:20:115:23 | null | Conditions.cs:115:20:115:23 | null | normal | -| Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:116:24:116:38 | ... < ... | false | -| Conditions.cs:116:17:116:21 | Int32 i = ... | Conditions.cs:116:17:116:21 | Int32 i = ... | normal | -| Conditions.cs:116:21:116:21 | 0 | Conditions.cs:116:21:116:21 | 0 | normal | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:24:116:24 | access to local variable i | normal | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:116:24:116:38 | ... < ... | false | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:116:24:116:38 | ... < ... | true | -| Conditions.cs:116:28:116:31 | access to parameter args | Conditions.cs:116:28:116:31 | access to parameter args | normal | -| Conditions.cs:116:28:116:38 | access to property Length | Conditions.cs:116:28:116:38 | access to property Length | normal | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:116:41:116:41 | access to local variable i | normal | -| Conditions.cs:116:41:116:43 | ...++ | Conditions.cs:116:41:116:43 | ...++ | normal | +| Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:116:25:116:39 | ... < ... | false | +| Conditions.cs:116:18:116:22 | Int32 i = ... | Conditions.cs:116:18:116:22 | Int32 i = ... | normal | +| Conditions.cs:116:22:116:22 | 0 | Conditions.cs:116:22:116:22 | 0 | normal | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:25:116:25 | access to local variable i | normal | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:116:25:116:39 | ... < ... | false | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:116:25:116:39 | ... < ... | true | +| Conditions.cs:116:29:116:32 | access to parameter args | Conditions.cs:116:29:116:32 | access to parameter args | normal | +| Conditions.cs:116:29:116:39 | access to property Length | Conditions.cs:116:29:116:39 | access to property Length | normal | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:116:42:116:42 | access to local variable i | normal | +| Conditions.cs:116:42:116:44 | ...++ | Conditions.cs:116:42:116:44 | ...++ | normal | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:121:17:121:20 | access to local variable last | false | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:122:17:122:24 | ... = ... | normal | | Conditions.cs:118:13:118:44 | ... ...; | Conditions.cs:118:17:118:43 | Boolean last = ... | normal | @@ -917,6 +1441,30 @@ | Conditions.cs:137:21:137:26 | this access | Conditions.cs:137:21:137:26 | this access | normal | | Conditions.cs:137:21:137:37 | call to method ToString | Conditions.cs:137:21:137:37 | call to method ToString | normal | | Conditions.cs:137:21:137:38 | ...; | Conditions.cs:137:21:137:37 | call to method ToString | normal | +| Conditions.cs:144:5:150:5 | {...} | Conditions.cs:147:13:147:48 | call to method WriteLine | normal | +| Conditions.cs:144:5:150:5 | {...} | Conditions.cs:149:13:149:48 | call to method WriteLine | normal | +| Conditions.cs:145:9:145:30 | ... ...; | Conditions.cs:145:13:145:29 | String s = ... | normal | +| Conditions.cs:145:13:145:29 | String s = ... | Conditions.cs:145:13:145:29 | String s = ... | normal | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:17:145:17 | access to parameter b | false | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:17:145:17 | access to parameter b | true | +| Conditions.cs:145:17:145:29 | ... ? ... : ... | Conditions.cs:145:21:145:23 | "a" | normal | +| Conditions.cs:145:17:145:29 | ... ? ... : ... | Conditions.cs:145:27:145:29 | "b" | normal | +| Conditions.cs:145:21:145:23 | "a" | Conditions.cs:145:21:145:23 | "a" | normal | +| Conditions.cs:145:27:145:29 | "b" | Conditions.cs:145:27:145:29 | "b" | normal | +| Conditions.cs:146:9:149:49 | if (...) ... | Conditions.cs:147:13:147:48 | call to method WriteLine | normal | +| Conditions.cs:146:9:149:49 | if (...) ... | Conditions.cs:149:13:149:48 | call to method WriteLine | normal | +| Conditions.cs:146:13:146:13 | access to parameter b | Conditions.cs:146:13:146:13 | access to parameter b | false | +| Conditions.cs:146:13:146:13 | access to parameter b | Conditions.cs:146:13:146:13 | access to parameter b | true | +| Conditions.cs:147:13:147:48 | call to method WriteLine | Conditions.cs:147:13:147:48 | call to method WriteLine | normal | +| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:13:147:48 | call to method WriteLine | normal | +| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:38:147:47 | $"..." | normal | +| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:40:147:43 | "a = " | normal | +| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:45:147:45 | access to local variable s | normal | +| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:149:13:149:48 | call to method WriteLine | normal | +| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:13:149:48 | call to method WriteLine | normal | +| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:38:149:47 | $"..." | normal | +| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:40:149:43 | "b = " | normal | +| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:45:149:45 | access to local variable s | normal | | ExitMethods.cs:8:5:11:5 | {...} | ExitMethods.cs:10:9:10:15 | return ...; | return | | ExitMethods.cs:9:9:9:24 | call to method ErrorMaybe | ExitMethods.cs:9:9:9:24 | call to method ErrorMaybe | normal | | ExitMethods.cs:9:9:9:25 | ...; | ExitMethods.cs:9:9:9:24 | call to method ErrorMaybe | normal | @@ -1051,7 +1599,7 @@ | ExitMethods.cs:120:5:123:5 | {...} | ExitMethods.cs:122:13:122:17 | Int32 x = ... | normal | | ExitMethods.cs:121:9:121:28 | call to method IsTrue | ExitMethods.cs:121:9:121:28 | call to method IsTrue | throw(AssertFailedException) | | ExitMethods.cs:121:9:121:29 | ...; | ExitMethods.cs:121:9:121:28 | call to method IsTrue | throw(AssertFailedException) | -| ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:121:23:121:27 | false | normal | +| ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:121:23:121:27 | false | false | | ExitMethods.cs:122:9:122:18 | ... ...; | ExitMethods.cs:122:13:122:17 | Int32 x = ... | normal | | ExitMethods.cs:122:13:122:17 | Int32 x = ... | ExitMethods.cs:122:13:122:17 | Int32 x = ... | normal | | ExitMethods.cs:122:17:122:17 | 0 | ExitMethods.cs:122:17:122:17 | 0 | normal | @@ -1064,13 +1612,15 @@ | ExitMethods.cs:128:13:128:17 | Int32 x = ... | ExitMethods.cs:128:13:128:17 | Int32 x = ... | normal | | ExitMethods.cs:128:17:128:17 | 0 | ExitMethods.cs:128:17:128:17 | 0 | normal | | ExitMethods.cs:131:33:131:49 | call to method IsFalse | ExitMethods.cs:131:33:131:49 | call to method IsFalse | normal | -| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:48:131:48 | access to parameter b | normal | +| ExitMethods.cs:131:33:131:49 | call to method IsFalse | ExitMethods.cs:131:33:131:49 | call to method IsFalse | throw(AssertFailedException) | +| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:48:131:48 | access to parameter b | false | +| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:48:131:48 | access to parameter b | true | | ExitMethods.cs:134:5:137:5 | {...} | ExitMethods.cs:135:9:135:25 | call to method AssertFalse | throw(AssertFailedException) | | ExitMethods.cs:134:5:137:5 | {...} | ExitMethods.cs:136:13:136:17 | Int32 x = ... | normal | | ExitMethods.cs:135:9:135:25 | call to method AssertFalse | ExitMethods.cs:135:9:135:25 | call to method AssertFalse | throw(AssertFailedException) | | ExitMethods.cs:135:9:135:25 | this access | ExitMethods.cs:135:9:135:25 | this access | normal | | ExitMethods.cs:135:9:135:26 | ...; | ExitMethods.cs:135:9:135:25 | call to method AssertFalse | throw(AssertFailedException) | -| ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:135:21:135:24 | true | normal | +| ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:135:21:135:24 | true | true | | ExitMethods.cs:136:9:136:18 | ... ...; | ExitMethods.cs:136:13:136:17 | Int32 x = ... | normal | | ExitMethods.cs:136:13:136:17 | Int32 x = ... | ExitMethods.cs:136:13:136:17 | Int32 x = ... | normal | | ExitMethods.cs:136:17:136:17 | 0 | ExitMethods.cs:136:17:136:17 | 0 | normal | @@ -1906,23 +2456,23 @@ | LoopUnrolling.cs:9:28:9:28 | 0 | LoopUnrolling.cs:9:28:9:28 | 0 | normal | | LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:10:13:10:19 | return ...; | return | | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:11:21:11:23 | String arg | normal | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:28:11:31 | access to parameter args | normal | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:11:22:11:24 | String arg | normal | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:29:11:32 | access to parameter args | normal | | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | normal | | LoopUnrolling.cs:12:13:12:35 | ...; | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | normal | | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | normal | | LoopUnrolling.cs:16:5:20:5 | {...} | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:17:9:17:47 | ... ...; | LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | normal | -| LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | normal | -| LoopUnrolling.cs:17:18:17:46 | 3 | LoopUnrolling.cs:17:18:17:46 | 3 | normal | -| LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | LoopUnrolling.cs:17:30:17:46 | { ..., ... } | normal | -| LoopUnrolling.cs:17:30:17:46 | { ..., ... } | LoopUnrolling.cs:17:30:17:46 | { ..., ... } | normal | -| LoopUnrolling.cs:17:32:17:34 | "a" | LoopUnrolling.cs:17:32:17:34 | "a" | normal | -| LoopUnrolling.cs:17:37:17:39 | "b" | LoopUnrolling.cs:17:37:17:39 | "b" | normal | -| LoopUnrolling.cs:17:42:17:44 | "c" | LoopUnrolling.cs:17:42:17:44 | "c" | normal | +| LoopUnrolling.cs:17:9:17:48 | ... ...; | LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | normal | +| LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | normal | +| LoopUnrolling.cs:17:18:17:47 | 3 | LoopUnrolling.cs:17:18:17:47 | 3 | normal | +| LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | LoopUnrolling.cs:17:31:17:47 | { ..., ... } | normal | +| LoopUnrolling.cs:17:31:17:47 | { ..., ... } | LoopUnrolling.cs:17:31:17:47 | { ..., ... } | normal | +| LoopUnrolling.cs:17:33:17:35 | "a" | LoopUnrolling.cs:17:33:17:35 | "a" | normal | +| LoopUnrolling.cs:17:38:17:40 | "b" | LoopUnrolling.cs:17:38:17:40 | "b" | normal | +| LoopUnrolling.cs:17:43:17:45 | "c" | LoopUnrolling.cs:17:43:17:45 | "c" | normal | | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:18:21:18:21 | String x | normal | -| LoopUnrolling.cs:18:26:18:27 | access to local variable xs | LoopUnrolling.cs:18:26:18:27 | access to local variable xs | normal | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:18:22:18:22 | String x | normal | +| LoopUnrolling.cs:18:27:18:28 | access to local variable xs | LoopUnrolling.cs:18:27:18:28 | access to local variable xs | normal | | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | normal | | LoopUnrolling.cs:19:13:19:33 | ...; | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | normal | | LoopUnrolling.cs:19:31:19:31 | access to local variable x | LoopUnrolling.cs:19:31:19:31 | access to local variable x | normal | @@ -1942,34 +2492,34 @@ | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | normal | | LoopUnrolling.cs:31:29:31:29 | 0 | LoopUnrolling.cs:31:29:31:29 | 0 | normal | | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:32:21:32:21 | String x | normal | -| LoopUnrolling.cs:32:26:32:27 | access to local variable xs | LoopUnrolling.cs:32:26:32:27 | access to local variable xs | normal | +| LoopUnrolling.cs:32:22:32:22 | String x | LoopUnrolling.cs:32:22:32:22 | String x | normal | +| LoopUnrolling.cs:32:27:32:28 | access to local variable xs | LoopUnrolling.cs:32:27:32:28 | access to local variable xs | normal | | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | normal | | LoopUnrolling.cs:33:13:33:33 | ...; | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | normal | | LoopUnrolling.cs:33:31:33:31 | access to local variable x | LoopUnrolling.cs:33:31:33:31 | access to local variable x | normal | | LoopUnrolling.cs:37:5:43:5 | {...} | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:38:9:38:47 | ... ...; | LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | normal | -| LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | normal | -| LoopUnrolling.cs:38:18:38:46 | 3 | LoopUnrolling.cs:38:18:38:46 | 3 | normal | -| LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | LoopUnrolling.cs:38:30:38:46 | { ..., ... } | normal | -| LoopUnrolling.cs:38:30:38:46 | { ..., ... } | LoopUnrolling.cs:38:30:38:46 | { ..., ... } | normal | -| LoopUnrolling.cs:38:32:38:34 | "a" | LoopUnrolling.cs:38:32:38:34 | "a" | normal | -| LoopUnrolling.cs:38:37:38:39 | "b" | LoopUnrolling.cs:38:37:38:39 | "b" | normal | -| LoopUnrolling.cs:38:42:38:44 | "c" | LoopUnrolling.cs:38:42:38:44 | "c" | normal | -| LoopUnrolling.cs:39:9:39:47 | ... ...; | LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | normal | -| LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | normal | -| LoopUnrolling.cs:39:18:39:46 | 3 | LoopUnrolling.cs:39:18:39:46 | 3 | normal | -| LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | LoopUnrolling.cs:39:30:39:46 | { ..., ... } | normal | -| LoopUnrolling.cs:39:30:39:46 | { ..., ... } | LoopUnrolling.cs:39:30:39:46 | { ..., ... } | normal | -| LoopUnrolling.cs:39:32:39:34 | "0" | LoopUnrolling.cs:39:32:39:34 | "0" | normal | -| LoopUnrolling.cs:39:37:39:39 | "1" | LoopUnrolling.cs:39:37:39:39 | "1" | normal | -| LoopUnrolling.cs:39:42:39:44 | "2" | LoopUnrolling.cs:39:42:39:44 | "2" | normal | +| LoopUnrolling.cs:38:9:38:48 | ... ...; | LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | normal | +| LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | normal | +| LoopUnrolling.cs:38:18:38:47 | 3 | LoopUnrolling.cs:38:18:38:47 | 3 | normal | +| LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | LoopUnrolling.cs:38:31:38:47 | { ..., ... } | normal | +| LoopUnrolling.cs:38:31:38:47 | { ..., ... } | LoopUnrolling.cs:38:31:38:47 | { ..., ... } | normal | +| LoopUnrolling.cs:38:33:38:35 | "a" | LoopUnrolling.cs:38:33:38:35 | "a" | normal | +| LoopUnrolling.cs:38:38:38:40 | "b" | LoopUnrolling.cs:38:38:38:40 | "b" | normal | +| LoopUnrolling.cs:38:43:38:45 | "c" | LoopUnrolling.cs:38:43:38:45 | "c" | normal | +| LoopUnrolling.cs:39:9:39:48 | ... ...; | LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | normal | +| LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | normal | +| LoopUnrolling.cs:39:18:39:47 | 3 | LoopUnrolling.cs:39:18:39:47 | 3 | normal | +| LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | LoopUnrolling.cs:39:31:39:47 | { ..., ... } | normal | +| LoopUnrolling.cs:39:31:39:47 | { ..., ... } | LoopUnrolling.cs:39:31:39:47 | { ..., ... } | normal | +| LoopUnrolling.cs:39:33:39:35 | "0" | LoopUnrolling.cs:39:33:39:35 | "0" | normal | +| LoopUnrolling.cs:39:38:39:40 | "1" | LoopUnrolling.cs:39:38:39:40 | "1" | normal | +| LoopUnrolling.cs:39:43:39:45 | "2" | LoopUnrolling.cs:39:43:39:45 | "2" | normal | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:40:21:40:21 | String x | normal | -| LoopUnrolling.cs:40:26:40:27 | access to local variable xs | LoopUnrolling.cs:40:26:40:27 | access to local variable xs | normal | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:40:22:40:22 | String x | normal | +| LoopUnrolling.cs:40:27:40:28 | access to local variable xs | LoopUnrolling.cs:40:27:40:28 | access to local variable xs | normal | | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:41:25:41:25 | String y | normal | -| LoopUnrolling.cs:41:30:41:31 | access to local variable ys | LoopUnrolling.cs:41:30:41:31 | access to local variable ys | normal | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:41:26:41:26 | String y | normal | +| LoopUnrolling.cs:41:31:41:32 | access to local variable ys | LoopUnrolling.cs:41:31:41:32 | access to local variable ys | normal | | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | normal | | LoopUnrolling.cs:42:17:42:41 | ...; | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | normal | | LoopUnrolling.cs:42:35:42:35 | access to local variable x | LoopUnrolling.cs:42:35:42:35 | access to local variable x | normal | @@ -1977,36 +2527,36 @@ | LoopUnrolling.cs:42:39:42:39 | access to local variable y | LoopUnrolling.cs:42:39:42:39 | access to local variable y | normal | | LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:48:9:52:9 | foreach (... ... in ...) ... | empty | | LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:51:13:51:23 | goto ...; | goto(Label) | -| LoopUnrolling.cs:47:9:47:47 | ... ...; | LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | normal | -| LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | normal | -| LoopUnrolling.cs:47:18:47:46 | 3 | LoopUnrolling.cs:47:18:47:46 | 3 | normal | -| LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | LoopUnrolling.cs:47:30:47:46 | { ..., ... } | normal | -| LoopUnrolling.cs:47:30:47:46 | { ..., ... } | LoopUnrolling.cs:47:30:47:46 | { ..., ... } | normal | -| LoopUnrolling.cs:47:32:47:34 | "a" | LoopUnrolling.cs:47:32:47:34 | "a" | normal | -| LoopUnrolling.cs:47:37:47:39 | "b" | LoopUnrolling.cs:47:37:47:39 | "b" | normal | -| LoopUnrolling.cs:47:42:47:44 | "c" | LoopUnrolling.cs:47:42:47:44 | "c" | normal | +| LoopUnrolling.cs:47:9:47:48 | ... ...; | LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | normal | +| LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | normal | +| LoopUnrolling.cs:47:18:47:47 | 3 | LoopUnrolling.cs:47:18:47:47 | 3 | normal | +| LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | LoopUnrolling.cs:47:31:47:47 | { ..., ... } | normal | +| LoopUnrolling.cs:47:31:47:47 | { ..., ... } | LoopUnrolling.cs:47:31:47:47 | { ..., ... } | normal | +| LoopUnrolling.cs:47:33:47:35 | "a" | LoopUnrolling.cs:47:33:47:35 | "a" | normal | +| LoopUnrolling.cs:47:38:47:40 | "b" | LoopUnrolling.cs:47:38:47:40 | "b" | normal | +| LoopUnrolling.cs:47:43:47:45 | "c" | LoopUnrolling.cs:47:43:47:45 | "c" | normal | | LoopUnrolling.cs:48:9:52:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:48:9:52:9 | foreach (... ... in ...) ... | empty | | LoopUnrolling.cs:48:9:52:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:51:13:51:23 | goto ...; | goto(Label) | -| LoopUnrolling.cs:48:21:48:21 | String x | LoopUnrolling.cs:48:21:48:21 | String x | normal | -| LoopUnrolling.cs:48:26:48:27 | access to local variable xs | LoopUnrolling.cs:48:26:48:27 | access to local variable xs | normal | +| LoopUnrolling.cs:48:22:48:22 | String x | LoopUnrolling.cs:48:22:48:22 | String x | normal | +| LoopUnrolling.cs:48:27:48:28 | access to local variable xs | LoopUnrolling.cs:48:27:48:28 | access to local variable xs | normal | | LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:51:13:51:23 | goto ...; | goto(Label) | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:50:13:50:17 | Label: | normal | -| LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | normal | -| LoopUnrolling.cs:50:20:50:40 | ...; | LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | normal | -| LoopUnrolling.cs:50:38:50:38 | access to local variable x | LoopUnrolling.cs:50:38:50:38 | access to local variable x | normal | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:50:9:50:13 | Label: | normal | +| LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | normal | +| LoopUnrolling.cs:50:16:50:36 | ...; | LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | normal | +| LoopUnrolling.cs:50:34:50:34 | access to local variable x | LoopUnrolling.cs:50:34:50:34 | access to local variable x | normal | | LoopUnrolling.cs:51:13:51:23 | goto ...; | LoopUnrolling.cs:51:13:51:23 | goto ...; | goto(Label) | | LoopUnrolling.cs:56:5:65:5 | {...} | LoopUnrolling.cs:58:9:64:9 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:57:9:57:47 | ... ...; | LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | normal | -| LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | normal | -| LoopUnrolling.cs:57:18:57:46 | 3 | LoopUnrolling.cs:57:18:57:46 | 3 | normal | -| LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | LoopUnrolling.cs:57:30:57:46 | { ..., ... } | normal | -| LoopUnrolling.cs:57:30:57:46 | { ..., ... } | LoopUnrolling.cs:57:30:57:46 | { ..., ... } | normal | -| LoopUnrolling.cs:57:32:57:34 | "a" | LoopUnrolling.cs:57:32:57:34 | "a" | normal | -| LoopUnrolling.cs:57:37:57:39 | "b" | LoopUnrolling.cs:57:37:57:39 | "b" | normal | -| LoopUnrolling.cs:57:42:57:44 | "c" | LoopUnrolling.cs:57:42:57:44 | "c" | normal | +| LoopUnrolling.cs:57:9:57:48 | ... ...; | LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | normal | +| LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | normal | +| LoopUnrolling.cs:57:18:57:47 | 3 | LoopUnrolling.cs:57:18:57:47 | 3 | normal | +| LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | LoopUnrolling.cs:57:31:57:47 | { ..., ... } | normal | +| LoopUnrolling.cs:57:31:57:47 | { ..., ... } | LoopUnrolling.cs:57:31:57:47 | { ..., ... } | normal | +| LoopUnrolling.cs:57:33:57:35 | "a" | LoopUnrolling.cs:57:33:57:35 | "a" | normal | +| LoopUnrolling.cs:57:38:57:40 | "b" | LoopUnrolling.cs:57:38:57:40 | "b" | normal | +| LoopUnrolling.cs:57:43:57:45 | "c" | LoopUnrolling.cs:57:43:57:45 | "c" | normal | | LoopUnrolling.cs:58:9:64:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:58:9:64:9 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:58:21:58:21 | String x | LoopUnrolling.cs:58:21:58:21 | String x | normal | -| LoopUnrolling.cs:58:26:58:27 | access to local variable xs | LoopUnrolling.cs:58:26:58:27 | access to local variable xs | normal | +| LoopUnrolling.cs:58:22:58:22 | String x | LoopUnrolling.cs:58:22:58:22 | String x | normal | +| LoopUnrolling.cs:58:27:58:28 | access to local variable xs | LoopUnrolling.cs:58:27:58:28 | access to local variable xs | normal | | LoopUnrolling.cs:59:9:64:9 | {...} | LoopUnrolling.cs:62:17:62:17 | access to parameter b | false | | LoopUnrolling.cs:59:9:64:9 | {...} | LoopUnrolling.cs:63:17:63:36 | call to method WriteLine | normal | | LoopUnrolling.cs:60:13:61:37 | if (...) ... | LoopUnrolling.cs:60:17:60:17 | access to parameter b | false | @@ -2037,11 +2587,133 @@ | LoopUnrolling.cs:71:9:71:20 | call to method Clear | LoopUnrolling.cs:71:9:71:20 | call to method Clear | normal | | LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:71:9:71:20 | call to method Clear | normal | | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | empty | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:72:21:72:23 | String arg | normal | -| LoopUnrolling.cs:72:28:72:31 | access to parameter args | LoopUnrolling.cs:72:28:72:31 | access to parameter args | normal | +| LoopUnrolling.cs:72:22:72:24 | String arg | LoopUnrolling.cs:72:22:72:24 | String arg | normal | +| LoopUnrolling.cs:72:29:72:32 | access to parameter args | LoopUnrolling.cs:72:29:72:32 | access to parameter args | normal | | LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | normal | | LoopUnrolling.cs:73:13:73:35 | ...; | LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | normal | | LoopUnrolling.cs:73:31:73:33 | access to local variable arg | LoopUnrolling.cs:73:31:73:33 | access to local variable arg | normal | +| LoopUnrolling.cs:77:5:83:5 | {...} | LoopUnrolling.cs:79:9:82:9 | foreach (... ... in ...) ... | empty | +| LoopUnrolling.cs:78:9:78:34 | ... ...; | LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | normal | +| LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | normal | +| LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | normal | +| LoopUnrolling.cs:78:29:78:29 | 2 | LoopUnrolling.cs:78:29:78:29 | 2 | normal | +| LoopUnrolling.cs:78:32:78:32 | 0 | LoopUnrolling.cs:78:32:78:32 | 0 | normal | +| LoopUnrolling.cs:79:9:82:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:79:9:82:9 | foreach (... ... in ...) ... | empty | +| LoopUnrolling.cs:79:22:79:22 | String x | LoopUnrolling.cs:79:22:79:22 | String x | normal | +| LoopUnrolling.cs:79:27:79:28 | access to local variable xs | LoopUnrolling.cs:79:27:79:28 | access to local variable xs | normal | +| LoopUnrolling.cs:80:9:82:9 | {...} | LoopUnrolling.cs:81:13:81:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:81:13:81:32 | call to method WriteLine | LoopUnrolling.cs:81:13:81:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:81:13:81:33 | ...; | LoopUnrolling.cs:81:13:81:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:81:31:81:31 | access to local variable x | LoopUnrolling.cs:81:31:81:31 | access to local variable x | normal | +| LoopUnrolling.cs:86:5:92:5 | {...} | LoopUnrolling.cs:88:9:91:9 | foreach (... ... in ...) ... | empty | +| LoopUnrolling.cs:87:9:87:34 | ... ...; | LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | normal | +| LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | normal | +| LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | normal | +| LoopUnrolling.cs:87:29:87:29 | 0 | LoopUnrolling.cs:87:29:87:29 | 0 | normal | +| LoopUnrolling.cs:87:32:87:32 | 2 | LoopUnrolling.cs:87:32:87:32 | 2 | normal | +| LoopUnrolling.cs:88:9:91:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:88:9:91:9 | foreach (... ... in ...) ... | empty | +| LoopUnrolling.cs:88:22:88:22 | String x | LoopUnrolling.cs:88:22:88:22 | String x | normal | +| LoopUnrolling.cs:88:27:88:28 | access to local variable xs | LoopUnrolling.cs:88:27:88:28 | access to local variable xs | normal | +| LoopUnrolling.cs:89:9:91:9 | {...} | LoopUnrolling.cs:90:13:90:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:90:13:90:32 | call to method WriteLine | LoopUnrolling.cs:90:13:90:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:90:13:90:33 | ...; | LoopUnrolling.cs:90:13:90:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:90:31:90:31 | access to local variable x | LoopUnrolling.cs:90:31:90:31 | access to local variable x | normal | +| LoopUnrolling.cs:95:5:101:5 | {...} | LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | empty | +| LoopUnrolling.cs:96:9:96:34 | ... ...; | LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | normal | +| LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | normal | +| LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | normal | +| LoopUnrolling.cs:96:29:96:29 | 2 | LoopUnrolling.cs:96:29:96:29 | 2 | normal | +| LoopUnrolling.cs:96:32:96:32 | 2 | LoopUnrolling.cs:96:32:96:32 | 2 | normal | +| LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | empty | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:97:22:97:22 | String x | normal | +| LoopUnrolling.cs:97:27:97:28 | access to local variable xs | LoopUnrolling.cs:97:27:97:28 | access to local variable xs | normal | +| LoopUnrolling.cs:98:9:100:9 | {...} | LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:99:13:99:33 | ...; | LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | normal | +| LoopUnrolling.cs:99:31:99:31 | access to local variable x | LoopUnrolling.cs:99:31:99:31 | access to local variable x | normal | +| MultiImplementationA.cs:6:22:6:31 | throw ... | MultiImplementationA.cs:6:22:6:31 | throw ... | throw(NullReferenceException) | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:28:6:31 | null | normal | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:27:7:37 | throw ...; | throw(NullReferenceException) | +| MultiImplementationA.cs:7:27:7:37 | throw ...; | MultiImplementationA.cs:7:27:7:37 | throw ...; | throw(NullReferenceException) | +| MultiImplementationA.cs:7:33:7:36 | null | MultiImplementationA.cs:7:33:7:36 | null | normal | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:47:7:57 | throw ...; | throw(NullReferenceException) | +| MultiImplementationA.cs:7:47:7:57 | throw ...; | MultiImplementationA.cs:7:47:7:57 | throw ...; | throw(NullReferenceException) | +| MultiImplementationA.cs:7:53:7:56 | null | MultiImplementationA.cs:7:53:7:56 | null | normal | +| MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationA.cs:8:23:8:32 | throw ... | throw(NullReferenceException) | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:29:8:32 | null | normal | +| MultiImplementationA.cs:13:16:13:16 | access to field F | MultiImplementationA.cs:13:16:13:16 | this access | normal | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:16 | this access | normal | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:13:16:13:20 | ... = ... | normal | +| MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:20:13:20 | 0 | normal | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | normal | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:42:15:50 | return ...; | return | +| MultiImplementationA.cs:15:42:15:50 | return ...; | MultiImplementationA.cs:15:42:15:50 | return ...; | return | +| MultiImplementationA.cs:15:49:15:49 | access to parameter s | MultiImplementationA.cs:15:49:15:49 | access to parameter s | normal | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationA.cs:15:58:15:60 | {...} | normal | +| MultiImplementationA.cs:16:28:16:28 | 0 | MultiImplementationA.cs:16:28:16:28 | 0 | normal | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:18:9:18:22 | M2(...) | normal | +| MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationA.cs:18:9:18:22 | M2(...) | normal | +| MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:21:18:21 | 0 | normal | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:24:20:28 | ... = ... | normal | +| MultiImplementationA.cs:20:24:20:24 | access to field F | MultiImplementationA.cs:20:24:20:24 | this access | normal | +| MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:24:20:24 | this access | normal | +| MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationA.cs:20:24:20:28 | ... = ... | normal | +| MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationA.cs:20:24:20:28 | ... = ... | normal | +| MultiImplementationA.cs:20:28:20:28 | access to parameter i | MultiImplementationA.cs:20:28:20:28 | access to parameter i | normal | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | normal | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:24:21:24 | 0 | normal | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | normal | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:11:22:13 | {...} | normal | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | normal | +| MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:16:24:16 | this access | normal | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:16:24:16 | this access | normal | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:24:32:24:34 | ... = ... | normal | +| MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:34:24:34 | 0 | normal | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationA.cs:30:28:30:37 | throw ... | throw(NullReferenceException) | +| MultiImplementationA.cs:30:34:30:37 | null | MultiImplementationA.cs:30:34:30:37 | null | normal | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:16:36:26 | throw ...; | throw(NullReferenceException) | +| MultiImplementationA.cs:36:16:36:26 | throw ...; | MultiImplementationA.cs:36:16:36:26 | throw ...; | throw(NullReferenceException) | +| MultiImplementationA.cs:36:22:36:25 | null | MultiImplementationA.cs:36:22:36:25 | null | normal | +| MultiImplementationA.cs:37:14:37:28 | {...} | MultiImplementationA.cs:37:16:37:26 | throw ...; | throw(NullReferenceException) | +| MultiImplementationA.cs:37:16:37:26 | throw ...; | MultiImplementationA.cs:37:16:37:26 | throw ...; | throw(NullReferenceException) | +| MultiImplementationA.cs:37:22:37:25 | null | MultiImplementationA.cs:37:22:37:25 | null | normal | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationB.cs:3:22:3:22 | 0 | normal | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:27:4:35 | return ...; | return | +| MultiImplementationB.cs:4:27:4:35 | return ...; | MultiImplementationB.cs:4:27:4:35 | return ...; | return | +| MultiImplementationB.cs:4:34:4:34 | 1 | MultiImplementationB.cs:4:34:4:34 | 1 | normal | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationB.cs:4:43:4:45 | {...} | normal | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | normal | +| MultiImplementationB.cs:11:16:11:16 | access to field F | MultiImplementationB.cs:11:16:11:16 | this access | normal | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:16 | this access | normal | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:11:16:11:20 | ... = ... | normal | +| MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:20:11:20 | 1 | normal | +| MultiImplementationB.cs:12:31:12:40 | throw ... | MultiImplementationB.cs:12:31:12:40 | throw ... | throw(NullReferenceException) | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:37:12:40 | null | normal | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:42:13:52 | throw ...; | throw(NullReferenceException) | +| MultiImplementationB.cs:13:42:13:52 | throw ...; | MultiImplementationB.cs:13:42:13:52 | throw ...; | throw(NullReferenceException) | +| MultiImplementationB.cs:13:48:13:51 | null | MultiImplementationB.cs:13:48:13:51 | null | normal | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationB.cs:13:60:13:62 | {...} | normal | +| MultiImplementationB.cs:14:28:14:28 | 1 | MultiImplementationB.cs:14:28:14:28 | 1 | normal | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:16:9:16:31 | M2(...) | normal | +| MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationB.cs:16:9:16:31 | M2(...) | normal | +| MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:21:16:30 | throw ... | throw(NullReferenceException) | +| MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:27:16:30 | null | normal | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:24:18:34 | throw ...; | throw(NullReferenceException) | +| MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:24:18:34 | throw ...; | throw(NullReferenceException) | +| MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:30:18:33 | null | normal | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | normal | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:24:19:24 | 1 | normal | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | normal | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:13:20:23 | throw ...; | throw(NullReferenceException) | +| MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationB.cs:20:13:20:23 | throw ...; | throw(NullReferenceException) | +| MultiImplementationB.cs:20:19:20:22 | null | MultiImplementationB.cs:20:19:20:22 | null | normal | +| MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationB.cs:21:50:21:59 | throw ... | throw(NullReferenceException) | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:56:21:59 | null | normal | +| MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:16:22:16 | this access | normal | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:16:22:16 | this access | normal | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:22:32:22:34 | ... = ... | normal | +| MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:34:22:34 | 1 | normal | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationB.cs:32:17:32:17 | 0 | normal | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:23:3:23 | access to parameter i | non-null | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:23:3:23 | access to parameter i | null | | NullCoalescing.cs:3:23:3:28 | ... ?? ... | NullCoalescing.cs:3:23:3:23 | access to parameter i | non-null | @@ -2351,11 +3023,11 @@ | Switch.cs:26:17:26:23 | return ...; | Switch.cs:26:17:26:23 | return ...; | return | | Switch.cs:27:13:27:39 | case ...: | Switch.cs:27:18:27:25 | Double d | no-match | | Switch.cs:27:13:27:39 | case ...: | Switch.cs:27:32:27:38 | call to method Throw | throw(Exception) | -| Switch.cs:27:13:27:39 | case ...: | Switch.cs:28:17:28:21 | Label: | normal | +| Switch.cs:27:13:27:39 | case ...: | Switch.cs:28:13:28:17 | Label: | normal | | Switch.cs:27:18:27:25 | Double d | Switch.cs:27:18:27:25 | Double d | match | | Switch.cs:27:18:27:25 | Double d | Switch.cs:27:18:27:25 | Double d | no-match | | Switch.cs:27:32:27:38 | call to method Throw | Switch.cs:27:32:27:38 | call to method Throw | throw(Exception) | -| Switch.cs:28:17:28:21 | Label: | Switch.cs:28:17:28:21 | Label: | normal | +| Switch.cs:28:13:28:17 | Label: | Switch.cs:28:13:28:17 | Label: | normal | | Switch.cs:29:17:29:23 | return ...; | Switch.cs:29:17:29:23 | return ...; | return | | Switch.cs:30:13:30:20 | default: | Switch.cs:31:17:31:27 | goto ...; | goto(Label) | | Switch.cs:31:17:31:27 | goto ...; | Switch.cs:31:17:31:27 | goto ...; | goto(Label) | @@ -2390,138 +3062,138 @@ | Switch.cs:50:30:50:38 | ... != ... | Switch.cs:50:30:50:38 | ... != ... | true | | Switch.cs:50:35:50:38 | null | Switch.cs:50:35:50:38 | null | normal | | Switch.cs:51:17:51:22 | break; | Switch.cs:51:17:51:22 | break; | break | -| Switch.cs:56:5:64:5 | {...} | Switch.cs:60:15:60:20 | break; | normal (break) | -| Switch.cs:56:5:64:5 | {...} | Switch.cs:62:15:62:20 | break; | normal (break) | -| Switch.cs:57:9:63:9 | switch (...) {...} | Switch.cs:60:15:60:20 | break; | normal (break) | -| Switch.cs:57:9:63:9 | switch (...) {...} | Switch.cs:62:15:62:20 | break; | normal (break) | +| Switch.cs:56:5:64:5 | {...} | Switch.cs:60:17:60:22 | break; | normal (break) | +| Switch.cs:56:5:64:5 | {...} | Switch.cs:62:17:62:22 | break; | normal (break) | +| Switch.cs:57:9:63:9 | switch (...) {...} | Switch.cs:60:17:60:22 | break; | normal (break) | +| Switch.cs:57:9:63:9 | switch (...) {...} | Switch.cs:62:17:62:22 | break; | normal (break) | | Switch.cs:57:17:57:17 | 1 | Switch.cs:57:17:57:17 | 1 | normal | | Switch.cs:57:17:57:21 | ... + ... | Switch.cs:57:17:57:21 | ... + ... | normal | | Switch.cs:57:21:57:21 | 2 | Switch.cs:57:21:57:21 | 2 | normal | -| Switch.cs:59:13:59:20 | case ...: | Switch.cs:59:18:59:18 | 2 | no-match | -| Switch.cs:59:13:59:20 | case ...: | Switch.cs:60:15:60:20 | break; | break | +| Switch.cs:59:13:59:19 | case ...: | Switch.cs:59:18:59:18 | 2 | no-match | +| Switch.cs:59:13:59:19 | case ...: | Switch.cs:60:17:60:22 | break; | break | | Switch.cs:59:18:59:18 | 2 | Switch.cs:59:18:59:18 | 2 | no-match | -| Switch.cs:60:15:60:20 | break; | Switch.cs:60:15:60:20 | break; | break | -| Switch.cs:61:13:61:20 | case ...: | Switch.cs:62:15:62:20 | break; | break | +| Switch.cs:60:17:60:22 | break; | Switch.cs:60:17:60:22 | break; | break | +| Switch.cs:61:13:61:19 | case ...: | Switch.cs:62:17:62:22 | break; | break | | Switch.cs:61:18:61:18 | 3 | Switch.cs:61:18:61:18 | 3 | match | -| Switch.cs:62:15:62:20 | break; | Switch.cs:62:15:62:20 | break; | break | -| Switch.cs:67:5:75:5 | {...} | Switch.cs:71:15:71:20 | break; | normal (break) | +| Switch.cs:62:17:62:22 | break; | Switch.cs:62:17:62:22 | break; | break | +| Switch.cs:67:5:75:5 | {...} | Switch.cs:71:17:71:22 | break; | normal (break) | | Switch.cs:67:5:75:5 | {...} | Switch.cs:72:18:72:19 | "" | no-match | -| Switch.cs:67:5:75:5 | {...} | Switch.cs:73:15:73:20 | break; | normal (break) | -| Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:71:15:71:20 | break; | normal (break) | +| Switch.cs:67:5:75:5 | {...} | Switch.cs:73:17:73:22 | break; | normal (break) | +| Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:71:17:71:22 | break; | normal (break) | | Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:72:18:72:19 | "" | no-match | -| Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:73:15:73:20 | break; | normal (break) | +| Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:73:17:73:22 | break; | normal (break) | | Switch.cs:68:17:68:25 | (...) ... | Switch.cs:68:17:68:25 | (...) ... | normal | | Switch.cs:68:25:68:25 | access to parameter s | Switch.cs:68:25:68:25 | access to parameter s | normal | -| Switch.cs:70:13:70:24 | case ...: | Switch.cs:70:18:70:20 | access to type Int32 | no-match | -| Switch.cs:70:13:70:24 | case ...: | Switch.cs:71:15:71:20 | break; | break | +| Switch.cs:70:13:70:23 | case ...: | Switch.cs:70:18:70:20 | access to type Int32 | no-match | +| Switch.cs:70:13:70:23 | case ...: | Switch.cs:71:17:71:22 | break; | break | | Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:70:18:70:20 | access to type Int32 | no-match | -| Switch.cs:71:15:71:20 | break; | Switch.cs:71:15:71:20 | break; | break | -| Switch.cs:72:13:72:21 | case ...: | Switch.cs:72:18:72:19 | "" | no-match | -| Switch.cs:72:13:72:21 | case ...: | Switch.cs:73:15:73:20 | break; | break | +| Switch.cs:71:17:71:22 | break; | Switch.cs:71:17:71:22 | break; | break | +| Switch.cs:72:13:72:20 | case ...: | Switch.cs:72:18:72:19 | "" | no-match | +| Switch.cs:72:13:72:20 | case ...: | Switch.cs:73:17:73:22 | break; | break | | Switch.cs:72:18:72:19 | "" | Switch.cs:72:18:72:19 | "" | match | | Switch.cs:72:18:72:19 | "" | Switch.cs:72:18:72:19 | "" | no-match | -| Switch.cs:73:15:73:20 | break; | Switch.cs:73:15:73:20 | break; | break | -| Switch.cs:78:5:89:5 | {...} | Switch.cs:82:15:82:26 | return ...; | return | -| Switch.cs:78:5:89:5 | {...} | Switch.cs:86:15:86:26 | return ...; | return | +| Switch.cs:73:17:73:22 | break; | Switch.cs:73:17:73:22 | break; | break | +| Switch.cs:78:5:89:5 | {...} | Switch.cs:82:17:82:28 | return ...; | return | +| Switch.cs:78:5:89:5 | {...} | Switch.cs:86:17:86:28 | return ...; | return | | Switch.cs:78:5:89:5 | {...} | Switch.cs:88:9:88:21 | return ...; | return | -| Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:82:15:82:26 | return ...; | return | +| Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:82:17:82:28 | return ...; | return | | Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:83:18:83:18 | 2 | no-match | -| Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:85:17:85:22 | break; | normal (break) | -| Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:86:15:86:26 | return ...; | return | +| Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:85:21:85:26 | break; | normal (break) | +| Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:86:17:86:28 | return ...; | return | | Switch.cs:79:17:79:17 | access to parameter i | Switch.cs:79:17:79:17 | access to parameter i | normal | -| Switch.cs:81:13:81:20 | case ...: | Switch.cs:81:18:81:18 | 1 | no-match | -| Switch.cs:81:13:81:20 | case ...: | Switch.cs:82:15:82:26 | return ...; | return | +| Switch.cs:81:13:81:19 | case ...: | Switch.cs:81:18:81:18 | 1 | no-match | +| Switch.cs:81:13:81:19 | case ...: | Switch.cs:82:17:82:28 | return ...; | return | | Switch.cs:81:18:81:18 | 1 | Switch.cs:81:18:81:18 | 1 | match | | Switch.cs:81:18:81:18 | 1 | Switch.cs:81:18:81:18 | 1 | no-match | -| Switch.cs:82:15:82:26 | return ...; | Switch.cs:82:15:82:26 | return ...; | return | -| Switch.cs:82:22:82:25 | true | Switch.cs:82:22:82:25 | true | normal | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:83:18:83:18 | 2 | no-match | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:84:19:84:23 | ... > ... | false | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:85:17:85:22 | break; | break | +| Switch.cs:82:17:82:28 | return ...; | Switch.cs:82:17:82:28 | return ...; | return | +| Switch.cs:82:24:82:27 | true | Switch.cs:82:24:82:27 | true | normal | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:83:18:83:18 | 2 | no-match | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:84:21:84:25 | ... > ... | false | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:85:21:85:26 | break; | break | | Switch.cs:83:18:83:18 | 2 | Switch.cs:83:18:83:18 | 2 | match | | Switch.cs:83:18:83:18 | 2 | Switch.cs:83:18:83:18 | 2 | no-match | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:84:19:84:23 | ... > ... | false | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:85:17:85:22 | break; | break | -| Switch.cs:84:19:84:19 | access to parameter j | Switch.cs:84:19:84:19 | access to parameter j | normal | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:84:19:84:23 | ... > ... | false | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:84:19:84:23 | ... > ... | true | -| Switch.cs:84:23:84:23 | 2 | Switch.cs:84:23:84:23 | 2 | normal | -| Switch.cs:85:17:85:22 | break; | Switch.cs:85:17:85:22 | break; | break | -| Switch.cs:86:15:86:26 | return ...; | Switch.cs:86:15:86:26 | return ...; | return | -| Switch.cs:86:22:86:25 | true | Switch.cs:86:22:86:25 | true | normal | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:84:21:84:25 | ... > ... | false | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:85:21:85:26 | break; | break | +| Switch.cs:84:21:84:21 | access to parameter j | Switch.cs:84:21:84:21 | access to parameter j | normal | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:84:21:84:25 | ... > ... | false | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:84:21:84:25 | ... > ... | true | +| Switch.cs:84:25:84:25 | 2 | Switch.cs:84:25:84:25 | 2 | normal | +| Switch.cs:85:21:85:26 | break; | Switch.cs:85:21:85:26 | break; | break | +| Switch.cs:86:17:86:28 | return ...; | Switch.cs:86:17:86:28 | return ...; | return | +| Switch.cs:86:24:86:27 | true | Switch.cs:86:24:86:27 | true | normal | | Switch.cs:88:9:88:21 | return ...; | Switch.cs:88:9:88:21 | return ...; | return | | Switch.cs:88:16:88:20 | false | Switch.cs:88:16:88:20 | false | normal | -| Switch.cs:92:5:99:5 | {...} | Switch.cs:96:15:96:26 | return ...; | return | +| Switch.cs:92:5:99:5 | {...} | Switch.cs:96:17:96:28 | return ...; | return | | Switch.cs:92:5:99:5 | {...} | Switch.cs:98:9:98:21 | return ...; | return | | Switch.cs:93:9:97:9 | switch (...) {...} | Switch.cs:95:18:95:20 | access to type Int32 | no-match | -| Switch.cs:93:9:97:9 | switch (...) {...} | Switch.cs:96:15:96:26 | return ...; | return | +| Switch.cs:93:9:97:9 | switch (...) {...} | Switch.cs:96:17:96:28 | return ...; | return | | Switch.cs:93:17:93:17 | access to parameter o | Switch.cs:93:17:93:17 | access to parameter o | normal | -| Switch.cs:95:13:95:24 | case ...: | Switch.cs:95:18:95:20 | access to type Int32 | no-match | -| Switch.cs:95:13:95:24 | case ...: | Switch.cs:96:15:96:26 | return ...; | return | +| Switch.cs:95:13:95:23 | case ...: | Switch.cs:95:18:95:20 | access to type Int32 | no-match | +| Switch.cs:95:13:95:23 | case ...: | Switch.cs:96:17:96:28 | return ...; | return | | Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:95:18:95:20 | access to type Int32 | match | | Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:95:18:95:20 | access to type Int32 | no-match | -| Switch.cs:96:15:96:26 | return ...; | Switch.cs:96:15:96:26 | return ...; | return | -| Switch.cs:96:22:96:25 | true | Switch.cs:96:22:96:25 | true | normal | +| Switch.cs:96:17:96:28 | return ...; | Switch.cs:96:17:96:28 | return ...; | return | +| Switch.cs:96:24:96:27 | true | Switch.cs:96:24:96:27 | true | normal | | Switch.cs:98:9:98:21 | return ...; | Switch.cs:98:9:98:21 | return ...; | return | | Switch.cs:98:16:98:20 | false | Switch.cs:98:16:98:20 | false | normal | -| Switch.cs:102:5:109:5 | {...} | Switch.cs:105:22:105:30 | return ...; | return | -| Switch.cs:102:5:109:5 | {...} | Switch.cs:106:22:106:30 | return ...; | return | +| Switch.cs:102:5:109:5 | {...} | Switch.cs:105:21:105:29 | return ...; | return | +| Switch.cs:102:5:109:5 | {...} | Switch.cs:106:21:106:29 | return ...; | return | | Switch.cs:102:5:109:5 | {...} | Switch.cs:108:9:108:18 | return ...; | return | -| Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:105:22:105:30 | return ...; | return | +| Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:105:21:105:29 | return ...; | return | | Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:106:18:106:18 | 1 | no-match | -| Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:106:22:106:30 | return ...; | return | +| Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:106:21:106:29 | return ...; | return | | Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:103:17:103:17 | access to parameter s | non-null | | Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:103:17:103:17 | access to parameter s | null | | Switch.cs:103:19:103:25 | access to property Length | Switch.cs:103:17:103:17 | access to parameter s | null | | Switch.cs:103:19:103:25 | access to property Length | Switch.cs:103:19:103:25 | access to property Length | normal | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:18:105:18 | 0 | no-match | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:22:105:30 | return ...; | return | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:18:105:18 | 0 | no-match | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:21:105:29 | return ...; | return | | Switch.cs:105:18:105:18 | 0 | Switch.cs:105:18:105:18 | 0 | match | | Switch.cs:105:18:105:18 | 0 | Switch.cs:105:18:105:18 | 0 | no-match | -| Switch.cs:105:22:105:30 | return ...; | Switch.cs:105:22:105:30 | return ...; | return | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:105:29:105:29 | 0 | normal | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:18:106:18 | 1 | no-match | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:22:106:30 | return ...; | return | +| Switch.cs:105:21:105:29 | return ...; | Switch.cs:105:21:105:29 | return ...; | return | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:105:28:105:28 | 0 | normal | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:18:106:18 | 1 | no-match | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:21:106:29 | return ...; | return | | Switch.cs:106:18:106:18 | 1 | Switch.cs:106:18:106:18 | 1 | match | | Switch.cs:106:18:106:18 | 1 | Switch.cs:106:18:106:18 | 1 | no-match | -| Switch.cs:106:22:106:30 | return ...; | Switch.cs:106:22:106:30 | return ...; | return | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:106:29:106:29 | 1 | normal | +| Switch.cs:106:21:106:29 | return ...; | Switch.cs:106:21:106:29 | return ...; | return | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:106:28:106:28 | 1 | normal | | Switch.cs:108:9:108:18 | return ...; | Switch.cs:108:9:108:18 | return ...; | return | | Switch.cs:108:16:108:17 | -... | Switch.cs:108:16:108:17 | -... | normal | | Switch.cs:108:17:108:17 | 1 | Switch.cs:108:17:108:17 | 1 | normal | | Switch.cs:111:28:111:48 | throw ... | Switch.cs:111:28:111:48 | throw ... | throw(Exception) | | Switch.cs:111:34:111:48 | object creation of type Exception | Switch.cs:111:34:111:48 | object creation of type Exception | normal | -| Switch.cs:114:5:121:5 | {...} | Switch.cs:117:36:117:44 | return ...; | return | -| Switch.cs:114:5:121:5 | {...} | Switch.cs:118:35:118:43 | return ...; | return | +| Switch.cs:114:5:121:5 | {...} | Switch.cs:117:37:117:45 | return ...; | return | +| Switch.cs:114:5:121:5 | {...} | Switch.cs:118:36:118:44 | return ...; | return | | Switch.cs:114:5:121:5 | {...} | Switch.cs:120:9:120:18 | return ...; | return | -| Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:117:36:117:44 | return ...; | return | +| Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:117:37:117:45 | return ...; | return | | Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:118:18:118:18 | 2 | no-match | -| Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:118:25:118:31 | ... == ... | false | -| Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:118:35:118:43 | return ...; | return | +| Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:118:25:118:33 | ... == ... | false | +| Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:118:36:118:44 | return ...; | return | | Switch.cs:115:17:115:17 | access to parameter s | Switch.cs:115:17:115:17 | access to parameter s | normal | | Switch.cs:115:17:115:24 | access to property Length | Switch.cs:115:17:115:24 | access to property Length | normal | -| Switch.cs:117:13:117:34 | case ...: | Switch.cs:117:18:117:18 | 3 | no-match | -| Switch.cs:117:13:117:34 | case ...: | Switch.cs:117:25:117:32 | ... == ... | false | -| Switch.cs:117:13:117:34 | case ...: | Switch.cs:117:36:117:44 | return ...; | return | +| Switch.cs:117:13:117:35 | case ...: | Switch.cs:117:18:117:18 | 3 | no-match | +| Switch.cs:117:13:117:35 | case ...: | Switch.cs:117:25:117:34 | ... == ... | false | +| Switch.cs:117:13:117:35 | case ...: | Switch.cs:117:37:117:45 | return ...; | return | | Switch.cs:117:18:117:18 | 3 | Switch.cs:117:18:117:18 | 3 | match | | Switch.cs:117:18:117:18 | 3 | Switch.cs:117:18:117:18 | 3 | no-match | | Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:25:117:25 | access to parameter s | normal | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:117:25:117:32 | ... == ... | false | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:117:25:117:32 | ... == ... | true | -| Switch.cs:117:28:117:32 | "foo" | Switch.cs:117:28:117:32 | "foo" | normal | -| Switch.cs:117:36:117:44 | return ...; | Switch.cs:117:36:117:44 | return ...; | return | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:117:43:117:43 | 1 | normal | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:18:118:18 | 2 | no-match | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:25:118:31 | ... == ... | false | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:35:118:43 | return ...; | return | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:117:25:117:34 | ... == ... | false | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:117:25:117:34 | ... == ... | true | +| Switch.cs:117:30:117:34 | "foo" | Switch.cs:117:30:117:34 | "foo" | normal | +| Switch.cs:117:37:117:45 | return ...; | Switch.cs:117:37:117:45 | return ...; | return | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:117:44:117:44 | 1 | normal | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:18:118:18 | 2 | no-match | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:25:118:33 | ... == ... | false | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:36:118:44 | return ...; | return | | Switch.cs:118:18:118:18 | 2 | Switch.cs:118:18:118:18 | 2 | match | | Switch.cs:118:18:118:18 | 2 | Switch.cs:118:18:118:18 | 2 | no-match | | Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:25:118:25 | access to parameter s | normal | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:118:25:118:31 | ... == ... | false | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:118:25:118:31 | ... == ... | true | -| Switch.cs:118:28:118:31 | "fu" | Switch.cs:118:28:118:31 | "fu" | normal | -| Switch.cs:118:35:118:43 | return ...; | Switch.cs:118:35:118:43 | return ...; | return | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:118:42:118:42 | 2 | normal | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:118:25:118:33 | ... == ... | false | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:118:25:118:33 | ... == ... | true | +| Switch.cs:118:30:118:33 | "fu" | Switch.cs:118:30:118:33 | "fu" | normal | +| Switch.cs:118:36:118:44 | return ...; | Switch.cs:118:36:118:44 | return ...; | return | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:118:43:118:43 | 2 | normal | | Switch.cs:120:9:120:18 | return ...; | Switch.cs:120:9:120:18 | return ...; | return | | Switch.cs:120:16:120:17 | -... | Switch.cs:120:16:120:17 | -... | normal | | Switch.cs:120:17:120:17 | 1 | Switch.cs:120:17:120:17 | 1 | normal | @@ -2611,6 +3283,41 @@ | Switch.cs:150:18:150:18 | 2 | Switch.cs:150:18:150:18 | 2 | no-match | | Switch.cs:150:21:150:29 | return ...; | Switch.cs:150:21:150:29 | return ...; | return | | Switch.cs:150:28:150:28 | 2 | Switch.cs:150:28:150:28 | 2 | normal | +| Switch.cs:155:5:161:5 | {...} | Switch.cs:156:41:156:45 | false | throw(InvalidOperationException) [no-match] | +| Switch.cs:155:5:161:5 | {...} | Switch.cs:158:13:158:48 | call to method WriteLine | normal | +| Switch.cs:155:5:161:5 | {...} | Switch.cs:160:13:160:48 | call to method WriteLine | normal | +| Switch.cs:156:9:156:55 | ... ...; | Switch.cs:156:13:156:54 | String s = ... | normal | +| Switch.cs:156:9:156:55 | ... ...; | Switch.cs:156:41:156:45 | false | throw(InvalidOperationException) [no-match] | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:13:156:54 | String s = ... | normal | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:156:41:156:45 | false | throw(InvalidOperationException) [no-match] | +| Switch.cs:156:17:156:17 | access to parameter b | Switch.cs:156:17:156:17 | access to parameter b | normal | +| Switch.cs:156:17:156:54 | ... switch { ... } | Switch.cs:156:36:156:38 | "a" | normal | +| Switch.cs:156:17:156:54 | ... switch { ... } | Switch.cs:156:41:156:45 | false | throw(InvalidOperationException) [no-match] | +| Switch.cs:156:17:156:54 | ... switch { ... } | Switch.cs:156:50:156:52 | "b" | normal | +| Switch.cs:156:28:156:31 | true | Switch.cs:156:28:156:31 | true | match | +| Switch.cs:156:28:156:31 | true | Switch.cs:156:28:156:31 | true | no-match | +| Switch.cs:156:28:156:38 | ... => ... | Switch.cs:156:28:156:31 | true | no-match | +| Switch.cs:156:28:156:38 | ... => ... | Switch.cs:156:36:156:38 | "a" | normal | +| Switch.cs:156:36:156:38 | "a" | Switch.cs:156:36:156:38 | "a" | normal | +| Switch.cs:156:41:156:45 | false | Switch.cs:156:41:156:45 | false | match | +| Switch.cs:156:41:156:45 | false | Switch.cs:156:41:156:45 | false | no-match | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:41:156:45 | false | no-match | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:50:156:52 | "b" | normal | +| Switch.cs:156:50:156:52 | "b" | Switch.cs:156:50:156:52 | "b" | normal | +| Switch.cs:157:9:160:49 | if (...) ... | Switch.cs:158:13:158:48 | call to method WriteLine | normal | +| Switch.cs:157:9:160:49 | if (...) ... | Switch.cs:160:13:160:48 | call to method WriteLine | normal | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:157:13:157:13 | access to parameter b | false | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:157:13:157:13 | access to parameter b | true | +| Switch.cs:158:13:158:48 | call to method WriteLine | Switch.cs:158:13:158:48 | call to method WriteLine | normal | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:48 | call to method WriteLine | normal | +| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:38:158:47 | $"..." | normal | +| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:40:158:43 | "a = " | normal | +| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:45:158:45 | access to local variable s | normal | +| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:160:13:160:48 | call to method WriteLine | normal | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:48 | call to method WriteLine | normal | +| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:38:160:47 | $"..." | normal | +| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:40:160:43 | "b = " | normal | +| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:45:160:45 | access to local variable s | normal | | TypeAccesses.cs:4:5:9:5 | {...} | TypeAccesses.cs:8:13:8:27 | Type t = ... | normal | | TypeAccesses.cs:5:9:5:26 | ... ...; | TypeAccesses.cs:5:13:5:25 | String s = ... | normal | | TypeAccesses.cs:5:13:5:25 | String s = ... | TypeAccesses.cs:5:13:5:25 | String s = ... | normal | @@ -2634,10 +3341,12 @@ | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:9:13:9:29 | return ...; | return | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:22:7:36 | Char* c1 = ... | normal | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:27:7:33 | access to parameter strings | normal | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:27:7:36 | (...) ... | normal | | VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:27:7:36 | access to array element | normal | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:35:7:35 | 0 | normal | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:7:39:7:53 | Char* c2 = ... | normal | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:44:7:50 | access to parameter strings | normal | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:44:7:53 | (...) ... | normal | | VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:44:7:53 | access to array element | normal | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:52:7:52 | 1 | normal | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:9:13:9:29 | return ...; | return | diff --git a/csharp/ql/test/library-tests/controlflow/graph/LoopUnrolling.cs b/csharp/ql/test/library-tests/controlflow/graph/LoopUnrolling.cs index 5e38a524405f..0121251aa0a4 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/LoopUnrolling.cs +++ b/csharp/ql/test/library-tests/controlflow/graph/LoopUnrolling.cs @@ -8,14 +8,14 @@ void M1(string[] args) { if (args.Length == 0) return; - foreach(var arg in args) // unroll + foreach (var arg in args) // unroll Console.WriteLine(arg); } void M2() { - var xs = new string[]{ "a", "b", "c" }; - foreach(var x in xs) // unroll + var xs = new string[] { "a", "b", "c" }; + foreach (var x in xs) // unroll Console.WriteLine(x); } @@ -29,33 +29,33 @@ void M3(string args) void M4() { var xs = new string[0]; - foreach(var x in xs) // no unroll + foreach (var x in xs) // skip Console.WriteLine(x); } void M5() { - var xs = new string[]{ "a", "b", "c" }; - var ys = new string[]{ "0", "1", "2" }; - foreach(var x in xs) // unroll - foreach(var y in ys) // unroll + var xs = new string[] { "a", "b", "c" }; + var ys = new string[] { "0", "1", "2" }; + foreach (var x in xs) // unroll + foreach (var y in ys) // unroll Console.WriteLine(x + y); } void M6() { - var xs = new string[]{ "a", "b", "c" }; - foreach(var x in xs) // unroll + var xs = new string[] { "a", "b", "c" }; + foreach (var x in xs) // unroll { - Label: Console.WriteLine(x); + Label: Console.WriteLine(x); goto Label; } } void M7(bool b) { - var xs = new string[]{ "a", "b", "c" }; - foreach(var x in xs) // unroll + var xs = new string[] { "a", "b", "c" }; + foreach (var x in xs) // unroll { if (b) Console.WriteLine(x); @@ -69,7 +69,34 @@ void M8(List args) if (!args.Any()) return; args.Clear(); - foreach(var arg in args) // no unroll + foreach (var arg in args) // skip Console.WriteLine(arg); } + + void M9() + { + var xs = new string[2, 0]; + foreach (var x in xs) //skip + { + Console.WriteLine(x); + } + } + + void M10() + { + var xs = new string[0, 2]; + foreach (var x in xs) // skip + { + Console.WriteLine(x); + } + } + + void M11() + { + var xs = new string[2, 2]; + foreach (var x in xs) // unroll + { + Console.WriteLine(x); + } + } } diff --git a/csharp/ql/test/library-tests/controlflow/graph/MultiImplementationA.cs b/csharp/ql/test/library-tests/controlflow/graph/MultiImplementationA.cs new file mode 100644 index 000000000000..cf988d3723be --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/graph/MultiImplementationA.cs @@ -0,0 +1,38 @@ +// semmle-extractor-options: --separate-compilation + +// Stub implementation +class C1 +{ + public int P1 => throw null; + public int P2 { get { throw null; } set { throw null; } } + public int M() => throw null; +} + +class C2 +{ + public int F = 0; + public int this[int i] => i; + public string this[string s] { get { return s; } set { } } + public void M1(int i = 0) + { + int M2() => 0; + } + public C2(int i) { F = i; } + public C2() : this(0) { } + ~C2() { } + public static implicit operator C2(int i) => null; + public int P { get; set; } = 0; +} + +// Stub implementation +class C3 +{ + public int P3 { get => throw null; } +} + +// Stub implementation +partial class C4 +{ + int M1() { throw null; } + int M2() { throw null; } +} diff --git a/csharp/ql/test/library-tests/controlflow/graph/MultiImplementationB.cs b/csharp/ql/test/library-tests/controlflow/graph/MultiImplementationB.cs new file mode 100644 index 000000000000..1422c7d593fb --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/graph/MultiImplementationB.cs @@ -0,0 +1,33 @@ +class C1 +{ + public int P1 => 0; + public int P2 { get { return 1; } set { } } + public int M() => 2; +} + +// Stub implementation +class C2 +{ + public int F = 1; + public int this[int i] => throw null; + public string this[string s] { get { throw null; } set { } } + public void M1(int i = 1) + { + int M2() => throw null; + } + public C2(int i) { throw null; } + public C2() : this(1) { } + ~C2() { throw null; } + public static implicit operator C2(int i) => throw null; + public int P { get; set; } = 1; +} + +class C3 +{ + public int P3 { get; } +} + +partial class C4 +{ + int M1() => 0; +} diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index f790cd0e90c2..4a853b45fbf0 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -293,12 +293,15 @@ | ArrayCreation.cs:5:20:5:32 | array creation of type Int32[,] | ArrayCreation.cs:5:12:5:13 | exit M2 | semmle.label | successor | | ArrayCreation.cs:5:28:5:28 | 0 | ArrayCreation.cs:5:31:5:31 | 1 | semmle.label | successor | | ArrayCreation.cs:5:31:5:31 | 1 | ArrayCreation.cs:5:20:5:32 | array creation of type Int32[,] | semmle.label | successor | -| ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | semmle.label | successor | +| ArrayCreation.cs:7:11:7:12 | enter M3 | ArrayCreation.cs:7:19:7:36 | 2 | semmle.label | successor | +| ArrayCreation.cs:7:19:7:36 | 2 | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | semmle.label | successor | | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | ArrayCreation.cs:7:31:7:31 | 0 | semmle.label | successor | | ArrayCreation.cs:7:29:7:36 | { ..., ... } | ArrayCreation.cs:7:11:7:12 | exit M3 | semmle.label | successor | | ArrayCreation.cs:7:31:7:31 | 0 | ArrayCreation.cs:7:34:7:34 | 1 | semmle.label | successor | | ArrayCreation.cs:7:34:7:34 | 1 | ArrayCreation.cs:7:29:7:36 | { ..., ... } | semmle.label | successor | -| ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | semmle.label | successor | +| ArrayCreation.cs:9:12:9:13 | enter M4 | ArrayCreation.cs:9:20:9:52 | 2 | semmle.label | successor | +| ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:20:9:52 | 2 | semmle.label | successor | +| ArrayCreation.cs:9:20:9:52 | 2 | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | semmle.label | successor | | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | ArrayCreation.cs:9:35:9:35 | 0 | semmle.label | successor | | ArrayCreation.cs:9:31:9:52 | { ..., ... } | ArrayCreation.cs:9:12:9:13 | exit M4 | semmle.label | successor | | ArrayCreation.cs:9:33:9:40 | { ..., ... } | ArrayCreation.cs:9:45:9:45 | 2 | semmle.label | successor | @@ -307,6 +310,565 @@ | ArrayCreation.cs:9:43:9:50 | { ..., ... } | ArrayCreation.cs:9:31:9:52 | { ..., ... } | semmle.label | successor | | ArrayCreation.cs:9:45:9:45 | 2 | ArrayCreation.cs:9:48:9:48 | 3 | semmle.label | successor | | ArrayCreation.cs:9:48:9:48 | 3 | ArrayCreation.cs:9:43:9:50 | { ..., ... } | semmle.label | successor | +| Assert.cs:7:10:7:11 | enter M1 | Assert.cs:8:5:12:5 | {...} | semmle.label | successor | +| Assert.cs:8:5:12:5 | {...} | Assert.cs:9:9:9:33 | ... ...; | semmle.label | successor | +| Assert.cs:9:9:9:33 | ... ...; | Assert.cs:9:20:9:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:9:16:9:32 | String s = ... | Assert.cs:10:9:10:32 | ...; | semmle.label | successor | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:24:9:27 | null | semmle.label | true | +| Assert.cs:9:20:9:20 | access to parameter b | Assert.cs:9:31:9:32 | "" | semmle.label | false | +| Assert.cs:9:20:9:32 | ... ? ... : ... | Assert.cs:9:20:9:20 | access to parameter b | semmle.label | successor | +| Assert.cs:9:24:9:27 | null | Assert.cs:9:16:9:32 | String s = ... | semmle.label | successor | +| Assert.cs:9:31:9:32 | "" | Assert.cs:9:16:9:32 | String s = ... | semmle.label | successor | +| Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | Assert.cs:7:10:7:11 | exit M1 | semmle.label | exit | +| Assert.cs:10:9:10:31 | [assertion success] call to method Assert | Assert.cs:11:9:11:36 | ...; | semmle.label | successor | +| Assert.cs:10:9:10:32 | ...; | Assert.cs:10:22:10:22 | access to local variable s | semmle.label | successor | +| Assert.cs:10:22:10:22 | access to local variable s | Assert.cs:10:27:10:30 | null | semmle.label | successor | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:9:10:31 | [assertion failure] call to method Assert | semmle.label | false | +| Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:9:10:31 | [assertion success] call to method Assert | semmle.label | true | +| Assert.cs:10:27:10:30 | null | Assert.cs:10:22:10:30 | ... != ... | semmle.label | successor | +| Assert.cs:11:9:11:35 | call to method WriteLine | Assert.cs:7:10:7:11 | exit M1 | semmle.label | successor | +| Assert.cs:11:9:11:36 | ...; | Assert.cs:11:27:11:27 | access to local variable s | semmle.label | successor | +| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:11:27:11:34 | access to property Length | semmle.label | successor | +| Assert.cs:11:27:11:34 | access to property Length | Assert.cs:11:9:11:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:14:10:14:11 | enter M2 | Assert.cs:15:5:19:5 | {...} | semmle.label | successor | +| Assert.cs:15:5:19:5 | {...} | Assert.cs:16:9:16:33 | ... ...; | semmle.label | successor | +| Assert.cs:16:9:16:33 | ... ...; | Assert.cs:16:20:16:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:16:16:16:32 | String s = ... | Assert.cs:17:9:17:25 | ...; | semmle.label | successor | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:24:16:27 | null | semmle.label | true | +| Assert.cs:16:20:16:20 | access to parameter b | Assert.cs:16:31:16:32 | "" | semmle.label | false | +| Assert.cs:16:20:16:32 | ... ? ... : ... | Assert.cs:16:20:16:20 | access to parameter b | semmle.label | successor | +| Assert.cs:16:24:16:27 | null | Assert.cs:16:16:16:32 | String s = ... | semmle.label | successor | +| Assert.cs:16:31:16:32 | "" | Assert.cs:16:16:16:32 | String s = ... | semmle.label | successor | +| Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | Assert.cs:14:10:14:11 | exit M2 | semmle.label | exception(AssertFailedException) | +| Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | Assert.cs:18:9:18:36 | ...; | semmle.label | successor | +| Assert.cs:17:9:17:25 | ...; | Assert.cs:17:23:17:23 | access to local variable s | semmle.label | successor | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:9:17:24 | [assertion failure] call to method IsNull | semmle.label | non-null | +| Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:9:17:24 | [assertion success] call to method IsNull | semmle.label | null | +| Assert.cs:18:9:18:35 | call to method WriteLine | Assert.cs:14:10:14:11 | exit M2 | semmle.label | successor | +| Assert.cs:18:9:18:36 | ...; | Assert.cs:18:27:18:27 | access to local variable s | semmle.label | successor | +| Assert.cs:18:27:18:27 | access to local variable s | Assert.cs:18:27:18:34 | access to property Length | semmle.label | successor | +| Assert.cs:18:27:18:34 | access to property Length | Assert.cs:18:9:18:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:21:10:21:11 | enter M3 | Assert.cs:22:5:26:5 | {...} | semmle.label | successor | +| Assert.cs:22:5:26:5 | {...} | Assert.cs:23:9:23:33 | ... ...; | semmle.label | successor | +| Assert.cs:23:9:23:33 | ... ...; | Assert.cs:23:20:23:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:23:16:23:32 | String s = ... | Assert.cs:24:9:24:28 | ...; | semmle.label | successor | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:24:23:27 | null | semmle.label | true | +| Assert.cs:23:20:23:20 | access to parameter b | Assert.cs:23:31:23:32 | "" | semmle.label | false | +| Assert.cs:23:20:23:32 | ... ? ... : ... | Assert.cs:23:20:23:20 | access to parameter b | semmle.label | successor | +| Assert.cs:23:24:23:27 | null | Assert.cs:23:16:23:32 | String s = ... | semmle.label | successor | +| Assert.cs:23:31:23:32 | "" | Assert.cs:23:16:23:32 | String s = ... | semmle.label | successor | +| Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | Assert.cs:21:10:21:11 | exit M3 | semmle.label | exception(AssertFailedException) | +| Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | Assert.cs:25:9:25:36 | ...; | semmle.label | successor | +| Assert.cs:24:9:24:28 | ...; | Assert.cs:24:26:24:26 | access to local variable s | semmle.label | successor | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:9:24:27 | [assertion failure] call to method IsNotNull | semmle.label | null | +| Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:9:24:27 | [assertion success] call to method IsNotNull | semmle.label | non-null | +| Assert.cs:25:9:25:35 | call to method WriteLine | Assert.cs:21:10:21:11 | exit M3 | semmle.label | successor | +| Assert.cs:25:9:25:36 | ...; | Assert.cs:25:27:25:27 | access to local variable s | semmle.label | successor | +| Assert.cs:25:27:25:27 | access to local variable s | Assert.cs:25:27:25:34 | access to property Length | semmle.label | successor | +| Assert.cs:25:27:25:34 | access to property Length | Assert.cs:25:9:25:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:28:10:28:11 | enter M4 | Assert.cs:29:5:33:5 | {...} | semmle.label | successor | +| Assert.cs:29:5:33:5 | {...} | Assert.cs:30:9:30:33 | ... ...; | semmle.label | successor | +| Assert.cs:30:9:30:33 | ... ...; | Assert.cs:30:20:30:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:30:16:30:32 | String s = ... | Assert.cs:31:9:31:33 | ...; | semmle.label | successor | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:24:30:27 | null | semmle.label | true | +| Assert.cs:30:20:30:20 | access to parameter b | Assert.cs:30:31:30:32 | "" | semmle.label | false | +| Assert.cs:30:20:30:32 | ... ? ... : ... | Assert.cs:30:20:30:20 | access to parameter b | semmle.label | successor | +| Assert.cs:30:24:30:27 | null | Assert.cs:30:16:30:32 | String s = ... | semmle.label | successor | +| Assert.cs:30:31:30:32 | "" | Assert.cs:30:16:30:32 | String s = ... | semmle.label | successor | +| Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | Assert.cs:28:10:28:11 | exit M4 | semmle.label | exception(AssertFailedException) | +| Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | Assert.cs:32:9:32:36 | ...; | semmle.label | successor | +| Assert.cs:31:9:31:33 | ...; | Assert.cs:31:23:31:23 | access to local variable s | semmle.label | successor | +| Assert.cs:31:23:31:23 | access to local variable s | Assert.cs:31:28:31:31 | null | semmle.label | successor | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:9:31:32 | [assertion failure] call to method IsTrue | semmle.label | false | +| Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:9:31:32 | [assertion success] call to method IsTrue | semmle.label | true | +| Assert.cs:31:28:31:31 | null | Assert.cs:31:23:31:31 | ... == ... | semmle.label | successor | +| Assert.cs:32:9:32:35 | call to method WriteLine | Assert.cs:28:10:28:11 | exit M4 | semmle.label | successor | +| Assert.cs:32:9:32:36 | ...; | Assert.cs:32:27:32:27 | access to local variable s | semmle.label | successor | +| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:32:27:32:34 | access to property Length | semmle.label | successor | +| Assert.cs:32:27:32:34 | access to property Length | Assert.cs:32:9:32:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:35:10:35:11 | enter M5 | Assert.cs:36:5:40:5 | {...} | semmle.label | successor | +| Assert.cs:36:5:40:5 | {...} | Assert.cs:37:9:37:33 | ... ...; | semmle.label | successor | +| Assert.cs:37:9:37:33 | ... ...; | Assert.cs:37:20:37:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:37:16:37:32 | String s = ... | Assert.cs:38:9:38:33 | ...; | semmle.label | successor | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:24:37:27 | null | semmle.label | true | +| Assert.cs:37:20:37:20 | access to parameter b | Assert.cs:37:31:37:32 | "" | semmle.label | false | +| Assert.cs:37:20:37:32 | ... ? ... : ... | Assert.cs:37:20:37:20 | access to parameter b | semmle.label | successor | +| Assert.cs:37:24:37:27 | null | Assert.cs:37:16:37:32 | String s = ... | semmle.label | successor | +| Assert.cs:37:31:37:32 | "" | Assert.cs:37:16:37:32 | String s = ... | semmle.label | successor | +| Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | Assert.cs:35:10:35:11 | exit M5 | semmle.label | exception(AssertFailedException) | +| Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | Assert.cs:39:9:39:36 | ...; | semmle.label | successor | +| Assert.cs:38:9:38:33 | ...; | Assert.cs:38:23:38:23 | access to local variable s | semmle.label | successor | +| Assert.cs:38:23:38:23 | access to local variable s | Assert.cs:38:28:38:31 | null | semmle.label | successor | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:9:38:32 | [assertion failure] call to method IsTrue | semmle.label | false | +| Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:9:38:32 | [assertion success] call to method IsTrue | semmle.label | true | +| Assert.cs:38:28:38:31 | null | Assert.cs:38:23:38:31 | ... != ... | semmle.label | successor | +| Assert.cs:39:9:39:35 | call to method WriteLine | Assert.cs:35:10:35:11 | exit M5 | semmle.label | successor | +| Assert.cs:39:9:39:36 | ...; | Assert.cs:39:27:39:27 | access to local variable s | semmle.label | successor | +| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:39:27:39:34 | access to property Length | semmle.label | successor | +| Assert.cs:39:27:39:34 | access to property Length | Assert.cs:39:9:39:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:42:10:42:11 | enter M6 | Assert.cs:43:5:47:5 | {...} | semmle.label | successor | +| Assert.cs:43:5:47:5 | {...} | Assert.cs:44:9:44:33 | ... ...; | semmle.label | successor | +| Assert.cs:44:9:44:33 | ... ...; | Assert.cs:44:20:44:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:44:16:44:32 | String s = ... | Assert.cs:45:9:45:34 | ...; | semmle.label | successor | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:24:44:27 | null | semmle.label | true | +| Assert.cs:44:20:44:20 | access to parameter b | Assert.cs:44:31:44:32 | "" | semmle.label | false | +| Assert.cs:44:20:44:32 | ... ? ... : ... | Assert.cs:44:20:44:20 | access to parameter b | semmle.label | successor | +| Assert.cs:44:24:44:27 | null | Assert.cs:44:16:44:32 | String s = ... | semmle.label | successor | +| Assert.cs:44:31:44:32 | "" | Assert.cs:44:16:44:32 | String s = ... | semmle.label | successor | +| Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | Assert.cs:42:10:42:11 | exit M6 | semmle.label | exception(AssertFailedException) | +| Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | Assert.cs:46:9:46:36 | ...; | semmle.label | successor | +| Assert.cs:45:9:45:34 | ...; | Assert.cs:45:24:45:24 | access to local variable s | semmle.label | successor | +| Assert.cs:45:24:45:24 | access to local variable s | Assert.cs:45:29:45:32 | null | semmle.label | successor | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:9:45:33 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:9:45:33 | [assertion success] call to method IsFalse | semmle.label | false | +| Assert.cs:45:29:45:32 | null | Assert.cs:45:24:45:32 | ... != ... | semmle.label | successor | +| Assert.cs:46:9:46:35 | call to method WriteLine | Assert.cs:42:10:42:11 | exit M6 | semmle.label | successor | +| Assert.cs:46:9:46:36 | ...; | Assert.cs:46:27:46:27 | access to local variable s | semmle.label | successor | +| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:46:27:46:34 | access to property Length | semmle.label | successor | +| Assert.cs:46:27:46:34 | access to property Length | Assert.cs:46:9:46:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:49:10:49:11 | enter M7 | Assert.cs:50:5:54:5 | {...} | semmle.label | successor | +| Assert.cs:50:5:54:5 | {...} | Assert.cs:51:9:51:33 | ... ...; | semmle.label | successor | +| Assert.cs:51:9:51:33 | ... ...; | Assert.cs:51:20:51:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:51:16:51:32 | String s = ... | Assert.cs:52:9:52:34 | ...; | semmle.label | successor | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:24:51:27 | null | semmle.label | true | +| Assert.cs:51:20:51:20 | access to parameter b | Assert.cs:51:31:51:32 | "" | semmle.label | false | +| Assert.cs:51:20:51:32 | ... ? ... : ... | Assert.cs:51:20:51:20 | access to parameter b | semmle.label | successor | +| Assert.cs:51:24:51:27 | null | Assert.cs:51:16:51:32 | String s = ... | semmle.label | successor | +| Assert.cs:51:31:51:32 | "" | Assert.cs:51:16:51:32 | String s = ... | semmle.label | successor | +| Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | Assert.cs:49:10:49:11 | exit M7 | semmle.label | exception(AssertFailedException) | +| Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | Assert.cs:53:9:53:36 | ...; | semmle.label | successor | +| Assert.cs:52:9:52:34 | ...; | Assert.cs:52:24:52:24 | access to local variable s | semmle.label | successor | +| Assert.cs:52:24:52:24 | access to local variable s | Assert.cs:52:29:52:32 | null | semmle.label | successor | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:9:52:33 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:9:52:33 | [assertion success] call to method IsFalse | semmle.label | false | +| Assert.cs:52:29:52:32 | null | Assert.cs:52:24:52:32 | ... == ... | semmle.label | successor | +| Assert.cs:53:9:53:35 | call to method WriteLine | Assert.cs:49:10:49:11 | exit M7 | semmle.label | successor | +| Assert.cs:53:9:53:36 | ...; | Assert.cs:53:27:53:27 | access to local variable s | semmle.label | successor | +| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:53:27:53:34 | access to property Length | semmle.label | successor | +| Assert.cs:53:27:53:34 | access to property Length | Assert.cs:53:9:53:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:56:10:56:11 | enter M8 | Assert.cs:57:5:61:5 | {...} | semmle.label | successor | +| Assert.cs:57:5:61:5 | {...} | Assert.cs:58:9:58:33 | ... ...; | semmle.label | successor | +| Assert.cs:58:9:58:33 | ... ...; | Assert.cs:58:20:58:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:58:16:58:32 | [b (line 56): false] String s = ... | Assert.cs:59:9:59:38 | [b (line 56): false] ...; | semmle.label | successor | +| Assert.cs:58:16:58:32 | [b (line 56): true] String s = ... | Assert.cs:59:9:59:38 | [b (line 56): true] ...; | semmle.label | successor | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:24:58:27 | [b (line 56): true] null | semmle.label | true | +| Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:31:58:32 | [b (line 56): false] "" | semmle.label | false | +| Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | semmle.label | successor | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | Assert.cs:58:16:58:32 | [b (line 56): true] String s = ... | semmle.label | successor | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | Assert.cs:58:16:58:32 | [b (line 56): false] String s = ... | semmle.label | successor | +| Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | Assert.cs:56:10:56:11 | exit M8 | semmle.label | exception(AssertFailedException) | +| Assert.cs:59:9:59:37 | [assertion success] call to method IsTrue | Assert.cs:60:9:60:36 | ...; | semmle.label | successor | +| Assert.cs:59:9:59:38 | [b (line 56): false] ...; | Assert.cs:59:23:59:36 | [b (line 56): false] ... && ... | semmle.label | successor | +| Assert.cs:59:9:59:38 | [b (line 56): true] ...; | Assert.cs:59:23:59:36 | [b (line 56): true] ... && ... | semmle.label | successor | +| Assert.cs:59:23:59:23 | [b (line 56): false] access to local variable s | Assert.cs:59:28:59:31 | [b (line 56): false] null | semmle.label | successor | +| Assert.cs:59:23:59:23 | [b (line 56): true] access to local variable s | Assert.cs:59:28:59:31 | [b (line 56): true] null | semmle.label | successor | +| Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | semmle.label | false | +| Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | semmle.label | true | +| Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | semmle.label | false | +| Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | semmle.label | true | +| Assert.cs:59:23:59:36 | [b (line 56): false] ... && ... | Assert.cs:59:23:59:23 | [b (line 56): false] access to local variable s | semmle.label | successor | +| Assert.cs:59:23:59:36 | [b (line 56): true] ... && ... | Assert.cs:59:23:59:23 | [b (line 56): true] access to local variable s | semmle.label | successor | +| Assert.cs:59:28:59:31 | [b (line 56): false] null | Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | semmle.label | successor | +| Assert.cs:59:28:59:31 | [b (line 56): true] null | Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | semmle.label | successor | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:59:9:59:37 | [assertion failure] call to method IsTrue | semmle.label | false | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:59:9:59:37 | [assertion success] call to method IsTrue | semmle.label | true | +| Assert.cs:60:9:60:35 | call to method WriteLine | Assert.cs:56:10:56:11 | exit M8 | semmle.label | successor | +| Assert.cs:60:9:60:36 | ...; | Assert.cs:60:27:60:27 | access to local variable s | semmle.label | successor | +| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:60:27:60:34 | access to property Length | semmle.label | successor | +| Assert.cs:60:27:60:34 | access to property Length | Assert.cs:60:9:60:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:63:10:63:11 | enter M9 | Assert.cs:64:5:68:5 | {...} | semmle.label | successor | +| Assert.cs:64:5:68:5 | {...} | Assert.cs:65:9:65:33 | ... ...; | semmle.label | successor | +| Assert.cs:65:9:65:33 | ... ...; | Assert.cs:65:20:65:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:65:16:65:32 | [b (line 63): false] String s = ... | Assert.cs:66:9:66:39 | [b (line 63): false] ...; | semmle.label | successor | +| Assert.cs:65:16:65:32 | [b (line 63): true] String s = ... | Assert.cs:66:9:66:39 | [b (line 63): true] ...; | semmle.label | successor | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:24:65:27 | [b (line 63): true] null | semmle.label | true | +| Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:31:65:32 | [b (line 63): false] "" | semmle.label | false | +| Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | semmle.label | successor | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | Assert.cs:65:16:65:32 | [b (line 63): true] String s = ... | semmle.label | successor | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | Assert.cs:65:16:65:32 | [b (line 63): false] String s = ... | semmle.label | successor | +| Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | Assert.cs:63:10:63:11 | exit M9 | semmle.label | exception(AssertFailedException) | +| Assert.cs:66:9:66:38 | [assertion success] call to method IsFalse | Assert.cs:67:9:67:36 | ...; | semmle.label | successor | +| Assert.cs:66:9:66:39 | [b (line 63): false] ...; | Assert.cs:66:24:66:37 | [b (line 63): false] ... \|\| ... | semmle.label | successor | +| Assert.cs:66:9:66:39 | [b (line 63): true] ...; | Assert.cs:66:24:66:37 | [b (line 63): true] ... \|\| ... | semmle.label | successor | +| Assert.cs:66:24:66:24 | [b (line 63): false] access to local variable s | Assert.cs:66:29:66:32 | [b (line 63): false] null | semmle.label | successor | +| Assert.cs:66:24:66:24 | [b (line 63): true] access to local variable s | Assert.cs:66:29:66:32 | [b (line 63): true] null | semmle.label | successor | +| Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | semmle.label | false | +| Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | semmle.label | false | +| Assert.cs:66:24:66:37 | [b (line 63): false] ... \|\| ... | Assert.cs:66:24:66:24 | [b (line 63): false] access to local variable s | semmle.label | successor | +| Assert.cs:66:24:66:37 | [b (line 63): true] ... \|\| ... | Assert.cs:66:24:66:24 | [b (line 63): true] access to local variable s | semmle.label | successor | +| Assert.cs:66:29:66:32 | [b (line 63): false] null | Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | semmle.label | successor | +| Assert.cs:66:29:66:32 | [b (line 63): true] null | Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | semmle.label | successor | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:66:9:66:38 | [assertion success] call to method IsFalse | semmle.label | false | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:66:9:66:38 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:67:9:67:35 | call to method WriteLine | Assert.cs:63:10:63:11 | exit M9 | semmle.label | successor | +| Assert.cs:67:9:67:36 | ...; | Assert.cs:67:27:67:27 | access to local variable s | semmle.label | successor | +| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:67:27:67:34 | access to property Length | semmle.label | successor | +| Assert.cs:67:27:67:34 | access to property Length | Assert.cs:67:9:67:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:70:10:70:12 | enter M10 | Assert.cs:71:5:75:5 | {...} | semmle.label | successor | +| Assert.cs:71:5:75:5 | {...} | Assert.cs:72:9:72:33 | ... ...; | semmle.label | successor | +| Assert.cs:72:9:72:33 | ... ...; | Assert.cs:72:20:72:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:72:16:72:32 | [b (line 70): false] String s = ... | Assert.cs:73:9:73:38 | [b (line 70): false] ...; | semmle.label | successor | +| Assert.cs:72:16:72:32 | [b (line 70): true] String s = ... | Assert.cs:73:9:73:38 | [b (line 70): true] ...; | semmle.label | successor | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:24:72:27 | [b (line 70): true] null | semmle.label | true | +| Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:31:72:32 | [b (line 70): false] "" | semmle.label | false | +| Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | semmle.label | successor | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | Assert.cs:72:16:72:32 | [b (line 70): true] String s = ... | semmle.label | successor | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | Assert.cs:72:16:72:32 | [b (line 70): false] String s = ... | semmle.label | successor | +| Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | Assert.cs:70:10:70:12 | exit M10 | semmle.label | exception(AssertFailedException) | +| Assert.cs:73:9:73:37 | [assertion success] call to method IsTrue | Assert.cs:74:9:74:36 | ...; | semmle.label | successor | +| Assert.cs:73:9:73:38 | [b (line 70): false] ...; | Assert.cs:73:23:73:36 | [b (line 70): false] ... && ... | semmle.label | successor | +| Assert.cs:73:9:73:38 | [b (line 70): true] ...; | Assert.cs:73:23:73:36 | [b (line 70): true] ... && ... | semmle.label | successor | +| Assert.cs:73:23:73:23 | [b (line 70): false] access to local variable s | Assert.cs:73:28:73:31 | [b (line 70): false] null | semmle.label | successor | +| Assert.cs:73:23:73:23 | [b (line 70): true] access to local variable s | Assert.cs:73:28:73:31 | [b (line 70): true] null | semmle.label | successor | +| Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | semmle.label | false | +| Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | semmle.label | true | +| Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | semmle.label | false | +| Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | semmle.label | true | +| Assert.cs:73:23:73:36 | [b (line 70): false] ... && ... | Assert.cs:73:23:73:23 | [b (line 70): false] access to local variable s | semmle.label | successor | +| Assert.cs:73:23:73:36 | [b (line 70): true] ... && ... | Assert.cs:73:23:73:23 | [b (line 70): true] access to local variable s | semmle.label | successor | +| Assert.cs:73:28:73:31 | [b (line 70): false] null | Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | semmle.label | successor | +| Assert.cs:73:28:73:31 | [b (line 70): true] null | Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | semmle.label | successor | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:73:9:73:37 | [assertion failure] call to method IsTrue | semmle.label | false | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:73:9:73:37 | [assertion success] call to method IsTrue | semmle.label | true | +| Assert.cs:74:9:74:35 | call to method WriteLine | Assert.cs:70:10:70:12 | exit M10 | semmle.label | successor | +| Assert.cs:74:9:74:36 | ...; | Assert.cs:74:27:74:27 | access to local variable s | semmle.label | successor | +| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:74:27:74:34 | access to property Length | semmle.label | successor | +| Assert.cs:74:27:74:34 | access to property Length | Assert.cs:74:9:74:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:77:10:77:12 | enter M11 | Assert.cs:78:5:82:5 | {...} | semmle.label | successor | +| Assert.cs:78:5:82:5 | {...} | Assert.cs:79:9:79:33 | ... ...; | semmle.label | successor | +| Assert.cs:79:9:79:33 | ... ...; | Assert.cs:79:20:79:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:79:16:79:32 | [b (line 77): false] String s = ... | Assert.cs:80:9:80:39 | [b (line 77): false] ...; | semmle.label | successor | +| Assert.cs:79:16:79:32 | [b (line 77): true] String s = ... | Assert.cs:80:9:80:39 | [b (line 77): true] ...; | semmle.label | successor | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:24:79:27 | [b (line 77): true] null | semmle.label | true | +| Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:31:79:32 | [b (line 77): false] "" | semmle.label | false | +| Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | semmle.label | successor | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | Assert.cs:79:16:79:32 | [b (line 77): true] String s = ... | semmle.label | successor | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | Assert.cs:79:16:79:32 | [b (line 77): false] String s = ... | semmle.label | successor | +| Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | Assert.cs:77:10:77:12 | exit M11 | semmle.label | exception(AssertFailedException) | +| Assert.cs:80:9:80:38 | [assertion success] call to method IsFalse | Assert.cs:81:9:81:36 | ...; | semmle.label | successor | +| Assert.cs:80:9:80:39 | [b (line 77): false] ...; | Assert.cs:80:24:80:37 | [b (line 77): false] ... \|\| ... | semmle.label | successor | +| Assert.cs:80:9:80:39 | [b (line 77): true] ...; | Assert.cs:80:24:80:37 | [b (line 77): true] ... \|\| ... | semmle.label | successor | +| Assert.cs:80:24:80:24 | [b (line 77): false] access to local variable s | Assert.cs:80:29:80:32 | [b (line 77): false] null | semmle.label | successor | +| Assert.cs:80:24:80:24 | [b (line 77): true] access to local variable s | Assert.cs:80:29:80:32 | [b (line 77): true] null | semmle.label | successor | +| Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | semmle.label | false | +| Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | semmle.label | false | +| Assert.cs:80:24:80:37 | [b (line 77): false] ... \|\| ... | Assert.cs:80:24:80:24 | [b (line 77): false] access to local variable s | semmle.label | successor | +| Assert.cs:80:24:80:37 | [b (line 77): true] ... \|\| ... | Assert.cs:80:24:80:24 | [b (line 77): true] access to local variable s | semmle.label | successor | +| Assert.cs:80:29:80:32 | [b (line 77): false] null | Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | semmle.label | successor | +| Assert.cs:80:29:80:32 | [b (line 77): true] null | Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | semmle.label | successor | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:80:9:80:38 | [assertion success] call to method IsFalse | semmle.label | false | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:80:9:80:38 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:81:9:81:35 | call to method WriteLine | Assert.cs:77:10:77:12 | exit M11 | semmle.label | successor | +| Assert.cs:81:9:81:36 | ...; | Assert.cs:81:27:81:27 | access to local variable s | semmle.label | successor | +| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:81:27:81:34 | access to property Length | semmle.label | successor | +| Assert.cs:81:27:81:34 | access to property Length | Assert.cs:81:9:81:35 | call to method WriteLine | semmle.label | successor | +| Assert.cs:84:10:84:12 | enter M12 | Assert.cs:85:5:129:5 | {...} | semmle.label | successor | +| Assert.cs:85:5:129:5 | {...} | Assert.cs:86:9:86:33 | ... ...; | semmle.label | successor | +| Assert.cs:86:9:86:33 | ... ...; | Assert.cs:86:20:86:32 | ... ? ... : ... | semmle.label | successor | +| Assert.cs:86:16:86:32 | [b (line 84): false] String s = ... | Assert.cs:87:9:87:32 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:86:16:86:32 | [b (line 84): true] String s = ... | Assert.cs:87:9:87:32 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:24:86:27 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:86:20:86:20 | access to parameter b | Assert.cs:86:31:86:32 | [b (line 84): false] "" | semmle.label | false | +| Assert.cs:86:20:86:32 | ... ? ... : ... | Assert.cs:86:20:86:20 | access to parameter b | semmle.label | successor | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | Assert.cs:86:16:86:32 | [b (line 84): true] String s = ... | semmle.label | successor | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | Assert.cs:86:16:86:32 | [b (line 84): false] String s = ... | semmle.label | successor | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exit | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exit | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | Assert.cs:88:9:88:36 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | Assert.cs:88:9:88:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:87:9:87:32 | [b (line 84): false] ...; | Assert.cs:87:22:87:22 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:87:9:87:32 | [b (line 84): true] ...; | Assert.cs:87:22:87:22 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:87:22:87:22 | [b (line 84): false] access to local variable s | Assert.cs:87:27:87:30 | [b (line 84): false] null | semmle.label | successor | +| Assert.cs:87:22:87:22 | [b (line 84): true] access to local variable s | Assert.cs:87:27:87:30 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | semmle.label | false | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | semmle.label | true | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | semmle.label | false | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | semmle.label | true | +| Assert.cs:87:27:87:30 | [b (line 84): false] null | Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | semmle.label | successor | +| Assert.cs:87:27:87:30 | [b (line 84): true] null | Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | semmle.label | successor | +| Assert.cs:88:9:88:35 | [b (line 84): false] call to method WriteLine | Assert.cs:90:9:90:26 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:88:9:88:35 | [b (line 84): true] call to method WriteLine | Assert.cs:90:9:90:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:88:9:88:36 | [b (line 84): false] ...; | Assert.cs:88:27:88:27 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:88:9:88:36 | [b (line 84): true] ...; | Assert.cs:88:27:88:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:88:27:88:27 | [b (line 84): false] access to local variable s | Assert.cs:88:27:88:34 | [b (line 84): false] access to property Length | semmle.label | successor | +| Assert.cs:88:27:88:27 | [b (line 84): true] access to local variable s | Assert.cs:88:27:88:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:88:27:88:34 | [b (line 84): false] access to property Length | Assert.cs:88:9:88:35 | [b (line 84): false] call to method WriteLine | semmle.label | successor | +| Assert.cs:88:27:88:34 | [b (line 84): true] access to property Length | Assert.cs:88:9:88:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:90:9:90:25 | [b (line 84): false] ... = ... | Assert.cs:91:9:91:25 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:90:9:90:25 | [b (line 84): true] ... = ... | Assert.cs:91:9:91:25 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:90:9:90:26 | [b (line 84): false] ...; | Assert.cs:90:13:90:25 | [b (line 84): false] ... ? ... : ... | semmle.label | successor | +| Assert.cs:90:9:90:26 | [b (line 84): true] ...; | Assert.cs:90:13:90:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | Assert.cs:90:24:90:25 | [b (line 84): false] "" | semmle.label | false | +| Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | Assert.cs:90:17:90:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:90:13:90:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | semmle.label | successor | +| Assert.cs:90:13:90:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:90:17:90:20 | [b (line 84): true] null | Assert.cs:90:9:90:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:90:24:90:25 | [b (line 84): false] "" | Assert.cs:90:9:90:25 | [b (line 84): false] ... = ... | semmle.label | successor | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | Assert.cs:92:9:92:36 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | Assert.cs:92:9:92:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:91:9:91:25 | [b (line 84): false] ...; | Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:91:9:91:25 | [b (line 84): true] ...; | Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | semmle.label | non-null | +| Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | semmle.label | null | +| Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | semmle.label | non-null | +| Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | semmle.label | null | +| Assert.cs:92:9:92:35 | [b (line 84): false] call to method WriteLine | Assert.cs:94:9:94:26 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:92:9:92:35 | [b (line 84): true] call to method WriteLine | Assert.cs:94:9:94:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:92:9:92:36 | [b (line 84): false] ...; | Assert.cs:92:27:92:27 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:92:9:92:36 | [b (line 84): true] ...; | Assert.cs:92:27:92:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:92:27:92:27 | [b (line 84): false] access to local variable s | Assert.cs:92:27:92:34 | [b (line 84): false] access to property Length | semmle.label | successor | +| Assert.cs:92:27:92:27 | [b (line 84): true] access to local variable s | Assert.cs:92:27:92:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:92:27:92:34 | [b (line 84): false] access to property Length | Assert.cs:92:9:92:35 | [b (line 84): false] call to method WriteLine | semmle.label | successor | +| Assert.cs:92:27:92:34 | [b (line 84): true] access to property Length | Assert.cs:92:9:92:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:94:9:94:25 | [b (line 84): false] ... = ... | Assert.cs:95:9:95:28 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:94:9:94:25 | [b (line 84): true] ... = ... | Assert.cs:95:9:95:28 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:94:9:94:26 | [b (line 84): false] ...; | Assert.cs:94:13:94:25 | [b (line 84): false] ... ? ... : ... | semmle.label | successor | +| Assert.cs:94:9:94:26 | [b (line 84): true] ...; | Assert.cs:94:13:94:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | Assert.cs:94:24:94:25 | [b (line 84): false] "" | semmle.label | false | +| Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | Assert.cs:94:17:94:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:94:13:94:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | semmle.label | successor | +| Assert.cs:94:13:94:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:94:17:94:20 | [b (line 84): true] null | Assert.cs:94:9:94:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:94:24:94:25 | [b (line 84): false] "" | Assert.cs:94:9:94:25 | [b (line 84): false] ... = ... | semmle.label | successor | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | Assert.cs:96:9:96:36 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | Assert.cs:96:9:96:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:95:9:95:28 | [b (line 84): false] ...; | Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:95:9:95:28 | [b (line 84): true] ...; | Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | semmle.label | null | +| Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | semmle.label | non-null | +| Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | semmle.label | null | +| Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | semmle.label | non-null | +| Assert.cs:96:9:96:35 | [b (line 84): false] call to method WriteLine | Assert.cs:98:9:98:26 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:96:9:96:35 | [b (line 84): true] call to method WriteLine | Assert.cs:98:9:98:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:96:9:96:36 | [b (line 84): false] ...; | Assert.cs:96:27:96:27 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:96:9:96:36 | [b (line 84): true] ...; | Assert.cs:96:27:96:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:96:27:96:27 | [b (line 84): false] access to local variable s | Assert.cs:96:27:96:34 | [b (line 84): false] access to property Length | semmle.label | successor | +| Assert.cs:96:27:96:27 | [b (line 84): true] access to local variable s | Assert.cs:96:27:96:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:96:27:96:34 | [b (line 84): false] access to property Length | Assert.cs:96:9:96:35 | [b (line 84): false] call to method WriteLine | semmle.label | successor | +| Assert.cs:96:27:96:34 | [b (line 84): true] access to property Length | Assert.cs:96:9:96:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:98:9:98:25 | [b (line 84): false] ... = ... | Assert.cs:99:9:99:33 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:98:9:98:25 | [b (line 84): true] ... = ... | Assert.cs:99:9:99:33 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:98:9:98:26 | [b (line 84): false] ...; | Assert.cs:98:13:98:25 | [b (line 84): false] ... ? ... : ... | semmle.label | successor | +| Assert.cs:98:9:98:26 | [b (line 84): true] ...; | Assert.cs:98:13:98:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | Assert.cs:98:24:98:25 | [b (line 84): false] "" | semmle.label | false | +| Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | Assert.cs:98:17:98:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:98:13:98:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | semmle.label | successor | +| Assert.cs:98:13:98:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:98:17:98:20 | [b (line 84): true] null | Assert.cs:98:9:98:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:98:24:98:25 | [b (line 84): false] "" | Assert.cs:98:9:98:25 | [b (line 84): false] ... = ... | semmle.label | successor | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:100:9:100:36 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:100:9:100:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:99:9:99:33 | [b (line 84): false] ...; | Assert.cs:99:23:99:23 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:99:9:99:33 | [b (line 84): true] ...; | Assert.cs:99:23:99:23 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:99:23:99:23 | [b (line 84): false] access to local variable s | Assert.cs:99:28:99:31 | [b (line 84): false] null | semmle.label | successor | +| Assert.cs:99:23:99:23 | [b (line 84): true] access to local variable s | Assert.cs:99:28:99:31 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | semmle.label | false | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | semmle.label | true | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | semmle.label | false | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | semmle.label | true | +| Assert.cs:99:28:99:31 | [b (line 84): false] null | Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | semmle.label | successor | +| Assert.cs:99:28:99:31 | [b (line 84): true] null | Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | semmle.label | successor | +| Assert.cs:100:9:100:35 | [b (line 84): false] call to method WriteLine | Assert.cs:102:9:102:26 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:100:9:100:35 | [b (line 84): true] call to method WriteLine | Assert.cs:102:9:102:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:100:9:100:36 | [b (line 84): false] ...; | Assert.cs:100:27:100:27 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:100:9:100:36 | [b (line 84): true] ...; | Assert.cs:100:27:100:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:100:27:100:27 | [b (line 84): false] access to local variable s | Assert.cs:100:27:100:34 | [b (line 84): false] access to property Length | semmle.label | successor | +| Assert.cs:100:27:100:27 | [b (line 84): true] access to local variable s | Assert.cs:100:27:100:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:100:27:100:34 | [b (line 84): false] access to property Length | Assert.cs:100:9:100:35 | [b (line 84): false] call to method WriteLine | semmle.label | successor | +| Assert.cs:100:27:100:34 | [b (line 84): true] access to property Length | Assert.cs:100:9:100:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:102:9:102:25 | [b (line 84): false] ... = ... | Assert.cs:103:9:103:33 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:102:9:102:25 | [b (line 84): true] ... = ... | Assert.cs:103:9:103:33 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:102:9:102:26 | [b (line 84): false] ...; | Assert.cs:102:13:102:25 | [b (line 84): false] ... ? ... : ... | semmle.label | successor | +| Assert.cs:102:9:102:26 | [b (line 84): true] ...; | Assert.cs:102:13:102:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | Assert.cs:102:24:102:25 | [b (line 84): false] "" | semmle.label | false | +| Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | Assert.cs:102:17:102:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:102:13:102:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | semmle.label | successor | +| Assert.cs:102:13:102:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:102:17:102:20 | [b (line 84): true] null | Assert.cs:102:9:102:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:102:24:102:25 | [b (line 84): false] "" | Assert.cs:102:9:102:25 | [b (line 84): false] ... = ... | semmle.label | successor | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | Assert.cs:104:9:104:36 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:104:9:104:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:103:9:103:33 | [b (line 84): false] ...; | Assert.cs:103:23:103:23 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:103:9:103:33 | [b (line 84): true] ...; | Assert.cs:103:23:103:23 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:103:23:103:23 | [b (line 84): false] access to local variable s | Assert.cs:103:28:103:31 | [b (line 84): false] null | semmle.label | successor | +| Assert.cs:103:23:103:23 | [b (line 84): true] access to local variable s | Assert.cs:103:28:103:31 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | semmle.label | false | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | semmle.label | true | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | semmle.label | false | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | semmle.label | true | +| Assert.cs:103:28:103:31 | [b (line 84): false] null | Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | semmle.label | successor | +| Assert.cs:103:28:103:31 | [b (line 84): true] null | Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | semmle.label | successor | +| Assert.cs:104:9:104:35 | [b (line 84): false] call to method WriteLine | Assert.cs:106:9:106:26 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:104:9:104:35 | [b (line 84): true] call to method WriteLine | Assert.cs:106:9:106:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:104:9:104:36 | [b (line 84): false] ...; | Assert.cs:104:27:104:27 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:104:9:104:36 | [b (line 84): true] ...; | Assert.cs:104:27:104:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:104:27:104:27 | [b (line 84): false] access to local variable s | Assert.cs:104:27:104:34 | [b (line 84): false] access to property Length | semmle.label | successor | +| Assert.cs:104:27:104:27 | [b (line 84): true] access to local variable s | Assert.cs:104:27:104:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:104:27:104:34 | [b (line 84): false] access to property Length | Assert.cs:104:9:104:35 | [b (line 84): false] call to method WriteLine | semmle.label | successor | +| Assert.cs:104:27:104:34 | [b (line 84): true] access to property Length | Assert.cs:104:9:104:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:106:9:106:25 | [b (line 84): false] ... = ... | Assert.cs:107:9:107:34 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:106:9:106:25 | [b (line 84): true] ... = ... | Assert.cs:107:9:107:34 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:106:9:106:26 | [b (line 84): false] ...; | Assert.cs:106:13:106:25 | [b (line 84): false] ... ? ... : ... | semmle.label | successor | +| Assert.cs:106:9:106:26 | [b (line 84): true] ...; | Assert.cs:106:13:106:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | Assert.cs:106:24:106:25 | [b (line 84): false] "" | semmle.label | false | +| Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | Assert.cs:106:17:106:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:106:13:106:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | semmle.label | successor | +| Assert.cs:106:13:106:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:106:17:106:20 | [b (line 84): true] null | Assert.cs:106:9:106:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:106:24:106:25 | [b (line 84): false] "" | Assert.cs:106:9:106:25 | [b (line 84): false] ... = ... | semmle.label | successor | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:108:9:108:36 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:108:9:108:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:107:9:107:34 | [b (line 84): false] ...; | Assert.cs:107:24:107:24 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:107:9:107:34 | [b (line 84): true] ...; | Assert.cs:107:24:107:24 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:107:24:107:24 | [b (line 84): false] access to local variable s | Assert.cs:107:29:107:32 | [b (line 84): false] null | semmle.label | successor | +| Assert.cs:107:24:107:24 | [b (line 84): true] access to local variable s | Assert.cs:107:29:107:32 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | semmle.label | true | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | semmle.label | false | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | semmle.label | true | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | semmle.label | false | +| Assert.cs:107:29:107:32 | [b (line 84): false] null | Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | semmle.label | successor | +| Assert.cs:107:29:107:32 | [b (line 84): true] null | Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | semmle.label | successor | +| Assert.cs:108:9:108:35 | [b (line 84): false] call to method WriteLine | Assert.cs:110:9:110:26 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:108:9:108:35 | [b (line 84): true] call to method WriteLine | Assert.cs:110:9:110:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:108:9:108:36 | [b (line 84): false] ...; | Assert.cs:108:27:108:27 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:108:9:108:36 | [b (line 84): true] ...; | Assert.cs:108:27:108:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:108:27:108:27 | [b (line 84): false] access to local variable s | Assert.cs:108:27:108:34 | [b (line 84): false] access to property Length | semmle.label | successor | +| Assert.cs:108:27:108:27 | [b (line 84): true] access to local variable s | Assert.cs:108:27:108:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:108:27:108:34 | [b (line 84): false] access to property Length | Assert.cs:108:9:108:35 | [b (line 84): false] call to method WriteLine | semmle.label | successor | +| Assert.cs:108:27:108:34 | [b (line 84): true] access to property Length | Assert.cs:108:9:108:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:110:9:110:25 | [b (line 84): false] ... = ... | Assert.cs:111:9:111:34 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:110:9:110:25 | [b (line 84): true] ... = ... | Assert.cs:111:9:111:34 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:110:9:110:26 | [b (line 84): false] ...; | Assert.cs:110:13:110:25 | [b (line 84): false] ... ? ... : ... | semmle.label | successor | +| Assert.cs:110:9:110:26 | [b (line 84): true] ...; | Assert.cs:110:13:110:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | Assert.cs:110:24:110:25 | [b (line 84): false] "" | semmle.label | false | +| Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | Assert.cs:110:17:110:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:110:13:110:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | semmle.label | successor | +| Assert.cs:110:13:110:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:110:17:110:20 | [b (line 84): true] null | Assert.cs:110:9:110:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:110:24:110:25 | [b (line 84): false] "" | Assert.cs:110:9:110:25 | [b (line 84): false] ... = ... | semmle.label | successor | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | Assert.cs:112:9:112:36 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:112:9:112:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:111:9:111:34 | [b (line 84): false] ...; | Assert.cs:111:24:111:24 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:111:9:111:34 | [b (line 84): true] ...; | Assert.cs:111:24:111:24 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:111:24:111:24 | [b (line 84): false] access to local variable s | Assert.cs:111:29:111:32 | [b (line 84): false] null | semmle.label | successor | +| Assert.cs:111:24:111:24 | [b (line 84): true] access to local variable s | Assert.cs:111:29:111:32 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | semmle.label | true | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | semmle.label | false | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | semmle.label | true | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | semmle.label | false | +| Assert.cs:111:29:111:32 | [b (line 84): false] null | Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | semmle.label | successor | +| Assert.cs:111:29:111:32 | [b (line 84): true] null | Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | semmle.label | successor | +| Assert.cs:112:9:112:35 | [b (line 84): false] call to method WriteLine | Assert.cs:114:9:114:26 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:112:9:112:35 | [b (line 84): true] call to method WriteLine | Assert.cs:114:9:114:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:112:9:112:36 | [b (line 84): false] ...; | Assert.cs:112:27:112:27 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:112:9:112:36 | [b (line 84): true] ...; | Assert.cs:112:27:112:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:112:27:112:27 | [b (line 84): false] access to local variable s | Assert.cs:112:27:112:34 | [b (line 84): false] access to property Length | semmle.label | successor | +| Assert.cs:112:27:112:27 | [b (line 84): true] access to local variable s | Assert.cs:112:27:112:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:112:27:112:34 | [b (line 84): false] access to property Length | Assert.cs:112:9:112:35 | [b (line 84): false] call to method WriteLine | semmle.label | successor | +| Assert.cs:112:27:112:34 | [b (line 84): true] access to property Length | Assert.cs:112:9:112:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:114:9:114:25 | [b (line 84): false] ... = ... | Assert.cs:115:9:115:38 | [b (line 84): false] ...; | semmle.label | successor | +| Assert.cs:114:9:114:25 | [b (line 84): true] ... = ... | Assert.cs:115:9:115:38 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:114:9:114:26 | [b (line 84): false] ...; | Assert.cs:114:13:114:25 | [b (line 84): false] ... ? ... : ... | semmle.label | successor | +| Assert.cs:114:9:114:26 | [b (line 84): true] ...; | Assert.cs:114:13:114:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | Assert.cs:114:24:114:25 | [b (line 84): false] "" | semmle.label | false | +| Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | Assert.cs:114:17:114:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:114:13:114:25 | [b (line 84): false] ... ? ... : ... | Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | semmle.label | successor | +| Assert.cs:114:13:114:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:114:17:114:20 | [b (line 84): true] null | Assert.cs:114:9:114:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:114:24:114:25 | [b (line 84): false] "" | Assert.cs:114:9:114:25 | [b (line 84): false] ... = ... | semmle.label | successor | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:116:9:116:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:115:9:115:38 | [b (line 84): false] ...; | Assert.cs:115:23:115:36 | [b (line 84): false] ... && ... | semmle.label | successor | +| Assert.cs:115:9:115:38 | [b (line 84): true] ...; | Assert.cs:115:23:115:36 | [b (line 84): true] ... && ... | semmle.label | successor | +| Assert.cs:115:23:115:23 | [b (line 84): false] access to local variable s | Assert.cs:115:28:115:31 | [b (line 84): false] null | semmle.label | successor | +| Assert.cs:115:23:115:23 | [b (line 84): true] access to local variable s | Assert.cs:115:28:115:31 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | semmle.label | false | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | semmle.label | true | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | semmle.label | false | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | semmle.label | true | +| Assert.cs:115:23:115:36 | [b (line 84): false] ... && ... | Assert.cs:115:23:115:23 | [b (line 84): false] access to local variable s | semmle.label | successor | +| Assert.cs:115:23:115:36 | [b (line 84): true] ... && ... | Assert.cs:115:23:115:23 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:115:28:115:31 | [b (line 84): false] null | Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | semmle.label | successor | +| Assert.cs:115:28:115:31 | [b (line 84): true] null | Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | semmle.label | successor | +| Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | semmle.label | false | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | semmle.label | true | +| Assert.cs:116:9:116:35 | [b (line 84): true] call to method WriteLine | Assert.cs:118:9:118:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:116:9:116:36 | [b (line 84): true] ...; | Assert.cs:116:27:116:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:116:27:116:27 | [b (line 84): true] access to local variable s | Assert.cs:116:27:116:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:116:27:116:34 | [b (line 84): true] access to property Length | Assert.cs:116:9:116:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:118:9:118:25 | [b (line 84): true] ... = ... | Assert.cs:119:9:119:40 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:118:9:118:26 | [b (line 84): true] ...; | Assert.cs:118:13:118:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | Assert.cs:118:17:118:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:118:13:118:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:118:17:118:20 | [b (line 84): true] null | Assert.cs:118:9:118:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | Assert.cs:120:9:120:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:119:9:119:40 | [b (line 84): true] ...; | Assert.cs:119:24:119:38 | [b (line 84): true] ... \|\| ... | semmle.label | successor | +| Assert.cs:119:24:119:24 | [b (line 84): true] access to local variable s | Assert.cs:119:29:119:32 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | semmle.label | true | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | Assert.cs:119:37:119:38 | [b (line 84): true] !... | semmle.label | false | +| Assert.cs:119:24:119:38 | [b (line 84): true] ... \|\| ... | Assert.cs:119:24:119:24 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:119:29:119:32 | [b (line 84): true] null | Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | semmle.label | successor | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | semmle.label | true | +| Assert.cs:120:9:120:35 | [b (line 84): true] call to method WriteLine | Assert.cs:122:9:122:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:120:9:120:36 | [b (line 84): true] ...; | Assert.cs:120:27:120:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:120:27:120:27 | [b (line 84): true] access to local variable s | Assert.cs:120:27:120:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:120:27:120:34 | [b (line 84): true] access to property Length | Assert.cs:120:9:120:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:122:9:122:25 | [b (line 84): true] ... = ... | Assert.cs:123:9:123:38 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:122:9:122:26 | [b (line 84): true] ...; | Assert.cs:122:13:122:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | Assert.cs:122:17:122:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:122:13:122:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:122:17:122:20 | [b (line 84): true] null | Assert.cs:122:9:122:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | Assert.cs:124:9:124:36 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:123:9:123:38 | [b (line 84): true] ...; | Assert.cs:123:23:123:36 | [b (line 84): true] ... && ... | semmle.label | successor | +| Assert.cs:123:23:123:23 | [b (line 84): true] access to local variable s | Assert.cs:123:28:123:31 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | semmle.label | false | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | semmle.label | true | +| Assert.cs:123:23:123:36 | [b (line 84): true] ... && ... | Assert.cs:123:23:123:23 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:123:28:123:31 | [b (line 84): true] null | Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | semmle.label | successor | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | semmle.label | true | +| Assert.cs:124:9:124:35 | [b (line 84): true] call to method WriteLine | Assert.cs:126:9:126:26 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:124:9:124:36 | [b (line 84): true] ...; | Assert.cs:124:27:124:27 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:124:27:124:27 | [b (line 84): true] access to local variable s | Assert.cs:124:27:124:34 | [b (line 84): true] access to property Length | semmle.label | successor | +| Assert.cs:124:27:124:34 | [b (line 84): true] access to property Length | Assert.cs:124:9:124:35 | [b (line 84): true] call to method WriteLine | semmle.label | successor | +| Assert.cs:126:9:126:25 | [b (line 84): true] ... = ... | Assert.cs:127:9:127:40 | [b (line 84): true] ...; | semmle.label | successor | +| Assert.cs:126:9:126:26 | [b (line 84): true] ...; | Assert.cs:126:13:126:25 | [b (line 84): true] ... ? ... : ... | semmle.label | successor | +| Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | Assert.cs:126:17:126:20 | [b (line 84): true] null | semmle.label | true | +| Assert.cs:126:13:126:25 | [b (line 84): true] ... ? ... : ... | Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:126:17:126:20 | [b (line 84): true] null | Assert.cs:126:9:126:25 | [b (line 84): true] ... = ... | semmle.label | successor | +| Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | Assert.cs:84:10:84:12 | exit M12 | semmle.label | exception(AssertFailedException) | +| Assert.cs:127:9:127:39 | [assertion success] call to method IsFalse | Assert.cs:128:9:128:36 | ...; | semmle.label | successor | +| Assert.cs:127:9:127:40 | [b (line 84): true] ...; | Assert.cs:127:24:127:38 | [b (line 84): true] ... \|\| ... | semmle.label | successor | +| Assert.cs:127:24:127:24 | [b (line 84): true] access to local variable s | Assert.cs:127:29:127:32 | [b (line 84): true] null | semmle.label | successor | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | Assert.cs:127:9:127:39 | [assertion failure] call to method IsFalse | semmle.label | true | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | Assert.cs:127:37:127:38 | [b (line 84): true] !... | semmle.label | false | +| Assert.cs:127:24:127:38 | [b (line 84): true] ... \|\| ... | Assert.cs:127:24:127:24 | [b (line 84): true] access to local variable s | semmle.label | successor | +| Assert.cs:127:29:127:32 | [b (line 84): true] null | Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | semmle.label | successor | +| Assert.cs:127:37:127:38 | [b (line 84): true] !... | Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | semmle.label | successor | +| Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | Assert.cs:127:9:127:39 | [assertion success] call to method IsFalse | semmle.label | true | +| Assert.cs:128:9:128:35 | call to method WriteLine | Assert.cs:84:10:84:12 | exit M12 | semmle.label | successor | +| Assert.cs:128:9:128:36 | ...; | Assert.cs:128:27:128:27 | access to local variable s | semmle.label | successor | +| Assert.cs:128:27:128:27 | access to local variable s | Assert.cs:128:27:128:34 | access to property Length | semmle.label | successor | +| Assert.cs:128:27:128:34 | access to property Length | Assert.cs:128:9:128:35 | call to method WriteLine | semmle.label | successor | | Assignments.cs:3:10:3:10 | enter M | Assignments.cs:4:5:15:5 | {...} | semmle.label | successor | | Assignments.cs:4:5:15:5 | {...} | Assignments.cs:5:9:5:18 | ... ...; | semmle.label | successor | | Assignments.cs:5:9:5:18 | ... ...; | Assignments.cs:5:17:5:17 | 0 | semmle.label | successor | @@ -495,6 +1057,7 @@ | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | CompileTimeOperators.cs:28:10:28:10 | exit M | semmle.label | successor | | CompileTimeOperators.cs:40:14:40:38 | ...; | CompileTimeOperators.cs:40:32:40:36 | "End" | semmle.label | successor | | CompileTimeOperators.cs:40:32:40:36 | "End" | CompileTimeOperators.cs:40:14:40:37 | call to method WriteLine | semmle.label | successor | +| ConditionalAccess.cs:1:7:1:23 | enter ConditionalAccess | ConditionalAccess.cs:30:32:30:32 | 0 | semmle.label | successor | | ConditionalAccess.cs:3:12:3:13 | enter M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | semmle.label | successor | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:12:3:13 | exit M1 | semmle.label | null | | ConditionalAccess.cs:3:26:3:26 | access to parameter i | ConditionalAccess.cs:3:28:3:38 | call to method ToString | semmle.label | non-null | @@ -554,12 +1117,28 @@ | ConditionalAccess.cs:25:13:25:14 | "" | ConditionalAccess.cs:25:31:25:31 | access to local variable s | semmle.label | non-null | | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | ConditionalAccess.cs:25:9:25:32 | ... = ... | semmle.label | successor | | ConditionalAccess.cs:25:31:25:31 | access to local variable s | ConditionalAccess.cs:25:16:25:32 | call to method CommaJoinWith | semmle.label | successor | -| ConditionalAccess.cs:31:26:31:38 | enter CommaJoinWith | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | semmle.label | successor | -| ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | ConditionalAccess.cs:31:75:31:78 | ", " | semmle.label | successor | -| ConditionalAccess.cs:31:70:31:78 | ... + ... | ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | semmle.label | successor | -| ConditionalAccess.cs:31:70:31:83 | ... + ... | ConditionalAccess.cs:31:26:31:38 | exit CommaJoinWith | semmle.label | successor | -| ConditionalAccess.cs:31:75:31:78 | ", " | ConditionalAccess.cs:31:70:31:78 | ... + ... | semmle.label | successor | -| ConditionalAccess.cs:31:82:31:83 | access to parameter s2 | ConditionalAccess.cs:31:70:31:83 | ... + ... | semmle.label | successor | +| ConditionalAccess.cs:30:10:30:12 | enter Out | ConditionalAccess.cs:30:32:30:32 | 0 | semmle.label | successor | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | semmle.label | successor | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:1:7:1:23 | exit ConditionalAccess | semmle.label | successor | +| ConditionalAccess.cs:30:28:30:32 | ... = ... | ConditionalAccess.cs:30:10:30:12 | exit Out | semmle.label | successor | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:28:30:32 | ... = ... | semmle.label | successor | +| ConditionalAccess.cs:30:32:30:32 | 0 | ConditionalAccess.cs:30:28:30:32 | ... = ... | semmle.label | successor | +| ConditionalAccess.cs:32:10:32:11 | enter M8 | ConditionalAccess.cs:33:5:36:5 | {...} | semmle.label | successor | +| ConditionalAccess.cs:33:5:36:5 | {...} | ConditionalAccess.cs:34:9:34:14 | ...; | semmle.label | successor | +| ConditionalAccess.cs:34:9:34:13 | ... = ... | ConditionalAccess.cs:35:9:35:25 | ...; | semmle.label | successor | +| ConditionalAccess.cs:34:9:34:14 | ...; | ConditionalAccess.cs:34:13:34:13 | 0 | semmle.label | successor | +| ConditionalAccess.cs:34:13:34:13 | 0 | ConditionalAccess.cs:34:9:34:13 | ... = ... | semmle.label | successor | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:32:10:32:11 | exit M8 | semmle.label | null | +| ConditionalAccess.cs:35:9:35:12 | access to property Prop | ConditionalAccess.cs:35:14:35:24 | call to method Out | semmle.label | non-null | +| ConditionalAccess.cs:35:9:35:12 | this access | ConditionalAccess.cs:35:9:35:12 | access to property Prop | semmle.label | successor | +| ConditionalAccess.cs:35:9:35:25 | ...; | ConditionalAccess.cs:35:9:35:12 | this access | semmle.label | successor | +| ConditionalAccess.cs:35:14:35:24 | call to method Out | ConditionalAccess.cs:32:10:32:11 | exit M8 | semmle.label | successor | +| ConditionalAccess.cs:41:26:41:38 | enter CommaJoinWith | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | semmle.label | successor | +| ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | ConditionalAccess.cs:41:75:41:78 | ", " | semmle.label | successor | +| ConditionalAccess.cs:41:70:41:78 | ... + ... | ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | semmle.label | successor | +| ConditionalAccess.cs:41:70:41:83 | ... + ... | ConditionalAccess.cs:41:26:41:38 | exit CommaJoinWith | semmle.label | successor | +| ConditionalAccess.cs:41:75:41:78 | ", " | ConditionalAccess.cs:41:70:41:78 | ... + ... | semmle.label | successor | +| ConditionalAccess.cs:41:82:41:83 | access to parameter s2 | ConditionalAccess.cs:41:70:41:83 | ... + ... | semmle.label | successor | | Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:4:5:9:5 | {...} | semmle.label | successor | | Conditions.cs:4:5:9:5 | {...} | Conditions.cs:5:9:6:16 | if (...) ... | semmle.label | successor | | Conditions.cs:5:9:6:16 | if (...) ... | Conditions.cs:5:13:5:15 | access to parameter inc | semmle.label | successor | @@ -776,9 +1355,9 @@ | Conditions.cs:79:17:79:25 | ... = ... | Conditions.cs:74:9:80:9 | foreach (... ... in ...) ... | semmle.label | successor | | Conditions.cs:79:17:79:26 | ...; | Conditions.cs:79:21:79:25 | false | semmle.label | successor | | Conditions.cs:79:21:79:25 | false | Conditions.cs:79:17:79:25 | ... = ... | semmle.label | successor | -| Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:12:81:12 | access to local variable b | semmle.label | successor | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:82:13:82:16 | ...; | semmle.label | true | -| Conditions.cs:81:12:81:12 | access to local variable b | Conditions.cs:83:16:83:16 | access to local variable x | semmle.label | false | +| Conditions.cs:81:9:82:16 | if (...) ... | Conditions.cs:81:13:81:13 | access to local variable b | semmle.label | successor | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:82:13:82:16 | ...; | semmle.label | true | +| Conditions.cs:81:13:81:13 | access to local variable b | Conditions.cs:83:16:83:16 | access to local variable x | semmle.label | false | | Conditions.cs:82:13:82:13 | access to local variable x | Conditions.cs:82:13:82:15 | ...++ | semmle.label | successor | | Conditions.cs:82:13:82:15 | ...++ | Conditions.cs:83:16:83:16 | access to local variable x | semmle.label | successor | | Conditions.cs:82:13:82:16 | ...; | Conditions.cs:82:13:82:13 | access to local variable x | semmle.label | successor | @@ -866,16 +1445,16 @@ | Conditions.cs:115:9:115:24 | ... ...; | Conditions.cs:115:20:115:23 | null | semmle.label | successor | | Conditions.cs:115:16:115:23 | String s = ... | Conditions.cs:116:9:123:9 | for (...;...;...) ... | semmle.label | successor | | Conditions.cs:115:20:115:23 | null | Conditions.cs:115:16:115:23 | String s = ... | semmle.label | successor | -| Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:116:21:116:21 | 0 | semmle.label | successor | -| Conditions.cs:116:17:116:21 | Int32 i = ... | Conditions.cs:116:24:116:24 | access to local variable i | semmle.label | successor | -| Conditions.cs:116:21:116:21 | 0 | Conditions.cs:116:17:116:21 | Int32 i = ... | semmle.label | successor | -| Conditions.cs:116:24:116:24 | access to local variable i | Conditions.cs:116:28:116:31 | access to parameter args | semmle.label | successor | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:113:10:113:11 | exit M9 | semmle.label | false | -| Conditions.cs:116:24:116:38 | ... < ... | Conditions.cs:117:9:123:9 | {...} | semmle.label | true | -| Conditions.cs:116:28:116:31 | access to parameter args | Conditions.cs:116:28:116:38 | access to property Length | semmle.label | successor | -| Conditions.cs:116:28:116:38 | access to property Length | Conditions.cs:116:24:116:38 | ... < ... | semmle.label | successor | -| Conditions.cs:116:41:116:41 | access to local variable i | Conditions.cs:116:41:116:43 | ...++ | semmle.label | successor | -| Conditions.cs:116:41:116:43 | ...++ | Conditions.cs:116:24:116:24 | access to local variable i | semmle.label | successor | +| Conditions.cs:116:9:123:9 | for (...;...;...) ... | Conditions.cs:116:22:116:22 | 0 | semmle.label | successor | +| Conditions.cs:116:18:116:22 | Int32 i = ... | Conditions.cs:116:25:116:25 | access to local variable i | semmle.label | successor | +| Conditions.cs:116:22:116:22 | 0 | Conditions.cs:116:18:116:22 | Int32 i = ... | semmle.label | successor | +| Conditions.cs:116:25:116:25 | access to local variable i | Conditions.cs:116:29:116:32 | access to parameter args | semmle.label | successor | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:113:10:113:11 | exit M9 | semmle.label | false | +| Conditions.cs:116:25:116:39 | ... < ... | Conditions.cs:117:9:123:9 | {...} | semmle.label | true | +| Conditions.cs:116:29:116:32 | access to parameter args | Conditions.cs:116:29:116:39 | access to property Length | semmle.label | successor | +| Conditions.cs:116:29:116:39 | access to property Length | Conditions.cs:116:25:116:39 | ... < ... | semmle.label | successor | +| Conditions.cs:116:42:116:42 | access to local variable i | Conditions.cs:116:42:116:44 | ...++ | semmle.label | successor | +| Conditions.cs:116:42:116:44 | ...++ | Conditions.cs:116:25:116:25 | access to local variable i | semmle.label | successor | | Conditions.cs:117:9:123:9 | {...} | Conditions.cs:118:13:118:44 | ... ...; | semmle.label | successor | | Conditions.cs:118:13:118:44 | ... ...; | Conditions.cs:118:24:118:24 | access to local variable i | semmle.label | successor | | Conditions.cs:118:17:118:43 | Boolean last = ... | Conditions.cs:119:13:120:23 | if (...) ... | semmle.label | successor | @@ -894,9 +1473,9 @@ | Conditions.cs:120:21:120:22 | [last (line 118): false] "" | Conditions.cs:120:17:120:22 | [last (line 118): false] ... = ... | semmle.label | successor | | Conditions.cs:121:13:122:25 | [last (line 118): false] if (...) ... | Conditions.cs:121:17:121:20 | [last (line 118): false] access to local variable last | semmle.label | successor | | Conditions.cs:121:13:122:25 | [last (line 118): true] if (...) ... | Conditions.cs:121:17:121:20 | [last (line 118): true] access to local variable last | semmle.label | successor | -| Conditions.cs:121:17:121:20 | [last (line 118): false] access to local variable last | Conditions.cs:116:41:116:41 | access to local variable i | semmle.label | false | +| Conditions.cs:121:17:121:20 | [last (line 118): false] access to local variable last | Conditions.cs:116:42:116:42 | access to local variable i | semmle.label | false | | Conditions.cs:121:17:121:20 | [last (line 118): true] access to local variable last | Conditions.cs:122:17:122:25 | ...; | semmle.label | true | -| Conditions.cs:122:17:122:24 | ... = ... | Conditions.cs:116:41:116:41 | access to local variable i | semmle.label | successor | +| Conditions.cs:122:17:122:24 | ... = ... | Conditions.cs:116:42:116:42 | access to local variable i | semmle.label | successor | | Conditions.cs:122:17:122:25 | ...; | Conditions.cs:122:21:122:24 | null | semmle.label | successor | | Conditions.cs:122:21:122:24 | null | Conditions.cs:122:17:122:24 | ... = ... | semmle.label | successor | | Conditions.cs:129:10:129:12 | enter M10 | Conditions.cs:130:5:141:5 | {...} | semmle.label | successor | @@ -941,6 +1520,30 @@ | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] this access | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] access to field Field1 | semmle.label | successor | | Conditions.cs:137:21:137:37 | [Field1 (line 129): true, Field2 (line 129): true] call to method ToString | Conditions.cs:131:16:131:19 | [Field1 (line 129): true, Field2 (line 129): true] true | semmle.label | successor | | Conditions.cs:137:21:137:38 | [Field1 (line 129): true, Field2 (line 129): true] ...; | Conditions.cs:137:21:137:26 | [Field1 (line 129): true, Field2 (line 129): true] this access | semmle.label | successor | +| Conditions.cs:143:10:143:12 | enter M11 | Conditions.cs:144:5:150:5 | {...} | semmle.label | successor | +| Conditions.cs:144:5:150:5 | {...} | Conditions.cs:145:9:145:30 | ... ...; | semmle.label | successor | +| Conditions.cs:145:9:145:30 | ... ...; | Conditions.cs:145:17:145:29 | ... ? ... : ... | semmle.label | successor | +| Conditions.cs:145:13:145:29 | [b (line 143): false] String s = ... | Conditions.cs:146:9:149:49 | [b (line 143): false] if (...) ... | semmle.label | successor | +| Conditions.cs:145:13:145:29 | [b (line 143): true] String s = ... | Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... | semmle.label | successor | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | semmle.label | true | +| Conditions.cs:145:17:145:17 | access to parameter b | Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | semmle.label | false | +| Conditions.cs:145:17:145:29 | ... ? ... : ... | Conditions.cs:145:17:145:17 | access to parameter b | semmle.label | successor | +| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | Conditions.cs:145:13:145:29 | [b (line 143): true] String s = ... | semmle.label | successor | +| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | Conditions.cs:145:13:145:29 | [b (line 143): false] String s = ... | semmle.label | successor | +| Conditions.cs:146:9:149:49 | [b (line 143): false] if (...) ... | Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | semmle.label | successor | +| Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... | Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | semmle.label | successor | +| Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | Conditions.cs:149:13:149:49 | ...; | semmle.label | false | +| Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | Conditions.cs:147:13:147:49 | ...; | semmle.label | true | +| Conditions.cs:147:13:147:48 | call to method WriteLine | Conditions.cs:143:10:143:12 | exit M11 | semmle.label | successor | +| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:40:147:43 | "a = " | semmle.label | successor | +| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:13:147:48 | call to method WriteLine | semmle.label | successor | +| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:45:147:45 | access to local variable s | semmle.label | successor | +| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:38:147:47 | $"..." | semmle.label | successor | +| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:143:10:143:12 | exit M11 | semmle.label | successor | +| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:40:149:43 | "b = " | semmle.label | successor | +| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:13:149:48 | call to method WriteLine | semmle.label | successor | +| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:45:149:45 | access to local variable s | semmle.label | successor | +| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:38:149:47 | $"..." | semmle.label | successor | | ExitMethods.cs:7:10:7:11 | enter M1 | ExitMethods.cs:8:5:11:5 | {...} | semmle.label | successor | | ExitMethods.cs:8:5:11:5 | {...} | ExitMethods.cs:9:9:9:25 | ...; | semmle.label | successor | | ExitMethods.cs:9:9:9:24 | call to method ErrorMaybe | ExitMethods.cs:10:9:10:15 | return ...; | semmle.label | successor | @@ -1061,23 +1664,25 @@ | ExitMethods.cs:116:38:116:38 | 1 | ExitMethods.cs:116:9:116:39 | return ...; | semmle.label | successor | | ExitMethods.cs:119:17:119:32 | enter FailingAssertion | ExitMethods.cs:120:5:123:5 | {...} | semmle.label | successor | | ExitMethods.cs:120:5:123:5 | {...} | ExitMethods.cs:121:9:121:29 | ...; | semmle.label | successor | -| ExitMethods.cs:121:9:121:28 | call to method IsTrue | ExitMethods.cs:119:17:119:32 | exit FailingAssertion | semmle.label | exception(AssertFailedException) | +| ExitMethods.cs:121:9:121:28 | [assertion failure] call to method IsTrue | ExitMethods.cs:119:17:119:32 | exit FailingAssertion | semmle.label | exception(AssertFailedException) | | ExitMethods.cs:121:9:121:29 | ...; | ExitMethods.cs:121:23:121:27 | false | semmle.label | successor | -| ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:121:9:121:28 | call to method IsTrue | semmle.label | successor | +| ExitMethods.cs:121:23:121:27 | false | ExitMethods.cs:121:9:121:28 | [assertion failure] call to method IsTrue | semmle.label | false | | ExitMethods.cs:125:17:125:33 | enter FailingAssertion2 | ExitMethods.cs:126:5:129:5 | {...} | semmle.label | successor | | ExitMethods.cs:126:5:129:5 | {...} | ExitMethods.cs:127:9:127:27 | ...; | semmle.label | successor | | ExitMethods.cs:127:9:127:26 | call to method FailingAssertion | ExitMethods.cs:125:17:125:33 | exit FailingAssertion2 | semmle.label | exception(AssertFailedException) | | ExitMethods.cs:127:9:127:26 | this access | ExitMethods.cs:127:9:127:26 | call to method FailingAssertion | semmle.label | successor | | ExitMethods.cs:127:9:127:27 | ...; | ExitMethods.cs:127:9:127:26 | this access | semmle.label | successor | | ExitMethods.cs:131:10:131:20 | enter AssertFalse | ExitMethods.cs:131:48:131:48 | access to parameter b | semmle.label | successor | -| ExitMethods.cs:131:33:131:49 | call to method IsFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | semmle.label | successor | -| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:33:131:49 | call to method IsFalse | semmle.label | successor | +| ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | semmle.label | exception(AssertFailedException) | +| ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | ExitMethods.cs:131:10:131:20 | exit AssertFalse | semmle.label | successor | +| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:33:131:49 | [assertion failure] call to method IsFalse | semmle.label | true | +| ExitMethods.cs:131:48:131:48 | access to parameter b | ExitMethods.cs:131:33:131:49 | [assertion success] call to method IsFalse | semmle.label | false | | ExitMethods.cs:133:17:133:33 | enter FailingAssertion3 | ExitMethods.cs:134:5:137:5 | {...} | semmle.label | successor | | ExitMethods.cs:134:5:137:5 | {...} | ExitMethods.cs:135:9:135:26 | ...; | semmle.label | successor | -| ExitMethods.cs:135:9:135:25 | call to method AssertFalse | ExitMethods.cs:133:17:133:33 | exit FailingAssertion3 | semmle.label | exception(AssertFailedException) | +| ExitMethods.cs:135:9:135:25 | [assertion failure] call to method AssertFalse | ExitMethods.cs:133:17:133:33 | exit FailingAssertion3 | semmle.label | exception(AssertFailedException) | | ExitMethods.cs:135:9:135:25 | this access | ExitMethods.cs:135:21:135:24 | true | semmle.label | successor | | ExitMethods.cs:135:9:135:26 | ...; | ExitMethods.cs:135:9:135:25 | this access | semmle.label | successor | -| ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:135:9:135:25 | call to method AssertFalse | semmle.label | successor | +| ExitMethods.cs:135:21:135:24 | true | ExitMethods.cs:135:9:135:25 | [assertion failure] call to method AssertFalse | semmle.label | true | | Extensions.cs:5:23:5:29 | enter ToInt32 | Extensions.cs:6:5:8:5 | {...} | semmle.label | successor | | Extensions.cs:6:5:8:5 | {...} | Extensions.cs:7:28:7:28 | access to parameter s | semmle.label | successor | | Extensions.cs:7:9:7:30 | return ...; | Extensions.cs:5:23:5:29 | exit ToInt32 | semmle.label | return | @@ -1930,8 +2535,9 @@ | Initializers.cs:14:47:14:47 | access to property G | Initializers.cs:14:47:14:51 | ... = ... | semmle.label | successor | | Initializers.cs:14:47:14:51 | ... = ... | Initializers.cs:14:38:14:53 | { ..., ... } | semmle.label | successor | | Initializers.cs:14:51:14:51 | 1 | Initializers.cs:14:47:14:47 | access to property G | semmle.label | successor | -| Initializers.cs:15:9:15:64 | ... ...; | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | semmle.label | successor | +| Initializers.cs:15:9:15:64 | ... ...; | Initializers.cs:15:18:15:63 | 2 | semmle.label | successor | | Initializers.cs:15:13:15:63 | Initializers[] iz = ... | Initializers.cs:12:10:12:10 | exit M | semmle.label | successor | +| Initializers.cs:15:18:15:63 | 2 | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | semmle.label | successor | | Initializers.cs:15:18:15:63 | array creation of type Initializers[] | Initializers.cs:15:39:15:39 | access to local variable i | semmle.label | successor | | Initializers.cs:15:37:15:63 | { ..., ... } | Initializers.cs:15:13:15:63 | Initializers[] iz = ... | semmle.label | successor | | Initializers.cs:15:39:15:39 | access to local variable i | Initializers.cs:15:59:15:60 | "" | semmle.label | successor | @@ -2088,31 +2694,32 @@ | LoopUnrolling.cs:9:13:9:16 | access to parameter args | LoopUnrolling.cs:9:13:9:23 | access to property Length | semmle.label | successor | | LoopUnrolling.cs:9:13:9:23 | access to property Length | LoopUnrolling.cs:9:28:9:28 | 0 | semmle.label | successor | | LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:10:13:10:19 | return ...; | semmle.label | true | -| LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:11:28:11:31 | access to parameter args | semmle.label | false | +| LoopUnrolling.cs:9:13:9:28 | ... == ... | LoopUnrolling.cs:11:29:11:32 | access to parameter args | semmle.label | false | | LoopUnrolling.cs:9:28:9:28 | 0 | LoopUnrolling.cs:9:13:9:28 | ... == ... | semmle.label | successor | | LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:7:10:7:11 | exit M1 | semmle.label | return | -| LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | LoopUnrolling.cs:11:21:11:23 | String arg | semmle.label | non-empty | +| LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | LoopUnrolling.cs:11:22:11:24 | String arg | semmle.label | non-empty | | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:7:10:7:11 | exit M1 | semmle.label | empty | -| LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:11:21:11:23 | String arg | semmle.label | non-empty | -| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:12:13:12:35 | ...; | semmle.label | successor | -| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:11:22:11:24 | String arg | semmle.label | non-empty | +| LoopUnrolling.cs:11:22:11:24 | String arg | LoopUnrolling.cs:12:13:12:35 | ...; | semmle.label | successor | +| LoopUnrolling.cs:11:29:11:32 | access to parameter args | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | semmle.label | successor | | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | semmle.label | successor | | LoopUnrolling.cs:12:13:12:35 | ...; | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | semmle.label | successor | | LoopUnrolling.cs:12:31:12:33 | access to local variable arg | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | semmle.label | successor | | LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:16:5:20:5 | {...} | semmle.label | successor | -| LoopUnrolling.cs:16:5:20:5 | {...} | LoopUnrolling.cs:17:9:17:47 | ... ...; | semmle.label | successor | -| LoopUnrolling.cs:17:9:17:47 | ... ...; | LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | semmle.label | successor | -| LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | LoopUnrolling.cs:18:26:18:27 | access to local variable xs | semmle.label | successor | -| LoopUnrolling.cs:17:18:17:46 | array creation of type String[] | LoopUnrolling.cs:17:32:17:34 | "a" | semmle.label | successor | -| LoopUnrolling.cs:17:30:17:46 | { ..., ... } | LoopUnrolling.cs:17:13:17:46 | String[] xs = ... | semmle.label | successor | -| LoopUnrolling.cs:17:32:17:34 | "a" | LoopUnrolling.cs:17:37:17:39 | "b" | semmle.label | successor | -| LoopUnrolling.cs:17:37:17:39 | "b" | LoopUnrolling.cs:17:42:17:44 | "c" | semmle.label | successor | -| LoopUnrolling.cs:17:42:17:44 | "c" | LoopUnrolling.cs:17:30:17:46 | { ..., ... } | semmle.label | successor | -| LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | LoopUnrolling.cs:18:21:18:21 | String x | semmle.label | non-empty | +| LoopUnrolling.cs:16:5:20:5 | {...} | LoopUnrolling.cs:17:9:17:48 | ... ...; | semmle.label | successor | +| LoopUnrolling.cs:17:9:17:48 | ... ...; | LoopUnrolling.cs:17:18:17:47 | 3 | semmle.label | successor | +| LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | LoopUnrolling.cs:18:27:18:28 | access to local variable xs | semmle.label | successor | +| LoopUnrolling.cs:17:18:17:47 | 3 | LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | semmle.label | successor | +| LoopUnrolling.cs:17:18:17:47 | array creation of type String[] | LoopUnrolling.cs:17:33:17:35 | "a" | semmle.label | successor | +| LoopUnrolling.cs:17:31:17:47 | { ..., ... } | LoopUnrolling.cs:17:13:17:47 | String[] xs = ... | semmle.label | successor | +| LoopUnrolling.cs:17:33:17:35 | "a" | LoopUnrolling.cs:17:38:17:40 | "b" | semmle.label | successor | +| LoopUnrolling.cs:17:38:17:40 | "b" | LoopUnrolling.cs:17:43:17:45 | "c" | semmle.label | successor | +| LoopUnrolling.cs:17:43:17:45 | "c" | LoopUnrolling.cs:17:31:17:47 | { ..., ... } | semmle.label | successor | +| LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | LoopUnrolling.cs:18:22:18:22 | String x | semmle.label | non-empty | | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:15:10:15:11 | exit M2 | semmle.label | empty | -| LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:18:21:18:21 | String x | semmle.label | non-empty | -| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:19:13:19:33 | ...; | semmle.label | successor | -| LoopUnrolling.cs:18:26:18:27 | access to local variable xs | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:18:22:18:22 | String x | semmle.label | non-empty | +| LoopUnrolling.cs:18:22:18:22 | String x | LoopUnrolling.cs:19:13:19:33 | ...; | semmle.label | successor | +| LoopUnrolling.cs:18:27:18:28 | access to local variable xs | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | semmle.label | successor | | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | semmle.label | successor | | LoopUnrolling.cs:19:13:19:33 | ...; | LoopUnrolling.cs:19:31:19:31 | access to local variable x | semmle.label | successor | | LoopUnrolling.cs:19:31:19:31 | access to local variable x | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | semmle.label | successor | @@ -2133,83 +2740,82 @@ | LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:30:5:34:5 | {...} | semmle.label | successor | | LoopUnrolling.cs:30:5:34:5 | {...} | LoopUnrolling.cs:31:9:31:31 | ... ...; | semmle.label | successor | | LoopUnrolling.cs:31:9:31:31 | ... ...; | LoopUnrolling.cs:31:29:31:29 | 0 | semmle.label | successor | -| LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | LoopUnrolling.cs:32:26:32:27 | access to local variable xs | semmle.label | successor | +| LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | LoopUnrolling.cs:32:27:32:28 | access to local variable xs | semmle.label | successor | | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | LoopUnrolling.cs:31:13:31:30 | String[] xs = ... | semmle.label | successor | | LoopUnrolling.cs:31:29:31:29 | 0 | LoopUnrolling.cs:31:18:31:30 | array creation of type String[] | semmle.label | successor | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | exit M4 | semmle.label | empty | -| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:21:32:21 | String x | semmle.label | non-empty | -| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:33:13:33:33 | ...; | semmle.label | successor | -| LoopUnrolling.cs:32:26:32:27 | access to local variable xs | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | semmle.label | successor | -| LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | semmle.label | successor | -| LoopUnrolling.cs:33:13:33:33 | ...; | LoopUnrolling.cs:33:31:33:31 | access to local variable x | semmle.label | successor | -| LoopUnrolling.cs:33:31:33:31 | access to local variable x | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | semmle.label | successor | +| LoopUnrolling.cs:32:9:33:33 | [skip (line 32)] foreach (... ... in ...) ... | LoopUnrolling.cs:29:10:29:11 | exit M4 | semmle.label | empty | +| LoopUnrolling.cs:32:27:32:28 | access to local variable xs | LoopUnrolling.cs:32:9:33:33 | [skip (line 32)] foreach (... ... in ...) ... | semmle.label | successor | | LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:37:5:43:5 | {...} | semmle.label | successor | -| LoopUnrolling.cs:37:5:43:5 | {...} | LoopUnrolling.cs:38:9:38:47 | ... ...; | semmle.label | successor | -| LoopUnrolling.cs:38:9:38:47 | ... ...; | LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | semmle.label | successor | -| LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | LoopUnrolling.cs:39:9:39:47 | ... ...; | semmle.label | successor | -| LoopUnrolling.cs:38:18:38:46 | array creation of type String[] | LoopUnrolling.cs:38:32:38:34 | "a" | semmle.label | successor | -| LoopUnrolling.cs:38:30:38:46 | { ..., ... } | LoopUnrolling.cs:38:13:38:46 | String[] xs = ... | semmle.label | successor | -| LoopUnrolling.cs:38:32:38:34 | "a" | LoopUnrolling.cs:38:37:38:39 | "b" | semmle.label | successor | -| LoopUnrolling.cs:38:37:38:39 | "b" | LoopUnrolling.cs:38:42:38:44 | "c" | semmle.label | successor | -| LoopUnrolling.cs:38:42:38:44 | "c" | LoopUnrolling.cs:38:30:38:46 | { ..., ... } | semmle.label | successor | -| LoopUnrolling.cs:39:9:39:47 | ... ...; | LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | semmle.label | successor | -| LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | LoopUnrolling.cs:40:26:40:27 | access to local variable xs | semmle.label | successor | -| LoopUnrolling.cs:39:18:39:46 | array creation of type String[] | LoopUnrolling.cs:39:32:39:34 | "0" | semmle.label | successor | -| LoopUnrolling.cs:39:30:39:46 | { ..., ... } | LoopUnrolling.cs:39:13:39:46 | String[] ys = ... | semmle.label | successor | -| LoopUnrolling.cs:39:32:39:34 | "0" | LoopUnrolling.cs:39:37:39:39 | "1" | semmle.label | successor | -| LoopUnrolling.cs:39:37:39:39 | "1" | LoopUnrolling.cs:39:42:39:44 | "2" | semmle.label | successor | -| LoopUnrolling.cs:39:42:39:44 | "2" | LoopUnrolling.cs:39:30:39:46 | { ..., ... } | semmle.label | successor | -| LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | LoopUnrolling.cs:40:21:40:21 | String x | semmle.label | non-empty | +| LoopUnrolling.cs:37:5:43:5 | {...} | LoopUnrolling.cs:38:9:38:48 | ... ...; | semmle.label | successor | +| LoopUnrolling.cs:38:9:38:48 | ... ...; | LoopUnrolling.cs:38:18:38:47 | 3 | semmle.label | successor | +| LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | LoopUnrolling.cs:39:9:39:48 | ... ...; | semmle.label | successor | +| LoopUnrolling.cs:38:18:38:47 | 3 | LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | semmle.label | successor | +| LoopUnrolling.cs:38:18:38:47 | array creation of type String[] | LoopUnrolling.cs:38:33:38:35 | "a" | semmle.label | successor | +| LoopUnrolling.cs:38:31:38:47 | { ..., ... } | LoopUnrolling.cs:38:13:38:47 | String[] xs = ... | semmle.label | successor | +| LoopUnrolling.cs:38:33:38:35 | "a" | LoopUnrolling.cs:38:38:38:40 | "b" | semmle.label | successor | +| LoopUnrolling.cs:38:38:38:40 | "b" | LoopUnrolling.cs:38:43:38:45 | "c" | semmle.label | successor | +| LoopUnrolling.cs:38:43:38:45 | "c" | LoopUnrolling.cs:38:31:38:47 | { ..., ... } | semmle.label | successor | +| LoopUnrolling.cs:39:9:39:48 | ... ...; | LoopUnrolling.cs:39:18:39:47 | 3 | semmle.label | successor | +| LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | LoopUnrolling.cs:40:27:40:28 | access to local variable xs | semmle.label | successor | +| LoopUnrolling.cs:39:18:39:47 | 3 | LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | semmle.label | successor | +| LoopUnrolling.cs:39:18:39:47 | array creation of type String[] | LoopUnrolling.cs:39:33:39:35 | "0" | semmle.label | successor | +| LoopUnrolling.cs:39:31:39:47 | { ..., ... } | LoopUnrolling.cs:39:13:39:47 | String[] ys = ... | semmle.label | successor | +| LoopUnrolling.cs:39:33:39:35 | "0" | LoopUnrolling.cs:39:38:39:40 | "1" | semmle.label | successor | +| LoopUnrolling.cs:39:38:39:40 | "1" | LoopUnrolling.cs:39:43:39:45 | "2" | semmle.label | successor | +| LoopUnrolling.cs:39:43:39:45 | "2" | LoopUnrolling.cs:39:31:39:47 | { ..., ... } | semmle.label | successor | +| LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | LoopUnrolling.cs:40:22:40:22 | String x | semmle.label | non-empty | | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:36:10:36:11 | exit M5 | semmle.label | empty | -| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:21:40:21 | String x | semmle.label | non-empty | -| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:41:30:41:31 | access to local variable ys | semmle.label | successor | -| LoopUnrolling.cs:40:26:40:27 | access to local variable xs | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | semmle.label | successor | -| LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | LoopUnrolling.cs:41:25:41:25 | String y | semmle.label | non-empty | +| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:22:40:22 | String x | semmle.label | non-empty | +| LoopUnrolling.cs:40:22:40:22 | String x | LoopUnrolling.cs:41:31:41:32 | access to local variable ys | semmle.label | successor | +| LoopUnrolling.cs:40:27:40:28 | access to local variable xs | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | LoopUnrolling.cs:41:26:41:26 | String y | semmle.label | non-empty | | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | semmle.label | empty | -| LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:25:41:25 | String y | semmle.label | non-empty | -| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:42:17:42:41 | ...; | semmle.label | successor | -| LoopUnrolling.cs:41:30:41:31 | access to local variable ys | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:26:41:26 | String y | semmle.label | non-empty | +| LoopUnrolling.cs:41:26:41:26 | String y | LoopUnrolling.cs:42:17:42:41 | ...; | semmle.label | successor | +| LoopUnrolling.cs:41:31:41:32 | access to local variable ys | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | semmle.label | successor | | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | semmle.label | successor | | LoopUnrolling.cs:42:17:42:41 | ...; | LoopUnrolling.cs:42:35:42:35 | access to local variable x | semmle.label | successor | | LoopUnrolling.cs:42:35:42:35 | access to local variable x | LoopUnrolling.cs:42:39:42:39 | access to local variable y | semmle.label | successor | | LoopUnrolling.cs:42:35:42:39 | ... + ... | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | semmle.label | successor | | LoopUnrolling.cs:42:39:42:39 | access to local variable y | LoopUnrolling.cs:42:35:42:39 | ... + ... | semmle.label | successor | | LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:46:5:53:5 | {...} | semmle.label | successor | -| LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:47:9:47:47 | ... ...; | semmle.label | successor | -| LoopUnrolling.cs:47:9:47:47 | ... ...; | LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | semmle.label | successor | -| LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | LoopUnrolling.cs:48:26:48:27 | access to local variable xs | semmle.label | successor | -| LoopUnrolling.cs:47:18:47:46 | array creation of type String[] | LoopUnrolling.cs:47:32:47:34 | "a" | semmle.label | successor | -| LoopUnrolling.cs:47:30:47:46 | { ..., ... } | LoopUnrolling.cs:47:13:47:46 | String[] xs = ... | semmle.label | successor | -| LoopUnrolling.cs:47:32:47:34 | "a" | LoopUnrolling.cs:47:37:47:39 | "b" | semmle.label | successor | -| LoopUnrolling.cs:47:37:47:39 | "b" | LoopUnrolling.cs:47:42:47:44 | "c" | semmle.label | successor | -| LoopUnrolling.cs:47:42:47:44 | "c" | LoopUnrolling.cs:47:30:47:46 | { ..., ... } | semmle.label | successor | -| LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | LoopUnrolling.cs:48:21:48:21 | String x | semmle.label | non-empty | -| LoopUnrolling.cs:48:21:48:21 | String x | LoopUnrolling.cs:49:9:52:9 | {...} | semmle.label | successor | -| LoopUnrolling.cs:48:26:48:27 | access to local variable xs | LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | semmle.label | successor | -| LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:50:13:50:17 | Label: | semmle.label | successor | -| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:50:20:50:40 | ...; | semmle.label | successor | -| LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | LoopUnrolling.cs:51:13:51:23 | goto ...; | semmle.label | successor | -| LoopUnrolling.cs:50:20:50:40 | ...; | LoopUnrolling.cs:50:38:50:38 | access to local variable x | semmle.label | successor | -| LoopUnrolling.cs:50:38:50:38 | access to local variable x | LoopUnrolling.cs:50:20:50:39 | call to method WriteLine | semmle.label | successor | -| LoopUnrolling.cs:51:13:51:23 | goto ...; | LoopUnrolling.cs:50:13:50:17 | Label: | semmle.label | goto(Label) | +| LoopUnrolling.cs:46:5:53:5 | {...} | LoopUnrolling.cs:47:9:47:48 | ... ...; | semmle.label | successor | +| LoopUnrolling.cs:47:9:47:48 | ... ...; | LoopUnrolling.cs:47:18:47:47 | 3 | semmle.label | successor | +| LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | LoopUnrolling.cs:48:27:48:28 | access to local variable xs | semmle.label | successor | +| LoopUnrolling.cs:47:18:47:47 | 3 | LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | semmle.label | successor | +| LoopUnrolling.cs:47:18:47:47 | array creation of type String[] | LoopUnrolling.cs:47:33:47:35 | "a" | semmle.label | successor | +| LoopUnrolling.cs:47:31:47:47 | { ..., ... } | LoopUnrolling.cs:47:13:47:47 | String[] xs = ... | semmle.label | successor | +| LoopUnrolling.cs:47:33:47:35 | "a" | LoopUnrolling.cs:47:38:47:40 | "b" | semmle.label | successor | +| LoopUnrolling.cs:47:38:47:40 | "b" | LoopUnrolling.cs:47:43:47:45 | "c" | semmle.label | successor | +| LoopUnrolling.cs:47:43:47:45 | "c" | LoopUnrolling.cs:47:31:47:47 | { ..., ... } | semmle.label | successor | +| LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | LoopUnrolling.cs:48:22:48:22 | String x | semmle.label | non-empty | +| LoopUnrolling.cs:48:22:48:22 | String x | LoopUnrolling.cs:49:9:52:9 | {...} | semmle.label | successor | +| LoopUnrolling.cs:48:27:48:28 | access to local variable xs | LoopUnrolling.cs:48:9:52:9 | [unroll (line 48)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:49:9:52:9 | {...} | LoopUnrolling.cs:50:9:50:13 | Label: | semmle.label | successor | +| LoopUnrolling.cs:50:9:50:13 | Label: | LoopUnrolling.cs:50:16:50:36 | ...; | semmle.label | successor | +| LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | LoopUnrolling.cs:51:13:51:23 | goto ...; | semmle.label | successor | +| LoopUnrolling.cs:50:16:50:36 | ...; | LoopUnrolling.cs:50:34:50:34 | access to local variable x | semmle.label | successor | +| LoopUnrolling.cs:50:34:50:34 | access to local variable x | LoopUnrolling.cs:50:16:50:35 | call to method WriteLine | semmle.label | successor | +| LoopUnrolling.cs:51:13:51:23 | goto ...; | LoopUnrolling.cs:50:9:50:13 | Label: | semmle.label | goto(Label) | | LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:56:5:65:5 | {...} | semmle.label | successor | -| LoopUnrolling.cs:56:5:65:5 | {...} | LoopUnrolling.cs:57:9:57:47 | ... ...; | semmle.label | successor | -| LoopUnrolling.cs:57:9:57:47 | ... ...; | LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | semmle.label | successor | -| LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | LoopUnrolling.cs:58:26:58:27 | access to local variable xs | semmle.label | successor | -| LoopUnrolling.cs:57:18:57:46 | array creation of type String[] | LoopUnrolling.cs:57:32:57:34 | "a" | semmle.label | successor | -| LoopUnrolling.cs:57:30:57:46 | { ..., ... } | LoopUnrolling.cs:57:13:57:46 | String[] xs = ... | semmle.label | successor | -| LoopUnrolling.cs:57:32:57:34 | "a" | LoopUnrolling.cs:57:37:57:39 | "b" | semmle.label | successor | -| LoopUnrolling.cs:57:37:57:39 | "b" | LoopUnrolling.cs:57:42:57:44 | "c" | semmle.label | successor | -| LoopUnrolling.cs:57:42:57:44 | "c" | LoopUnrolling.cs:57:30:57:46 | { ..., ... } | semmle.label | successor | +| LoopUnrolling.cs:56:5:65:5 | {...} | LoopUnrolling.cs:57:9:57:48 | ... ...; | semmle.label | successor | +| LoopUnrolling.cs:57:9:57:48 | ... ...; | LoopUnrolling.cs:57:18:57:47 | 3 | semmle.label | successor | +| LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | LoopUnrolling.cs:58:27:58:28 | access to local variable xs | semmle.label | successor | +| LoopUnrolling.cs:57:18:57:47 | 3 | LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | semmle.label | successor | +| LoopUnrolling.cs:57:18:57:47 | array creation of type String[] | LoopUnrolling.cs:57:33:57:35 | "a" | semmle.label | successor | +| LoopUnrolling.cs:57:31:57:47 | { ..., ... } | LoopUnrolling.cs:57:13:57:47 | String[] xs = ... | semmle.label | successor | +| LoopUnrolling.cs:57:33:57:35 | "a" | LoopUnrolling.cs:57:38:57:40 | "b" | semmle.label | successor | +| LoopUnrolling.cs:57:38:57:40 | "b" | LoopUnrolling.cs:57:43:57:45 | "c" | semmle.label | successor | +| LoopUnrolling.cs:57:43:57:45 | "c" | LoopUnrolling.cs:57:31:57:47 | { ..., ... } | semmle.label | successor | | LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | LoopUnrolling.cs:55:10:55:11 | exit M7 | semmle.label | empty | -| LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | semmle.label | non-empty | +| LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | semmle.label | non-empty | | LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | LoopUnrolling.cs:55:10:55:11 | exit M7 | semmle.label | empty | -| LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | semmle.label | non-empty | -| LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | LoopUnrolling.cs:58:21:58:21 | String x | semmle.label | non-empty | -| LoopUnrolling.cs:58:21:58:21 | String x | LoopUnrolling.cs:59:9:64:9 | {...} | semmle.label | successor | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | semmle.label | successor | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | semmle.label | successor | -| LoopUnrolling.cs:58:26:58:27 | access to local variable xs | LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | semmle.label | non-empty | +| LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | LoopUnrolling.cs:58:22:58:22 | String x | semmle.label | non-empty | +| LoopUnrolling.cs:58:22:58:22 | String x | LoopUnrolling.cs:59:9:64:9 | {...} | semmle.label | successor | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | semmle.label | successor | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | semmle.label | successor | +| LoopUnrolling.cs:58:27:58:28 | access to local variable xs | LoopUnrolling.cs:58:9:64:9 | [unroll (line 58)] foreach (... ... in ...) ... | semmle.label | successor | | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | LoopUnrolling.cs:60:13:61:37 | [b (line 55): false] if (...) ... | semmle.label | successor | | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | LoopUnrolling.cs:60:13:61:37 | [b (line 55): true] if (...) ... | semmle.label | successor | | LoopUnrolling.cs:59:9:64:9 | {...} | LoopUnrolling.cs:60:13:61:37 | if (...) ... | semmle.label | successor | @@ -2239,15 +2845,220 @@ | LoopUnrolling.cs:69:14:69:23 | call to method Any | LoopUnrolling.cs:71:9:71:21 | ...; | semmle.label | true | | LoopUnrolling.cs:70:13:70:19 | return ...; | LoopUnrolling.cs:67:10:67:11 | exit M8 | semmle.label | return | | LoopUnrolling.cs:71:9:71:12 | access to parameter args | LoopUnrolling.cs:71:9:71:20 | call to method Clear | semmle.label | successor | -| LoopUnrolling.cs:71:9:71:20 | call to method Clear | LoopUnrolling.cs:72:28:72:31 | access to parameter args | semmle.label | successor | +| LoopUnrolling.cs:71:9:71:20 | call to method Clear | LoopUnrolling.cs:72:29:72:32 | access to parameter args | semmle.label | successor | | LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:71:9:71:12 | access to parameter args | semmle.label | successor | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:67:10:67:11 | exit M8 | semmle.label | empty | -| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:21:72:23 | String arg | semmle.label | non-empty | -| LoopUnrolling.cs:72:21:72:23 | String arg | LoopUnrolling.cs:73:13:73:35 | ...; | semmle.label | successor | -| LoopUnrolling.cs:72:28:72:31 | access to parameter args | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | semmle.label | successor | -| LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | semmle.label | successor | -| LoopUnrolling.cs:73:13:73:35 | ...; | LoopUnrolling.cs:73:31:73:33 | access to local variable arg | semmle.label | successor | -| LoopUnrolling.cs:73:31:73:33 | access to local variable arg | LoopUnrolling.cs:73:13:73:34 | call to method WriteLine | semmle.label | successor | +| LoopUnrolling.cs:72:9:73:35 | [skip (line 72)] foreach (... ... in ...) ... | LoopUnrolling.cs:67:10:67:11 | exit M8 | semmle.label | empty | +| LoopUnrolling.cs:72:29:72:32 | access to parameter args | LoopUnrolling.cs:72:9:73:35 | [skip (line 72)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:76:10:76:11 | enter M9 | LoopUnrolling.cs:77:5:83:5 | {...} | semmle.label | successor | +| LoopUnrolling.cs:77:5:83:5 | {...} | LoopUnrolling.cs:78:9:78:34 | ... ...; | semmle.label | successor | +| LoopUnrolling.cs:78:9:78:34 | ... ...; | LoopUnrolling.cs:78:29:78:29 | 2 | semmle.label | successor | +| LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | LoopUnrolling.cs:79:27:79:28 | access to local variable xs | semmle.label | successor | +| LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | LoopUnrolling.cs:78:13:78:33 | String[,] xs = ... | semmle.label | successor | +| LoopUnrolling.cs:78:29:78:29 | 2 | LoopUnrolling.cs:78:32:78:32 | 0 | semmle.label | successor | +| LoopUnrolling.cs:78:32:78:32 | 0 | LoopUnrolling.cs:78:18:78:33 | array creation of type String[,] | semmle.label | successor | +| LoopUnrolling.cs:79:9:82:9 | [skip (line 79)] foreach (... ... in ...) ... | LoopUnrolling.cs:76:10:76:11 | exit M9 | semmle.label | empty | +| LoopUnrolling.cs:79:27:79:28 | access to local variable xs | LoopUnrolling.cs:79:9:82:9 | [skip (line 79)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:85:10:85:12 | enter M10 | LoopUnrolling.cs:86:5:92:5 | {...} | semmle.label | successor | +| LoopUnrolling.cs:86:5:92:5 | {...} | LoopUnrolling.cs:87:9:87:34 | ... ...; | semmle.label | successor | +| LoopUnrolling.cs:87:9:87:34 | ... ...; | LoopUnrolling.cs:87:29:87:29 | 0 | semmle.label | successor | +| LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | LoopUnrolling.cs:88:27:88:28 | access to local variable xs | semmle.label | successor | +| LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | LoopUnrolling.cs:87:13:87:33 | String[,] xs = ... | semmle.label | successor | +| LoopUnrolling.cs:87:29:87:29 | 0 | LoopUnrolling.cs:87:32:87:32 | 2 | semmle.label | successor | +| LoopUnrolling.cs:87:32:87:32 | 2 | LoopUnrolling.cs:87:18:87:33 | array creation of type String[,] | semmle.label | successor | +| LoopUnrolling.cs:88:9:91:9 | [skip (line 88)] foreach (... ... in ...) ... | LoopUnrolling.cs:85:10:85:12 | exit M10 | semmle.label | empty | +| LoopUnrolling.cs:88:27:88:28 | access to local variable xs | LoopUnrolling.cs:88:9:91:9 | [skip (line 88)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:94:10:94:12 | enter M11 | LoopUnrolling.cs:95:5:101:5 | {...} | semmle.label | successor | +| LoopUnrolling.cs:95:5:101:5 | {...} | LoopUnrolling.cs:96:9:96:34 | ... ...; | semmle.label | successor | +| LoopUnrolling.cs:96:9:96:34 | ... ...; | LoopUnrolling.cs:96:29:96:29 | 2 | semmle.label | successor | +| LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | LoopUnrolling.cs:97:27:97:28 | access to local variable xs | semmle.label | successor | +| LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | LoopUnrolling.cs:96:13:96:33 | String[,] xs = ... | semmle.label | successor | +| LoopUnrolling.cs:96:29:96:29 | 2 | LoopUnrolling.cs:96:32:96:32 | 2 | semmle.label | successor | +| LoopUnrolling.cs:96:32:96:32 | 2 | LoopUnrolling.cs:96:18:96:33 | array creation of type String[,] | semmle.label | successor | +| LoopUnrolling.cs:97:9:100:9 | [unroll (line 97)] foreach (... ... in ...) ... | LoopUnrolling.cs:97:22:97:22 | String x | semmle.label | non-empty | +| LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:94:10:94:12 | exit M11 | semmle.label | empty | +| LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | LoopUnrolling.cs:97:22:97:22 | String x | semmle.label | non-empty | +| LoopUnrolling.cs:97:22:97:22 | String x | LoopUnrolling.cs:98:9:100:9 | {...} | semmle.label | successor | +| LoopUnrolling.cs:97:27:97:28 | access to local variable xs | LoopUnrolling.cs:97:9:100:9 | [unroll (line 97)] foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:98:9:100:9 | {...} | LoopUnrolling.cs:99:13:99:33 | ...; | semmle.label | successor | +| LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | LoopUnrolling.cs:97:9:100:9 | foreach (... ... in ...) ... | semmle.label | successor | +| LoopUnrolling.cs:99:13:99:33 | ...; | LoopUnrolling.cs:99:31:99:31 | access to local variable x | semmle.label | successor | +| LoopUnrolling.cs:99:31:99:31 | access to local variable x | LoopUnrolling.cs:99:13:99:32 | call to method WriteLine | semmle.label | successor | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationA.cs:6:28:6:31 | null | semmle.label | successor | +| MultiImplementationA.cs:6:22:6:31 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | semmle.label | successor | +| MultiImplementationA.cs:6:22:6:31 | throw ... | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:6:22:6:31 | throw ... | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:6:28:6:31 | null | MultiImplementationA.cs:6:22:6:31 | throw ... | semmle.label | successor | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | semmle.label | successor | +| MultiImplementationA.cs:7:21:7:23 | enter get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | semmle.label | successor | +| MultiImplementationA.cs:7:25:7:39 | {...} | MultiImplementationA.cs:7:33:7:36 | null | semmle.label | successor | +| MultiImplementationA.cs:7:27:7:37 | throw ...; | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:7:27:7:37 | throw ...; | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:7:33:7:36 | null | MultiImplementationA.cs:7:27:7:37 | throw ...; | semmle.label | successor | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | semmle.label | successor | +| MultiImplementationA.cs:7:41:7:43 | enter set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | semmle.label | successor | +| MultiImplementationA.cs:7:45:7:59 | {...} | MultiImplementationA.cs:7:53:7:56 | null | semmle.label | successor | +| MultiImplementationA.cs:7:47:7:57 | throw ...; | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:7:47:7:57 | throw ...; | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:7:53:7:56 | null | MultiImplementationA.cs:7:47:7:57 | throw ...; | semmle.label | successor | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationA.cs:8:29:8:32 | null | semmle.label | successor | +| MultiImplementationA.cs:8:16:8:16 | enter M | MultiImplementationB.cs:5:23:5:23 | 2 | semmle.label | successor | +| MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationA.cs:8:16:8:16 | exit M | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationB.cs:5:16:5:16 | exit M | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:23:8:32 | throw ... | semmle.label | successor | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:20:13:20 | 0 | semmle.label | successor | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:20 | ... = ... | semmle.label | successor | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | exit get_Item | semmle.label | successor | +| MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | exit get_Item | semmle.label | successor | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | semmle.label | successor | +| MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationB.cs:12:37:12:40 | null | semmle.label | successor | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | semmle.label | successor | +| MultiImplementationA.cs:15:36:15:38 | enter get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | semmle.label | successor | +| MultiImplementationA.cs:15:40:15:52 | {...} | MultiImplementationA.cs:15:49:15:49 | access to parameter s | semmle.label | successor | +| MultiImplementationA.cs:15:42:15:50 | return ...; | MultiImplementationA.cs:15:36:15:38 | exit get_Item | semmle.label | return | +| MultiImplementationA.cs:15:42:15:50 | return ...; | MultiImplementationB.cs:13:36:13:38 | exit get_Item | semmle.label | return | +| MultiImplementationA.cs:15:49:15:49 | access to parameter s | MultiImplementationA.cs:15:42:15:50 | return ...; | semmle.label | successor | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | semmle.label | successor | +| MultiImplementationA.cs:15:54:15:56 | enter set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | semmle.label | successor | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationA.cs:15:54:15:56 | exit set_Item | semmle.label | successor | +| MultiImplementationA.cs:15:58:15:60 | {...} | MultiImplementationB.cs:13:56:13:58 | exit set_Item | semmle.label | successor | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationA.cs:17:5:19:5 | {...} | semmle.label | successor | +| MultiImplementationA.cs:16:17:16:18 | enter M1 | MultiImplementationB.cs:15:5:17:5 | {...} | semmle.label | successor | +| MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:18:9:18:22 | M2(...) | semmle.label | successor | +| MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationA.cs:16:17:16:18 | exit M1 | semmle.label | successor | +| MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationB.cs:14:17:14:18 | exit M1 | semmle.label | successor | +| MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:21:18:21 | 0 | semmle.label | successor | +| MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | exit M2 | semmle.label | successor | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:24:20:29 | ...; | semmle.label | successor | +| MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:28:20:28 | access to parameter i | semmle.label | successor | +| MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationA.cs:20:12:20:13 | exit C2 | semmle.label | successor | +| MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationB.cs:18:12:18:13 | exit C2 | semmle.label | successor | +| MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationA.cs:20:24:20:24 | this access | semmle.label | successor | +| MultiImplementationA.cs:20:28:20:28 | access to parameter i | MultiImplementationA.cs:20:24:20:28 | ... = ... | semmle.label | successor | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | semmle.label | successor | +| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | semmle.label | successor | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:27:21:29 | {...} | semmle.label | successor | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationB.cs:19:27:19:29 | {...} | semmle.label | successor | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | semmle.label | successor | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:12:21:13 | exit C2 | semmle.label | successor | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationB.cs:19:12:19:13 | exit C2 | semmle.label | successor | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | semmle.label | successor | +| MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | semmle.label | successor | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | semmle.label | successor | +| MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | semmle.label | successor | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | semmle.label | successor | +| MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | semmle.label | successor | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | semmle.label | successor | +| MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | semmle.label | successor | +| MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:32:24:34 | ... = ... | semmle.label | successor | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:34:24:34 | 0 | semmle.label | successor | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:20:22:20:31 | {...} | semmle.label | successor | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationB.cs:18:22:18:36 | {...} | semmle.label | successor | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | access to property P | semmle.label | successor | +| MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | semmle.label | successor | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:30:28:30:37 | throw ... | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:30:34:30:37 | null | MultiImplementationA.cs:30:28:30:37 | throw ... | semmle.label | successor | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:14:36:28 | {...} | semmle.label | successor | +| MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationB.cs:32:17:32:17 | 0 | semmle.label | successor | +| MultiImplementationA.cs:36:14:36:28 | {...} | MultiImplementationA.cs:36:22:36:25 | null | semmle.label | successor | +| MultiImplementationA.cs:36:16:36:26 | throw ...; | MultiImplementationA.cs:36:9:36:10 | exit M1 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:36:16:36:26 | throw ...; | MultiImplementationB.cs:32:9:32:10 | exit M1 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:36:22:36:25 | null | MultiImplementationA.cs:36:16:36:26 | throw ...; | semmle.label | successor | +| MultiImplementationA.cs:37:9:37:10 | enter M2 | MultiImplementationA.cs:37:14:37:28 | {...} | semmle.label | successor | +| MultiImplementationA.cs:37:14:37:28 | {...} | MultiImplementationA.cs:37:22:37:25 | null | semmle.label | successor | +| MultiImplementationA.cs:37:16:37:26 | throw ...; | MultiImplementationA.cs:37:9:37:10 | exit M2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationA.cs:37:22:37:25 | null | MultiImplementationA.cs:37:16:37:26 | throw ...; | semmle.label | successor | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationA.cs:6:22:6:31 | exit get_P1 | semmle.label | successor | +| MultiImplementationB.cs:3:22:3:22 | 0 | MultiImplementationB.cs:3:22:3:22 | exit get_P1 | semmle.label | successor | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationA.cs:6:28:6:31 | null | semmle.label | successor | +| MultiImplementationB.cs:3:22:3:22 | enter get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | semmle.label | successor | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | semmle.label | successor | +| MultiImplementationB.cs:4:21:4:23 | enter get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | semmle.label | successor | +| MultiImplementationB.cs:4:25:4:37 | {...} | MultiImplementationB.cs:4:34:4:34 | 1 | semmle.label | successor | +| MultiImplementationB.cs:4:27:4:35 | return ...; | MultiImplementationA.cs:7:21:7:23 | exit get_P2 | semmle.label | return | +| MultiImplementationB.cs:4:27:4:35 | return ...; | MultiImplementationB.cs:4:21:4:23 | exit get_P2 | semmle.label | return | +| MultiImplementationB.cs:4:34:4:34 | 1 | MultiImplementationB.cs:4:27:4:35 | return ...; | semmle.label | successor | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | semmle.label | successor | +| MultiImplementationB.cs:4:39:4:41 | enter set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | semmle.label | successor | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationA.cs:7:41:7:43 | exit set_P2 | semmle.label | successor | +| MultiImplementationB.cs:4:43:4:45 | {...} | MultiImplementationB.cs:4:39:4:41 | exit set_P2 | semmle.label | successor | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationA.cs:8:29:8:32 | null | semmle.label | successor | +| MultiImplementationB.cs:5:16:5:16 | enter M | MultiImplementationB.cs:5:23:5:23 | 2 | semmle.label | successor | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | exit M | semmle.label | successor | +| MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | exit M | semmle.label | successor | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:20:11:20 | 1 | semmle.label | successor | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:20 | ... = ... | semmle.label | successor | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | semmle.label | successor | +| MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:37:12:40 | null | semmle.label | successor | +| MultiImplementationB.cs:12:31:12:40 | throw ... | MultiImplementationA.cs:14:31:14:31 | exit get_Item | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:12:31:12:40 | throw ... | MultiImplementationB.cs:12:31:12:40 | exit get_Item | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:12:37:12:40 | null | MultiImplementationB.cs:12:31:12:40 | throw ... | semmle.label | successor | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | semmle.label | successor | +| MultiImplementationB.cs:13:36:13:38 | enter get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | semmle.label | successor | +| MultiImplementationB.cs:13:40:13:54 | {...} | MultiImplementationB.cs:13:48:13:51 | null | semmle.label | successor | +| MultiImplementationB.cs:13:42:13:52 | throw ...; | MultiImplementationA.cs:15:36:15:38 | exit get_Item | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:13:42:13:52 | throw ...; | MultiImplementationB.cs:13:36:13:38 | exit get_Item | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:13:48:13:51 | null | MultiImplementationB.cs:13:42:13:52 | throw ...; | semmle.label | successor | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | semmle.label | successor | +| MultiImplementationB.cs:13:56:13:58 | enter set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | semmle.label | successor | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationA.cs:15:54:15:56 | exit set_Item | semmle.label | successor | +| MultiImplementationB.cs:13:60:13:62 | {...} | MultiImplementationB.cs:13:56:13:58 | exit set_Item | semmle.label | successor | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationA.cs:17:5:19:5 | {...} | semmle.label | successor | +| MultiImplementationB.cs:14:17:14:18 | enter M1 | MultiImplementationB.cs:15:5:17:5 | {...} | semmle.label | successor | +| MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:16:9:16:31 | M2(...) | semmle.label | successor | +| MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationA.cs:16:17:16:18 | exit M1 | semmle.label | successor | +| MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationB.cs:14:17:14:18 | exit M1 | semmle.label | successor | +| MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:27:16:30 | null | semmle.label | successor | +| MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:9:16:31 | exit M2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:21:16:30 | throw ... | semmle.label | successor | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:30:18:33 | null | semmle.label | successor | +| MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationA.cs:20:12:20:13 | exit C2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:12:18:13 | exit C2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:24:18:34 | throw ...; | semmle.label | successor | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | semmle.label | successor | +| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | semmle.label | successor | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationA.cs:21:27:21:29 | {...} | semmle.label | successor | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:27:19:29 | {...} | semmle.label | successor | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | semmle.label | successor | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationA.cs:21:12:21:13 | exit C2 | semmle.label | successor | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:12:19:13 | exit C2 | semmle.label | successor | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | semmle.label | successor | +| MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | semmle.label | successor | +| MultiImplementationB.cs:20:11:20:25 | {...} | MultiImplementationB.cs:20:19:20:22 | null | semmle.label | successor | +| MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:20:19:20:22 | null | MultiImplementationB.cs:20:13:20:23 | throw ...; | semmle.label | successor | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | semmle.label | successor | +| MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | semmle.label | successor | +| MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | semmle.label | exception(NullReferenceException) | +| MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:50:21:59 | throw ... | semmle.label | successor | +| MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:32:22:34 | ... = ... | semmle.label | successor | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:34:22:34 | 1 | semmle.label | successor | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationA.cs:20:22:20:31 | {...} | semmle.label | successor | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:18:22:18:36 | {...} | semmle.label | successor | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | access to property P | semmle.label | successor | +| MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | semmle.label | successor | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:14:36:28 | {...} | semmle.label | successor | +| MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationB.cs:32:17:32:17 | 0 | semmle.label | successor | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationA.cs:36:9:36:10 | exit M1 | semmle.label | successor | +| MultiImplementationB.cs:32:17:32:17 | 0 | MultiImplementationB.cs:32:9:32:10 | exit M1 | semmle.label | successor | | NullCoalescing.cs:3:9:3:10 | enter M1 | NullCoalescing.cs:3:23:3:28 | ... ?? ... | semmle.label | successor | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:9:3:10 | exit M1 | semmle.label | non-null | | NullCoalescing.cs:3:23:3:23 | access to parameter i | NullCoalescing.cs:3:28:3:28 | 0 | semmle.label | null | @@ -2505,10 +3316,10 @@ | Switch.cs:27:18:27:25 | Double d | Switch.cs:27:32:27:38 | call to method Throw | semmle.label | match | | Switch.cs:27:18:27:25 | Double d | Switch.cs:30:13:30:20 | default: | semmle.label | no-match | | Switch.cs:27:32:27:38 | call to method Throw | Switch.cs:10:10:10:11 | exit M2 | semmle.label | exception(Exception) | -| Switch.cs:28:17:28:21 | Label: | Switch.cs:29:17:29:23 | return ...; | semmle.label | successor | +| Switch.cs:28:13:28:17 | Label: | Switch.cs:29:17:29:23 | return ...; | semmle.label | successor | | Switch.cs:29:17:29:23 | return ...; | Switch.cs:10:10:10:11 | exit M2 | semmle.label | return | | Switch.cs:30:13:30:20 | default: | Switch.cs:31:17:31:27 | goto ...; | semmle.label | successor | -| Switch.cs:31:17:31:27 | goto ...; | Switch.cs:28:17:28:21 | Label: | semmle.label | goto(Label) | +| Switch.cs:31:17:31:27 | goto ...; | Switch.cs:28:13:28:17 | Label: | semmle.label | goto(Label) | | Switch.cs:35:10:35:11 | enter M3 | Switch.cs:36:5:42:5 | {...} | semmle.label | successor | | Switch.cs:36:5:42:5 | {...} | Switch.cs:37:9:41:9 | switch (...) {...} | semmle.label | successor | | Switch.cs:37:9:41:9 | switch (...) {...} | Switch.cs:37:17:37:23 | call to method Throw | semmle.label | successor | @@ -2533,73 +3344,73 @@ | Switch.cs:56:5:64:5 | {...} | Switch.cs:57:9:63:9 | switch (...) {...} | semmle.label | successor | | Switch.cs:57:9:63:9 | switch (...) {...} | Switch.cs:57:17:57:17 | 1 | semmle.label | successor | | Switch.cs:57:17:57:17 | 1 | Switch.cs:57:21:57:21 | 2 | semmle.label | successor | -| Switch.cs:57:17:57:21 | ... + ... | Switch.cs:59:13:59:20 | case ...: | semmle.label | successor | +| Switch.cs:57:17:57:21 | ... + ... | Switch.cs:59:13:59:19 | case ...: | semmle.label | successor | | Switch.cs:57:21:57:21 | 2 | Switch.cs:57:17:57:21 | ... + ... | semmle.label | successor | -| Switch.cs:59:13:59:20 | case ...: | Switch.cs:59:18:59:18 | 2 | semmle.label | successor | -| Switch.cs:59:18:59:18 | 2 | Switch.cs:61:13:61:20 | case ...: | semmle.label | no-match | -| Switch.cs:61:13:61:20 | case ...: | Switch.cs:61:18:61:18 | 3 | semmle.label | successor | -| Switch.cs:61:18:61:18 | 3 | Switch.cs:62:15:62:20 | break; | semmle.label | match | -| Switch.cs:62:15:62:20 | break; | Switch.cs:55:10:55:11 | exit M5 | semmle.label | break | +| Switch.cs:59:13:59:19 | case ...: | Switch.cs:59:18:59:18 | 2 | semmle.label | successor | +| Switch.cs:59:18:59:18 | 2 | Switch.cs:61:13:61:19 | case ...: | semmle.label | no-match | +| Switch.cs:61:13:61:19 | case ...: | Switch.cs:61:18:61:18 | 3 | semmle.label | successor | +| Switch.cs:61:18:61:18 | 3 | Switch.cs:62:17:62:22 | break; | semmle.label | match | +| Switch.cs:62:17:62:22 | break; | Switch.cs:55:10:55:11 | exit M5 | semmle.label | break | | Switch.cs:66:10:66:11 | enter M6 | Switch.cs:67:5:75:5 | {...} | semmle.label | successor | | Switch.cs:67:5:75:5 | {...} | Switch.cs:68:9:74:9 | switch (...) {...} | semmle.label | successor | | Switch.cs:68:9:74:9 | switch (...) {...} | Switch.cs:68:25:68:25 | access to parameter s | semmle.label | successor | -| Switch.cs:68:17:68:25 | (...) ... | Switch.cs:70:13:70:24 | case ...: | semmle.label | successor | +| Switch.cs:68:17:68:25 | (...) ... | Switch.cs:70:13:70:23 | case ...: | semmle.label | successor | | Switch.cs:68:25:68:25 | access to parameter s | Switch.cs:68:17:68:25 | (...) ... | semmle.label | successor | -| Switch.cs:70:13:70:24 | case ...: | Switch.cs:70:18:70:20 | access to type Int32 | semmle.label | successor | -| Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:72:13:72:21 | case ...: | semmle.label | no-match | -| Switch.cs:72:13:72:21 | case ...: | Switch.cs:72:18:72:19 | "" | semmle.label | successor | +| Switch.cs:70:13:70:23 | case ...: | Switch.cs:70:18:70:20 | access to type Int32 | semmle.label | successor | +| Switch.cs:70:18:70:20 | access to type Int32 | Switch.cs:72:13:72:20 | case ...: | semmle.label | no-match | +| Switch.cs:72:13:72:20 | case ...: | Switch.cs:72:18:72:19 | "" | semmle.label | successor | | Switch.cs:72:18:72:19 | "" | Switch.cs:66:10:66:11 | exit M6 | semmle.label | no-match | -| Switch.cs:72:18:72:19 | "" | Switch.cs:73:15:73:20 | break; | semmle.label | match | -| Switch.cs:73:15:73:20 | break; | Switch.cs:66:10:66:11 | exit M6 | semmle.label | break | +| Switch.cs:72:18:72:19 | "" | Switch.cs:73:17:73:22 | break; | semmle.label | match | +| Switch.cs:73:17:73:22 | break; | Switch.cs:66:10:66:11 | exit M6 | semmle.label | break | | Switch.cs:77:10:77:11 | enter M7 | Switch.cs:78:5:89:5 | {...} | semmle.label | successor | | Switch.cs:78:5:89:5 | {...} | Switch.cs:79:9:87:9 | switch (...) {...} | semmle.label | successor | | Switch.cs:79:9:87:9 | switch (...) {...} | Switch.cs:79:17:79:17 | access to parameter i | semmle.label | successor | -| Switch.cs:79:17:79:17 | access to parameter i | Switch.cs:81:13:81:20 | case ...: | semmle.label | successor | -| Switch.cs:81:13:81:20 | case ...: | Switch.cs:81:18:81:18 | 1 | semmle.label | successor | -| Switch.cs:81:18:81:18 | 1 | Switch.cs:82:22:82:25 | true | semmle.label | match | -| Switch.cs:81:18:81:18 | 1 | Switch.cs:83:13:83:20 | case ...: | semmle.label | no-match | -| Switch.cs:82:15:82:26 | return ...; | Switch.cs:77:10:77:11 | exit M7 | semmle.label | return | -| Switch.cs:82:22:82:25 | true | Switch.cs:82:15:82:26 | return ...; | semmle.label | successor | -| Switch.cs:83:13:83:20 | case ...: | Switch.cs:83:18:83:18 | 2 | semmle.label | successor | -| Switch.cs:83:18:83:18 | 2 | Switch.cs:84:15:85:22 | if (...) ... | semmle.label | match | +| Switch.cs:79:17:79:17 | access to parameter i | Switch.cs:81:13:81:19 | case ...: | semmle.label | successor | +| Switch.cs:81:13:81:19 | case ...: | Switch.cs:81:18:81:18 | 1 | semmle.label | successor | +| Switch.cs:81:18:81:18 | 1 | Switch.cs:82:24:82:27 | true | semmle.label | match | +| Switch.cs:81:18:81:18 | 1 | Switch.cs:83:13:83:19 | case ...: | semmle.label | no-match | +| Switch.cs:82:17:82:28 | return ...; | Switch.cs:77:10:77:11 | exit M7 | semmle.label | return | +| Switch.cs:82:24:82:27 | true | Switch.cs:82:17:82:28 | return ...; | semmle.label | successor | +| Switch.cs:83:13:83:19 | case ...: | Switch.cs:83:18:83:18 | 2 | semmle.label | successor | +| Switch.cs:83:18:83:18 | 2 | Switch.cs:84:17:85:26 | if (...) ... | semmle.label | match | | Switch.cs:83:18:83:18 | 2 | Switch.cs:88:16:88:20 | false | semmle.label | no-match | -| Switch.cs:84:15:85:22 | if (...) ... | Switch.cs:84:19:84:19 | access to parameter j | semmle.label | successor | -| Switch.cs:84:19:84:19 | access to parameter j | Switch.cs:84:23:84:23 | 2 | semmle.label | successor | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:85:17:85:22 | break; | semmle.label | true | -| Switch.cs:84:19:84:23 | ... > ... | Switch.cs:86:22:86:25 | true | semmle.label | false | -| Switch.cs:84:23:84:23 | 2 | Switch.cs:84:19:84:23 | ... > ... | semmle.label | successor | -| Switch.cs:85:17:85:22 | break; | Switch.cs:88:16:88:20 | false | semmle.label | break | -| Switch.cs:86:15:86:26 | return ...; | Switch.cs:77:10:77:11 | exit M7 | semmle.label | return | -| Switch.cs:86:22:86:25 | true | Switch.cs:86:15:86:26 | return ...; | semmle.label | successor | +| Switch.cs:84:17:85:26 | if (...) ... | Switch.cs:84:21:84:21 | access to parameter j | semmle.label | successor | +| Switch.cs:84:21:84:21 | access to parameter j | Switch.cs:84:25:84:25 | 2 | semmle.label | successor | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:85:21:85:26 | break; | semmle.label | true | +| Switch.cs:84:21:84:25 | ... > ... | Switch.cs:86:24:86:27 | true | semmle.label | false | +| Switch.cs:84:25:84:25 | 2 | Switch.cs:84:21:84:25 | ... > ... | semmle.label | successor | +| Switch.cs:85:21:85:26 | break; | Switch.cs:88:16:88:20 | false | semmle.label | break | +| Switch.cs:86:17:86:28 | return ...; | Switch.cs:77:10:77:11 | exit M7 | semmle.label | return | +| Switch.cs:86:24:86:27 | true | Switch.cs:86:17:86:28 | return ...; | semmle.label | successor | | Switch.cs:88:9:88:21 | return ...; | Switch.cs:77:10:77:11 | exit M7 | semmle.label | return | | Switch.cs:88:16:88:20 | false | Switch.cs:88:9:88:21 | return ...; | semmle.label | successor | | Switch.cs:91:10:91:11 | enter M8 | Switch.cs:92:5:99:5 | {...} | semmle.label | successor | | Switch.cs:92:5:99:5 | {...} | Switch.cs:93:9:97:9 | switch (...) {...} | semmle.label | successor | | Switch.cs:93:9:97:9 | switch (...) {...} | Switch.cs:93:17:93:17 | access to parameter o | semmle.label | successor | -| Switch.cs:93:17:93:17 | access to parameter o | Switch.cs:95:13:95:24 | case ...: | semmle.label | successor | -| Switch.cs:95:13:95:24 | case ...: | Switch.cs:95:18:95:20 | access to type Int32 | semmle.label | successor | -| Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:96:22:96:25 | true | semmle.label | match | +| Switch.cs:93:17:93:17 | access to parameter o | Switch.cs:95:13:95:23 | case ...: | semmle.label | successor | +| Switch.cs:95:13:95:23 | case ...: | Switch.cs:95:18:95:20 | access to type Int32 | semmle.label | successor | +| Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:96:24:96:27 | true | semmle.label | match | | Switch.cs:95:18:95:20 | access to type Int32 | Switch.cs:98:16:98:20 | false | semmle.label | no-match | -| Switch.cs:96:15:96:26 | return ...; | Switch.cs:91:10:91:11 | exit M8 | semmle.label | return | -| Switch.cs:96:22:96:25 | true | Switch.cs:96:15:96:26 | return ...; | semmle.label | successor | +| Switch.cs:96:17:96:28 | return ...; | Switch.cs:91:10:91:11 | exit M8 | semmle.label | return | +| Switch.cs:96:24:96:27 | true | Switch.cs:96:17:96:28 | return ...; | semmle.label | successor | | Switch.cs:98:9:98:21 | return ...; | Switch.cs:91:10:91:11 | exit M8 | semmle.label | return | | Switch.cs:98:16:98:20 | false | Switch.cs:98:9:98:21 | return ...; | semmle.label | successor | | Switch.cs:101:9:101:10 | enter M9 | Switch.cs:102:5:109:5 | {...} | semmle.label | successor | | Switch.cs:102:5:109:5 | {...} | Switch.cs:103:9:107:9 | switch (...) {...} | semmle.label | successor | | Switch.cs:103:9:107:9 | switch (...) {...} | Switch.cs:103:17:103:17 | access to parameter s | semmle.label | successor | | Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:103:19:103:25 | access to property Length | semmle.label | non-null | -| Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:105:13:105:20 | case ...: | semmle.label | null | -| Switch.cs:103:19:103:25 | access to property Length | Switch.cs:105:13:105:20 | case ...: | semmle.label | successor | -| Switch.cs:105:13:105:20 | case ...: | Switch.cs:105:18:105:18 | 0 | semmle.label | successor | -| Switch.cs:105:18:105:18 | 0 | Switch.cs:105:29:105:29 | 0 | semmle.label | match | -| Switch.cs:105:18:105:18 | 0 | Switch.cs:106:13:106:20 | case ...: | semmle.label | no-match | -| Switch.cs:105:22:105:30 | return ...; | Switch.cs:101:9:101:10 | exit M9 | semmle.label | return | -| Switch.cs:105:29:105:29 | 0 | Switch.cs:105:22:105:30 | return ...; | semmle.label | successor | -| Switch.cs:106:13:106:20 | case ...: | Switch.cs:106:18:106:18 | 1 | semmle.label | successor | -| Switch.cs:106:18:106:18 | 1 | Switch.cs:106:29:106:29 | 1 | semmle.label | match | +| Switch.cs:103:17:103:17 | access to parameter s | Switch.cs:105:13:105:19 | case ...: | semmle.label | null | +| Switch.cs:103:19:103:25 | access to property Length | Switch.cs:105:13:105:19 | case ...: | semmle.label | successor | +| Switch.cs:105:13:105:19 | case ...: | Switch.cs:105:18:105:18 | 0 | semmle.label | successor | +| Switch.cs:105:18:105:18 | 0 | Switch.cs:105:28:105:28 | 0 | semmle.label | match | +| Switch.cs:105:18:105:18 | 0 | Switch.cs:106:13:106:19 | case ...: | semmle.label | no-match | +| Switch.cs:105:21:105:29 | return ...; | Switch.cs:101:9:101:10 | exit M9 | semmle.label | return | +| Switch.cs:105:28:105:28 | 0 | Switch.cs:105:21:105:29 | return ...; | semmle.label | successor | +| Switch.cs:106:13:106:19 | case ...: | Switch.cs:106:18:106:18 | 1 | semmle.label | successor | +| Switch.cs:106:18:106:18 | 1 | Switch.cs:106:28:106:28 | 1 | semmle.label | match | | Switch.cs:106:18:106:18 | 1 | Switch.cs:108:17:108:17 | 1 | semmle.label | no-match | -| Switch.cs:106:22:106:30 | return ...; | Switch.cs:101:9:101:10 | exit M9 | semmle.label | return | -| Switch.cs:106:29:106:29 | 1 | Switch.cs:106:22:106:30 | return ...; | semmle.label | successor | +| Switch.cs:106:21:106:29 | return ...; | Switch.cs:101:9:101:10 | exit M9 | semmle.label | return | +| Switch.cs:106:28:106:28 | 1 | Switch.cs:106:21:106:29 | return ...; | semmle.label | successor | | Switch.cs:108:9:108:18 | return ...; | Switch.cs:101:9:101:10 | exit M9 | semmle.label | return | | Switch.cs:108:16:108:17 | -... | Switch.cs:108:9:108:18 | return ...; | semmle.label | successor | | Switch.cs:108:17:108:17 | 1 | Switch.cs:108:16:108:17 | -... | semmle.label | successor | @@ -2610,25 +3421,25 @@ | Switch.cs:114:5:121:5 | {...} | Switch.cs:115:9:119:9 | switch (...) {...} | semmle.label | successor | | Switch.cs:115:9:119:9 | switch (...) {...} | Switch.cs:115:17:115:17 | access to parameter s | semmle.label | successor | | Switch.cs:115:17:115:17 | access to parameter s | Switch.cs:115:17:115:24 | access to property Length | semmle.label | successor | -| Switch.cs:115:17:115:24 | access to property Length | Switch.cs:117:13:117:34 | case ...: | semmle.label | successor | -| Switch.cs:117:13:117:34 | case ...: | Switch.cs:117:18:117:18 | 3 | semmle.label | successor | +| Switch.cs:115:17:115:24 | access to property Length | Switch.cs:117:13:117:35 | case ...: | semmle.label | successor | +| Switch.cs:117:13:117:35 | case ...: | Switch.cs:117:18:117:18 | 3 | semmle.label | successor | | Switch.cs:117:18:117:18 | 3 | Switch.cs:117:25:117:25 | access to parameter s | semmle.label | match | -| Switch.cs:117:18:117:18 | 3 | Switch.cs:118:13:118:33 | case ...: | semmle.label | no-match | -| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:28:117:32 | "foo" | semmle.label | successor | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:117:43:117:43 | 1 | semmle.label | true | -| Switch.cs:117:25:117:32 | ... == ... | Switch.cs:118:13:118:33 | case ...: | semmle.label | false | -| Switch.cs:117:28:117:32 | "foo" | Switch.cs:117:25:117:32 | ... == ... | semmle.label | successor | -| Switch.cs:117:36:117:44 | return ...; | Switch.cs:113:9:113:11 | exit M10 | semmle.label | return | -| Switch.cs:117:43:117:43 | 1 | Switch.cs:117:36:117:44 | return ...; | semmle.label | successor | -| Switch.cs:118:13:118:33 | case ...: | Switch.cs:118:18:118:18 | 2 | semmle.label | successor | +| Switch.cs:117:18:117:18 | 3 | Switch.cs:118:13:118:34 | case ...: | semmle.label | no-match | +| Switch.cs:117:25:117:25 | access to parameter s | Switch.cs:117:30:117:34 | "foo" | semmle.label | successor | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:117:44:117:44 | 1 | semmle.label | true | +| Switch.cs:117:25:117:34 | ... == ... | Switch.cs:118:13:118:34 | case ...: | semmle.label | false | +| Switch.cs:117:30:117:34 | "foo" | Switch.cs:117:25:117:34 | ... == ... | semmle.label | successor | +| Switch.cs:117:37:117:45 | return ...; | Switch.cs:113:9:113:11 | exit M10 | semmle.label | return | +| Switch.cs:117:44:117:44 | 1 | Switch.cs:117:37:117:45 | return ...; | semmle.label | successor | +| Switch.cs:118:13:118:34 | case ...: | Switch.cs:118:18:118:18 | 2 | semmle.label | successor | | Switch.cs:118:18:118:18 | 2 | Switch.cs:118:25:118:25 | access to parameter s | semmle.label | match | | Switch.cs:118:18:118:18 | 2 | Switch.cs:120:17:120:17 | 1 | semmle.label | no-match | -| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:28:118:31 | "fu" | semmle.label | successor | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:118:42:118:42 | 2 | semmle.label | true | -| Switch.cs:118:25:118:31 | ... == ... | Switch.cs:120:17:120:17 | 1 | semmle.label | false | -| Switch.cs:118:28:118:31 | "fu" | Switch.cs:118:25:118:31 | ... == ... | semmle.label | successor | -| Switch.cs:118:35:118:43 | return ...; | Switch.cs:113:9:113:11 | exit M10 | semmle.label | return | -| Switch.cs:118:42:118:42 | 2 | Switch.cs:118:35:118:43 | return ...; | semmle.label | successor | +| Switch.cs:118:25:118:25 | access to parameter s | Switch.cs:118:30:118:33 | "fu" | semmle.label | successor | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:118:43:118:43 | 2 | semmle.label | true | +| Switch.cs:118:25:118:33 | ... == ... | Switch.cs:120:17:120:17 | 1 | semmle.label | false | +| Switch.cs:118:30:118:33 | "fu" | Switch.cs:118:25:118:33 | ... == ... | semmle.label | successor | +| Switch.cs:118:36:118:44 | return ...; | Switch.cs:113:9:113:11 | exit M10 | semmle.label | return | +| Switch.cs:118:43:118:43 | 2 | Switch.cs:118:36:118:44 | return ...; | semmle.label | successor | | Switch.cs:120:9:120:18 | return ...; | Switch.cs:113:9:113:11 | exit M10 | semmle.label | return | | Switch.cs:120:16:120:17 | -... | Switch.cs:120:9:120:18 | return ...; | semmle.label | successor | | Switch.cs:120:17:120:17 | 1 | Switch.cs:120:16:120:17 | -... | semmle.label | successor | @@ -2696,6 +3507,33 @@ | Switch.cs:150:18:150:18 | 2 | Switch.cs:150:28:150:28 | 2 | semmle.label | match | | Switch.cs:150:21:150:29 | return ...; | Switch.cs:144:9:144:11 | exit M14 | semmle.label | return | | Switch.cs:150:28:150:28 | 2 | Switch.cs:150:21:150:29 | return ...; | semmle.label | successor | +| Switch.cs:154:10:154:12 | enter M15 | Switch.cs:155:5:161:5 | {...} | semmle.label | successor | +| Switch.cs:155:5:161:5 | {...} | Switch.cs:156:9:156:55 | ... ...; | semmle.label | successor | +| Switch.cs:156:9:156:55 | ... ...; | Switch.cs:156:17:156:54 | ... switch { ... } | semmle.label | successor | +| Switch.cs:156:13:156:54 | String s = ... | Switch.cs:157:9:160:49 | if (...) ... | semmle.label | successor | +| Switch.cs:156:17:156:17 | access to parameter b | Switch.cs:156:28:156:38 | ... => ... | semmle.label | successor | +| Switch.cs:156:17:156:54 | ... switch { ... } | Switch.cs:156:17:156:17 | access to parameter b | semmle.label | successor | +| Switch.cs:156:28:156:31 | true | Switch.cs:156:36:156:38 | "a" | semmle.label | match | +| Switch.cs:156:28:156:31 | true | Switch.cs:156:41:156:52 | ... => ... | semmle.label | no-match | +| Switch.cs:156:28:156:38 | ... => ... | Switch.cs:156:28:156:31 | true | semmle.label | successor | +| Switch.cs:156:36:156:38 | "a" | Switch.cs:156:13:156:54 | String s = ... | semmle.label | successor | +| Switch.cs:156:41:156:45 | false | Switch.cs:154:10:154:12 | exit M15 | semmle.label | exception(InvalidOperationException) | +| Switch.cs:156:41:156:45 | false | Switch.cs:156:50:156:52 | "b" | semmle.label | match | +| Switch.cs:156:41:156:52 | ... => ... | Switch.cs:156:41:156:45 | false | semmle.label | successor | +| Switch.cs:156:50:156:52 | "b" | Switch.cs:156:13:156:54 | String s = ... | semmle.label | successor | +| Switch.cs:157:9:160:49 | if (...) ... | Switch.cs:157:13:157:13 | access to parameter b | semmle.label | successor | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:158:13:158:49 | ...; | semmle.label | true | +| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:160:13:160:49 | ...; | semmle.label | false | +| Switch.cs:158:13:158:48 | call to method WriteLine | Switch.cs:154:10:154:12 | exit M15 | semmle.label | successor | +| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:40:158:43 | "a = " | semmle.label | successor | +| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:13:158:48 | call to method WriteLine | semmle.label | successor | +| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:45:158:45 | access to local variable s | semmle.label | successor | +| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:38:158:47 | $"..." | semmle.label | successor | +| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:154:10:154:12 | exit M15 | semmle.label | successor | +| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:40:160:43 | "b = " | semmle.label | successor | +| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:13:160:48 | call to method WriteLine | semmle.label | successor | +| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:45:160:45 | access to local variable s | semmle.label | successor | +| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:38:160:47 | $"..." | semmle.label | successor | | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:4:5:9:5 | {...} | semmle.label | successor | | TypeAccesses.cs:4:5:9:5 | {...} | TypeAccesses.cs:5:9:5:26 | ... ...; | semmle.label | successor | | TypeAccesses.cs:5:9:5:26 | ... ...; | TypeAccesses.cs:5:25:5:25 | access to parameter o | semmle.label | successor | @@ -2720,11 +3558,13 @@ | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:7:27:7:33 | access to parameter strings | semmle.label | successor | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:44:7:50 | access to parameter strings | semmle.label | successor | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:35:7:35 | 0 | semmle.label | successor | -| VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:22:7:36 | Char* c1 = ... | semmle.label | successor | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:22:7:36 | Char* c1 = ... | semmle.label | successor | +| VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:27:7:36 | (...) ... | semmle.label | successor | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:27:7:36 | access to array element | semmle.label | successor | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:8:9:10:9 | {...} | semmle.label | successor | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:52:7:52 | 1 | semmle.label | successor | -| VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:39:7:53 | Char* c2 = ... | semmle.label | successor | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:39:7:53 | Char* c2 = ... | semmle.label | successor | +| VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:44:7:53 | (...) ... | semmle.label | successor | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:44:7:53 | access to array element | semmle.label | successor | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:9:27:9:28 | access to local variable c1 | semmle.label | successor | | VarDecls.cs:9:13:9:29 | return ...; | VarDecls.cs:5:18:5:19 | exit M1 | semmle.label | return | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected b/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected index f6d10fdb2ba7..caa10ca9d348 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected @@ -1,4 +1,340 @@ booleanNode +| Assert.cs:58:16:58:32 | [b (line 56): false] String s = ... | b (line 56): false | +| Assert.cs:58:16:58:32 | [b (line 56): true] String s = ... | b (line 56): true | +| Assert.cs:58:24:58:27 | [b (line 56): true] null | b (line 56): true | +| Assert.cs:58:31:58:32 | [b (line 56): false] "" | b (line 56): false | +| Assert.cs:59:9:59:38 | [b (line 56): false] ...; | b (line 56): false | +| Assert.cs:59:9:59:38 | [b (line 56): true] ...; | b (line 56): true | +| Assert.cs:59:23:59:23 | [b (line 56): false] access to local variable s | b (line 56): false | +| Assert.cs:59:23:59:23 | [b (line 56): true] access to local variable s | b (line 56): true | +| Assert.cs:59:23:59:31 | [b (line 56): false] ... != ... | b (line 56): false | +| Assert.cs:59:23:59:31 | [b (line 56): true] ... != ... | b (line 56): true | +| Assert.cs:59:23:59:36 | [b (line 56): false] ... && ... | b (line 56): false | +| Assert.cs:59:23:59:36 | [b (line 56): true] ... && ... | b (line 56): true | +| Assert.cs:59:28:59:31 | [b (line 56): false] null | b (line 56): false | +| Assert.cs:59:28:59:31 | [b (line 56): true] null | b (line 56): true | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | b (line 56): false | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | b (line 56): true | +| Assert.cs:65:16:65:32 | [b (line 63): false] String s = ... | b (line 63): false | +| Assert.cs:65:16:65:32 | [b (line 63): true] String s = ... | b (line 63): true | +| Assert.cs:65:24:65:27 | [b (line 63): true] null | b (line 63): true | +| Assert.cs:65:31:65:32 | [b (line 63): false] "" | b (line 63): false | +| Assert.cs:66:9:66:39 | [b (line 63): false] ...; | b (line 63): false | +| Assert.cs:66:9:66:39 | [b (line 63): true] ...; | b (line 63): true | +| Assert.cs:66:24:66:24 | [b (line 63): false] access to local variable s | b (line 63): false | +| Assert.cs:66:24:66:24 | [b (line 63): true] access to local variable s | b (line 63): true | +| Assert.cs:66:24:66:32 | [b (line 63): false] ... == ... | b (line 63): false | +| Assert.cs:66:24:66:32 | [b (line 63): true] ... == ... | b (line 63): true | +| Assert.cs:66:24:66:37 | [b (line 63): false] ... \|\| ... | b (line 63): false | +| Assert.cs:66:24:66:37 | [b (line 63): true] ... \|\| ... | b (line 63): true | +| Assert.cs:66:29:66:32 | [b (line 63): false] null | b (line 63): false | +| Assert.cs:66:29:66:32 | [b (line 63): true] null | b (line 63): true | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | b (line 63): false | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | b (line 63): true | +| Assert.cs:72:16:72:32 | [b (line 70): false] String s = ... | b (line 70): false | +| Assert.cs:72:16:72:32 | [b (line 70): true] String s = ... | b (line 70): true | +| Assert.cs:72:24:72:27 | [b (line 70): true] null | b (line 70): true | +| Assert.cs:72:31:72:32 | [b (line 70): false] "" | b (line 70): false | +| Assert.cs:73:9:73:38 | [b (line 70): false] ...; | b (line 70): false | +| Assert.cs:73:9:73:38 | [b (line 70): true] ...; | b (line 70): true | +| Assert.cs:73:23:73:23 | [b (line 70): false] access to local variable s | b (line 70): false | +| Assert.cs:73:23:73:23 | [b (line 70): true] access to local variable s | b (line 70): true | +| Assert.cs:73:23:73:31 | [b (line 70): false] ... == ... | b (line 70): false | +| Assert.cs:73:23:73:31 | [b (line 70): true] ... == ... | b (line 70): true | +| Assert.cs:73:23:73:36 | [b (line 70): false] ... && ... | b (line 70): false | +| Assert.cs:73:23:73:36 | [b (line 70): true] ... && ... | b (line 70): true | +| Assert.cs:73:28:73:31 | [b (line 70): false] null | b (line 70): false | +| Assert.cs:73:28:73:31 | [b (line 70): true] null | b (line 70): true | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | b (line 70): false | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | b (line 70): true | +| Assert.cs:79:16:79:32 | [b (line 77): false] String s = ... | b (line 77): false | +| Assert.cs:79:16:79:32 | [b (line 77): true] String s = ... | b (line 77): true | +| Assert.cs:79:24:79:27 | [b (line 77): true] null | b (line 77): true | +| Assert.cs:79:31:79:32 | [b (line 77): false] "" | b (line 77): false | +| Assert.cs:80:9:80:39 | [b (line 77): false] ...; | b (line 77): false | +| Assert.cs:80:9:80:39 | [b (line 77): true] ...; | b (line 77): true | +| Assert.cs:80:24:80:24 | [b (line 77): false] access to local variable s | b (line 77): false | +| Assert.cs:80:24:80:24 | [b (line 77): true] access to local variable s | b (line 77): true | +| Assert.cs:80:24:80:32 | [b (line 77): false] ... != ... | b (line 77): false | +| Assert.cs:80:24:80:32 | [b (line 77): true] ... != ... | b (line 77): true | +| Assert.cs:80:24:80:37 | [b (line 77): false] ... \|\| ... | b (line 77): false | +| Assert.cs:80:24:80:37 | [b (line 77): true] ... \|\| ... | b (line 77): true | +| Assert.cs:80:29:80:32 | [b (line 77): false] null | b (line 77): false | +| Assert.cs:80:29:80:32 | [b (line 77): true] null | b (line 77): true | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | b (line 77): false | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | b (line 77): true | +| Assert.cs:86:16:86:32 | [b (line 84): false] String s = ... | b (line 84): false | +| Assert.cs:86:16:86:32 | [b (line 84): true] String s = ... | b (line 84): true | +| Assert.cs:86:24:86:27 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:86:31:86:32 | [b (line 84): false] "" | b (line 84): false | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): false] call to method Assert | b (line 84): false | +| Assert.cs:87:9:87:31 | [assertion failure, b (line 84): true] call to method Assert | b (line 84): true | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): false] call to method Assert | b (line 84): false | +| Assert.cs:87:9:87:31 | [assertion success, b (line 84): true] call to method Assert | b (line 84): true | +| Assert.cs:87:9:87:32 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:87:9:87:32 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:87:22:87:22 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:87:22:87:22 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:87:22:87:30 | [b (line 84): false] ... != ... | b (line 84): false | +| Assert.cs:87:22:87:30 | [b (line 84): true] ... != ... | b (line 84): true | +| Assert.cs:87:27:87:30 | [b (line 84): false] null | b (line 84): false | +| Assert.cs:87:27:87:30 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:88:9:88:35 | [b (line 84): false] call to method WriteLine | b (line 84): false | +| Assert.cs:88:9:88:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:88:9:88:36 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:88:9:88:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:88:27:88:27 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:88:27:88:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:88:27:88:34 | [b (line 84): false] access to property Length | b (line 84): false | +| Assert.cs:88:27:88:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:90:9:90:25 | [b (line 84): false] ... = ... | b (line 84): false | +| Assert.cs:90:9:90:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:90:9:90:26 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:90:9:90:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:90:13:90:13 | [b (line 84): false] access to parameter b | b (line 84): false | +| Assert.cs:90:13:90:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:90:13:90:25 | [b (line 84): false] ... ? ... : ... | b (line 84): false | +| Assert.cs:90:13:90:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:90:17:90:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:90:24:90:25 | [b (line 84): false] "" | b (line 84): false | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): false] call to method IsNull | b (line 84): false | +| Assert.cs:91:9:91:24 | [assertion failure, b (line 84): true] call to method IsNull | b (line 84): true | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): false] call to method IsNull | b (line 84): false | +| Assert.cs:91:9:91:24 | [assertion success, b (line 84): true] call to method IsNull | b (line 84): true | +| Assert.cs:91:9:91:25 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:91:9:91:25 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:91:23:91:23 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:91:23:91:23 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:92:9:92:35 | [b (line 84): false] call to method WriteLine | b (line 84): false | +| Assert.cs:92:9:92:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:92:9:92:36 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:92:9:92:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:92:27:92:27 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:92:27:92:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:92:27:92:34 | [b (line 84): false] access to property Length | b (line 84): false | +| Assert.cs:92:27:92:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:94:9:94:25 | [b (line 84): false] ... = ... | b (line 84): false | +| Assert.cs:94:9:94:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:94:9:94:26 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:94:9:94:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:94:13:94:13 | [b (line 84): false] access to parameter b | b (line 84): false | +| Assert.cs:94:13:94:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:94:13:94:25 | [b (line 84): false] ... ? ... : ... | b (line 84): false | +| Assert.cs:94:13:94:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:94:17:94:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:94:24:94:25 | [b (line 84): false] "" | b (line 84): false | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): false] call to method IsNotNull | b (line 84): false | +| Assert.cs:95:9:95:27 | [assertion failure, b (line 84): true] call to method IsNotNull | b (line 84): true | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): false] call to method IsNotNull | b (line 84): false | +| Assert.cs:95:9:95:27 | [assertion success, b (line 84): true] call to method IsNotNull | b (line 84): true | +| Assert.cs:95:9:95:28 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:95:9:95:28 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:95:26:95:26 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:95:26:95:26 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:96:9:96:35 | [b (line 84): false] call to method WriteLine | b (line 84): false | +| Assert.cs:96:9:96:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:96:9:96:36 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:96:9:96:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:96:27:96:27 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:96:27:96:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:96:27:96:34 | [b (line 84): false] access to property Length | b (line 84): false | +| Assert.cs:96:27:96:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:98:9:98:25 | [b (line 84): false] ... = ... | b (line 84): false | +| Assert.cs:98:9:98:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:98:9:98:26 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:98:9:98:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:98:13:98:13 | [b (line 84): false] access to parameter b | b (line 84): false | +| Assert.cs:98:13:98:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:98:13:98:25 | [b (line 84): false] ... ? ... : ... | b (line 84): false | +| Assert.cs:98:13:98:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:98:17:98:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:98:24:98:25 | [b (line 84): false] "" | b (line 84): false | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): false] call to method IsTrue | b (line 84): false | +| Assert.cs:99:9:99:32 | [assertion failure, b (line 84): true] call to method IsTrue | b (line 84): true | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): false] call to method IsTrue | b (line 84): false | +| Assert.cs:99:9:99:32 | [assertion success, b (line 84): true] call to method IsTrue | b (line 84): true | +| Assert.cs:99:9:99:33 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:99:9:99:33 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:99:23:99:23 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:99:23:99:23 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:99:23:99:31 | [b (line 84): false] ... == ... | b (line 84): false | +| Assert.cs:99:23:99:31 | [b (line 84): true] ... == ... | b (line 84): true | +| Assert.cs:99:28:99:31 | [b (line 84): false] null | b (line 84): false | +| Assert.cs:99:28:99:31 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:100:9:100:35 | [b (line 84): false] call to method WriteLine | b (line 84): false | +| Assert.cs:100:9:100:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:100:9:100:36 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:100:9:100:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:100:27:100:27 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:100:27:100:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:100:27:100:34 | [b (line 84): false] access to property Length | b (line 84): false | +| Assert.cs:100:27:100:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:102:9:102:25 | [b (line 84): false] ... = ... | b (line 84): false | +| Assert.cs:102:9:102:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:102:9:102:26 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:102:9:102:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:102:13:102:13 | [b (line 84): false] access to parameter b | b (line 84): false | +| Assert.cs:102:13:102:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:102:13:102:25 | [b (line 84): false] ... ? ... : ... | b (line 84): false | +| Assert.cs:102:13:102:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:102:17:102:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:102:24:102:25 | [b (line 84): false] "" | b (line 84): false | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): false] call to method IsTrue | b (line 84): false | +| Assert.cs:103:9:103:32 | [assertion failure, b (line 84): true] call to method IsTrue | b (line 84): true | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): false] call to method IsTrue | b (line 84): false | +| Assert.cs:103:9:103:32 | [assertion success, b (line 84): true] call to method IsTrue | b (line 84): true | +| Assert.cs:103:9:103:33 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:103:9:103:33 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:103:23:103:23 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:103:23:103:23 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:103:23:103:31 | [b (line 84): false] ... != ... | b (line 84): false | +| Assert.cs:103:23:103:31 | [b (line 84): true] ... != ... | b (line 84): true | +| Assert.cs:103:28:103:31 | [b (line 84): false] null | b (line 84): false | +| Assert.cs:103:28:103:31 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:104:9:104:35 | [b (line 84): false] call to method WriteLine | b (line 84): false | +| Assert.cs:104:9:104:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:104:9:104:36 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:104:9:104:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:104:27:104:27 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:104:27:104:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:104:27:104:34 | [b (line 84): false] access to property Length | b (line 84): false | +| Assert.cs:104:27:104:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:106:9:106:25 | [b (line 84): false] ... = ... | b (line 84): false | +| Assert.cs:106:9:106:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:106:9:106:26 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:106:9:106:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:106:13:106:13 | [b (line 84): false] access to parameter b | b (line 84): false | +| Assert.cs:106:13:106:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:106:13:106:25 | [b (line 84): false] ... ? ... : ... | b (line 84): false | +| Assert.cs:106:13:106:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:106:17:106:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:106:24:106:25 | [b (line 84): false] "" | b (line 84): false | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): false] call to method IsFalse | b (line 84): false | +| Assert.cs:107:9:107:33 | [assertion failure, b (line 84): true] call to method IsFalse | b (line 84): true | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): false] call to method IsFalse | b (line 84): false | +| Assert.cs:107:9:107:33 | [assertion success, b (line 84): true] call to method IsFalse | b (line 84): true | +| Assert.cs:107:9:107:34 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:107:9:107:34 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:107:24:107:24 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:107:24:107:24 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:107:24:107:32 | [b (line 84): false] ... != ... | b (line 84): false | +| Assert.cs:107:24:107:32 | [b (line 84): true] ... != ... | b (line 84): true | +| Assert.cs:107:29:107:32 | [b (line 84): false] null | b (line 84): false | +| Assert.cs:107:29:107:32 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:108:9:108:35 | [b (line 84): false] call to method WriteLine | b (line 84): false | +| Assert.cs:108:9:108:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:108:9:108:36 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:108:9:108:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:108:27:108:27 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:108:27:108:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:108:27:108:34 | [b (line 84): false] access to property Length | b (line 84): false | +| Assert.cs:108:27:108:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:110:9:110:25 | [b (line 84): false] ... = ... | b (line 84): false | +| Assert.cs:110:9:110:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:110:9:110:26 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:110:9:110:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:110:13:110:13 | [b (line 84): false] access to parameter b | b (line 84): false | +| Assert.cs:110:13:110:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:110:13:110:25 | [b (line 84): false] ... ? ... : ... | b (line 84): false | +| Assert.cs:110:13:110:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:110:17:110:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:110:24:110:25 | [b (line 84): false] "" | b (line 84): false | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): false] call to method IsFalse | b (line 84): false | +| Assert.cs:111:9:111:33 | [assertion failure, b (line 84): true] call to method IsFalse | b (line 84): true | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): false] call to method IsFalse | b (line 84): false | +| Assert.cs:111:9:111:33 | [assertion success, b (line 84): true] call to method IsFalse | b (line 84): true | +| Assert.cs:111:9:111:34 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:111:9:111:34 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:111:24:111:24 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:111:24:111:24 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:111:24:111:32 | [b (line 84): false] ... == ... | b (line 84): false | +| Assert.cs:111:24:111:32 | [b (line 84): true] ... == ... | b (line 84): true | +| Assert.cs:111:29:111:32 | [b (line 84): false] null | b (line 84): false | +| Assert.cs:111:29:111:32 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:112:9:112:35 | [b (line 84): false] call to method WriteLine | b (line 84): false | +| Assert.cs:112:9:112:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:112:9:112:36 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:112:9:112:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:112:27:112:27 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:112:27:112:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:112:27:112:34 | [b (line 84): false] access to property Length | b (line 84): false | +| Assert.cs:112:27:112:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:114:9:114:25 | [b (line 84): false] ... = ... | b (line 84): false | +| Assert.cs:114:9:114:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:114:9:114:26 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:114:9:114:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:114:13:114:13 | [b (line 84): false] access to parameter b | b (line 84): false | +| Assert.cs:114:13:114:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:114:13:114:25 | [b (line 84): false] ... ? ... : ... | b (line 84): false | +| Assert.cs:114:13:114:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:114:17:114:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:114:24:114:25 | [b (line 84): false] "" | b (line 84): false | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): false] call to method IsTrue | b (line 84): false | +| Assert.cs:115:9:115:37 | [assertion failure, b (line 84): true] call to method IsTrue | b (line 84): true | +| Assert.cs:115:9:115:37 | [assertion success, b (line 84): true] call to method IsTrue | b (line 84): true | +| Assert.cs:115:9:115:38 | [b (line 84): false] ...; | b (line 84): false | +| Assert.cs:115:9:115:38 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:115:23:115:23 | [b (line 84): false] access to local variable s | b (line 84): false | +| Assert.cs:115:23:115:23 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:115:23:115:31 | [b (line 84): false] ... != ... | b (line 84): false | +| Assert.cs:115:23:115:31 | [b (line 84): true] ... != ... | b (line 84): true | +| Assert.cs:115:23:115:36 | [b (line 84): false] ... && ... | b (line 84): false | +| Assert.cs:115:23:115:36 | [b (line 84): true] ... && ... | b (line 84): true | +| Assert.cs:115:28:115:31 | [b (line 84): false] null | b (line 84): false | +| Assert.cs:115:28:115:31 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:115:36:115:36 | [b (line 84): false] access to parameter b | b (line 84): false | +| Assert.cs:115:36:115:36 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:116:9:116:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:116:9:116:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:116:27:116:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:116:27:116:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:118:9:118:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:118:9:118:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:118:13:118:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:118:13:118:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:118:17:118:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:119:9:119:39 | [assertion failure, b (line 84): true] call to method IsFalse | b (line 84): true | +| Assert.cs:119:9:119:39 | [assertion success, b (line 84): true] call to method IsFalse | b (line 84): true | +| Assert.cs:119:9:119:40 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:119:24:119:24 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:119:24:119:32 | [b (line 84): true] ... == ... | b (line 84): true | +| Assert.cs:119:24:119:38 | [b (line 84): true] ... \|\| ... | b (line 84): true | +| Assert.cs:119:29:119:32 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:119:37:119:38 | [b (line 84): true] !... | b (line 84): true | +| Assert.cs:119:38:119:38 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:120:9:120:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:120:9:120:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:120:27:120:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:120:27:120:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:122:9:122:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:122:9:122:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:122:13:122:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:122:13:122:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:122:17:122:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:123:9:123:37 | [assertion failure, b (line 84): true] call to method IsTrue | b (line 84): true | +| Assert.cs:123:9:123:37 | [assertion success, b (line 84): true] call to method IsTrue | b (line 84): true | +| Assert.cs:123:9:123:38 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:123:23:123:23 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:123:23:123:31 | [b (line 84): true] ... == ... | b (line 84): true | +| Assert.cs:123:23:123:36 | [b (line 84): true] ... && ... | b (line 84): true | +| Assert.cs:123:28:123:31 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:123:36:123:36 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:124:9:124:35 | [b (line 84): true] call to method WriteLine | b (line 84): true | +| Assert.cs:124:9:124:36 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:124:27:124:27 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:124:27:124:34 | [b (line 84): true] access to property Length | b (line 84): true | +| Assert.cs:126:9:126:25 | [b (line 84): true] ... = ... | b (line 84): true | +| Assert.cs:126:9:126:26 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:126:13:126:13 | [b (line 84): true] access to parameter b | b (line 84): true | +| Assert.cs:126:13:126:25 | [b (line 84): true] ... ? ... : ... | b (line 84): true | +| Assert.cs:126:17:126:20 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:127:9:127:40 | [b (line 84): true] ...; | b (line 84): true | +| Assert.cs:127:24:127:24 | [b (line 84): true] access to local variable s | b (line 84): true | +| Assert.cs:127:24:127:32 | [b (line 84): true] ... != ... | b (line 84): true | +| Assert.cs:127:24:127:38 | [b (line 84): true] ... \|\| ... | b (line 84): true | +| Assert.cs:127:29:127:32 | [b (line 84): true] null | b (line 84): true | +| Assert.cs:127:37:127:38 | [b (line 84): true] !... | b (line 84): true | +| Assert.cs:127:38:127:38 | [b (line 84): true] access to parameter b | b (line 84): true | | Conditions.cs:6:13:6:13 | [inc (line 3): true] access to parameter x | inc (line 3): true | | Conditions.cs:6:13:6:15 | [inc (line 3): true] ...++ | inc (line 3): true | | Conditions.cs:6:13:6:16 | [inc (line 3): true] ...; | inc (line 3): true | @@ -160,6 +496,14 @@ booleanNode | Conditions.cs:137:21:137:37 | [Field1 (line 129): true, Field2 (line 129): true] call to method ToString | Field2 (line 129): true | | Conditions.cs:137:21:137:38 | [Field1 (line 129): true, Field2 (line 129): true] ...; | Field1 (line 129): true | | Conditions.cs:137:21:137:38 | [Field1 (line 129): true, Field2 (line 129): true] ...; | Field2 (line 129): true | +| Conditions.cs:145:13:145:29 | [b (line 143): false] String s = ... | b (line 143): false | +| Conditions.cs:145:13:145:29 | [b (line 143): true] String s = ... | b (line 143): true | +| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | b (line 143): true | +| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | b (line 143): false | +| Conditions.cs:146:9:149:49 | [b (line 143): false] if (...) ... | b (line 143): false | +| Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... | b (line 143): true | +| Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | b (line 143): false | +| Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | b (line 143): true | | Finally.cs:180:21:180:43 | [b1 (line 176): true] throw ...; | b1 (line 176): true | | Finally.cs:180:27:180:42 | [b1 (line 176): true] object creation of type ExceptionA | b1 (line 176): true | | Finally.cs:183:9:192:9 | [b1 (line 176): false] {...} | b1 (line 176): false | @@ -224,8 +568,8 @@ booleanNode | Finally.cs:190:21:190:22 | [finally: exception(ExceptionA), b1 (line 176): true] access to parameter b1 | b1 (line 176): true | | LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | b (line 55): false | | LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | b (line 55): true | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | b (line 55): false | -| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | b (line 55): true | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): false] String x | b (line 55): false | +| LoopUnrolling.cs:58:22:58:22 | [b (line 55): true] String x | b (line 55): true | | LoopUnrolling.cs:59:9:64:9 | [b (line 55): false] {...} | b (line 55): false | | LoopUnrolling.cs:59:9:64:9 | [b (line 55): true] {...} | b (line 55): true | | LoopUnrolling.cs:60:13:61:37 | [b (line 55): false] if (...) ... | b (line 55): false | @@ -642,8 +986,20 @@ entryPoint | AccessorCalls.cs:66:10:66:11 | M9 | AccessorCalls.cs:67:5:74:5 | {...} | | ArrayCreation.cs:3:11:3:12 | M1 | ArrayCreation.cs:3:27:3:27 | 0 | | ArrayCreation.cs:5:12:5:13 | M2 | ArrayCreation.cs:5:28:5:28 | 0 | -| ArrayCreation.cs:7:11:7:12 | M3 | ArrayCreation.cs:7:19:7:36 | array creation of type Int32[] | -| ArrayCreation.cs:9:12:9:13 | M4 | ArrayCreation.cs:9:20:9:52 | array creation of type Int32[,] | +| ArrayCreation.cs:7:11:7:12 | M3 | ArrayCreation.cs:7:19:7:36 | 2 | +| ArrayCreation.cs:9:12:9:13 | M4 | ArrayCreation.cs:9:20:9:52 | 2 | +| Assert.cs:7:10:7:11 | M1 | Assert.cs:8:5:12:5 | {...} | +| Assert.cs:14:10:14:11 | M2 | Assert.cs:15:5:19:5 | {...} | +| Assert.cs:21:10:21:11 | M3 | Assert.cs:22:5:26:5 | {...} | +| Assert.cs:28:10:28:11 | M4 | Assert.cs:29:5:33:5 | {...} | +| Assert.cs:35:10:35:11 | M5 | Assert.cs:36:5:40:5 | {...} | +| Assert.cs:42:10:42:11 | M6 | Assert.cs:43:5:47:5 | {...} | +| Assert.cs:49:10:49:11 | M7 | Assert.cs:50:5:54:5 | {...} | +| Assert.cs:56:10:56:11 | M8 | Assert.cs:57:5:61:5 | {...} | +| Assert.cs:63:10:63:11 | M9 | Assert.cs:64:5:68:5 | {...} | +| Assert.cs:70:10:70:12 | M10 | Assert.cs:71:5:75:5 | {...} | +| Assert.cs:77:10:77:12 | M11 | Assert.cs:78:5:82:5 | {...} | +| Assert.cs:84:10:84:12 | M12 | Assert.cs:85:5:129:5 | {...} | | Assignments.cs:3:10:3:10 | M | Assignments.cs:4:5:15:5 | {...} | | Assignments.cs:14:18:14:35 | (...) => ... | Assignments.cs:14:33:14:35 | {...} | | Assignments.cs:17:40:17:40 | + | Assignments.cs:18:5:20:5 | {...} | @@ -656,6 +1012,7 @@ entryPoint | CompileTimeOperators.cs:15:10:15:15 | Typeof | CompileTimeOperators.cs:16:5:18:5 | {...} | | CompileTimeOperators.cs:20:12:20:17 | Nameof | CompileTimeOperators.cs:21:5:23:5 | {...} | | CompileTimeOperators.cs:28:10:28:10 | M | CompileTimeOperators.cs:29:5:41:5 | {...} | +| ConditionalAccess.cs:1:7:1:23 | ConditionalAccess | ConditionalAccess.cs:30:32:30:32 | 0 | | ConditionalAccess.cs:3:12:3:13 | M1 | ConditionalAccess.cs:3:26:3:26 | access to parameter i | | ConditionalAccess.cs:5:10:5:11 | M2 | ConditionalAccess.cs:5:26:5:26 | access to parameter s | | ConditionalAccess.cs:7:10:7:11 | M3 | ConditionalAccess.cs:7:39:7:46 | ... ?? ... | @@ -663,7 +1020,9 @@ entryPoint | ConditionalAccess.cs:11:9:11:10 | M5 | ConditionalAccess.cs:12:5:17:5 | {...} | | ConditionalAccess.cs:19:12:19:13 | M6 | ConditionalAccess.cs:19:40:19:41 | access to parameter s1 | | ConditionalAccess.cs:21:10:21:11 | M7 | ConditionalAccess.cs:22:5:26:5 | {...} | -| ConditionalAccess.cs:31:26:31:38 | CommaJoinWith | ConditionalAccess.cs:31:70:31:71 | access to parameter s1 | +| ConditionalAccess.cs:30:10:30:12 | Out | ConditionalAccess.cs:30:32:30:32 | 0 | +| ConditionalAccess.cs:32:10:32:11 | M8 | ConditionalAccess.cs:33:5:36:5 | {...} | +| ConditionalAccess.cs:41:26:41:38 | CommaJoinWith | ConditionalAccess.cs:41:70:41:71 | access to parameter s1 | | Conditions.cs:3:10:3:19 | IncrOrDecr | Conditions.cs:4:5:9:5 | {...} | | Conditions.cs:11:9:11:10 | M1 | Conditions.cs:12:5:20:5 | {...} | | Conditions.cs:22:9:22:10 | M2 | Conditions.cs:23:5:31:5 | {...} | @@ -675,6 +1034,7 @@ entryPoint | Conditions.cs:102:12:102:13 | M8 | Conditions.cs:103:5:111:5 | {...} | | Conditions.cs:113:10:113:11 | M9 | Conditions.cs:114:5:124:5 | {...} | | Conditions.cs:129:10:129:12 | M10 | Conditions.cs:130:5:141:5 | {...} | +| Conditions.cs:143:10:143:12 | M11 | Conditions.cs:144:5:150:5 | {...} | | ExitMethods.cs:7:10:7:11 | M1 | ExitMethods.cs:8:5:11:5 | {...} | | ExitMethods.cs:13:10:13:11 | M2 | ExitMethods.cs:14:5:17:5 | {...} | | ExitMethods.cs:19:10:19:11 | M3 | ExitMethods.cs:20:5:23:5 | {...} | @@ -733,6 +1093,66 @@ entryPoint | LoopUnrolling.cs:45:10:45:11 | M6 | LoopUnrolling.cs:46:5:53:5 | {...} | | LoopUnrolling.cs:55:10:55:11 | M7 | LoopUnrolling.cs:56:5:65:5 | {...} | | LoopUnrolling.cs:67:10:67:11 | M8 | LoopUnrolling.cs:68:5:74:5 | {...} | +| LoopUnrolling.cs:76:10:76:11 | M9 | LoopUnrolling.cs:77:5:83:5 | {...} | +| LoopUnrolling.cs:85:10:85:12 | M10 | LoopUnrolling.cs:86:5:92:5 | {...} | +| LoopUnrolling.cs:94:10:94:12 | M11 | LoopUnrolling.cs:95:5:101:5 | {...} | +| MultiImplementationA.cs:6:22:6:31 | get_P1 | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationA.cs:6:22:6:31 | get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationA.cs:7:21:7:23 | get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationA.cs:7:21:7:23 | get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationA.cs:7:41:7:43 | set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationA.cs:7:41:7:43 | set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationA.cs:8:16:8:16 | M | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:8:16:8:16 | M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationA.cs:14:31:14:31 | get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationA.cs:14:31:14:31 | get_Item | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationA.cs:15:36:15:38 | get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationA.cs:15:36:15:38 | get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationA.cs:15:54:15:56 | set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationA.cs:15:54:15:56 | set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationA.cs:16:17:16:18 | M1 | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationA.cs:16:17:16:18 | M1 | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationA.cs:18:9:18:22 | M2 | MultiImplementationA.cs:18:21:18:21 | 0 | +| MultiImplementationA.cs:20:12:20:13 | C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:21:12:21:13 | C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:12:21:13 | C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationA.cs:22:6:22:7 | ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationA.cs:22:6:22:7 | ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationA.cs:23:28:23:35 | implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationA.cs:23:28:23:35 | implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationA.cs:30:21:30:23 | get_P3 | MultiImplementationA.cs:30:34:30:37 | null | +| MultiImplementationA.cs:36:9:36:10 | M1 | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationA.cs:36:9:36:10 | M1 | MultiImplementationB.cs:32:17:32:17 | 0 | +| MultiImplementationA.cs:37:9:37:10 | M2 | MultiImplementationA.cs:37:14:37:28 | {...} | +| MultiImplementationB.cs:3:22:3:22 | get_P1 | MultiImplementationA.cs:6:28:6:31 | null | +| MultiImplementationB.cs:3:22:3:22 | get_P1 | MultiImplementationB.cs:3:22:3:22 | 0 | +| MultiImplementationB.cs:4:21:4:23 | get_P2 | MultiImplementationA.cs:7:25:7:39 | {...} | +| MultiImplementationB.cs:4:21:4:23 | get_P2 | MultiImplementationB.cs:4:25:4:37 | {...} | +| MultiImplementationB.cs:4:39:4:41 | set_P2 | MultiImplementationA.cs:7:45:7:59 | {...} | +| MultiImplementationB.cs:4:39:4:41 | set_P2 | MultiImplementationB.cs:4:43:4:45 | {...} | +| MultiImplementationB.cs:5:16:5:16 | M | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationB.cs:5:16:5:16 | M | MultiImplementationB.cs:5:23:5:23 | 2 | +| MultiImplementationB.cs:12:31:12:40 | get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | +| MultiImplementationB.cs:12:31:12:40 | get_Item | MultiImplementationB.cs:12:37:12:40 | null | +| MultiImplementationB.cs:13:36:13:38 | get_Item | MultiImplementationA.cs:15:40:15:52 | {...} | +| MultiImplementationB.cs:13:36:13:38 | get_Item | MultiImplementationB.cs:13:40:13:54 | {...} | +| MultiImplementationB.cs:13:56:13:58 | set_Item | MultiImplementationA.cs:15:58:15:60 | {...} | +| MultiImplementationB.cs:13:56:13:58 | set_Item | MultiImplementationB.cs:13:60:13:62 | {...} | +| MultiImplementationB.cs:14:17:14:18 | M1 | MultiImplementationA.cs:17:5:19:5 | {...} | +| MultiImplementationB.cs:14:17:14:18 | M1 | MultiImplementationB.cs:15:5:17:5 | {...} | +| MultiImplementationB.cs:16:9:16:31 | M2 | MultiImplementationB.cs:16:27:16:30 | null | +| MultiImplementationB.cs:18:12:18:13 | C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:19:12:19:13 | C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationB.cs:19:12:19:13 | C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:20:6:20:7 | ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | +| MultiImplementationB.cs:20:6:20:7 | ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | +| MultiImplementationB.cs:21:28:21:35 | implicit conversion | MultiImplementationA.cs:23:50:23:53 | null | +| MultiImplementationB.cs:21:28:21:35 | implicit conversion | MultiImplementationB.cs:21:56:21:59 | null | +| MultiImplementationB.cs:27:21:27:23 | get_P3 | MultiImplementationA.cs:30:34:30:37 | null | +| MultiImplementationB.cs:32:9:32:10 | M1 | MultiImplementationA.cs:36:14:36:28 | {...} | +| MultiImplementationB.cs:32:9:32:10 | M1 | MultiImplementationB.cs:32:17:32:17 | 0 | | NullCoalescing.cs:3:9:3:10 | M1 | NullCoalescing.cs:3:23:3:28 | ... ?? ... | | NullCoalescing.cs:5:9:5:10 | M2 | NullCoalescing.cs:5:24:5:43 | ... ? ... : ... | | NullCoalescing.cs:7:12:7:13 | M3 | NullCoalescing.cs:7:40:7:53 | ... ?? ... | @@ -758,6 +1178,7 @@ entryPoint | Switch.cs:129:12:129:14 | M12 | Switch.cs:130:5:132:5 | {...} | | Switch.cs:134:9:134:11 | M13 | Switch.cs:135:5:142:5 | {...} | | Switch.cs:144:9:144:11 | M14 | Switch.cs:145:5:152:5 | {...} | +| Switch.cs:154:10:154:12 | M15 | Switch.cs:155:5:161:5 | {...} | | TypeAccesses.cs:3:10:3:10 | M | TypeAccesses.cs:4:5:9:5 | {...} | | VarDecls.cs:5:18:5:19 | M1 | VarDecls.cs:6:5:11:5 | {...} | | VarDecls.cs:13:12:13:13 | M2 | VarDecls.cs:14:5:17:5 | {...} | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Switch.cs b/csharp/ql/test/library-tests/controlflow/graph/Switch.cs index 6b670908224d..45912ead10e5 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Switch.cs +++ b/csharp/ql/test/library-tests/controlflow/graph/Switch.cs @@ -25,7 +25,7 @@ void M2(object o) Console.WriteLine(s); return; case double d when Throw(): - Label: + Label: return; default: goto Label; @@ -56,10 +56,10 @@ void M5() { switch (1 + 2) { - case 2 : - break; - case 3 : - break; + case 2: + break; + case 3: + break; } } @@ -67,10 +67,10 @@ void M6(string s) { switch ((object)s) { - case int _ : - break; - case "" : - break; + case int _: + break; + case "": + break; } } @@ -78,12 +78,12 @@ bool M7(int i, int j) { switch (i) { - case 1 : - return true; - case 2 : - if (j > 2) - break; - return true; + case 1: + return true; + case 2: + if (j > 2) + break; + return true; } return false; } @@ -92,8 +92,8 @@ bool M8(object o) { switch (o) { - case int _ : - return true; + case int _: + return true; } return false; } @@ -102,8 +102,8 @@ int M9(string s) { switch (s?.Length) { - case 0 : return 0; - case 1 : return 1; + case 0: return 0; + case 1: return 1; } return -1; } @@ -114,8 +114,8 @@ int M10(string s) { switch (s.Length) { - case 3 when s=="foo" : return 1; - case 2 when s=="fu" : return 2; + case 3 when s == "foo": return 1; + case 2 when s == "fu": return 2; } return -1; } @@ -150,4 +150,13 @@ int M14(int i) case 2: return 2; } } + + void M15(bool b) + { + var s = b switch { true => "a", false => "b" }; + if (b) + System.Console.WriteLine($"a = {s}"); + else + System.Console.WriteLine($"b = {s}"); + } } diff --git a/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.expected b/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.expected index 25bc436216bf..cff9d3249ef8 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.expected @@ -18,7 +18,7 @@ abstractValue | 0 | Collections.cs:78:36:78:36 | 0 | | 0 | Collections.cs:80:35:80:35 | 0 | | 0 | Collections.cs:81:36:81:36 | 0 | -| 0 | Collections.cs:87:17:87:31 | 0 | +| 0 | Collections.cs:87:17:87:32 | 0 | | 0 | Guards.cs:12:24:12:24 | 0 | | 0 | Guards.cs:78:26:78:26 | 0 | | 0 | Guards.cs:80:25:80:25 | 0 | @@ -32,7 +32,7 @@ abstractValue | 0 | Guards.cs:322:18:322:18 | 0 | | 0 | Guards.cs:329:17:329:19 | access to constant A | | 0 | Guards.cs:334:20:334:20 | 0 | -| 0 | Splitting.cs:136:20:136:20 | 0 | +| 0 | Splitting.cs:137:20:137:20 | 0 | | 1 | Collections.cs:13:28:13:28 | 1 | | 1 | Collections.cs:15:28:15:28 | 1 | | 1 | Collections.cs:18:28:18:28 | 1 | @@ -59,8 +59,8 @@ abstractValue | 1 | Guards.cs:331:17:331:19 | access to constant B | | 1 | Guards.cs:334:13:334:15 | access to constant B | | 1 | Guards.cs:335:18:335:18 | 1 | -| 3 | Collections.cs:55:13:55:41 | 3 | -| 3 | Collections.cs:63:17:63:45 | 3 | +| 3 | Collections.cs:55:13:55:42 | 3 | +| 3 | Collections.cs:63:17:63:46 | 3 | | 10 | Guards.cs:84:25:84:26 | 10 | | 10 | Guards.cs:86:26:86:27 | 10 | | empty | Collections.cs:54:13:54:16 | access to parameter args | @@ -69,22 +69,22 @@ abstractValue | empty | Collections.cs:58:9:58:13 | ... = ... | | empty | Collections.cs:58:13:58:13 | access to local variable x | | empty | Collections.cs:65:13:65:13 | access to local variable x | -| empty | Collections.cs:87:17:87:31 | array creation of type String[] | -| empty | Collections.cs:87:30:87:31 | { ..., ... } | -| empty | Collections.cs:88:22:88:23 | { ..., ... } | +| empty | Collections.cs:87:17:87:32 | array creation of type String[] | +| empty | Collections.cs:87:30:87:32 | { ..., ... } | +| empty | Collections.cs:88:22:88:24 | { ..., ... } | | false | Guards.cs:178:16:178:20 | false | | false | Guards.cs:181:53:181:57 | false | | false | Guards.cs:217:18:217:22 | false | | false | Guards.cs:228:18:228:22 | false | | false | Guards.cs:295:18:295:22 | false | | false | Guards.cs:305:18:305:22 | false | -| non-empty | Collections.cs:55:9:55:41 | ... = ... | -| non-empty | Collections.cs:55:13:55:41 | array creation of type String[] | -| non-empty | Collections.cs:55:25:55:41 | { ..., ... } | +| non-empty | Collections.cs:55:9:55:42 | ... = ... | +| non-empty | Collections.cs:55:13:55:42 | array creation of type String[] | +| non-empty | Collections.cs:55:26:55:42 | { ..., ... } | | non-empty | Collections.cs:56:9:56:13 | ... = ... | | non-empty | Collections.cs:56:13:56:13 | access to local variable x | -| non-empty | Collections.cs:63:17:63:45 | array creation of type String[] | -| non-empty | Collections.cs:63:29:63:45 | { ..., ... } | +| non-empty | Collections.cs:63:17:63:46 | array creation of type String[] | +| non-empty | Collections.cs:63:30:63:46 | { ..., ... } | | non-empty | Collections.cs:68:13:68:13 | access to local variable x | | non-empty | Collections.cs:89:9:89:32 | ... = ... | | non-empty | Collections.cs:89:13:89:32 | array creation of type String[] | @@ -155,11 +155,11 @@ abstractValue | non-null | Collections.cs:54:13:54:16 | access to parameter args | | non-null | Collections.cs:54:13:54:26 | call to method ToArray | | non-null | Collections.cs:55:9:55:9 | access to local variable x | -| non-null | Collections.cs:55:9:55:41 | ... = ... | -| non-null | Collections.cs:55:13:55:41 | array creation of type String[] | -| non-null | Collections.cs:55:27:55:29 | "a" | -| non-null | Collections.cs:55:32:55:34 | "b" | -| non-null | Collections.cs:55:37:55:39 | "c" | +| non-null | Collections.cs:55:9:55:42 | ... = ... | +| non-null | Collections.cs:55:13:55:42 | array creation of type String[] | +| non-null | Collections.cs:55:28:55:30 | "a" | +| non-null | Collections.cs:55:33:55:35 | "b" | +| non-null | Collections.cs:55:38:55:40 | "c" | | non-null | Collections.cs:56:9:56:9 | access to local variable x | | non-null | Collections.cs:56:9:56:13 | ... = ... | | non-null | Collections.cs:56:13:56:13 | access to local variable x | @@ -169,11 +169,11 @@ abstractValue | non-null | Collections.cs:58:9:58:9 | access to local variable x | | non-null | Collections.cs:58:9:58:13 | ... = ... | | non-null | Collections.cs:58:13:58:13 | access to local variable x | -| non-null | Collections.cs:63:17:63:45 | array creation of type String[] | -| non-null | Collections.cs:63:17:63:54 | call to method ToList | -| non-null | Collections.cs:63:31:63:33 | "a" | -| non-null | Collections.cs:63:36:63:38 | "b" | -| non-null | Collections.cs:63:41:63:43 | "c" | +| non-null | Collections.cs:63:17:63:46 | array creation of type String[] | +| non-null | Collections.cs:63:17:63:55 | call to method ToList | +| non-null | Collections.cs:63:32:63:34 | "a" | +| non-null | Collections.cs:63:37:63:39 | "b" | +| non-null | Collections.cs:63:42:63:44 | "c" | | non-null | Collections.cs:64:9:64:9 | access to local variable x | | non-null | Collections.cs:65:13:65:13 | access to local variable x | | non-null | Collections.cs:67:13:67:13 | access to local variable x | @@ -214,14 +214,20 @@ abstractValue | non-null | Collections.cs:82:24:82:30 | access to local function IsEmpty | | non-null | Collections.cs:82:24:82:30 | delegate creation of type Func | | non-null | Collections.cs:82:24:82:30 | this access | -| non-null | Collections.cs:87:17:87:31 | array creation of type String[] | -| non-null | Collections.cs:88:22:88:23 | array creation of type String[] | +| non-null | Collections.cs:87:17:87:32 | array creation of type String[] | +| non-null | Collections.cs:88:22:88:24 | array creation of type String[] | | non-null | Collections.cs:89:9:89:9 | access to local variable x | | non-null | Collections.cs:89:9:89:32 | ... = ... | | non-null | Collections.cs:89:13:89:32 | array creation of type String[] | | non-null | Collections.cs:89:28:89:30 | "a" | | non-null | Collections.cs:90:22:90:28 | array creation of type String[] | | non-null | Collections.cs:90:24:90:26 | "a" | +| non-null | Collections.cs:95:29:95:32 | access to parameter args | +| non-null | Collections.cs:96:13:96:19 | access to type Console | +| non-null | Collections.cs:96:31:96:34 | access to parameter args | +| non-null | Collections.cs:101:29:101:32 | access to parameter args | +| non-null | Collections.cs:103:9:103:15 | access to type Console | +| non-null | Collections.cs:103:27:103:30 | access to parameter args | | non-null | Guards.cs:12:13:12:13 | access to parameter s | | non-null | Guards.cs:14:13:14:19 | access to type Console | | non-null | Guards.cs:14:31:14:31 | access to parameter s | @@ -359,6 +365,9 @@ abstractValue | non-null | Guards.cs:281:17:281:17 | access to local variable a | | non-null | Guards.cs:283:17:283:17 | access to parameter o | | non-null | Guards.cs:287:17:287:17 | access to parameter o | +| non-null | Guards.cs:341:31:341:32 | "" | +| non-null | Guards.cs:343:13:343:19 | access to type Console | +| non-null | Guards.cs:343:31:343:31 | access to local variable s | | non-null | Splitting.cs:13:17:13:17 | access to parameter o | | non-null | Splitting.cs:23:24:23:24 | access to parameter o | | non-null | Splitting.cs:33:24:33:25 | "" | @@ -378,12 +387,13 @@ abstractValue | non-null | Splitting.cs:117:9:117:9 | access to parameter o | | non-null | Splitting.cs:119:13:119:13 | access to parameter o | | non-null | Splitting.cs:120:16:120:16 | access to parameter o | -| non-null | Splitting.cs:132:17:132:17 | access to local variable o | -| non-null | Splitting.cs:132:17:132:29 | ... = ... | -| non-null | Splitting.cs:132:21:132:29 | call to method M11 | -| non-null | Splitting.cs:132:21:132:29 | this access | -| non-null | Splitting.cs:132:28:132:28 | access to local variable o | +| non-null | Splitting.cs:129:17:129:17 | access to local variable o | | non-null | Splitting.cs:133:17:133:17 | access to local variable o | +| non-null | Splitting.cs:133:17:133:29 | ... = ... | +| non-null | Splitting.cs:133:21:133:29 | call to method M11 | +| non-null | Splitting.cs:133:21:133:29 | this access | +| non-null | Splitting.cs:133:28:133:28 | access to local variable o | +| non-null | Splitting.cs:134:17:134:17 | access to local variable o | | null | Assert.cs:9:24:9:27 | null | | null | Assert.cs:10:27:10:30 | null | | null | Assert.cs:16:24:16:27 | null | @@ -432,6 +442,8 @@ abstractValue | null | Guards.cs:181:39:181:42 | null | | null | Guards.cs:185:43:185:46 | null | | null | Guards.cs:203:18:203:21 | null | +| null | Guards.cs:341:24:341:27 | null | +| null | Guards.cs:342:18:342:21 | null | | null | Splitting.cs:12:22:12:25 | null | | null | Splitting.cs:22:22:22:25 | null | | null | Splitting.cs:32:22:32:25 | null | @@ -444,7 +456,7 @@ abstractValue | null | Splitting.cs:105:27:105:30 | null | | null | Splitting.cs:116:27:116:30 | null | | null | Splitting.cs:125:21:125:24 | null | -| null | Splitting.cs:128:22:128:25 | null | +| null | Splitting.cs:129:22:129:25 | null | | true | Guards.cs:177:20:177:23 | true | | true | Guards.cs:181:46:181:49 | true | | true | Guards.cs:185:50:185:53 | true | diff --git a/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected index 2aa76cde6a0c..2264c96644f6 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected @@ -5,16 +5,12 @@ | Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:24 | access to local variable s | false | | Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | false | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | | Collections.cs:53:9:53:12 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | | Collections.cs:54:13:54:16 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | @@ -75,6 +71,8 @@ | Guards.cs:208:17:208:17 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | | Guards.cs:269:13:269:14 | access to parameter o1 | Guards.cs:268:13:268:41 | call to operator == | Guards.cs:268:13:268:14 | access to parameter o1 | true | | Guards.cs:271:13:271:14 | access to parameter o1 | Guards.cs:270:13:270:42 | call to operator == | Guards.cs:270:13:270:14 | access to parameter o1 | true | +| Guards.cs:342:27:342:27 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | false | +| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true | | Splitting.cs:23:24:23:24 | access to parameter o | Splitting.cs:22:17:22:25 | ... != ... | Splitting.cs:22:17:22:17 | access to parameter o | true | | Splitting.cs:25:13:25:13 | access to parameter o | Splitting.cs:22:17:22:25 | ... != ... | Splitting.cs:22:17:22:17 | access to parameter o | false | @@ -93,4 +91,4 @@ | Splitting.cs:117:9:117:9 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true | | Splitting.cs:119:13:119:13 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true | | Splitting.cs:120:16:120:16 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true | -| Splitting.cs:132:25:132:25 | access to parameter b | Splitting.cs:130:21:130:21 | access to parameter b | Splitting.cs:130:21:130:21 | access to parameter b | false | +| Splitting.cs:133:25:133:25 | access to parameter b | Splitting.cs:131:21:131:21 | access to parameter b | Splitting.cs:131:21:131:21 | access to parameter b | false | diff --git a/csharp/ql/test/library-tests/controlflow/guards/Collections.cs b/csharp/ql/test/library-tests/controlflow/guards/Collections.cs index 65f07491ae54..93a19595c84e 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/Collections.cs +++ b/csharp/ql/test/library-tests/controlflow/guards/Collections.cs @@ -52,7 +52,7 @@ void M5(List args) var x = args.ToArray(); args.Clear(); x = args.ToArray(); - x = new string[]{ "a", "b", "c" }; + x = new string[] { "a", "b", "c" }; x = x; x = new string[0]; x = x; @@ -60,7 +60,7 @@ void M5(List args) void M6() { - var x = new string[]{ "a", "b", "c" }.ToList(); + var x = new string[] { "a", "b", "c" }.ToList(); x.Clear(); if (x.Count == 0) { @@ -84,10 +84,23 @@ void M7(string[] args) void M8() { - var x = new string[] {}; - string[] y = {}; + var x = new string[] { }; + string[] y = { }; x = new string[] { "a" }; string[] z = { "a" }; } + + void M9(string[] args) + { + foreach (var arg in args) + Console.WriteLine(args); // guarded by `args` being non-empty + } + + void M10(string[] args) + { + foreach (var arg in args) + ; + Console.WriteLine(args); + } } diff --git a/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected b/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected index 7a4cbaf0df49..a61814184f7e 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected @@ -10,26 +10,34 @@ | Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:24:45:24 | access to local variable s | false | | Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:52:24:52:24 | access to local variable s | Assert.cs:52:24:52:24 | access to local variable s | non-null | | Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:24 | access to local variable s | false | -| Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | false | -| Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | false | +| Assert.cs:59:36:59:36 | [b (line 56): false] access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | false | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | true | +| Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | -| Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | +| Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | true | +| Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | -| Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | false | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | +| Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | +| Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | -| Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | false | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | +| Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | +| Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:16 | access to parameter args | Collections.cs:50:13:50:16 | access to parameter args | non-empty | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | | Collections.cs:53:9:53:12 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | @@ -37,6 +45,7 @@ | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:13 | access to local variable x | Collections.cs:65:13:65:13 | access to local variable x | empty | | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | +| Collections.cs:96:31:96:34 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | non-empty | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | @@ -191,6 +200,13 @@ | Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match access to type Action | | Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match null | | Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:342:27:342:27 | [b (line 339): false] access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | false | +| Guards.cs:342:27:342:27 | [b (line 339): false] access to parameter b | Guards.cs:341:20:341:32 | ... ? ... : ... | Guards.cs:341:20:341:20 | access to parameter b | non-null | +| Guards.cs:342:27:342:27 | [b (line 339): true] access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | false | +| Guards.cs:342:27:342:27 | [b (line 339): true] access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | true | +| Guards.cs:342:27:342:27 | [b (line 339): true] access to parameter b | Guards.cs:341:20:341:32 | ... ? ... : ... | Guards.cs:341:20:341:20 | access to parameter b | non-null | +| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | non-null | +| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Splitting.cs:13:17:13:17 | [b (line 9): true] access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null | | Splitting.cs:13:17:13:17 | [b (line 9): true] access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true | | Splitting.cs:14:13:14:13 | [b (line 9): false] access to parameter b | Splitting.cs:11:13:11:13 | access to parameter b | Splitting.cs:11:13:11:13 | access to parameter b | false | @@ -255,5 +271,5 @@ | Splitting.cs:119:13:119:13 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true | | Splitting.cs:120:16:120:16 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | non-null | | Splitting.cs:120:16:120:16 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true | -| Splitting.cs:130:21:130:21 | [b (line 123): false] access to parameter b | Splitting.cs:130:21:130:21 | access to parameter b | Splitting.cs:130:21:130:21 | access to parameter b | false | -| Splitting.cs:132:25:132:25 | [b (line 123): false] access to parameter b | Splitting.cs:130:21:130:21 | access to parameter b | Splitting.cs:130:21:130:21 | access to parameter b | false | +| Splitting.cs:131:21:131:21 | [b (line 123): false] access to parameter b | Splitting.cs:131:21:131:21 | access to parameter b | Splitting.cs:131:21:131:21 | access to parameter b | false | +| Splitting.cs:133:25:133:25 | [b (line 123): false] access to parameter b | Splitting.cs:131:21:131:21 | access to parameter b | Splitting.cs:131:21:131:21 | access to parameter b | false | diff --git a/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected index e8d85631cb76..7e74adb0fc67 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected @@ -14,22 +14,18 @@ | Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:16 | access to parameter args | Collections.cs:50:13:50:16 | access to parameter args | non-empty | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | | Collections.cs:53:9:53:12 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | @@ -37,6 +33,7 @@ | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:13 | access to local variable x | Collections.cs:65:13:65:13 | access to local variable x | empty | | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | +| Collections.cs:96:31:96:34 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | non-empty | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | @@ -191,6 +188,10 @@ | Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match access to type Action | | Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-match null | | Guards.cs:287:17:287:17 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:342:27:342:27 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | false | +| Guards.cs:342:27:342:27 | access to parameter b | Guards.cs:341:20:341:32 | ... ? ... : ... | Guards.cs:341:20:341:20 | access to parameter b | non-null | +| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | non-null | +| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null | | Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true | | Splitting.cs:23:24:23:24 | access to parameter o | Splitting.cs:22:17:22:17 | access to parameter o | Splitting.cs:22:17:22:17 | access to parameter o | non-null | @@ -227,4 +228,4 @@ | Splitting.cs:119:13:119:13 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true | | Splitting.cs:120:16:120:16 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | Splitting.cs:116:22:116:22 | access to parameter o | non-null | | Splitting.cs:120:16:120:16 | access to parameter o | Splitting.cs:116:22:116:30 | ... != ... | Splitting.cs:116:22:116:22 | access to parameter o | true | -| Splitting.cs:132:25:132:25 | access to parameter b | Splitting.cs:130:21:130:21 | access to parameter b | Splitting.cs:130:21:130:21 | access to parameter b | false | +| Splitting.cs:133:25:133:25 | access to parameter b | Splitting.cs:131:21:131:21 | access to parameter b | Splitting.cs:131:21:131:21 | access to parameter b | false | diff --git a/csharp/ql/test/library-tests/controlflow/guards/Guards.cs b/csharp/ql/test/library-tests/controlflow/guards/Guards.cs index 67a58f59aac7..0c4563b3c9fa 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/Guards.cs +++ b/csharp/ql/test/library-tests/controlflow/guards/Guards.cs @@ -335,5 +335,12 @@ int M27(bool b) _ => 1 }; } + + void M28(bool b) + { + string s = b ? null : ""; + if (s != null && !b) + Console.WriteLine(s.Length); // null guarded + } } diff --git a/csharp/ql/test/library-tests/controlflow/guards/Implications.expected b/csharp/ql/test/library-tests/controlflow/guards/Implications.expected index 69bfefd6d9f5..1f2bdd595ed8 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/Implications.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/Implications.expected @@ -18,7 +18,9 @@ | Assert.cs:16:20:16:32 | ... ? ... : ... | null | Assert.cs:16:24:16:27 | null | null | | Assert.cs:17:23:17:23 | access to local variable s | empty | Assert.cs:16:20:16:32 | ... ? ... : ... | empty | | Assert.cs:17:23:17:23 | access to local variable s | non-empty | Assert.cs:16:20:16:32 | ... ? ... : ... | non-empty | +| Assert.cs:17:23:17:23 | access to local variable s | non-null | Assert.cs:16:20:16:20 | access to parameter b | false | | Assert.cs:17:23:17:23 | access to local variable s | non-null | Assert.cs:16:20:16:32 | ... ? ... : ... | non-null | +| Assert.cs:17:23:17:23 | access to local variable s | null | Assert.cs:16:20:16:20 | access to parameter b | true | | Assert.cs:17:23:17:23 | access to local variable s | null | Assert.cs:16:20:16:32 | ... ? ... : ... | null | | Assert.cs:18:27:18:27 | access to local variable s | non-null | Assert.cs:16:20:16:32 | ... ? ... : ... | non-null | | Assert.cs:18:27:18:27 | access to local variable s | null | Assert.cs:16:20:16:32 | ... ? ... : ... | null | @@ -28,7 +30,9 @@ | Assert.cs:23:20:23:32 | ... ? ... : ... | null | Assert.cs:23:24:23:27 | null | null | | Assert.cs:24:26:24:26 | access to local variable s | empty | Assert.cs:23:20:23:32 | ... ? ... : ... | empty | | Assert.cs:24:26:24:26 | access to local variable s | non-empty | Assert.cs:23:20:23:32 | ... ? ... : ... | non-empty | +| Assert.cs:24:26:24:26 | access to local variable s | non-null | Assert.cs:23:20:23:20 | access to parameter b | false | | Assert.cs:24:26:24:26 | access to local variable s | non-null | Assert.cs:23:20:23:32 | ... ? ... : ... | non-null | +| Assert.cs:24:26:24:26 | access to local variable s | null | Assert.cs:23:20:23:20 | access to parameter b | true | | Assert.cs:24:26:24:26 | access to local variable s | null | Assert.cs:23:20:23:32 | ... ? ... : ... | null | | Assert.cs:25:27:25:27 | access to local variable s | non-null | Assert.cs:23:20:23:32 | ... ? ... : ... | non-null | | Assert.cs:25:27:25:27 | access to local variable s | null | Assert.cs:23:20:23:32 | ... ? ... : ... | null | @@ -180,26 +184,26 @@ | Collections.cs:45:17:45:26 | call to method Any | true | Collections.cs:45:17:45:20 | access to parameter args | non-empty | | Collections.cs:50:13:50:27 | ... == ... | false | Collections.cs:50:13:50:16 | access to parameter args | non-empty | | Collections.cs:50:13:50:27 | ... == ... | true | Collections.cs:50:13:50:16 | access to parameter args | empty | -| Collections.cs:56:13:56:13 | access to local variable x | empty | Collections.cs:55:13:55:41 | array creation of type String[] | empty | -| Collections.cs:56:13:56:13 | access to local variable x | non-empty | Collections.cs:55:13:55:41 | array creation of type String[] | non-empty | -| Collections.cs:56:13:56:13 | access to local variable x | non-null | Collections.cs:55:13:55:41 | array creation of type String[] | non-null | -| Collections.cs:56:13:56:13 | access to local variable x | null | Collections.cs:55:13:55:41 | array creation of type String[] | null | +| Collections.cs:56:13:56:13 | access to local variable x | empty | Collections.cs:55:13:55:42 | array creation of type String[] | empty | +| Collections.cs:56:13:56:13 | access to local variable x | non-empty | Collections.cs:55:13:55:42 | array creation of type String[] | non-empty | +| Collections.cs:56:13:56:13 | access to local variable x | non-null | Collections.cs:55:13:55:42 | array creation of type String[] | non-null | +| Collections.cs:56:13:56:13 | access to local variable x | null | Collections.cs:55:13:55:42 | array creation of type String[] | null | | Collections.cs:58:13:58:13 | access to local variable x | empty | Collections.cs:57:13:57:25 | array creation of type String[] | empty | | Collections.cs:58:13:58:13 | access to local variable x | non-empty | Collections.cs:57:13:57:25 | array creation of type String[] | non-empty | | Collections.cs:58:13:58:13 | access to local variable x | non-null | Collections.cs:57:13:57:25 | array creation of type String[] | non-null | | Collections.cs:58:13:58:13 | access to local variable x | null | Collections.cs:57:13:57:25 | array creation of type String[] | null | -| Collections.cs:64:9:64:9 | access to local variable x | empty | Collections.cs:63:17:63:54 | call to method ToList | empty | -| Collections.cs:64:9:64:9 | access to local variable x | non-empty | Collections.cs:63:17:63:54 | call to method ToList | non-empty | -| Collections.cs:64:9:64:9 | access to local variable x | non-null | Collections.cs:63:17:63:54 | call to method ToList | non-null | -| Collections.cs:64:9:64:9 | access to local variable x | null | Collections.cs:63:17:63:54 | call to method ToList | null | -| Collections.cs:65:13:65:13 | access to local variable x | non-null | Collections.cs:63:17:63:54 | call to method ToList | non-null | -| Collections.cs:65:13:65:13 | access to local variable x | null | Collections.cs:63:17:63:54 | call to method ToList | null | +| Collections.cs:64:9:64:9 | access to local variable x | empty | Collections.cs:63:17:63:55 | call to method ToList | empty | +| Collections.cs:64:9:64:9 | access to local variable x | non-empty | Collections.cs:63:17:63:55 | call to method ToList | non-empty | +| Collections.cs:64:9:64:9 | access to local variable x | non-null | Collections.cs:63:17:63:55 | call to method ToList | non-null | +| Collections.cs:64:9:64:9 | access to local variable x | null | Collections.cs:63:17:63:55 | call to method ToList | null | +| Collections.cs:65:13:65:13 | access to local variable x | non-null | Collections.cs:63:17:63:55 | call to method ToList | non-null | +| Collections.cs:65:13:65:13 | access to local variable x | null | Collections.cs:63:17:63:55 | call to method ToList | null | | Collections.cs:65:13:65:24 | ... == ... | false | Collections.cs:65:13:65:13 | access to local variable x | non-empty | | Collections.cs:65:13:65:24 | ... == ... | true | Collections.cs:65:13:65:13 | access to local variable x | empty | -| Collections.cs:67:13:67:13 | access to local variable x | non-null | Collections.cs:63:17:63:54 | call to method ToList | non-null | -| Collections.cs:67:13:67:13 | access to local variable x | null | Collections.cs:63:17:63:54 | call to method ToList | null | -| Collections.cs:68:13:68:13 | access to local variable x | non-null | Collections.cs:63:17:63:54 | call to method ToList | non-null | -| Collections.cs:68:13:68:13 | access to local variable x | null | Collections.cs:63:17:63:54 | call to method ToList | null | +| Collections.cs:67:13:67:13 | access to local variable x | non-null | Collections.cs:63:17:63:55 | call to method ToList | non-null | +| Collections.cs:67:13:67:13 | access to local variable x | null | Collections.cs:63:17:63:55 | call to method ToList | null | +| Collections.cs:68:13:68:13 | access to local variable x | non-null | Collections.cs:63:17:63:55 | call to method ToList | non-null | +| Collections.cs:68:13:68:13 | access to local variable x | null | Collections.cs:63:17:63:55 | call to method ToList | null | | Collections.cs:74:35:74:41 | ... == ... | true | Collections.cs:74:35:74:35 | access to parameter s | non-null | | Collections.cs:74:35:74:41 | ... == ... | true | Collections.cs:74:40:74:41 | "" | non-null | | Collections.cs:75:17:75:33 | call to method Any | true | Collections.cs:75:17:75:20 | access to parameter args | non-empty | @@ -397,8 +401,14 @@ | Guards.cs:276:16:276:16 | access to parameter o | match access to type Action | Guards.cs:276:16:276:16 | access to parameter o | non-null | | Guards.cs:276:16:276:16 | access to parameter o | match null | Guards.cs:276:16:276:16 | access to parameter o | null | | Guards.cs:276:16:276:16 | access to parameter o | non-match null | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:278:13:279:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:280:13:281:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | non-null | | Guards.cs:281:17:281:17 | access to local variable a | non-null | Guards.cs:276:16:276:16 | access to parameter o | non-null | | Guards.cs:281:17:281:17 | access to local variable a | null | Guards.cs:276:16:276:16 | access to parameter o | null | +| Guards.cs:282:13:283:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:284:13:285:28 | ... => ... | false | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:284:13:285:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | null | +| Guards.cs:286:13:287:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | non-null | | Guards.cs:296:16:296:17 | access to local variable b2 | match true | Guards.cs:294:13:294:14 | access to parameter b1 | false | | Guards.cs:296:16:296:17 | access to local variable b2 | match true | Guards.cs:296:16:296:17 | access to local variable b2 | true | | Guards.cs:308:16:308:17 | access to local variable b2 | match true | Guards.cs:306:13:306:14 | access to parameter b1 | true | @@ -410,6 +420,24 @@ | Guards.cs:332:16:332:16 | access to local variable e | match access to constant B | Guards.cs:330:13:330:13 | access to parameter b | true | | Guards.cs:332:16:332:16 | access to local variable e | match access to constant B | Guards.cs:332:16:332:16 | access to local variable e | 1 | | Guards.cs:332:16:332:16 | access to local variable e | non-match access to constant B | Guards.cs:330:13:330:13 | access to parameter b | false | +| Guards.cs:341:20:341:32 | ... ? ... : ... | non-null | Guards.cs:341:20:341:20 | access to parameter b | false | +| Guards.cs:341:20:341:32 | ... ? ... : ... | non-null | Guards.cs:341:31:341:32 | "" | non-null | +| Guards.cs:341:20:341:32 | ... ? ... : ... | null | Guards.cs:341:20:341:20 | access to parameter b | true | +| Guards.cs:341:20:341:32 | ... ? ... : ... | null | Guards.cs:341:24:341:27 | null | null | +| Guards.cs:342:13:342:13 | access to local variable s | empty | Guards.cs:341:20:341:32 | ... ? ... : ... | empty | +| Guards.cs:342:13:342:13 | access to local variable s | non-empty | Guards.cs:341:20:341:32 | ... ? ... : ... | non-empty | +| Guards.cs:342:13:342:13 | access to local variable s | non-null | Guards.cs:341:20:341:32 | ... ? ... : ... | non-null | +| Guards.cs:342:13:342:13 | access to local variable s | null | Guards.cs:341:20:341:32 | ... ? ... : ... | null | +| Guards.cs:342:13:342:21 | ... != ... | false | Guards.cs:341:20:341:20 | access to parameter b | true | +| Guards.cs:342:13:342:21 | ... != ... | false | Guards.cs:342:13:342:13 | access to local variable s | null | +| Guards.cs:342:13:342:21 | ... != ... | true | Guards.cs:341:20:341:20 | access to parameter b | false | +| Guards.cs:342:13:342:21 | ... != ... | true | Guards.cs:342:13:342:13 | access to local variable s | non-null | +| Guards.cs:342:13:342:27 | ... && ... | true | Guards.cs:342:13:342:21 | ... != ... | true | +| Guards.cs:342:13:342:27 | ... && ... | true | Guards.cs:342:26:342:27 | !... | true | +| Guards.cs:342:26:342:27 | !... | false | Guards.cs:342:27:342:27 | access to parameter b | true | +| Guards.cs:342:26:342:27 | !... | true | Guards.cs:342:27:342:27 | access to parameter b | false | +| Guards.cs:343:31:343:31 | access to local variable s | non-null | Guards.cs:341:20:341:32 | ... ? ... : ... | non-null | +| Guards.cs:343:31:343:31 | access to local variable s | null | Guards.cs:341:20:341:32 | ... ? ... : ... | null | | Splitting.cs:12:17:12:25 | ... != ... | false | Splitting.cs:12:17:12:17 | access to parameter o | null | | Splitting.cs:12:17:12:25 | ... != ... | true | Splitting.cs:12:17:12:17 | access to parameter o | non-null | | Splitting.cs:22:17:22:25 | ... != ... | false | Splitting.cs:22:17:22:17 | access to parameter o | null | @@ -432,7 +460,8 @@ | Splitting.cs:105:22:105:30 | ... != ... | true | Splitting.cs:105:22:105:22 | access to parameter o | non-null | | Splitting.cs:116:22:116:30 | ... != ... | false | Splitting.cs:116:22:116:22 | access to parameter o | null | | Splitting.cs:116:22:116:30 | ... != ... | true | Splitting.cs:116:22:116:22 | access to parameter o | non-null | -| Splitting.cs:128:17:128:25 | ... != ... | false | Splitting.cs:128:17:128:17 | access to local variable o | null | -| Splitting.cs:128:17:128:25 | ... != ... | true | Splitting.cs:128:17:128:17 | access to local variable o | non-null | -| Splitting.cs:133:17:133:17 | access to local variable o | non-null | Splitting.cs:132:21:132:29 | call to method M11 | non-null | -| Splitting.cs:133:17:133:17 | access to local variable o | null | Splitting.cs:132:21:132:29 | call to method M11 | null | +| Splitting.cs:129:17:129:25 | ... != ... | false | Splitting.cs:129:17:129:17 | access to local variable o | null | +| Splitting.cs:129:17:129:25 | ... != ... | false | Splitting.cs:129:22:129:25 | null | non-null | +| Splitting.cs:129:17:129:25 | ... != ... | true | Splitting.cs:129:17:129:17 | access to local variable o | non-null | +| Splitting.cs:134:17:134:17 | access to local variable o | non-null | Splitting.cs:133:21:133:29 | call to method M11 | non-null | +| Splitting.cs:134:17:134:17 | access to local variable o | null | Splitting.cs:133:21:133:29 | call to method M11 | null | diff --git a/csharp/ql/test/library-tests/controlflow/guards/NullGuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards/NullGuardedExpr.expected index 7c153f6e7aaa..eea5a4dadd34 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/NullGuardedExpr.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/NullGuardedExpr.expected @@ -45,6 +45,7 @@ | Guards.cs:279:17:279:17 | access to parameter o | | Guards.cs:283:17:283:17 | access to parameter o | | Guards.cs:287:17:287:17 | access to parameter o | +| Guards.cs:343:31:343:31 | access to local variable s | | Splitting.cs:13:17:13:17 | access to parameter o | | Splitting.cs:23:24:23:24 | access to parameter o | | Splitting.cs:35:13:35:13 | access to parameter o | diff --git a/csharp/ql/test/library-tests/controlflow/guards/Splitting.cs b/csharp/ql/test/library-tests/controlflow/guards/Splitting.cs index 1efbaa8251ff..76ccdd0f6336 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/Splitting.cs +++ b/csharp/ql/test/library-tests/controlflow/guards/Splitting.cs @@ -125,6 +125,7 @@ public void M12(int i, bool b) object? o = null; do { + o.GetHashCode(); // not null guarded if (o != null) { if (b) diff --git a/csharp/ql/test/library-tests/conversion/nullable/Nullable.cs b/csharp/ql/test/library-tests/conversion/nullable/Nullable.cs index 08d714f123f7..af9c518b2384 100644 --- a/csharp/ql/test/library-tests/conversion/nullable/Nullable.cs +++ b/csharp/ql/test/library-tests/conversion/nullable/Nullable.cs @@ -1,20 +1,29 @@ using System; +using System.Runtime.Serialization; class C { - sbyte x1; - sbyte? x2; + int x1; + int? x2; Nullable x3; Nullable x4; // Verify conversions void M() { - x2 = x1; - x3 = x4; + x2 = x1; // T -> T? conversion: implicit, nullable -> implicit cast + x3 = x4; // T1? -> T2? conversion: implicit, nullable -> implicit cast + + x12 = x1; // T1 -> T2? conversion: implicit, nullable -> implicit cast + x12 = null; // null -> T? conversion: implicit, null literal -> no cast + + x3 = x2; // T? -> T? no conversion + + x14 = x15; // T1? -> T2? conversion: implicit, user defined -> implicit cast } // Cause the following types to exist in the database: + sbyte? x0; byte? x5; short? x6; ushort? x7; @@ -24,4 +33,19 @@ void M() double? x11; long? x12; float? x13; + + A? x14; + B? x15; + + struct A + { + } + + struct B + { + public static implicit operator A(B value) + { + return new A(); + } + } } diff --git a/csharp/ql/test/library-tests/conversion/operator/PrintAst.expected b/csharp/ql/test/library-tests/conversion/operator/PrintAst.expected new file mode 100644 index 000000000000..9ce74d7c956f --- /dev/null +++ b/csharp/ql/test/library-tests/conversion/operator/PrintAst.expected @@ -0,0 +1,17 @@ +Operator.cs: +# 3| [Class] C +# 5| 5: [ImplicitConversionOperator] implicit conversion +#-----| 2: (Parameters) +# 5| 0: [Parameter] i +# 5| 4: [BlockStmt] {...} +# 5| 0: [ReturnStmt] return ...; +# 5| 0: [NullLiteral] null +# 7| 6: [Field] x1 +# 8| 7: [Field] x2 +# 11| 8: [Method] M +# 12| 4: [BlockStmt] {...} +# 13| 0: [ExprStmt] ...; +# 13| 0: [AssignExpr] ... = ... +# 13| 0: [OperatorCall] call to operator implicit conversion +# 13| 0: [FieldAccess] access to field x1 +# 13| 1: [FieldAccess] access to field x2 diff --git a/csharp/ql/test/library-tests/conversion/operator/PrintAst.qlref b/csharp/ql/test/library-tests/conversion/operator/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/conversion/operator/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/conversion/pointer/Pointer.cs b/csharp/ql/test/library-tests/conversion/pointer/Pointer.cs new file mode 100644 index 000000000000..c7e12ac39b49 --- /dev/null +++ b/csharp/ql/test/library-tests/conversion/pointer/Pointer.cs @@ -0,0 +1,28 @@ +using System; + +class C +{ + unsafe static void M1(int[] arr) + { + fixed (int* i1 = arr) + { + } + + fixed (int* i2 = &arr[0]) + { + int* i3 = i2; + i3 = i3 + 1; + *i2 = *i2 + 1; + void* v2 = i2; + } + + int* i4 = null; + + int number = 1024; + byte* p = (byte*)&number; + + var s = "some string"; + fixed (char* c1 = s) + { } + } +} diff --git a/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected b/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected new file mode 100644 index 000000000000..5aca5582d142 --- /dev/null +++ b/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected @@ -0,0 +1,11 @@ +| Pointer.cs:7:21:7:28 | Pointer.cs:7:21:7:28 | Int32* | Int32* | (...) ... | +| Pointer.cs:11:21:11:32 | Pointer.cs:11:21:11:32 | Int32* | Int32* | &... | +| Pointer.cs:13:18:13:24 | Pointer.cs:13:18:13:24 | Int32* | Int32* | access to local variable i2 | +| Pointer.cs:14:13:14:23 | Pointer.cs:14:13:14:23 | Int32* | Int32* | ... + ... | +| Pointer.cs:15:13:15:25 | Pointer.cs:15:13:15:25 | Int32 | Int32 | ... + ... | +| Pointer.cs:16:19:16:25 | Pointer.cs:16:19:16:25 | Void* | Void* | (...) ... | +| Pointer.cs:19:14:19:22 | Pointer.cs:19:14:19:22 | Int32* | null | null | +| Pointer.cs:21:13:21:25 | Pointer.cs:21:13:21:25 | Int32 | Int32 | 1024 | +| Pointer.cs:22:15:22:32 | Pointer.cs:22:15:22:32 | Byte* | Byte* | (...) ... | +| Pointer.cs:24:13:24:29 | Pointer.cs:24:13:24:29 | String | String | "some string" | +| Pointer.cs:25:22:25:27 | Pointer.cs:25:22:25:27 | Char* | Char* | (...) ... | diff --git a/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql b/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql new file mode 100644 index 000000000000..69e7db8c1cf1 --- /dev/null +++ b/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql @@ -0,0 +1,5 @@ +import csharp + +from Assignment a +select a.getLocation(), a.getLValue().getType().toString(), a.getRValue().getType().toString(), + a.getRValue().toString() diff --git a/csharp/ql/test/library-tests/csharp6/PrintAst.expected b/csharp/ql/test/library-tests/csharp6/PrintAst.expected new file mode 100644 index 000000000000..92a1d2f8709a --- /dev/null +++ b/csharp/ql/test/library-tests/csharp6/PrintAst.expected @@ -0,0 +1,236 @@ +csharp6.cs: +# 10| [Class] TestCSharp6 +# 12| 6: [Property] Value +# 15| 2: [AssignExpr] ... = ... +# 15| 0: [IntLiteral] 20 +# 12| 1: [PropertyCall] access to property Value +# 14| 3: [Getter] get_Value +# 17| 7: [Method] Fn +#-----| 2: (Parameters) +# 17| 0: [Parameter] x +# 17| 4: [MethodCall] call to method WriteLine +# 17| 0: [ParameterAccess] access to parameter x +# 19| 8: [Method] Main +# 20| 4: [BlockStmt] {...} +# 21| 0: [TryStmt] try {...} ... +# 22| 0: [BlockStmt] {...} +# 23| 0: [LocalVariableDeclStmt] ... ...; +# 23| 0: [LocalVariableDeclAndInitExpr] String foo = ... +# 23| 0: [NameOfExpr] nameof(...) +# 23| 0: [TypeAccess] access to type TestCSharp6 +# 23| 1: [LocalVariableAccess] access to local variable foo +# 23| 1: [LocalVariableDeclAndInitExpr] String bar = ... +# 23| 0: [NullLiteral] null +# 23| 1: [LocalVariableAccess] access to local variable bar +# 25| 1: [ExprStmt] ...; +# 25| 0: [MethodCall] call to method WriteLine +# 25| 0: [InterpolatedStringExpr] $"..." +# 25| 0: [NameOfExpr] nameof(...) +# 25| 0: [LocalVariableAccess] access to local variable foo +# 25| 1: [StringLiteral] " is " +# 25| 2: [LocalVariableAccess] access to local variable foo +# 25| 3: [StringLiteral] ", and " +# 25| 4: [NameOfExpr] nameof(...) +# 25| 0: [LocalVariableAccess] access to local variable bar +# 25| 5: [StringLiteral] " has length " +# 25| 6: [NullCoalescingExpr] ... ?? ... +# 25| 0: [PropertyCall] access to property Length +# 25| -1: [LocalVariableAccess] access to local variable bar +# 25| 1: [IntLiteral] 0 +# 27| 2: [ExprStmt] ...; +# 27| 0: [MethodCall] call to method Fn +# 27| 0: [InterpolatedStringExpr] $"..." +# 27| 0: [NameOfExpr] nameof(...) +# 27| 0: [LocalVariableAccess] access to local variable foo +# 27| 1: [StringLiteral] " is " +# 27| 2: [LocalVariableAccess] access to local variable foo +# 27| 3: [StringLiteral] ", and " +# 27| 4: [NameOfExpr] nameof(...) +# 27| 0: [LocalVariableAccess] access to local variable bar +# 27| 5: [StringLiteral] " has length " +# 27| 6: [NullCoalescingExpr] ... ?? ... +# 27| 0: [PropertyCall] access to property Length +# 27| -1: [LocalVariableAccess] access to local variable bar +# 27| 1: [IntLiteral] 0 +# 29| 3: [LocalVariableDeclStmt] ... ...; +# 29| 0: [LocalVariableDeclAndInitExpr] Nullable anythingInBar = ... +# 29| 0: [MethodCall] call to method Any +# 29| -1: [LocalVariableAccess] access to local variable bar +# 29| 1: [LocalVariableAccess] access to local variable anythingInBar +# 30| 4: [LocalVariableDeclStmt] ... ...; +# 30| 0: [LocalVariableDeclAndInitExpr] Nullable countTInFoo = ... +# 30| 0: [MethodCall] call to method Count +# 30| -1: [MethodCall] call to method Select +# 30| -1: [MethodCall] call to method ToUpper +# 30| -1: [LocalVariableAccess] access to local variable foo +# 30| 0: [LambdaExpr] (...) => ... +#-----| 2: (Parameters) +# 30| 0: [Parameter] c +# 30| 4: [EQExpr] ... == ... +# 30| 0: [CastExpr] (...) ... +# 30| 0: [ParameterAccess] access to parameter c +# 30| 1: [CastExpr] (...) ... +# 30| 0: [CharLiteral] T +# 30| 1: [LocalVariableAccess] access to local variable countTInFoo +# 32| 5: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclAndInitExpr] Nullable testElementBinding = ... +# 32| 0: [IndexerCall] access to indexer +# 32| -1: [IndexerCall] access to indexer +# 32| -1: [ObjectCreation] object creation of type Dictionary +# 32| 0: [IntLiteral] 2 +# 32| 0: [IntLiteral] 1 +# 32| 1: [LocalVariableAccess] access to local variable testElementBinding +# 34| 1: [SpecificCatchClause] catch (...) {...} +# 35| 1: [BlockStmt] {...} +# 34| 2: [EQExpr] ... == ... +# 34| 0: [PropertyCall] access to property Value +# 34| 1: [IntLiteral] 20 +# 37| 2: [GeneralCatchClause] catch {...} +# 38| 1: [BlockStmt] {...} +# 37| 2: [EQExpr] ... == ... +# 37| 0: [PropertyCall] access to property Value +# 37| 1: [IntLiteral] 30 +# 40| 3: [GeneralCatchClause] catch {...} +# 41| 1: [BlockStmt] {...} +# 45| 9: [EQOperator] == +#-----| 2: (Parameters) +# 45| 0: [Parameter] t1 +# 45| 1: [Parameter] t2 +# 45| 4: [BoolLiteral] true +# 46| 10: [NEOperator] != +#-----| 2: (Parameters) +# 46| 0: [Parameter] t1 +# 46| 1: [Parameter] t2 +# 46| 4: [BoolLiteral] false +# 48| 11: [Property] ExprProperty +# 48| 3: [Getter] get_ExprProperty +# 48| 4: [IntLiteral] 3 +# 50| 12: [Indexer] Item +#-----| 1: (Parameters) +# 50| 0: [Parameter] i +# 50| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 50| 0: [Parameter] i +# 50| 4: [ParameterAccess] access to parameter i +# 53| [Class] IndexInitializers +# 55| 5: [Class] Compound +# 57| 5: [Field] DictionaryField +# 58| 6: [IndexerProperty] DictionaryProperty +# 58| 3: [Getter] get_DictionaryProperty +# 58| 4: [Setter] set_DictionaryProperty +#-----| 2: (Parameters) +# 58| 0: [Parameter] value +# 59| 7: [Field] ArrayField +# 60| 8: [Property] ArrayProperty +# 60| 3: [Getter] get_ArrayProperty +# 60| 4: [Setter] set_ArrayProperty +#-----| 2: (Parameters) +# 60| 0: [Parameter] value +# 61| 9: [Field] ArrayField2 +# 62| 10: [Property] ArrayProperty2 +# 62| 3: [Getter] get_ArrayProperty2 +# 62| 4: [Setter] set_ArrayProperty2 +#-----| 2: (Parameters) +# 62| 0: [Parameter] value +# 65| 6: [Method] Test +# 66| 4: [BlockStmt] {...} +# 68| 0: [LocalVariableDeclStmt] ... ...; +# 68| 0: [LocalVariableDeclAndInitExpr] Dictionary dict = ... +# 68| 0: [ObjectCreation] object creation of type Dictionary +# 68| -1: [ObjectInitializer] { ..., ... } +# 68| 0: [MemberInitializer] ... = ... +# 68| 0: [StringLiteral] "Zero" +# 68| 1: [IndexerCall] access to indexer +# 68| 0: [IntLiteral] 0 +# 68| 1: [MemberInitializer] ... = ... +# 68| 0: [StringLiteral] "One" +# 68| 1: [IndexerCall] access to indexer +# 68| 0: [IntLiteral] 1 +# 68| 2: [MemberInitializer] ... = ... +# 68| 0: [StringLiteral] "Two" +# 68| 1: [IndexerCall] access to indexer +# 68| 0: [IntLiteral] 2 +# 68| 1: [LocalVariableAccess] access to local variable dict +# 71| 1: [LocalVariableDeclStmt] ... ...; +# 71| 0: [LocalVariableDeclAndInitExpr] Compound compound = ... +# 71| 0: [ObjectCreation] object creation of type Compound +# 72| -1: [ObjectInitializer] { ..., ... } +# 73| 0: [MemberInitializer] ... = ... +# 73| 0: [ObjectInitializer] { ..., ... } +# 73| 0: [MemberInitializer] ... = ... +# 73| 0: [StringLiteral] "Zero" +# 73| 1: [IndexerCall] access to indexer +# 73| 0: [IntLiteral] 0 +# 73| 1: [MemberInitializer] ... = ... +# 73| 0: [StringLiteral] "One" +# 73| 1: [IndexerCall] access to indexer +# 73| 0: [IntLiteral] 1 +# 73| 2: [MemberInitializer] ... = ... +# 73| 0: [StringLiteral] "Two" +# 73| 1: [IndexerCall] access to indexer +# 73| 0: [IntLiteral] 2 +# 73| 1: [FieldAccess] access to field DictionaryField +# 74| 1: [MemberInitializer] ... = ... +# 74| 0: [ObjectInitializer] { ..., ... } +# 74| 0: [MemberInitializer] ... = ... +# 74| 0: [StringLiteral] "Three" +# 74| 1: [IndexerCall] access to indexer +# 74| 0: [IntLiteral] 3 +# 74| 1: [MemberInitializer] ... = ... +# 74| 0: [StringLiteral] "Two" +# 74| 1: [IndexerCall] access to indexer +# 74| 0: [IntLiteral] 2 +# 74| 2: [MemberInitializer] ... = ... +# 74| 0: [StringLiteral] "One" +# 74| 1: [IndexerCall] access to indexer +# 74| 0: [IntLiteral] 1 +# 74| 1: [PropertyCall] access to property DictionaryProperty +# 75| 2: [MemberInitializer] ... = ... +# 75| 0: [ObjectInitializer] { ..., ... } +# 75| 0: [MemberInitializer] ... = ... +# 75| 0: [StringLiteral] "Zero" +# 75| 1: [ArrayAccess] access to array element +# 75| 0: [IntLiteral] 0 +# 75| 1: [MemberInitializer] ... = ... +# 75| 0: [StringLiteral] "One" +# 75| 1: [ArrayAccess] access to array element +# 75| 0: [IntLiteral] 1 +# 75| 1: [FieldAccess] access to field ArrayField +# 76| 3: [MemberInitializer] ... = ... +# 76| 0: [ObjectInitializer] { ..., ... } +# 76| 0: [MemberInitializer] ... = ... +# 76| 0: [StringLiteral] "i" +# 76| 1: [ArrayAccess] access to array element +# 76| 0: [IntLiteral] 0 +# 76| 1: [IntLiteral] 1 +# 76| 1: [MemberInitializer] ... = ... +# 76| 0: [StringLiteral] "1" +# 76| 1: [ArrayAccess] access to array element +# 76| 0: [IntLiteral] 1 +# 76| 1: [IntLiteral] 0 +# 76| 1: [FieldAccess] access to field ArrayField2 +# 77| 4: [MemberInitializer] ... = ... +# 77| 0: [ObjectInitializer] { ..., ... } +# 77| 0: [MemberInitializer] ... = ... +# 77| 0: [StringLiteral] "One" +# 77| 1: [ArrayAccess] access to array element +# 77| 0: [IntLiteral] 1 +# 77| 1: [MemberInitializer] ... = ... +# 77| 0: [StringLiteral] "Two" +# 77| 1: [ArrayAccess] access to array element +# 77| 0: [IntLiteral] 2 +# 77| 1: [PropertyCall] access to property ArrayProperty +# 78| 5: [MemberInitializer] ... = ... +# 78| 0: [ObjectInitializer] { ..., ... } +# 78| 0: [MemberInitializer] ... = ... +# 78| 0: [StringLiteral] "i" +# 78| 1: [ArrayAccess] access to array element +# 78| 0: [IntLiteral] 0 +# 78| 1: [IntLiteral] 1 +# 78| 1: [MemberInitializer] ... = ... +# 78| 0: [StringLiteral] "1" +# 78| 1: [ArrayAccess] access to array element +# 78| 0: [IntLiteral] 1 +# 78| 1: [IntLiteral] 0 +# 78| 1: [PropertyCall] access to property ArrayProperty2 +# 71| 1: [LocalVariableAccess] access to local variable compound diff --git a/csharp/ql/test/library-tests/csharp6/PrintAst.qlref b/csharp/ql/test/library-tests/csharp6/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/csharp6/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/csharp7.1/PrintAst.expected b/csharp/ql/test/library-tests/csharp7.1/PrintAst.expected new file mode 100644 index 000000000000..381ae9dee74b --- /dev/null +++ b/csharp/ql/test/library-tests/csharp7.1/PrintAst.expected @@ -0,0 +1,67 @@ +csharp71.cs: +# 3| [Class] DefaultLiterals +# 5| 5: [Method] f +# 6| 4: [BlockStmt] {...} +# 7| 0: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 7| 0: [CastExpr] (...) ... +# 7| 0: [DefaultValueExpr] default +# 7| 1: [LocalVariableAccess] access to local variable x +# 7| 1: [LocalVariableDeclAndInitExpr] Int32 y = ... +# 7| 0: [DefaultValueExpr] default(...) +# 7| 0: [TypeAccess] access to type Int32 +# 7| 1: [LocalVariableAccess] access to local variable y +# 8| 1: [IfStmt] if (...) ... +# 8| 0: [EQExpr] ... == ... +# 8| 0: [LocalVariableAccess] access to local variable x +# 8| 1: [CastExpr] (...) ... +# 8| 0: [DefaultValueExpr] default +# 9| 1: [EmptyStmt] ; +# 10| 2: [SwitchStmt] switch (...) {...} +# 10| 0: [LocalVariableAccess] access to local variable x +# 12| 0: [CaseStmt] case ...: +# 12| 0: [DiscardPatternExpr] _ +# 12| 1: [BreakStmt] break; +# 14| 3: [ExprStmt] ...; +# 14| 0: [AssignExpr] ... = ... +# 14| 0: [CastExpr] (...) ... +# 14| 0: [DefaultValueExpr] default +# 14| 1: [LocalVariableAccess] access to local variable x +# 15| 4: [LocalVariableDeclStmt] ... ...; +# 15| 0: [LocalVariableDeclAndInitExpr] String s = ... +# 15| 0: [CastExpr] (...) ... +# 15| 0: [DefaultValueExpr] default +# 15| 1: [LocalVariableAccess] access to local variable s +# 16| 5: [LocalVariableDeclStmt] ... ...; +# 16| 0: [LocalVariableDeclAndInitExpr] Boolean b = ... +# 16| 0: [CastExpr] (...) ... +# 16| 0: [DefaultValueExpr] default +# 16| 1: [LocalVariableAccess] access to local variable b +# 17| 6: [LocalVariableDeclStmt] ... ...; +# 17| 0: [LocalVariableDeclAndInitExpr] Double d = ... +# 17| 0: [CastExpr] (...) ... +# 17| 0: [DefaultValueExpr] default +# 17| 1: [LocalVariableAccess] access to local variable d +# 21| [Class] IsConstants +# 23| 5: [Method] f +# 24| 4: [BlockStmt] {...} +# 25| 0: [LocalVariableDeclStmt] ... ...; +# 25| 0: [LocalVariableDeclExpr] Boolean b +# 26| 1: [ExprStmt] ...; +# 26| 0: [AssignExpr] ... = ... +# 26| 0: [IsExpr] ... is ... +# 26| 0: [ObjectCreation] object creation of type Object +# 26| 1: [ConstantPatternExpr,StringLiteral] "abc" +# 26| 1: [LocalVariableAccess] access to local variable b +# 27| 2: [ExprStmt] ...; +# 27| 0: [AssignExpr] ... = ... +# 27| 0: [IsExpr] ... is ... +# 27| 0: [StringLiteral] "" +# 27| 1: [ConstantPatternExpr,NullLiteral] null +# 27| 1: [LocalVariableAccess] access to local variable b +# 28| 3: [ExprStmt] ...; +# 28| 0: [AssignExpr] ... = ... +# 28| 0: [IsExpr] ... is ... +# 28| 0: [LocalVariableAccess] access to local variable b +# 28| 1: [BoolLiteral,ConstantPatternExpr] true +# 28| 1: [LocalVariableAccess] access to local variable b diff --git a/csharp/ql/test/library-tests/csharp7.1/PrintAst.qlref b/csharp/ql/test/library-tests/csharp7.1/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/csharp7.1/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/csharp7.2/PrintAst.expected b/csharp/ql/test/library-tests/csharp7.2/PrintAst.expected new file mode 100644 index 000000000000..d6c9866cc088 --- /dev/null +++ b/csharp/ql/test/library-tests/csharp7.2/PrintAst.expected @@ -0,0 +1,39 @@ +csharp72.cs: +# 5| [Class] InModifiers +# 7| 5: [Struct] S +# 11| 6: [Method] F +#-----| 2: (Parameters) +# 11| 0: [Parameter] s +# 12| 4: [BlockStmt] {...} +# 15| 7: [Method] CallF +# 16| 4: [BlockStmt] {...} +# 17| 0: [LocalVariableDeclStmt] ... ...; +# 17| 0: [LocalVariableDeclAndInitExpr] S s = ... +# 17| 0: [ObjectCreation] object creation of type S +# 17| 1: [LocalVariableAccess] access to local variable s +# 18| 1: [ExprStmt] ...; +# 18| 0: [MethodCall] call to method F +# 18| 0: [LocalVariableAccess] access to local variable s +# 22| [Class] RefReadonlyReturns +# 24| 5: [Field] s +# 26| 6: [Method] F +# 27| 4: [BlockStmt] {...} +# 28| 0: [ReturnStmt] return ...; +# 28| 0: [RefExpr] ref ... +# 28| 0: [FieldAccess] access to field s +# 31| 7: [DelegateType] Del +# 34| [Struct] ReadonlyStruct +# 38| [Struct] RefStruct +# 42| [Struct] ReadonlyRefStruct +# 46| [Class] NumericLiterals +# 48| 5: [Field] binaryValue +# 48| 1: [AssignExpr] ... = ... +# 48| 0: [IntLiteral] 85 +# 48| 1: [FieldAccess] access to field binaryValue +# 51| [Class] PrivateProtected +# 53| 5: [Field] X +# 53| 1: [AssignExpr] ... = ... +# 53| 0: [IntLiteral] 1 +# 53| 1: [FieldAccess] access to field X +# 55| 6: [Method] F +# 55| 4: [BlockStmt] {...} diff --git a/csharp/ql/test/library-tests/csharp7.2/PrintAst.qlref b/csharp/ql/test/library-tests/csharp7.2/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/csharp7.2/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected b/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected new file mode 100644 index 000000000000..4574aa105900 --- /dev/null +++ b/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected @@ -0,0 +1,80 @@ +csharp73.cs: +# 5| [Class] StackAllocs +# 7| 5: [Method] Fn +# 8| 4: [BlockStmt] {...} +# 9| 0: [LocalVariableDeclStmt] ... ...; +# 9| 0: [LocalVariableDeclAndInitExpr] Char* arr1 = ... +# 9| 0: [Stackalloc] array creation of type Char* +# 9| -1: [ArrayInitializer] { ..., ... } +# 9| 0: [CharLiteral] x +# 9| 1: [CharLiteral] y +# 9| 1: [LocalVariableAccess] access to local variable arr1 +# 10| 1: [LocalVariableDeclStmt] ... ...; +# 10| 0: [LocalVariableDeclAndInitExpr] Char* arr2 = ... +# 10| 0: [Stackalloc] array creation of type Char* +# 10| -1: [ArrayInitializer] { ..., ... } +# 10| 0: [CharLiteral] x +# 10| 0: [IntLiteral] 1 +# 10| 1: [LocalVariableAccess] access to local variable arr2 +# 11| 2: [LocalVariableDeclStmt] ... ...; +# 11| 0: [LocalVariableDeclAndInitExpr] Char[] arr3 = ... +# 11| 0: [ArrayCreation] array creation of type Char[] +# 11| -1: [ArrayInitializer] { ..., ... } +# 11| 0: [CharLiteral] x +# 11| 1: [LocalVariableAccess] access to local variable arr3 +# 12| 3: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclAndInitExpr] Char* arr4 = ... +# 12| 0: [Stackalloc] array creation of type Char* +# 12| 0: [IntLiteral] 10 +# 12| 1: [LocalVariableAccess] access to local variable arr4 +# 13| 4: [LocalVariableDeclStmt] ... ...; +# 13| 0: [LocalVariableDeclAndInitExpr] Char[] arr5 = ... +# 13| 0: [ArrayCreation] array creation of type Char[] +# 13| 0: [IntLiteral] 10 +# 13| 1: [LocalVariableAccess] access to local variable arr5 +# 14| 5: [LocalVariableDeclStmt] ... ...; +# 14| 0: [LocalVariableDeclAndInitExpr] Int32* arr6 = ... +# 14| 0: [Stackalloc] array creation of type Int32* +# 14| -1: [ArrayInitializer] { ..., ... } +# 14| 0: [IntLiteral] 1 +# 14| 1: [IntLiteral] 2 +# 14| 2: [IntLiteral] 3 +# 14| 1: [LocalVariableAccess] access to local variable arr6 +# 18| [Class] PinnedReference +# 20| 5: [Method] F +# 21| 4: [BlockStmt] {...} +# 22| 0: [LocalVariableDeclStmt] ... ...; +# 22| 0: [LocalVariableDeclAndInitExpr] Span t = ... +# 22| 0: [OperatorCall] call to operator implicit conversion +# 22| 0: [ArrayCreation] array creation of type Int32[] +# 22| 0: [IntLiteral] 10 +# 22| 1: [LocalVariableAccess] access to local variable t +# 25| 1: [BlockStmt] {...} +# 30| [Class] UnmanagedConstraint<> +#-----| 1: (Type parameters) +# 30| 0: [TypeParameter] T +# 34| [Class] EnumConstraint<> +#-----| 1: (Type parameters) +# 34| 0: [TypeParameter] T +# 38| [Class] DelegateConstraint<> +#-----| 1: (Type parameters) +# 38| 0: [TypeParameter] T +# 42| [Class] ExpressionVariables +# 44| 4: [InstanceConstructor] ExpressionVariables +#-----| 2: (Parameters) +# 44| 0: [Parameter] x +# 45| 4: [BlockStmt] {...} +# 46| 0: [ExprStmt] ...; +# 46| 0: [AssignExpr] ... = ... +# 46| 0: [IntLiteral] 5 +# 46| 1: [ParameterAccess] access to parameter x +# 49| 5: [InstanceConstructor] ExpressionVariables +# 49| 3: [ConstructorInitializer] call to constructor ExpressionVariables +# 49| 0: [LocalVariableDeclExpr] Int32 x +# 50| 4: [BlockStmt] {...} +# 51| 0: [ExprStmt] ...; +# 51| 0: [MethodCall] call to method WriteLine +# 51| -1: [TypeAccess] access to type Console +# 51| 0: [InterpolatedStringExpr] $"..." +# 51| 0: [StringLiteral] "x is " +# 51| 1: [LocalVariableAccess] access to local variable x diff --git a/csharp/ql/test/library-tests/csharp7.3/PrintAst.qlref b/csharp/ql/test/library-tests/csharp7.3/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/csharp7.3/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/csharp7/CSharp7.cs b/csharp/ql/test/library-tests/csharp7/CSharp7.cs index 0b42403f6c0a..0339ccc87284 100644 --- a/csharp/ql/test/library-tests/csharp7/CSharp7.cs +++ b/csharp/ql/test/library-tests/csharp7/CSharp7.cs @@ -61,9 +61,9 @@ void G() class Tuples { - (int, int) F() + (int A, int B) F() { - return (1, 2); + return (A: 1, B: 2); } void Expressions() @@ -71,7 +71,7 @@ void Expressions() (var x, var y) = F(); var z = F(); (x, y) = F(); - x = F().Item1; + x = F().A; (x, y, z.Item1) = (1, 2, 3); (x, y) = (x, y) = (1, 2); (var a, (var b, var c)) = (1, z); @@ -148,7 +148,8 @@ int f8() return f9(1); } - Action a = () => { + Action a = () => + { int f9() => 0; }; @@ -295,7 +296,7 @@ class ForLoops { void Test() { - for(int x=0; x<10 && x is int y; ++x) + for (int x = 0; x < 10 && x is int y; ++x) { Console.WriteLine(y); } diff --git a/csharp/ql/test/library-tests/csharp7/CaseCondition.expected b/csharp/ql/test/library-tests/csharp7/CaseCondition.expected index 67bff16713d0..f538575183e2 100644 --- a/csharp/ql/test/library-tests/csharp7/CaseCondition.expected +++ b/csharp/ql/test/library-tests/csharp7/CaseCondition.expected @@ -1,3 +1,3 @@ -| CSharp7.cs:253:13:253:31 | case ...: | CSharp7.cs:253:26:253:30 | ... < ... | -| CSharp7.cs:255:13:255:41 | case ...: | CSharp7.cs:255:27:255:40 | ... is ... | -| CSharp7.cs:258:13:258:36 | case ...: | CSharp7.cs:258:30:258:35 | ... > ... | +| CSharp7.cs:254:13:254:31 | case ...: | CSharp7.cs:254:26:254:30 | ... < ... | +| CSharp7.cs:256:13:256:41 | case ...: | CSharp7.cs:256:27:256:40 | ... is ... | +| CSharp7.cs:259:13:259:36 | case ...: | CSharp7.cs:259:30:259:35 | ... > ... | diff --git a/csharp/ql/test/library-tests/csharp7/ConstructedLocalFunctions.expected b/csharp/ql/test/library-tests/csharp7/ConstructedLocalFunctions.expected index 0b67eaba5dff..a945d2621982 100644 --- a/csharp/ql/test/library-tests/csharp7/ConstructedLocalFunctions.expected +++ b/csharp/ql/test/library-tests/csharp7/ConstructedLocalFunctions.expected @@ -1,4 +1,4 @@ -| CSharp7.cs:160:9:160:24 | f | f() | f() | -| CSharp7.cs:161:9:161:25 | g | g(U) | g(T) | -| CSharp7.cs:163:9:168:9 | h | h(int, int) | h(T, U) | -| CSharp7.cs:163:9:168:9 | h | h(string, bool) | h(T, U) | +| CSharp7.cs:161:9:161:24 | f | f() | f() | +| CSharp7.cs:162:9:162:25 | g | g(U) | g(T) | +| CSharp7.cs:164:9:169:9 | h | h(int, int) | h(T, U) | +| CSharp7.cs:164:9:169:9 | h | h(string, bool) | h(T, U) | diff --git a/csharp/ql/test/library-tests/csharp7/DefUse.expected b/csharp/ql/test/library-tests/csharp7/DefUse.expected index 93bdff568c26..62cc5ea4b1bf 100644 --- a/csharp/ql/test/library-tests/csharp7/DefUse.expected +++ b/csharp/ql/test/library-tests/csharp7/DefUse.expected @@ -29,47 +29,47 @@ | CSharp7.cs:141:20:141:20 | x | CSharp7.cs:141:41:141:41 | access to parameter x | | CSharp7.cs:143:20:143:20 | x | CSharp7.cs:143:29:143:29 | access to parameter x | | CSharp7.cs:147:24:147:24 | x | CSharp7.cs:147:33:147:33 | access to parameter x | -| CSharp7.cs:161:18:161:18 | t | CSharp7.cs:161:24:161:24 | access to parameter t | -| CSharp7.cs:163:26:163:26 | u | CSharp7.cs:167:22:167:22 | access to parameter u | -| CSharp7.cs:176:16:176:30 | String src = ... | CSharp7.cs:181:23:181:25 | access to local variable src | -| CSharp7.cs:176:16:176:30 | String src = ... | CSharp7.cs:182:23:182:25 | access to local variable src | -| CSharp7.cs:176:16:176:30 | String src = ... | CSharp7.cs:183:23:183:25 | access to local variable src | -| CSharp7.cs:177:25:177:25 | s | CSharp7.cs:177:33:177:33 | access to parameter s | -| CSharp7.cs:178:25:178:25 | s | CSharp7.cs:178:31:178:31 | access to parameter s | -| CSharp7.cs:179:25:179:25 | s | CSharp7.cs:179:37:179:37 | access to parameter s | -| CSharp7.cs:191:13:191:18 | Int32 v1 = ... | CSharp7.cs:192:26:192:27 | access to local variable v1 | -| CSharp7.cs:191:13:191:18 | Int32 v1 = ... | CSharp7.cs:198:21:198:22 | access to local variable v1 | -| CSharp7.cs:193:13:193:31 | Int32[] array = ... | CSharp7.cs:195:14:195:18 | access to local variable array | -| CSharp7.cs:193:13:193:31 | Int32[] array = ... | CSharp7.cs:196:26:196:30 | access to local variable array | -| CSharp7.cs:195:9:195:21 | ... = ... | CSharp7.cs:197:26:197:27 | access to local variable r1 | -| CSharp7.cs:195:9:195:21 | ... = ... | CSharp7.cs:199:33:199:34 | access to local variable r1 | -| CSharp7.cs:195:9:195:21 | ... = ... | CSharp7.cs:200:16:200:17 | access to local variable r1 | -| CSharp7.cs:203:24:203:24 | p | CSharp7.cs:206:20:206:20 | access to parameter p | -| CSharp7.cs:205:28:205:28 | q | CSharp7.cs:205:44:205:44 | access to parameter q | -| CSharp7.cs:233:16:233:23 | Object o = ... | CSharp7.cs:234:13:234:13 | access to local variable o | -| CSharp7.cs:233:16:233:23 | Object o = ... | CSharp7.cs:238:18:238:18 | access to local variable o | -| CSharp7.cs:233:16:233:23 | Object o = ... | CSharp7.cs:242:18:242:18 | access to local variable o | -| CSharp7.cs:233:16:233:23 | Object o = ... | CSharp7.cs:245:18:245:18 | access to local variable o | -| CSharp7.cs:233:16:233:23 | Object o = ... | CSharp7.cs:249:17:249:17 | access to local variable o | -| CSharp7.cs:233:16:233:23 | Object o = ... | CSharp7.cs:255:27:255:27 | access to local variable o | -| CSharp7.cs:234:18:234:23 | Int32 i1 | CSharp7.cs:234:28:234:29 | access to local variable i1 | -| CSharp7.cs:234:18:234:23 | Int32 i1 | CSharp7.cs:236:38:236:39 | access to local variable i1 | -| CSharp7.cs:238:23:238:31 | String s1 | CSharp7.cs:240:41:240:42 | access to local variable s1 | -| CSharp7.cs:255:32:255:40 | String s4 | CSharp7.cs:256:40:256:41 | access to local variable s4 | -| CSharp7.cs:258:18:258:23 | Int32 i2 | CSharp7.cs:258:30:258:31 | access to local variable i2 | -| CSharp7.cs:258:18:258:23 | Int32 i2 | CSharp7.cs:259:47:259:48 | access to local variable i2 | -| CSharp7.cs:261:18:261:23 | Int32 i3 | CSharp7.cs:262:42:262:43 | access to local variable i3 | -| CSharp7.cs:264:18:264:26 | String s2 | CSharp7.cs:265:45:265:46 | access to local variable s2 | -| CSharp7.cs:283:13:283:48 | Dictionary dict = ... | CSharp7.cs:284:20:284:23 | access to local variable dict | -| CSharp7.cs:284:13:284:62 | IEnumerable<(Int32,String)> list = ... | CSharp7.cs:286:39:286:42 | access to local variable list | -| CSharp7.cs:284:13:284:62 | IEnumerable<(Int32,String)> list = ... | CSharp7.cs:288:36:288:39 | access to local variable list | -| CSharp7.cs:284:13:284:62 | IEnumerable<(Int32,String)> list = ... | CSharp7.cs:290:32:290:35 | access to local variable list | -| CSharp7.cs:284:32:284:35 | item | CSharp7.cs:284:41:284:44 | access to parameter item | -| CSharp7.cs:284:32:284:35 | item | CSharp7.cs:284:51:284:54 | access to parameter item | -| CSharp7.cs:298:17:298:19 | Int32 x = ... | CSharp7.cs:298:22:298:22 | access to local variable x | -| CSharp7.cs:298:17:298:19 | Int32 x = ... | CSharp7.cs:298:30:298:30 | access to local variable x | -| CSharp7.cs:298:17:298:19 | Int32 x = ... | CSharp7.cs:298:44:298:44 | access to local variable x | -| CSharp7.cs:298:35:298:39 | Int32 y | CSharp7.cs:300:31:300:31 | access to local variable y | -| CSharp7.cs:298:42:298:44 | ++... | CSharp7.cs:298:22:298:22 | access to local variable x | -| CSharp7.cs:298:42:298:44 | ++... | CSharp7.cs:298:30:298:30 | access to local variable x | -| CSharp7.cs:298:42:298:44 | ++... | CSharp7.cs:298:44:298:44 | access to local variable x | +| CSharp7.cs:162:18:162:18 | t | CSharp7.cs:162:24:162:24 | access to parameter t | +| CSharp7.cs:164:26:164:26 | u | CSharp7.cs:168:22:168:22 | access to parameter u | +| CSharp7.cs:177:16:177:30 | String src = ... | CSharp7.cs:182:23:182:25 | access to local variable src | +| CSharp7.cs:177:16:177:30 | String src = ... | CSharp7.cs:183:23:183:25 | access to local variable src | +| CSharp7.cs:177:16:177:30 | String src = ... | CSharp7.cs:184:23:184:25 | access to local variable src | +| CSharp7.cs:178:25:178:25 | s | CSharp7.cs:178:33:178:33 | access to parameter s | +| CSharp7.cs:179:25:179:25 | s | CSharp7.cs:179:31:179:31 | access to parameter s | +| CSharp7.cs:180:25:180:25 | s | CSharp7.cs:180:37:180:37 | access to parameter s | +| CSharp7.cs:192:13:192:18 | Int32 v1 = ... | CSharp7.cs:193:26:193:27 | access to local variable v1 | +| CSharp7.cs:192:13:192:18 | Int32 v1 = ... | CSharp7.cs:199:21:199:22 | access to local variable v1 | +| CSharp7.cs:194:13:194:31 | Int32[] array = ... | CSharp7.cs:196:14:196:18 | access to local variable array | +| CSharp7.cs:194:13:194:31 | Int32[] array = ... | CSharp7.cs:197:26:197:30 | access to local variable array | +| CSharp7.cs:196:9:196:21 | ... = ... | CSharp7.cs:198:26:198:27 | access to local variable r1 | +| CSharp7.cs:196:9:196:21 | ... = ... | CSharp7.cs:200:33:200:34 | access to local variable r1 | +| CSharp7.cs:196:9:196:21 | ... = ... | CSharp7.cs:201:16:201:17 | access to local variable r1 | +| CSharp7.cs:204:24:204:24 | p | CSharp7.cs:207:20:207:20 | access to parameter p | +| CSharp7.cs:206:28:206:28 | q | CSharp7.cs:206:44:206:44 | access to parameter q | +| CSharp7.cs:234:16:234:23 | Object o = ... | CSharp7.cs:235:13:235:13 | access to local variable o | +| CSharp7.cs:234:16:234:23 | Object o = ... | CSharp7.cs:239:18:239:18 | access to local variable o | +| CSharp7.cs:234:16:234:23 | Object o = ... | CSharp7.cs:243:18:243:18 | access to local variable o | +| CSharp7.cs:234:16:234:23 | Object o = ... | CSharp7.cs:246:18:246:18 | access to local variable o | +| CSharp7.cs:234:16:234:23 | Object o = ... | CSharp7.cs:250:17:250:17 | access to local variable o | +| CSharp7.cs:234:16:234:23 | Object o = ... | CSharp7.cs:256:27:256:27 | access to local variable o | +| CSharp7.cs:235:18:235:23 | Int32 i1 | CSharp7.cs:235:28:235:29 | access to local variable i1 | +| CSharp7.cs:235:18:235:23 | Int32 i1 | CSharp7.cs:237:38:237:39 | access to local variable i1 | +| CSharp7.cs:239:23:239:31 | String s1 | CSharp7.cs:241:41:241:42 | access to local variable s1 | +| CSharp7.cs:256:32:256:40 | String s4 | CSharp7.cs:257:40:257:41 | access to local variable s4 | +| CSharp7.cs:259:18:259:23 | Int32 i2 | CSharp7.cs:259:30:259:31 | access to local variable i2 | +| CSharp7.cs:259:18:259:23 | Int32 i2 | CSharp7.cs:260:47:260:48 | access to local variable i2 | +| CSharp7.cs:262:18:262:23 | Int32 i3 | CSharp7.cs:263:42:263:43 | access to local variable i3 | +| CSharp7.cs:265:18:265:26 | String s2 | CSharp7.cs:266:45:266:46 | access to local variable s2 | +| CSharp7.cs:284:13:284:48 | Dictionary dict = ... | CSharp7.cs:285:20:285:23 | access to local variable dict | +| CSharp7.cs:285:13:285:62 | IEnumerable<(Int32,String)> list = ... | CSharp7.cs:287:39:287:42 | access to local variable list | +| CSharp7.cs:285:13:285:62 | IEnumerable<(Int32,String)> list = ... | CSharp7.cs:289:36:289:39 | access to local variable list | +| CSharp7.cs:285:13:285:62 | IEnumerable<(Int32,String)> list = ... | CSharp7.cs:291:32:291:35 | access to local variable list | +| CSharp7.cs:285:32:285:35 | item | CSharp7.cs:285:41:285:44 | access to parameter item | +| CSharp7.cs:285:32:285:35 | item | CSharp7.cs:285:51:285:54 | access to parameter item | +| CSharp7.cs:299:18:299:22 | Int32 x = ... | CSharp7.cs:299:25:299:25 | access to local variable x | +| CSharp7.cs:299:18:299:22 | Int32 x = ... | CSharp7.cs:299:35:299:35 | access to local variable x | +| CSharp7.cs:299:18:299:22 | Int32 x = ... | CSharp7.cs:299:49:299:49 | access to local variable x | +| CSharp7.cs:299:40:299:44 | Int32 y | CSharp7.cs:301:31:301:31 | access to local variable y | +| CSharp7.cs:299:47:299:49 | ++... | CSharp7.cs:299:25:299:25 | access to local variable x | +| CSharp7.cs:299:47:299:49 | ++... | CSharp7.cs:299:35:299:35 | access to local variable x | +| CSharp7.cs:299:47:299:49 | ++... | CSharp7.cs:299:49:299:49 | access to local variable x | diff --git a/csharp/ql/test/library-tests/csharp7/Discards.expected b/csharp/ql/test/library-tests/csharp7/Discards.expected index 41f6e47ac17e..0fffde7c1c0a 100644 --- a/csharp/ql/test/library-tests/csharp7/Discards.expected +++ b/csharp/ql/test/library-tests/csharp7/Discards.expected @@ -1,8 +1,8 @@ -| CSharp7.cs:222:9:222:9 | _ | (Int32,Double) | -| CSharp7.cs:222:19:222:19 | _ | Boolean | -| CSharp7.cs:223:10:223:10 | _ | Int32 | -| CSharp7.cs:223:13:223:13 | _ | Double | -| CSharp7.cs:223:24:223:24 | _ | Boolean | -| CSharp7.cs:224:17:224:17 | _ | Double | -| CSharp7.cs:224:28:224:28 | _ | Boolean | -| CSharp7.cs:225:10:225:10 | _ | Int32 | +| CSharp7.cs:223:9:223:9 | _ | (Int32,Double) | +| CSharp7.cs:223:19:223:19 | _ | Boolean | +| CSharp7.cs:224:10:224:10 | _ | Int32 | +| CSharp7.cs:224:13:224:13 | _ | Double | +| CSharp7.cs:224:24:224:24 | _ | Boolean | +| CSharp7.cs:225:17:225:17 | _ | Double | +| CSharp7.cs:225:28:225:28 | _ | Boolean | +| CSharp7.cs:226:10:226:10 | _ | Int32 | diff --git a/csharp/ql/test/library-tests/csharp7/ExpressionBodies.expected b/csharp/ql/test/library-tests/csharp7/ExpressionBodies.expected index b7dd392b22ab..5a09ce22d438 100644 --- a/csharp/ql/test/library-tests/csharp7/ExpressionBodies.expected +++ b/csharp/ql/test/library-tests/csharp7/ExpressionBodies.expected @@ -9,10 +9,10 @@ | CSharp7.cs:141:9:141:51 | f6 | CSharp7.cs:141:26:141:50 | ... ? ... : ... | | CSharp7.cs:143:9:143:31 | f7 | CSharp7.cs:143:26:143:30 | call to local function f6 | | CSharp7.cs:147:13:147:35 | f9 | CSharp7.cs:147:30:147:34 | call to local function f7 | -| CSharp7.cs:152:13:152:26 | f9 | CSharp7.cs:152:25:152:25 | 0 | -| CSharp7.cs:160:9:160:24 | f | CSharp7.cs:160:23:160:23 | 1 | -| CSharp7.cs:161:9:161:25 | g | CSharp7.cs:161:24:161:24 | access to parameter t | -| CSharp7.cs:165:13:165:43 | f2 | CSharp7.cs:165:37:165:42 | call to local function f | -| CSharp7.cs:177:9:177:40 | f | CSharp7.cs:177:31:177:39 | ... + ... | -| CSharp7.cs:178:9:178:32 | g | CSharp7.cs:178:31:178:31 | access to parameter s | -| CSharp7.cs:284:32:284:61 | (...) => ... | CSharp7.cs:284:40:284:61 | (..., ...) | +| CSharp7.cs:153:13:153:26 | f9 | CSharp7.cs:153:25:153:25 | 0 | +| CSharp7.cs:161:9:161:24 | f | CSharp7.cs:161:23:161:23 | 1 | +| CSharp7.cs:162:9:162:25 | g | CSharp7.cs:162:24:162:24 | access to parameter t | +| CSharp7.cs:166:13:166:43 | f2 | CSharp7.cs:166:37:166:42 | call to local function f | +| CSharp7.cs:178:9:178:40 | f | CSharp7.cs:178:31:178:39 | ... + ... | +| CSharp7.cs:179:9:179:32 | g | CSharp7.cs:179:31:179:31 | access to parameter s | +| CSharp7.cs:285:32:285:61 | (...) => ... | CSharp7.cs:285:40:285:61 | (..., ...) | diff --git a/csharp/ql/test/library-tests/csharp7/ForEach.expected b/csharp/ql/test/library-tests/csharp7/ForEach.expected index 71f3d7cb0ae1..d0699faabf07 100644 --- a/csharp/ql/test/library-tests/csharp7/ForEach.expected +++ b/csharp/ql/test/library-tests/csharp7/ForEach.expected @@ -1,6 +1,6 @@ -| CSharp7.cs:286:9:286:47 | foreach (... ... in ...) ... | 0 | CSharp7.cs:286:23:286:23 | Int32 a | CSharp7.cs:286:23:286:23 | a | CSharp7.cs:286:39:286:42 | access to local variable list | CSharp7.cs:286:45:286:47 | {...} | -| CSharp7.cs:286:9:286:47 | foreach (... ... in ...) ... | 1 | CSharp7.cs:286:33:286:33 | String b | CSharp7.cs:286:33:286:33 | b | CSharp7.cs:286:39:286:42 | access to local variable list | CSharp7.cs:286:45:286:47 | {...} | -| CSharp7.cs:288:9:288:44 | foreach (... ... in ...) ... | 0 | CSharp7.cs:288:23:288:23 | Int32 a | CSharp7.cs:288:23:288:23 | a | CSharp7.cs:288:36:288:39 | access to local variable list | CSharp7.cs:288:42:288:44 | {...} | -| CSharp7.cs:288:9:288:44 | foreach (... ... in ...) ... | 1 | CSharp7.cs:288:30:288:30 | String b | CSharp7.cs:288:30:288:30 | b | CSharp7.cs:288:36:288:39 | access to local variable list | CSharp7.cs:288:42:288:44 | {...} | -| CSharp7.cs:290:9:290:40 | foreach (... ... in ...) ... | 0 | CSharp7.cs:290:23:290:23 | Int32 a | CSharp7.cs:290:23:290:23 | a | CSharp7.cs:290:32:290:35 | access to local variable list | CSharp7.cs:290:38:290:40 | {...} | -| CSharp7.cs:290:9:290:40 | foreach (... ... in ...) ... | 1 | CSharp7.cs:290:26:290:26 | String b | CSharp7.cs:290:26:290:26 | b | CSharp7.cs:290:32:290:35 | access to local variable list | CSharp7.cs:290:38:290:40 | {...} | +| CSharp7.cs:287:9:287:47 | foreach (... ... in ...) ... | 0 | CSharp7.cs:287:23:287:23 | Int32 a | CSharp7.cs:287:23:287:23 | a | CSharp7.cs:287:39:287:42 | access to local variable list | CSharp7.cs:287:45:287:47 | {...} | +| CSharp7.cs:287:9:287:47 | foreach (... ... in ...) ... | 1 | CSharp7.cs:287:33:287:33 | String b | CSharp7.cs:287:33:287:33 | b | CSharp7.cs:287:39:287:42 | access to local variable list | CSharp7.cs:287:45:287:47 | {...} | +| CSharp7.cs:289:9:289:44 | foreach (... ... in ...) ... | 0 | CSharp7.cs:289:23:289:23 | Int32 a | CSharp7.cs:289:23:289:23 | a | CSharp7.cs:289:36:289:39 | access to local variable list | CSharp7.cs:289:42:289:44 | {...} | +| CSharp7.cs:289:9:289:44 | foreach (... ... in ...) ... | 1 | CSharp7.cs:289:30:289:30 | String b | CSharp7.cs:289:30:289:30 | b | CSharp7.cs:289:36:289:39 | access to local variable list | CSharp7.cs:289:42:289:44 | {...} | +| CSharp7.cs:291:9:291:40 | foreach (... ... in ...) ... | 0 | CSharp7.cs:291:23:291:23 | Int32 a | CSharp7.cs:291:23:291:23 | a | CSharp7.cs:291:32:291:35 | access to local variable list | CSharp7.cs:291:38:291:40 | {...} | +| CSharp7.cs:291:9:291:40 | foreach (... ... in ...) ... | 1 | CSharp7.cs:291:26:291:26 | String b | CSharp7.cs:291:26:291:26 | b | CSharp7.cs:291:32:291:35 | access to local variable list | CSharp7.cs:291:38:291:40 | {...} | diff --git a/csharp/ql/test/library-tests/csharp7/GlobalFlow.expected b/csharp/ql/test/library-tests/csharp7/GlobalFlow.expected index ffe1e741542a..b0b50f92ff6c 100644 --- a/csharp/ql/test/library-tests/csharp7/GlobalFlow.expected +++ b/csharp/ql/test/library-tests/csharp7/GlobalFlow.expected @@ -1,5 +1,5 @@ | CSharp7.cs:41:13:41:21 | "tainted" | CSharp7.cs:53:18:53:19 | access to local variable t1 | | CSharp7.cs:57:11:57:19 | "tainted" | CSharp7.cs:58:18:58:19 | access to local variable t4 | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:176:22:176:30 | "tainted" | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:182:21:182:26 | call to local function g | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:183:21:183:26 | call to local function h | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:177:22:177:30 | "tainted" | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:183:21:183:26 | call to local function g | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:184:21:184:26 | call to local function h | diff --git a/csharp/ql/test/library-tests/csharp7/GlobalTaintTracking.expected b/csharp/ql/test/library-tests/csharp7/GlobalTaintTracking.expected index 46a09f16377c..e22bec46795a 100644 --- a/csharp/ql/test/library-tests/csharp7/GlobalTaintTracking.expected +++ b/csharp/ql/test/library-tests/csharp7/GlobalTaintTracking.expected @@ -2,7 +2,7 @@ | CSharp7.cs:57:11:57:19 | "tainted" | CSharp7.cs:58:18:58:19 | access to local variable t4 | | CSharp7.cs:89:19:89:27 | "tainted" | CSharp7.cs:89:18:89:34 | (..., ...) | | CSharp7.cs:89:19:89:27 | "tainted" | CSharp7.cs:92:18:92:28 | call to method I | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:176:22:176:30 | "tainted" | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:181:21:181:26 | call to local function f | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:182:21:182:26 | call to local function g | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:183:21:183:26 | call to local function h | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:177:22:177:30 | "tainted" | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:182:21:182:26 | call to local function f | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:183:21:183:26 | call to local function g | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:184:21:184:26 | call to local function h | diff --git a/csharp/ql/test/library-tests/csharp7/IsFlow.expected b/csharp/ql/test/library-tests/csharp7/IsFlow.expected index 345e2f6347e2..5652f980d7ee 100644 --- a/csharp/ql/test/library-tests/csharp7/IsFlow.expected +++ b/csharp/ql/test/library-tests/csharp7/IsFlow.expected @@ -1,73 +1,73 @@ -| CSharp7.cs:249:9:275:9 | switch (...) {...} | CSharp7.cs:249:17:249:17 | access to local variable o | semmle.label | successor | -| CSharp7.cs:249:17:249:17 | access to local variable o | CSharp7.cs:251:13:251:23 | case ...: | semmle.label | successor | -| CSharp7.cs:251:13:251:23 | case ...: | CSharp7.cs:251:18:251:22 | "xyz" | semmle.label | successor | -| CSharp7.cs:251:18:251:22 | "xyz" | CSharp7.cs:252:17:252:22 | break; | semmle.label | match | -| CSharp7.cs:251:18:251:22 | "xyz" | CSharp7.cs:253:13:253:31 | case ...: | semmle.label | no-match | -| CSharp7.cs:252:17:252:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | -| CSharp7.cs:253:13:253:31 | case ...: | CSharp7.cs:253:18:253:19 | "" | semmle.label | successor | -| CSharp7.cs:253:18:253:19 | "" | CSharp7.cs:253:26:253:26 | 1 | semmle.label | match | -| CSharp7.cs:253:18:253:19 | "" | CSharp7.cs:255:13:255:41 | case ...: | semmle.label | no-match | -| CSharp7.cs:253:26:253:26 | 1 | CSharp7.cs:253:30:253:30 | 2 | semmle.label | successor | -| CSharp7.cs:253:26:253:30 | ... < ... | CSharp7.cs:254:17:254:22 | break; | semmle.label | true | -| CSharp7.cs:253:30:253:30 | 2 | CSharp7.cs:253:26:253:30 | ... < ... | semmle.label | successor | -| CSharp7.cs:254:17:254:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | -| CSharp7.cs:255:13:255:41 | case ...: | CSharp7.cs:255:18:255:20 | "x" | semmle.label | successor | -| CSharp7.cs:255:18:255:20 | "x" | CSharp7.cs:255:27:255:27 | access to local variable o | semmle.label | match | -| CSharp7.cs:255:18:255:20 | "x" | CSharp7.cs:258:13:258:36 | case ...: | semmle.label | no-match | -| CSharp7.cs:255:27:255:27 | access to local variable o | CSharp7.cs:255:32:255:40 | String s4 | semmle.label | successor | -| CSharp7.cs:255:27:255:40 | ... is ... | CSharp7.cs:256:17:256:45 | ...; | semmle.label | true | -| CSharp7.cs:255:27:255:40 | ... is ... | CSharp7.cs:258:13:258:36 | case ...: | semmle.label | false | -| CSharp7.cs:255:32:255:40 | String s4 | CSharp7.cs:255:27:255:40 | ... is ... | semmle.label | successor | -| CSharp7.cs:256:17:256:44 | call to method WriteLine | CSharp7.cs:257:17:257:22 | break; | semmle.label | successor | -| CSharp7.cs:256:17:256:45 | ...; | CSharp7.cs:256:37:256:38 | "x " | semmle.label | successor | -| CSharp7.cs:256:35:256:43 | $"..." | CSharp7.cs:256:17:256:44 | call to method WriteLine | semmle.label | successor | -| CSharp7.cs:256:37:256:38 | "x " | CSharp7.cs:256:40:256:41 | access to local variable s4 | semmle.label | successor | -| CSharp7.cs:256:40:256:41 | access to local variable s4 | CSharp7.cs:256:35:256:43 | $"..." | semmle.label | successor | -| CSharp7.cs:257:17:257:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | -| CSharp7.cs:258:13:258:36 | case ...: | CSharp7.cs:258:18:258:23 | Int32 i2 | semmle.label | successor | -| CSharp7.cs:258:18:258:23 | Int32 i2 | CSharp7.cs:258:30:258:31 | access to local variable i2 | semmle.label | match | -| CSharp7.cs:258:18:258:23 | Int32 i2 | CSharp7.cs:261:13:261:24 | case ...: | semmle.label | no-match | -| CSharp7.cs:258:30:258:31 | access to local variable i2 | CSharp7.cs:258:35:258:35 | 0 | semmle.label | successor | -| CSharp7.cs:258:30:258:35 | ... > ... | CSharp7.cs:259:17:259:52 | ...; | semmle.label | true | -| CSharp7.cs:258:30:258:35 | ... > ... | CSharp7.cs:261:13:261:24 | case ...: | semmle.label | false | -| CSharp7.cs:258:35:258:35 | 0 | CSharp7.cs:258:30:258:35 | ... > ... | semmle.label | successor | -| CSharp7.cs:259:17:259:51 | call to method WriteLine | CSharp7.cs:260:17:260:22 | break; | semmle.label | successor | -| CSharp7.cs:259:17:259:52 | ...; | CSharp7.cs:259:37:259:45 | "positive " | semmle.label | successor | -| CSharp7.cs:259:35:259:50 | $"..." | CSharp7.cs:259:17:259:51 | call to method WriteLine | semmle.label | successor | -| CSharp7.cs:259:37:259:45 | "positive " | CSharp7.cs:259:47:259:48 | access to local variable i2 | semmle.label | successor | -| CSharp7.cs:259:47:259:48 | access to local variable i2 | CSharp7.cs:259:35:259:50 | $"..." | semmle.label | successor | -| CSharp7.cs:260:17:260:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | -| CSharp7.cs:261:13:261:24 | case ...: | CSharp7.cs:261:18:261:23 | Int32 i3 | semmle.label | successor | -| CSharp7.cs:261:18:261:23 | Int32 i3 | CSharp7.cs:262:17:262:47 | ...; | semmle.label | match | -| CSharp7.cs:261:18:261:23 | Int32 i3 | CSharp7.cs:264:13:264:27 | case ...: | semmle.label | no-match | -| CSharp7.cs:262:17:262:46 | call to method WriteLine | CSharp7.cs:263:17:263:22 | break; | semmle.label | successor | -| CSharp7.cs:262:17:262:47 | ...; | CSharp7.cs:262:37:262:40 | "int " | semmle.label | successor | -| CSharp7.cs:262:35:262:45 | $"..." | CSharp7.cs:262:17:262:46 | call to method WriteLine | semmle.label | successor | -| CSharp7.cs:262:37:262:40 | "int " | CSharp7.cs:262:42:262:43 | access to local variable i3 | semmle.label | successor | -| CSharp7.cs:262:42:262:43 | access to local variable i3 | CSharp7.cs:262:35:262:45 | $"..." | semmle.label | successor | -| CSharp7.cs:263:17:263:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | -| CSharp7.cs:264:13:264:27 | case ...: | CSharp7.cs:264:18:264:26 | String s2 | semmle.label | successor | -| CSharp7.cs:264:18:264:26 | String s2 | CSharp7.cs:265:17:265:50 | ...; | semmle.label | match | -| CSharp7.cs:264:18:264:26 | String s2 | CSharp7.cs:267:13:267:26 | case ...: | semmle.label | no-match | -| CSharp7.cs:265:17:265:49 | call to method WriteLine | CSharp7.cs:266:17:266:22 | break; | semmle.label | successor | -| CSharp7.cs:265:17:265:50 | ...; | CSharp7.cs:265:37:265:43 | "string " | semmle.label | successor | -| CSharp7.cs:265:35:265:48 | $"..." | CSharp7.cs:265:17:265:49 | call to method WriteLine | semmle.label | successor | -| CSharp7.cs:265:37:265:43 | "string " | CSharp7.cs:265:45:265:46 | access to local variable s2 | semmle.label | successor | -| CSharp7.cs:265:45:265:46 | access to local variable s2 | CSharp7.cs:265:35:265:48 | $"..." | semmle.label | successor | -| CSharp7.cs:266:17:266:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | -| CSharp7.cs:267:13:267:26 | case ...: | CSharp7.cs:267:18:267:23 | access to type Double | semmle.label | successor | -| CSharp7.cs:267:18:267:23 | access to type Double | CSharp7.cs:268:17:268:44 | ...; | semmle.label | match | -| CSharp7.cs:267:18:267:23 | access to type Double | CSharp7.cs:270:13:270:24 | case ...: | semmle.label | no-match | -| CSharp7.cs:268:17:268:43 | call to method WriteLine | CSharp7.cs:269:17:269:22 | break; | semmle.label | successor | -| CSharp7.cs:268:17:268:44 | ...; | CSharp7.cs:268:35:268:42 | "Double" | semmle.label | successor | -| CSharp7.cs:268:35:268:42 | "Double" | CSharp7.cs:268:17:268:43 | call to method WriteLine | semmle.label | successor | -| CSharp7.cs:269:17:269:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | -| CSharp7.cs:270:13:270:24 | case ...: | CSharp7.cs:270:18:270:23 | Object v2 | semmle.label | successor | -| CSharp7.cs:270:18:270:23 | Object v2 | CSharp7.cs:271:17:271:22 | break; | semmle.label | match | -| CSharp7.cs:270:18:270:23 | Object v2 | CSharp7.cs:272:13:272:20 | default: | semmle.label | no-match | -| CSharp7.cs:271:17:271:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | -| CSharp7.cs:272:13:272:20 | default: | CSharp7.cs:273:17:273:52 | ...; | semmle.label | successor | -| CSharp7.cs:273:17:273:51 | call to method WriteLine | CSharp7.cs:274:17:274:22 | break; | semmle.label | successor | -| CSharp7.cs:273:17:273:52 | ...; | CSharp7.cs:273:35:273:50 | "Something else" | semmle.label | successor | -| CSharp7.cs:273:35:273:50 | "Something else" | CSharp7.cs:273:17:273:51 | call to method WriteLine | semmle.label | successor | -| CSharp7.cs:274:17:274:22 | break; | CSharp7.cs:231:10:231:13 | exit Test | semmle.label | break | +| CSharp7.cs:250:9:276:9 | switch (...) {...} | CSharp7.cs:250:17:250:17 | access to local variable o | semmle.label | successor | +| CSharp7.cs:250:17:250:17 | access to local variable o | CSharp7.cs:252:13:252:23 | case ...: | semmle.label | successor | +| CSharp7.cs:252:13:252:23 | case ...: | CSharp7.cs:252:18:252:22 | "xyz" | semmle.label | successor | +| CSharp7.cs:252:18:252:22 | "xyz" | CSharp7.cs:253:17:253:22 | break; | semmle.label | match | +| CSharp7.cs:252:18:252:22 | "xyz" | CSharp7.cs:254:13:254:31 | case ...: | semmle.label | no-match | +| CSharp7.cs:253:17:253:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | +| CSharp7.cs:254:13:254:31 | case ...: | CSharp7.cs:254:18:254:19 | "" | semmle.label | successor | +| CSharp7.cs:254:18:254:19 | "" | CSharp7.cs:254:26:254:26 | 1 | semmle.label | match | +| CSharp7.cs:254:18:254:19 | "" | CSharp7.cs:256:13:256:41 | case ...: | semmle.label | no-match | +| CSharp7.cs:254:26:254:26 | 1 | CSharp7.cs:254:30:254:30 | 2 | semmle.label | successor | +| CSharp7.cs:254:26:254:30 | ... < ... | CSharp7.cs:255:17:255:22 | break; | semmle.label | true | +| CSharp7.cs:254:30:254:30 | 2 | CSharp7.cs:254:26:254:30 | ... < ... | semmle.label | successor | +| CSharp7.cs:255:17:255:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | +| CSharp7.cs:256:13:256:41 | case ...: | CSharp7.cs:256:18:256:20 | "x" | semmle.label | successor | +| CSharp7.cs:256:18:256:20 | "x" | CSharp7.cs:256:27:256:27 | access to local variable o | semmle.label | match | +| CSharp7.cs:256:18:256:20 | "x" | CSharp7.cs:259:13:259:36 | case ...: | semmle.label | no-match | +| CSharp7.cs:256:27:256:27 | access to local variable o | CSharp7.cs:256:32:256:40 | String s4 | semmle.label | successor | +| CSharp7.cs:256:27:256:40 | ... is ... | CSharp7.cs:257:17:257:45 | ...; | semmle.label | true | +| CSharp7.cs:256:27:256:40 | ... is ... | CSharp7.cs:259:13:259:36 | case ...: | semmle.label | false | +| CSharp7.cs:256:32:256:40 | String s4 | CSharp7.cs:256:27:256:40 | ... is ... | semmle.label | successor | +| CSharp7.cs:257:17:257:44 | call to method WriteLine | CSharp7.cs:258:17:258:22 | break; | semmle.label | successor | +| CSharp7.cs:257:17:257:45 | ...; | CSharp7.cs:257:37:257:38 | "x " | semmle.label | successor | +| CSharp7.cs:257:35:257:43 | $"..." | CSharp7.cs:257:17:257:44 | call to method WriteLine | semmle.label | successor | +| CSharp7.cs:257:37:257:38 | "x " | CSharp7.cs:257:40:257:41 | access to local variable s4 | semmle.label | successor | +| CSharp7.cs:257:40:257:41 | access to local variable s4 | CSharp7.cs:257:35:257:43 | $"..." | semmle.label | successor | +| CSharp7.cs:258:17:258:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | +| CSharp7.cs:259:13:259:36 | case ...: | CSharp7.cs:259:18:259:23 | Int32 i2 | semmle.label | successor | +| CSharp7.cs:259:18:259:23 | Int32 i2 | CSharp7.cs:259:30:259:31 | access to local variable i2 | semmle.label | match | +| CSharp7.cs:259:18:259:23 | Int32 i2 | CSharp7.cs:262:13:262:24 | case ...: | semmle.label | no-match | +| CSharp7.cs:259:30:259:31 | access to local variable i2 | CSharp7.cs:259:35:259:35 | 0 | semmle.label | successor | +| CSharp7.cs:259:30:259:35 | ... > ... | CSharp7.cs:260:17:260:52 | ...; | semmle.label | true | +| CSharp7.cs:259:30:259:35 | ... > ... | CSharp7.cs:262:13:262:24 | case ...: | semmle.label | false | +| CSharp7.cs:259:35:259:35 | 0 | CSharp7.cs:259:30:259:35 | ... > ... | semmle.label | successor | +| CSharp7.cs:260:17:260:51 | call to method WriteLine | CSharp7.cs:261:17:261:22 | break; | semmle.label | successor | +| CSharp7.cs:260:17:260:52 | ...; | CSharp7.cs:260:37:260:45 | "positive " | semmle.label | successor | +| CSharp7.cs:260:35:260:50 | $"..." | CSharp7.cs:260:17:260:51 | call to method WriteLine | semmle.label | successor | +| CSharp7.cs:260:37:260:45 | "positive " | CSharp7.cs:260:47:260:48 | access to local variable i2 | semmle.label | successor | +| CSharp7.cs:260:47:260:48 | access to local variable i2 | CSharp7.cs:260:35:260:50 | $"..." | semmle.label | successor | +| CSharp7.cs:261:17:261:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | +| CSharp7.cs:262:13:262:24 | case ...: | CSharp7.cs:262:18:262:23 | Int32 i3 | semmle.label | successor | +| CSharp7.cs:262:18:262:23 | Int32 i3 | CSharp7.cs:263:17:263:47 | ...; | semmle.label | match | +| CSharp7.cs:262:18:262:23 | Int32 i3 | CSharp7.cs:265:13:265:27 | case ...: | semmle.label | no-match | +| CSharp7.cs:263:17:263:46 | call to method WriteLine | CSharp7.cs:264:17:264:22 | break; | semmle.label | successor | +| CSharp7.cs:263:17:263:47 | ...; | CSharp7.cs:263:37:263:40 | "int " | semmle.label | successor | +| CSharp7.cs:263:35:263:45 | $"..." | CSharp7.cs:263:17:263:46 | call to method WriteLine | semmle.label | successor | +| CSharp7.cs:263:37:263:40 | "int " | CSharp7.cs:263:42:263:43 | access to local variable i3 | semmle.label | successor | +| CSharp7.cs:263:42:263:43 | access to local variable i3 | CSharp7.cs:263:35:263:45 | $"..." | semmle.label | successor | +| CSharp7.cs:264:17:264:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | +| CSharp7.cs:265:13:265:27 | case ...: | CSharp7.cs:265:18:265:26 | String s2 | semmle.label | successor | +| CSharp7.cs:265:18:265:26 | String s2 | CSharp7.cs:266:17:266:50 | ...; | semmle.label | match | +| CSharp7.cs:265:18:265:26 | String s2 | CSharp7.cs:268:13:268:26 | case ...: | semmle.label | no-match | +| CSharp7.cs:266:17:266:49 | call to method WriteLine | CSharp7.cs:267:17:267:22 | break; | semmle.label | successor | +| CSharp7.cs:266:17:266:50 | ...; | CSharp7.cs:266:37:266:43 | "string " | semmle.label | successor | +| CSharp7.cs:266:35:266:48 | $"..." | CSharp7.cs:266:17:266:49 | call to method WriteLine | semmle.label | successor | +| CSharp7.cs:266:37:266:43 | "string " | CSharp7.cs:266:45:266:46 | access to local variable s2 | semmle.label | successor | +| CSharp7.cs:266:45:266:46 | access to local variable s2 | CSharp7.cs:266:35:266:48 | $"..." | semmle.label | successor | +| CSharp7.cs:267:17:267:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | +| CSharp7.cs:268:13:268:26 | case ...: | CSharp7.cs:268:18:268:23 | access to type Double | semmle.label | successor | +| CSharp7.cs:268:18:268:23 | access to type Double | CSharp7.cs:269:17:269:44 | ...; | semmle.label | match | +| CSharp7.cs:268:18:268:23 | access to type Double | CSharp7.cs:271:13:271:24 | case ...: | semmle.label | no-match | +| CSharp7.cs:269:17:269:43 | call to method WriteLine | CSharp7.cs:270:17:270:22 | break; | semmle.label | successor | +| CSharp7.cs:269:17:269:44 | ...; | CSharp7.cs:269:35:269:42 | "Double" | semmle.label | successor | +| CSharp7.cs:269:35:269:42 | "Double" | CSharp7.cs:269:17:269:43 | call to method WriteLine | semmle.label | successor | +| CSharp7.cs:270:17:270:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | +| CSharp7.cs:271:13:271:24 | case ...: | CSharp7.cs:271:18:271:23 | Object v2 | semmle.label | successor | +| CSharp7.cs:271:18:271:23 | Object v2 | CSharp7.cs:272:17:272:22 | break; | semmle.label | match | +| CSharp7.cs:271:18:271:23 | Object v2 | CSharp7.cs:273:13:273:20 | default: | semmle.label | no-match | +| CSharp7.cs:272:17:272:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | +| CSharp7.cs:273:13:273:20 | default: | CSharp7.cs:274:17:274:52 | ...; | semmle.label | successor | +| CSharp7.cs:274:17:274:51 | call to method WriteLine | CSharp7.cs:275:17:275:22 | break; | semmle.label | successor | +| CSharp7.cs:274:17:274:52 | ...; | CSharp7.cs:274:35:274:50 | "Something else" | semmle.label | successor | +| CSharp7.cs:274:35:274:50 | "Something else" | CSharp7.cs:274:17:274:51 | call to method WriteLine | semmle.label | successor | +| CSharp7.cs:275:17:275:22 | break; | CSharp7.cs:232:10:232:13 | exit Test | semmle.label | break | diff --git a/csharp/ql/test/library-tests/csharp7/IsPatterns.expected b/csharp/ql/test/library-tests/csharp7/IsPatterns.expected index 63bc44144904..d40737bd4620 100644 --- a/csharp/ql/test/library-tests/csharp7/IsPatterns.expected +++ b/csharp/ql/test/library-tests/csharp7/IsPatterns.expected @@ -1,5 +1,5 @@ -| CSharp7.cs:234:13:234:23 | ... is ... | Int32 | CSharp7.cs:234:18:234:23 | Int32 i1 | false | -| CSharp7.cs:238:18:238:31 | ... is ... | String | CSharp7.cs:238:23:238:31 | String s1 | false | -| CSharp7.cs:245:18:245:28 | ... is ... | Object | CSharp7.cs:245:23:245:28 | Object v1 | true | -| CSharp7.cs:255:27:255:40 | ... is ... | String | CSharp7.cs:255:32:255:40 | String s4 | false | -| CSharp7.cs:298:30:298:39 | ... is ... | Int32 | CSharp7.cs:298:35:298:39 | Int32 y | false | +| CSharp7.cs:235:13:235:23 | ... is ... | Int32 | CSharp7.cs:235:18:235:23 | Int32 i1 | false | +| CSharp7.cs:239:18:239:31 | ... is ... | String | CSharp7.cs:239:23:239:31 | String s1 | false | +| CSharp7.cs:246:18:246:28 | ... is ... | Object | CSharp7.cs:246:23:246:28 | Object v1 | true | +| CSharp7.cs:256:27:256:40 | ... is ... | String | CSharp7.cs:256:32:256:40 | String s4 | false | +| CSharp7.cs:299:35:299:44 | ... is ... | Int32 | CSharp7.cs:299:40:299:44 | Int32 y | false | diff --git a/csharp/ql/test/library-tests/csharp7/LocalFunctionCallArguments.expected b/csharp/ql/test/library-tests/csharp7/LocalFunctionCallArguments.expected index 52772ab037e2..2cf7be36a117 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalFunctionCallArguments.expected +++ b/csharp/ql/test/library-tests/csharp7/LocalFunctionCallArguments.expected @@ -2,13 +2,13 @@ | CSharp7.cs:143:26:143:30 | call to local function f6 | 0 | CSharp7.cs:143:29:143:29 | access to parameter x | | CSharp7.cs:147:30:147:34 | call to local function f7 | 0 | CSharp7.cs:147:33:147:33 | access to parameter x | | CSharp7.cs:148:20:148:24 | call to local function f9 | 0 | CSharp7.cs:148:23:148:23 | 1 | -| CSharp7.cs:155:16:155:20 | call to local function f1 | 0 | CSharp7.cs:155:19:155:19 | 2 | -| CSharp7.cs:167:20:167:23 | call to local function g | 0 | CSharp7.cs:167:22:167:22 | access to parameter u | -| CSharp7.cs:170:9:170:15 | call to local function h | 0 | CSharp7.cs:170:11:170:11 | 0 | -| CSharp7.cs:170:9:170:15 | call to local function h | 1 | CSharp7.cs:170:14:170:14 | 0 | -| CSharp7.cs:171:9:171:19 | call to local function h | 0 | CSharp7.cs:171:11:171:12 | "" | -| CSharp7.cs:171:9:171:19 | call to local function h | 1 | CSharp7.cs:171:15:171:18 | true | -| CSharp7.cs:177:31:177:34 | call to local function g | 0 | CSharp7.cs:177:33:177:33 | access to parameter s | -| CSharp7.cs:181:21:181:26 | call to local function f | 0 | CSharp7.cs:181:23:181:25 | access to local variable src | -| CSharp7.cs:182:21:182:26 | call to local function g | 0 | CSharp7.cs:182:23:182:25 | access to local variable src | -| CSharp7.cs:183:21:183:26 | call to local function h | 0 | CSharp7.cs:183:23:183:25 | access to local variable src | +| CSharp7.cs:156:16:156:20 | call to local function f1 | 0 | CSharp7.cs:156:19:156:19 | 2 | +| CSharp7.cs:168:20:168:23 | call to local function g | 0 | CSharp7.cs:168:22:168:22 | access to parameter u | +| CSharp7.cs:171:9:171:15 | call to local function h | 0 | CSharp7.cs:171:11:171:11 | 0 | +| CSharp7.cs:171:9:171:15 | call to local function h | 1 | CSharp7.cs:171:14:171:14 | 0 | +| CSharp7.cs:172:9:172:19 | call to local function h | 0 | CSharp7.cs:172:11:172:12 | "" | +| CSharp7.cs:172:9:172:19 | call to local function h | 1 | CSharp7.cs:172:15:172:18 | true | +| CSharp7.cs:178:31:178:34 | call to local function g | 0 | CSharp7.cs:178:33:178:33 | access to parameter s | +| CSharp7.cs:182:21:182:26 | call to local function f | 0 | CSharp7.cs:182:23:182:25 | access to local variable src | +| CSharp7.cs:183:21:183:26 | call to local function g | 0 | CSharp7.cs:183:23:183:25 | access to local variable src | +| CSharp7.cs:184:21:184:26 | call to local function h | 0 | CSharp7.cs:184:23:184:25 | access to local variable src | diff --git a/csharp/ql/test/library-tests/csharp7/LocalFunctionCalls.expected b/csharp/ql/test/library-tests/csharp7/LocalFunctionCalls.expected index 9ecf8790464b..f465839b3cfb 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalFunctionCalls.expected +++ b/csharp/ql/test/library-tests/csharp7/LocalFunctionCalls.expected @@ -2,13 +2,13 @@ | CSharp7.cs:143:26:143:30 | call to local function f6 | CSharp7.cs:141:9:141:51 | f6 | CSharp7.cs:141:9:141:51 | f6 | | CSharp7.cs:147:30:147:34 | call to local function f7 | CSharp7.cs:143:9:143:31 | f7 | CSharp7.cs:143:9:143:31 | f7 | | CSharp7.cs:148:20:148:24 | call to local function f9 | CSharp7.cs:147:13:147:35 | f9 | CSharp7.cs:147:13:147:35 | f9 | -| CSharp7.cs:155:16:155:20 | call to local function f1 | CSharp7.cs:131:9:131:39 | f1 | CSharp7.cs:131:9:131:39 | f1 | -| CSharp7.cs:165:37:165:42 | call to local function f | CSharp7.cs:160:9:160:24 | f | CSharp7.cs:160:9:160:24 | f | -| CSharp7.cs:166:13:166:18 | call to local function f | CSharp7.cs:160:9:160:24 | f | CSharp7.cs:160:9:160:24 | f | -| CSharp7.cs:167:20:167:23 | call to local function g | CSharp7.cs:161:9:161:25 | g | CSharp7.cs:161:9:161:25 | g | -| CSharp7.cs:170:9:170:15 | call to local function h | CSharp7.cs:163:9:168:9 | h | CSharp7.cs:163:9:168:9 | h | -| CSharp7.cs:171:9:171:19 | call to local function h | CSharp7.cs:163:9:168:9 | h | CSharp7.cs:163:9:168:9 | h | -| CSharp7.cs:177:31:177:34 | call to local function g | CSharp7.cs:178:9:178:32 | g | CSharp7.cs:178:9:178:32 | g | -| CSharp7.cs:181:21:181:26 | call to local function f | CSharp7.cs:177:9:177:40 | f | CSharp7.cs:177:9:177:40 | f | -| CSharp7.cs:182:21:182:26 | call to local function g | CSharp7.cs:178:9:178:32 | g | CSharp7.cs:178:9:178:32 | g | -| CSharp7.cs:183:21:183:26 | call to local function h | CSharp7.cs:179:9:179:40 | h | CSharp7.cs:179:9:179:40 | h | +| CSharp7.cs:156:16:156:20 | call to local function f1 | CSharp7.cs:131:9:131:39 | f1 | CSharp7.cs:131:9:131:39 | f1 | +| CSharp7.cs:166:37:166:42 | call to local function f | CSharp7.cs:161:9:161:24 | f | CSharp7.cs:161:9:161:24 | f | +| CSharp7.cs:167:13:167:18 | call to local function f | CSharp7.cs:161:9:161:24 | f | CSharp7.cs:161:9:161:24 | f | +| CSharp7.cs:168:20:168:23 | call to local function g | CSharp7.cs:162:9:162:25 | g | CSharp7.cs:162:9:162:25 | g | +| CSharp7.cs:171:9:171:15 | call to local function h | CSharp7.cs:164:9:169:9 | h | CSharp7.cs:164:9:169:9 | h | +| CSharp7.cs:172:9:172:19 | call to local function h | CSharp7.cs:164:9:169:9 | h | CSharp7.cs:164:9:169:9 | h | +| CSharp7.cs:178:31:178:34 | call to local function g | CSharp7.cs:179:9:179:32 | g | CSharp7.cs:179:9:179:32 | g | +| CSharp7.cs:182:21:182:26 | call to local function f | CSharp7.cs:178:9:178:40 | f | CSharp7.cs:178:9:178:40 | f | +| CSharp7.cs:183:21:183:26 | call to local function g | CSharp7.cs:179:9:179:32 | g | CSharp7.cs:179:9:179:32 | g | +| CSharp7.cs:184:21:184:26 | call to local function h | CSharp7.cs:180:9:180:40 | h | CSharp7.cs:180:9:180:40 | h | diff --git a/csharp/ql/test/library-tests/csharp7/LocalFunctionParameters.expected b/csharp/ql/test/library-tests/csharp7/LocalFunctionParameters.expected index a3aabd4f16ff..a9ae61c7b868 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalFunctionParameters.expected +++ b/csharp/ql/test/library-tests/csharp7/LocalFunctionParameters.expected @@ -4,17 +4,17 @@ | CSharp7.cs:141:9:141:51 | f6 | 0 | CSharp7.cs:141:20:141:20 | x | Int32 | | CSharp7.cs:143:9:143:31 | f7 | 0 | CSharp7.cs:143:20:143:20 | x | Int32 | | CSharp7.cs:147:13:147:35 | f9 | 0 | CSharp7.cs:147:24:147:24 | x | Int32 | -| CSharp7.cs:161:9:161:25 | g | 0 | CSharp7.cs:161:18:161:18 | t | T | -| CSharp7.cs:161:9:161:25 | g | 0 | CSharp7.cs:161:18:161:18 | t | U | -| CSharp7.cs:163:9:168:9 | h | 0 | CSharp7.cs:163:21:163:21 | t | Int32 | -| CSharp7.cs:163:9:168:9 | h | 0 | CSharp7.cs:163:21:163:21 | t | String | -| CSharp7.cs:163:9:168:9 | h | 0 | CSharp7.cs:163:21:163:21 | t | T | -| CSharp7.cs:163:9:168:9 | h | 1 | CSharp7.cs:163:26:163:26 | u | Boolean | -| CSharp7.cs:163:9:168:9 | h | 1 | CSharp7.cs:163:26:163:26 | u | Int32 | -| CSharp7.cs:163:9:168:9 | h | 1 | CSharp7.cs:163:26:163:26 | u | U | -| CSharp7.cs:165:13:165:43 | f2 | 0 | CSharp7.cs:165:25:165:25 | s | S | -| CSharp7.cs:165:13:165:43 | f2 | 1 | CSharp7.cs:165:30:165:31 | _t | T | -| CSharp7.cs:177:9:177:40 | f | 0 | CSharp7.cs:177:25:177:25 | s | String | -| CSharp7.cs:178:9:178:32 | g | 0 | CSharp7.cs:178:25:178:25 | s | String | -| CSharp7.cs:179:9:179:40 | h | 0 | CSharp7.cs:179:25:179:25 | s | String | -| CSharp7.cs:205:9:205:47 | F3 | 0 | CSharp7.cs:205:28:205:28 | q | Int32 | +| CSharp7.cs:162:9:162:25 | g | 0 | CSharp7.cs:162:18:162:18 | t | T | +| CSharp7.cs:162:9:162:25 | g | 0 | CSharp7.cs:162:18:162:18 | t | U | +| CSharp7.cs:164:9:169:9 | h | 0 | CSharp7.cs:164:21:164:21 | t | Int32 | +| CSharp7.cs:164:9:169:9 | h | 0 | CSharp7.cs:164:21:164:21 | t | String | +| CSharp7.cs:164:9:169:9 | h | 0 | CSharp7.cs:164:21:164:21 | t | T | +| CSharp7.cs:164:9:169:9 | h | 1 | CSharp7.cs:164:26:164:26 | u | Boolean | +| CSharp7.cs:164:9:169:9 | h | 1 | CSharp7.cs:164:26:164:26 | u | Int32 | +| CSharp7.cs:164:9:169:9 | h | 1 | CSharp7.cs:164:26:164:26 | u | U | +| CSharp7.cs:166:13:166:43 | f2 | 0 | CSharp7.cs:166:25:166:25 | s | S | +| CSharp7.cs:166:13:166:43 | f2 | 1 | CSharp7.cs:166:30:166:31 | _t | T | +| CSharp7.cs:178:9:178:40 | f | 0 | CSharp7.cs:178:25:178:25 | s | String | +| CSharp7.cs:179:9:179:32 | g | 0 | CSharp7.cs:179:25:179:25 | s | String | +| CSharp7.cs:180:9:180:40 | h | 0 | CSharp7.cs:180:25:180:25 | s | String | +| CSharp7.cs:206:9:206:47 | F3 | 0 | CSharp7.cs:206:28:206:28 | q | Int32 | diff --git a/csharp/ql/test/library-tests/csharp7/LocalFunctionStmts.expected b/csharp/ql/test/library-tests/csharp7/LocalFunctionStmts.expected index 06375392141c..748bef67d7e9 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalFunctionStmts.expected +++ b/csharp/ql/test/library-tests/csharp7/LocalFunctionStmts.expected @@ -5,12 +5,12 @@ | CSharp7.cs:143:9:143:31 | f7(...) | CSharp7.cs:143:9:143:31 | f7 | | CSharp7.cs:145:9:149:9 | f8(...) | CSharp7.cs:145:9:149:9 | f8 | | CSharp7.cs:147:13:147:35 | f9(...) | CSharp7.cs:147:13:147:35 | f9 | -| CSharp7.cs:152:13:152:26 | f9(...) | CSharp7.cs:152:13:152:26 | f9 | -| CSharp7.cs:160:9:160:24 | f(...) | CSharp7.cs:160:9:160:24 | f | -| CSharp7.cs:161:9:161:25 | g(...) | CSharp7.cs:161:9:161:25 | g | -| CSharp7.cs:163:9:168:9 | h(...) | CSharp7.cs:163:9:168:9 | h | -| CSharp7.cs:165:13:165:43 | f2(...) | CSharp7.cs:165:13:165:43 | f2 | -| CSharp7.cs:177:9:177:40 | f(...) | CSharp7.cs:177:9:177:40 | f | -| CSharp7.cs:178:9:178:32 | g(...) | CSharp7.cs:178:9:178:32 | g | -| CSharp7.cs:179:9:179:40 | h(...) | CSharp7.cs:179:9:179:40 | h | -| CSharp7.cs:205:9:205:47 | F3(...) | CSharp7.cs:205:9:205:47 | F3 | +| CSharp7.cs:153:13:153:26 | f9(...) | CSharp7.cs:153:13:153:26 | f9 | +| CSharp7.cs:161:9:161:24 | f(...) | CSharp7.cs:161:9:161:24 | f | +| CSharp7.cs:162:9:162:25 | g(...) | CSharp7.cs:162:9:162:25 | g | +| CSharp7.cs:164:9:169:9 | h(...) | CSharp7.cs:164:9:169:9 | h | +| CSharp7.cs:166:13:166:43 | f2(...) | CSharp7.cs:166:13:166:43 | f2 | +| CSharp7.cs:178:9:178:40 | f(...) | CSharp7.cs:178:9:178:40 | f | +| CSharp7.cs:179:9:179:32 | g(...) | CSharp7.cs:179:9:179:32 | g | +| CSharp7.cs:180:9:180:40 | h(...) | CSharp7.cs:180:9:180:40 | h | +| CSharp7.cs:206:9:206:47 | F3(...) | CSharp7.cs:206:9:206:47 | F3 | diff --git a/csharp/ql/test/library-tests/csharp7/LocalFunctions.expected b/csharp/ql/test/library-tests/csharp7/LocalFunctions.expected index 89c25f6102d4..789895f434d5 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalFunctions.expected +++ b/csharp/ql/test/library-tests/csharp7/LocalFunctions.expected @@ -1,20 +1,20 @@ -| CSharp7.cs:131:9:131:39 | f1 | f1 | Int32 | CSharp7.cs:130:5:156:5 | {...} | CSharp7.cs:131:9:131:39 | f1(...) | f1(int) | -| CSharp7.cs:133:9:133:42 | f2 | f2 | T | CSharp7.cs:130:5:156:5 | {...} | CSharp7.cs:133:9:133:42 | f2(...) | f2(T, U) | -| CSharp7.cs:137:9:137:22 | f3 | f3 | Int32 | CSharp7.cs:130:5:156:5 | {...} | CSharp7.cs:137:9:137:22 | f3(...) | f3() | -| CSharp7.cs:141:9:141:51 | f6 | f6 | Int32 | CSharp7.cs:130:5:156:5 | {...} | CSharp7.cs:141:9:141:51 | f6(...) | f6(int) | -| CSharp7.cs:143:9:143:31 | f7 | f7 | Int32 | CSharp7.cs:130:5:156:5 | {...} | CSharp7.cs:143:9:143:31 | f7(...) | f7(int) | -| CSharp7.cs:145:9:149:9 | f8 | f8 | Int32 | CSharp7.cs:130:5:156:5 | {...} | CSharp7.cs:145:9:149:9 | f8(...) | f8() | +| CSharp7.cs:131:9:131:39 | f1 | f1 | Int32 | CSharp7.cs:130:5:157:5 | {...} | CSharp7.cs:131:9:131:39 | f1(...) | f1(int) | +| CSharp7.cs:133:9:133:42 | f2 | f2 | T | CSharp7.cs:130:5:157:5 | {...} | CSharp7.cs:133:9:133:42 | f2(...) | f2(T, U) | +| CSharp7.cs:137:9:137:22 | f3 | f3 | Int32 | CSharp7.cs:130:5:157:5 | {...} | CSharp7.cs:137:9:137:22 | f3(...) | f3() | +| CSharp7.cs:141:9:141:51 | f6 | f6 | Int32 | CSharp7.cs:130:5:157:5 | {...} | CSharp7.cs:141:9:141:51 | f6(...) | f6(int) | +| CSharp7.cs:143:9:143:31 | f7 | f7 | Int32 | CSharp7.cs:130:5:157:5 | {...} | CSharp7.cs:143:9:143:31 | f7(...) | f7(int) | +| CSharp7.cs:145:9:149:9 | f8 | f8 | Int32 | CSharp7.cs:130:5:157:5 | {...} | CSharp7.cs:145:9:149:9 | f8(...) | f8() | | CSharp7.cs:147:13:147:35 | f9 | f9 | Int32 | CSharp7.cs:146:9:149:9 | {...} | CSharp7.cs:147:13:147:35 | f9(...) | f9(int) | -| CSharp7.cs:152:13:152:26 | f9 | f9 | Int32 | CSharp7.cs:151:26:153:9 | {...} | CSharp7.cs:152:13:152:26 | f9(...) | f9() | -| CSharp7.cs:160:9:160:24 | f | f | Int32 | CSharp7.cs:159:5:172:5 | {...} | CSharp7.cs:160:9:160:24 | f(...) | f() | -| CSharp7.cs:160:9:160:24 | f | f | Int32 | CSharp7.cs:159:5:172:5 | {...} | CSharp7.cs:160:9:160:24 | f(...) | f() | -| CSharp7.cs:161:9:161:25 | g | g | T | CSharp7.cs:159:5:172:5 | {...} | CSharp7.cs:161:9:161:25 | g(...) | g(T) | -| CSharp7.cs:161:9:161:25 | g | g | U | CSharp7.cs:159:5:172:5 | {...} | CSharp7.cs:161:9:161:25 | g(...) | g(U) | -| CSharp7.cs:163:9:168:9 | h | h | Boolean | CSharp7.cs:159:5:172:5 | {...} | CSharp7.cs:163:9:168:9 | h(...) | h(string, bool) | -| CSharp7.cs:163:9:168:9 | h | h | Int32 | CSharp7.cs:159:5:172:5 | {...} | CSharp7.cs:163:9:168:9 | h(...) | h(int, int) | -| CSharp7.cs:163:9:168:9 | h | h | U | CSharp7.cs:159:5:172:5 | {...} | CSharp7.cs:163:9:168:9 | h(...) | h(T, U) | -| CSharp7.cs:165:13:165:43 | f2 | f2 | Int32 | CSharp7.cs:164:9:168:9 | {...} | CSharp7.cs:165:13:165:43 | f2(...) | f2(S, T) | -| CSharp7.cs:177:9:177:40 | f | f | String | CSharp7.cs:175:5:184:5 | {...} | CSharp7.cs:177:9:177:40 | f(...) | f(string) | -| CSharp7.cs:178:9:178:32 | g | g | String | CSharp7.cs:175:5:184:5 | {...} | CSharp7.cs:178:9:178:32 | g(...) | g(string) | -| CSharp7.cs:179:9:179:40 | h | h | String | CSharp7.cs:175:5:184:5 | {...} | CSharp7.cs:179:9:179:40 | h(...) | h(string) | -| CSharp7.cs:205:9:205:47 | F3 | F3 | Int32 | CSharp7.cs:204:5:207:5 | {...} | CSharp7.cs:205:9:205:47 | F3(...) | F3(ref int) | +| CSharp7.cs:153:13:153:26 | f9 | f9 | Int32 | CSharp7.cs:152:9:154:9 | {...} | CSharp7.cs:153:13:153:26 | f9(...) | f9() | +| CSharp7.cs:161:9:161:24 | f | f | Int32 | CSharp7.cs:160:5:173:5 | {...} | CSharp7.cs:161:9:161:24 | f(...) | f() | +| CSharp7.cs:161:9:161:24 | f | f | Int32 | CSharp7.cs:160:5:173:5 | {...} | CSharp7.cs:161:9:161:24 | f(...) | f() | +| CSharp7.cs:162:9:162:25 | g | g | T | CSharp7.cs:160:5:173:5 | {...} | CSharp7.cs:162:9:162:25 | g(...) | g(T) | +| CSharp7.cs:162:9:162:25 | g | g | U | CSharp7.cs:160:5:173:5 | {...} | CSharp7.cs:162:9:162:25 | g(...) | g(U) | +| CSharp7.cs:164:9:169:9 | h | h | Boolean | CSharp7.cs:160:5:173:5 | {...} | CSharp7.cs:164:9:169:9 | h(...) | h(string, bool) | +| CSharp7.cs:164:9:169:9 | h | h | Int32 | CSharp7.cs:160:5:173:5 | {...} | CSharp7.cs:164:9:169:9 | h(...) | h(int, int) | +| CSharp7.cs:164:9:169:9 | h | h | U | CSharp7.cs:160:5:173:5 | {...} | CSharp7.cs:164:9:169:9 | h(...) | h(T, U) | +| CSharp7.cs:166:13:166:43 | f2 | f2 | Int32 | CSharp7.cs:165:9:169:9 | {...} | CSharp7.cs:166:13:166:43 | f2(...) | f2(S, T) | +| CSharp7.cs:178:9:178:40 | f | f | String | CSharp7.cs:176:5:185:5 | {...} | CSharp7.cs:178:9:178:40 | f(...) | f(string) | +| CSharp7.cs:179:9:179:32 | g | g | String | CSharp7.cs:176:5:185:5 | {...} | CSharp7.cs:179:9:179:32 | g(...) | g(string) | +| CSharp7.cs:180:9:180:40 | h | h | String | CSharp7.cs:176:5:185:5 | {...} | CSharp7.cs:180:9:180:40 | h(...) | h(string) | +| CSharp7.cs:206:9:206:47 | F3 | F3 | Int32 | CSharp7.cs:205:5:208:5 | {...} | CSharp7.cs:206:9:206:47 | F3(...) | F3(ref int) | diff --git a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected index cad303fa914f..e5201d0df7d5 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected +++ b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected @@ -29,8 +29,8 @@ | CSharp7.cs:54:9:54:17 | this access | CSharp7.cs:57:9:57:32 | this access | | CSharp7.cs:54:15:54:16 | SSA def(t1) | CSharp7.cs:55:14:55:15 | access to local variable t1 | | CSharp7.cs:57:30:57:31 | SSA def(t4) | CSharp7.cs:58:18:58:19 | access to local variable t4 | -| CSharp7.cs:66:17:66:17 | 1 | CSharp7.cs:66:16:66:21 | (..., ...) | -| CSharp7.cs:66:20:66:20 | 2 | CSharp7.cs:66:16:66:21 | (..., ...) | +| CSharp7.cs:66:20:66:20 | 1 | CSharp7.cs:66:16:66:27 | (..., ...) | +| CSharp7.cs:66:26:66:26 | 2 | CSharp7.cs:66:16:66:27 | (..., ...) | | CSharp7.cs:69:10:69:20 | this | CSharp7.cs:71:26:71:28 | this access | | CSharp7.cs:71:26:71:28 | [post] this access | CSharp7.cs:72:17:72:19 | this access | | CSharp7.cs:71:26:71:28 | this access | CSharp7.cs:72:17:72:19 | this access | @@ -40,7 +40,7 @@ | CSharp7.cs:72:17:72:19 | this access | CSharp7.cs:73:18:73:20 | this access | | CSharp7.cs:73:18:73:20 | [post] this access | CSharp7.cs:74:13:74:15 | this access | | CSharp7.cs:73:18:73:20 | this access | CSharp7.cs:74:13:74:15 | this access | -| CSharp7.cs:74:13:74:15 | call to method F | CSharp7.cs:74:13:74:21 | access to field Item1 | +| CSharp7.cs:74:13:74:15 | call to method F | CSharp7.cs:74:13:74:17 | access to field A | | CSharp7.cs:75:16:75:16 | [post] access to local variable z | CSharp7.cs:77:39:77:39 | access to local variable z | | CSharp7.cs:75:16:75:16 | access to local variable z | CSharp7.cs:77:39:77:39 | access to local variable z | | CSharp7.cs:75:28:75:28 | 1 | CSharp7.cs:75:27:75:35 | (..., ...) | @@ -116,7 +116,7 @@ | CSharp7.cs:131:32:131:32 | access to parameter x | CSharp7.cs:131:32:131:36 | ... + ... | | CSharp7.cs:131:36:131:36 | 1 | CSharp7.cs:131:32:131:36 | ... + ... | | CSharp7.cs:133:22:133:22 | t | CSharp7.cs:133:39:133:39 | access to parameter t | -| CSharp7.cs:135:24:135:25 | this access | CSharp7.cs:155:16:155:17 | this access | +| CSharp7.cs:135:24:135:25 | this access | CSharp7.cs:156:16:156:17 | this access | | CSharp7.cs:139:29:139:29 | x | CSharp7.cs:139:34:139:34 | access to parameter x | | CSharp7.cs:139:34:139:34 | access to parameter x | CSharp7.cs:139:34:139:38 | ... + ... | | CSharp7.cs:139:38:139:38 | 1 | CSharp7.cs:139:34:139:38 | ... + ... | @@ -133,125 +133,119 @@ | CSharp7.cs:145:9:149:9 | this | CSharp7.cs:148:20:148:21 | this access | | CSharp7.cs:147:13:147:35 | this | CSharp7.cs:147:30:147:31 | this access | | CSharp7.cs:147:24:147:24 | x | CSharp7.cs:147:33:147:33 | access to parameter x | -| CSharp7.cs:158:10:158:17 | this | CSharp7.cs:170:9:170:9 | this access | -| CSharp7.cs:161:18:161:18 | t | CSharp7.cs:161:24:161:24 | access to parameter t | -| CSharp7.cs:163:9:168:9 | this | CSharp7.cs:166:13:166:16 | this access | -| CSharp7.cs:163:26:163:26 | u | CSharp7.cs:167:22:167:22 | access to parameter u | -| CSharp7.cs:165:13:165:43 | this | CSharp7.cs:165:37:165:40 | this access | -| CSharp7.cs:166:13:166:16 | this access | CSharp7.cs:167:20:167:20 | this access | -| CSharp7.cs:170:9:170:9 | this access | CSharp7.cs:171:9:171:9 | this access | -| CSharp7.cs:174:10:174:19 | this | CSharp7.cs:181:21:181:21 | this access | -| CSharp7.cs:176:16:176:30 | SSA def(src) | CSharp7.cs:181:23:181:25 | access to local variable src | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:176:16:176:30 | SSA def(src) | -| CSharp7.cs:177:9:177:40 | this | CSharp7.cs:177:31:177:31 | this access | -| CSharp7.cs:177:25:177:25 | s | CSharp7.cs:177:33:177:33 | access to parameter s | -| CSharp7.cs:177:31:177:34 | call to local function g | CSharp7.cs:177:31:177:39 | ... + ... | -| CSharp7.cs:177:38:177:39 | "" | CSharp7.cs:177:31:177:39 | ... + ... | -| CSharp7.cs:178:25:178:25 | s | CSharp7.cs:178:31:178:31 | access to parameter s | -| CSharp7.cs:179:25:179:25 | s | CSharp7.cs:179:37:179:37 | access to parameter s | -| CSharp7.cs:181:21:181:21 | this access | CSharp7.cs:182:21:182:21 | this access | -| CSharp7.cs:181:23:181:25 | [post] access to local variable src | CSharp7.cs:182:23:182:25 | access to local variable src | -| CSharp7.cs:181:23:181:25 | access to local variable src | CSharp7.cs:182:23:182:25 | access to local variable src | +| CSharp7.cs:159:10:159:17 | this | CSharp7.cs:171:9:171:9 | this access | +| CSharp7.cs:162:18:162:18 | t | CSharp7.cs:162:24:162:24 | access to parameter t | +| CSharp7.cs:164:9:169:9 | this | CSharp7.cs:167:13:167:16 | this access | +| CSharp7.cs:164:26:164:26 | u | CSharp7.cs:168:22:168:22 | access to parameter u | +| CSharp7.cs:166:13:166:43 | this | CSharp7.cs:166:37:166:40 | this access | +| CSharp7.cs:167:13:167:16 | this access | CSharp7.cs:168:20:168:20 | this access | +| CSharp7.cs:171:9:171:9 | this access | CSharp7.cs:172:9:172:9 | this access | +| CSharp7.cs:175:10:175:19 | this | CSharp7.cs:182:21:182:21 | this access | +| CSharp7.cs:177:16:177:30 | SSA def(src) | CSharp7.cs:182:23:182:25 | access to local variable src | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:177:16:177:30 | SSA def(src) | +| CSharp7.cs:178:9:178:40 | this | CSharp7.cs:178:31:178:31 | this access | +| CSharp7.cs:178:25:178:25 | s | CSharp7.cs:178:33:178:33 | access to parameter s | +| CSharp7.cs:178:31:178:34 | call to local function g | CSharp7.cs:178:31:178:39 | ... + ... | +| CSharp7.cs:178:38:178:39 | "" | CSharp7.cs:178:31:178:39 | ... + ... | +| CSharp7.cs:179:25:179:25 | s | CSharp7.cs:179:31:179:31 | access to parameter s | +| CSharp7.cs:180:25:180:25 | s | CSharp7.cs:180:37:180:37 | access to parameter s | | CSharp7.cs:182:21:182:21 | this access | CSharp7.cs:183:21:183:21 | this access | | CSharp7.cs:182:23:182:25 | [post] access to local variable src | CSharp7.cs:183:23:183:25 | access to local variable src | | CSharp7.cs:182:23:182:25 | access to local variable src | CSharp7.cs:183:23:183:25 | access to local variable src | -| CSharp7.cs:189:10:189:11 | this | CSharp7.cs:198:14:198:23 | this access | -| CSharp7.cs:191:13:191:18 | SSA def(v1) | CSharp7.cs:192:26:192:27 | access to local variable v1 | -| CSharp7.cs:191:18:191:18 | 2 | CSharp7.cs:191:13:191:18 | SSA def(v1) | -| CSharp7.cs:192:22:192:27 | ref ... | CSharp7.cs:192:17:192:27 | SSA def(r1) | -| CSharp7.cs:192:26:192:27 | access to local variable v1 | CSharp7.cs:198:21:198:22 | access to local variable v1 | -| CSharp7.cs:193:13:193:31 | SSA def(array) | CSharp7.cs:195:14:195:18 | access to local variable array | -| CSharp7.cs:193:21:193:31 | array creation of type Int32[] | CSharp7.cs:193:13:193:31 | SSA def(array) | -| CSharp7.cs:194:14:194:14 | 3 | CSharp7.cs:194:9:194:14 | SSA def(r1) | -| CSharp7.cs:195:9:195:21 | SSA def(r1) | CSharp7.cs:197:26:197:27 | access to local variable r1 | -| CSharp7.cs:195:14:195:18 | access to local variable array | CSharp7.cs:195:14:195:21 | access to array element | -| CSharp7.cs:195:14:195:18 | access to local variable array | CSharp7.cs:196:26:196:30 | access to local variable array | -| CSharp7.cs:195:14:195:21 | access to array element | CSharp7.cs:195:9:195:21 | SSA def(r1) | -| CSharp7.cs:196:26:196:30 | access to local variable array | CSharp7.cs:196:26:196:33 | access to array element | -| CSharp7.cs:197:26:197:27 | access to local variable r1 | CSharp7.cs:199:33:199:34 | access to local variable r1 | -| CSharp7.cs:198:14:198:23 | [post] this access | CSharp7.cs:199:26:199:35 | this access | -| CSharp7.cs:198:14:198:23 | this access | CSharp7.cs:199:26:199:35 | this access | -| CSharp7.cs:199:26:199:35 | [post] this access | CSharp7.cs:200:9:200:18 | this access | -| CSharp7.cs:199:26:199:35 | this access | CSharp7.cs:200:9:200:18 | this access | -| CSharp7.cs:199:33:199:34 | access to local variable r1 | CSharp7.cs:200:16:200:17 | access to local variable r1 | -| CSharp7.cs:203:24:203:24 | p | CSharp7.cs:206:20:206:20 | access to parameter p | -| CSharp7.cs:205:28:205:28 | q | CSharp7.cs:205:44:205:44 | access to parameter q | -| CSharp7.cs:216:13:216:17 | false | CSharp7.cs:216:9:216:17 | SSA def(x) | -| CSharp7.cs:217:17:217:17 | 0 | CSharp7.cs:217:16:217:23 | (..., ...) | -| CSharp7.cs:217:20:217:22 | 0 | CSharp7.cs:217:16:217:23 | (..., ...) | -| CSharp7.cs:220:10:220:13 | this | CSharp7.cs:222:13:222:20 | this access | -| CSharp7.cs:222:13:222:20 | [post] this access | CSharp7.cs:223:18:223:25 | this access | -| CSharp7.cs:222:13:222:20 | this access | CSharp7.cs:223:18:223:25 | this access | -| CSharp7.cs:223:18:223:25 | [post] this access | CSharp7.cs:224:22:224:29 | this access | -| CSharp7.cs:223:18:223:25 | this access | CSharp7.cs:224:22:224:29 | this access | -| CSharp7.cs:224:22:224:29 | [post] this access | CSharp7.cs:225:22:225:33 | this access | -| CSharp7.cs:224:22:224:29 | this access | CSharp7.cs:225:22:225:33 | this access | -| CSharp7.cs:233:16:233:23 | SSA def(o) | CSharp7.cs:234:13:234:13 | access to local variable o | -| CSharp7.cs:233:20:233:23 | null | CSharp7.cs:233:16:233:23 | SSA def(o) | -| CSharp7.cs:234:13:234:13 | access to local variable o | CSharp7.cs:234:18:234:23 | SSA def(i1) | -| CSharp7.cs:234:13:234:13 | access to local variable o | CSharp7.cs:238:18:238:18 | access to local variable o | -| CSharp7.cs:234:13:234:13 | access to local variable o | CSharp7.cs:249:17:249:17 | access to local variable o | -| CSharp7.cs:234:13:234:23 | ... is ... | CSharp7.cs:234:13:234:33 | ... && ... | -| CSharp7.cs:234:18:234:23 | SSA def(i1) | CSharp7.cs:234:28:234:29 | access to local variable i1 | -| CSharp7.cs:234:28:234:29 | access to local variable i1 | CSharp7.cs:234:28:234:33 | ... > ... | -| CSharp7.cs:234:28:234:29 | access to local variable i1 | CSharp7.cs:236:38:236:39 | access to local variable i1 | -| CSharp7.cs:234:28:234:33 | ... > ... | CSharp7.cs:234:13:234:33 | ... && ... | -| CSharp7.cs:236:33:236:36 | "int " | CSharp7.cs:236:31:236:41 | $"..." | -| CSharp7.cs:236:38:236:39 | access to local variable i1 | CSharp7.cs:236:31:236:41 | $"..." | -| CSharp7.cs:238:18:238:18 | access to local variable o | CSharp7.cs:238:23:238:31 | SSA def(s1) | -| CSharp7.cs:238:18:238:18 | access to local variable o | CSharp7.cs:242:18:242:18 | access to local variable o | -| CSharp7.cs:238:18:238:18 | access to local variable o | CSharp7.cs:249:17:249:17 | access to local variable o | -| CSharp7.cs:238:23:238:31 | SSA def(s1) | CSharp7.cs:240:41:240:42 | access to local variable s1 | -| CSharp7.cs:240:33:240:39 | "string " | CSharp7.cs:240:31:240:44 | $"..." | -| CSharp7.cs:240:41:240:42 | access to local variable s1 | CSharp7.cs:240:31:240:44 | $"..." | -| CSharp7.cs:242:18:242:18 | access to local variable o | CSharp7.cs:245:18:245:18 | access to local variable o | -| CSharp7.cs:242:18:242:18 | access to local variable o | CSharp7.cs:249:17:249:17 | access to local variable o | -| CSharp7.cs:245:18:245:18 | access to local variable o | CSharp7.cs:249:17:249:17 | access to local variable o | -| CSharp7.cs:249:17:249:17 | access to local variable o | CSharp7.cs:255:27:255:27 | access to local variable o | -| CSharp7.cs:249:17:249:17 | access to local variable o | CSharp7.cs:258:18:258:23 | SSA def(i2) | -| CSharp7.cs:249:17:249:17 | access to local variable o | CSharp7.cs:261:18:261:23 | SSA def(i3) | -| CSharp7.cs:249:17:249:17 | access to local variable o | CSharp7.cs:264:18:264:26 | SSA def(s2) | -| CSharp7.cs:253:26:253:26 | 1 | CSharp7.cs:253:26:253:30 | ... < ... | -| CSharp7.cs:253:30:253:30 | 2 | CSharp7.cs:253:26:253:30 | ... < ... | -| CSharp7.cs:255:27:255:27 | access to local variable o | CSharp7.cs:255:32:255:40 | SSA def(s4) | -| CSharp7.cs:255:32:255:40 | SSA def(s4) | CSharp7.cs:256:40:256:41 | access to local variable s4 | -| CSharp7.cs:256:37:256:38 | "x " | CSharp7.cs:256:35:256:43 | $"..." | -| CSharp7.cs:256:40:256:41 | access to local variable s4 | CSharp7.cs:256:35:256:43 | $"..." | -| CSharp7.cs:258:18:258:23 | SSA def(i2) | CSharp7.cs:258:30:258:31 | access to local variable i2 | -| CSharp7.cs:258:30:258:31 | access to local variable i2 | CSharp7.cs:258:30:258:35 | ... > ... | -| CSharp7.cs:258:30:258:31 | access to local variable i2 | CSharp7.cs:259:47:259:48 | access to local variable i2 | -| CSharp7.cs:259:37:259:45 | "positive " | CSharp7.cs:259:35:259:50 | $"..." | -| CSharp7.cs:259:47:259:48 | access to local variable i2 | CSharp7.cs:259:35:259:50 | $"..." | -| CSharp7.cs:261:18:261:23 | SSA def(i3) | CSharp7.cs:262:42:262:43 | access to local variable i3 | -| CSharp7.cs:262:37:262:40 | "int " | CSharp7.cs:262:35:262:45 | $"..." | -| CSharp7.cs:262:42:262:43 | access to local variable i3 | CSharp7.cs:262:35:262:45 | $"..." | -| CSharp7.cs:264:18:264:26 | SSA def(s2) | CSharp7.cs:265:45:265:46 | access to local variable s2 | -| CSharp7.cs:265:37:265:43 | "string " | CSharp7.cs:265:35:265:48 | $"..." | -| CSharp7.cs:265:45:265:46 | access to local variable s2 | CSharp7.cs:265:35:265:48 | $"..." | -| CSharp7.cs:283:13:283:48 | SSA def(dict) | CSharp7.cs:284:20:284:23 | access to local variable dict | -| CSharp7.cs:283:20:283:48 | object creation of type Dictionary | CSharp7.cs:283:13:283:48 | SSA def(dict) | -| CSharp7.cs:284:13:284:62 | SSA def(list) | CSharp7.cs:286:39:286:42 | access to local variable list | -| CSharp7.cs:284:20:284:23 | access to local variable dict | CSharp7.cs:284:20:284:62 | [library code] call to method Select | -| CSharp7.cs:284:20:284:62 | [library code] call to method Select | CSharp7.cs:284:20:284:62 | call to method Select | -| CSharp7.cs:284:20:284:62 | [library code] call to method Select | CSharp7.cs:284:32:284:61 | [implicit argument 0] (...) => ... | -| CSharp7.cs:284:20:284:62 | call to method Select | CSharp7.cs:284:13:284:62 | SSA def(list) | -| CSharp7.cs:284:32:284:35 | item | CSharp7.cs:284:41:284:44 | access to parameter item | -| CSharp7.cs:284:32:284:61 | [output] (...) => ... | CSharp7.cs:284:20:284:62 | [library code] call to method Select | -| CSharp7.cs:284:41:284:44 | access to parameter item | CSharp7.cs:284:51:284:54 | access to parameter item | -| CSharp7.cs:284:41:284:48 | access to property Key | CSharp7.cs:284:40:284:61 | (..., ...) | -| CSharp7.cs:284:51:284:54 | access to parameter item | CSharp7.cs:284:51:284:60 | [library code] access to property Value | -| CSharp7.cs:284:51:284:60 | [library code] access to property Value | CSharp7.cs:284:51:284:60 | access to property Value | -| CSharp7.cs:284:51:284:60 | access to property Value | CSharp7.cs:284:40:284:61 | (..., ...) | -| CSharp7.cs:286:39:286:42 | access to local variable list | CSharp7.cs:288:36:288:39 | access to local variable list | -| CSharp7.cs:288:36:288:39 | access to local variable list | CSharp7.cs:290:32:290:35 | access to local variable list | -| CSharp7.cs:298:17:298:19 | SSA def(x) | CSharp7.cs:298:22:298:39 | SSA phi(x) | -| CSharp7.cs:298:19:298:19 | 0 | CSharp7.cs:298:17:298:19 | SSA def(x) | -| CSharp7.cs:298:22:298:22 | access to local variable x | CSharp7.cs:298:22:298:25 | ... < ... | -| CSharp7.cs:298:22:298:22 | access to local variable x | CSharp7.cs:298:30:298:30 | access to local variable x | -| CSharp7.cs:298:22:298:25 | ... < ... | CSharp7.cs:298:22:298:39 | ... && ... | -| CSharp7.cs:298:22:298:39 | SSA phi(x) | CSharp7.cs:298:22:298:22 | access to local variable x | -| CSharp7.cs:298:30:298:30 | access to local variable x | CSharp7.cs:298:35:298:39 | SSA def(y) | -| CSharp7.cs:298:30:298:30 | access to local variable x | CSharp7.cs:298:44:298:44 | access to local variable x | -| CSharp7.cs:298:30:298:39 | ... is ... | CSharp7.cs:298:22:298:39 | ... && ... | -| CSharp7.cs:298:35:298:39 | SSA def(y) | CSharp7.cs:300:31:300:31 | access to local variable y | -| CSharp7.cs:298:42:298:44 | SSA def(x) | CSharp7.cs:298:22:298:39 | SSA phi(x) | +| CSharp7.cs:183:21:183:21 | this access | CSharp7.cs:184:21:184:21 | this access | +| CSharp7.cs:183:23:183:25 | [post] access to local variable src | CSharp7.cs:184:23:184:25 | access to local variable src | +| CSharp7.cs:183:23:183:25 | access to local variable src | CSharp7.cs:184:23:184:25 | access to local variable src | +| CSharp7.cs:190:10:190:11 | this | CSharp7.cs:199:14:199:23 | this access | +| CSharp7.cs:192:13:192:18 | SSA def(v1) | CSharp7.cs:193:26:193:27 | access to local variable v1 | +| CSharp7.cs:192:18:192:18 | 2 | CSharp7.cs:192:13:192:18 | SSA def(v1) | +| CSharp7.cs:193:22:193:27 | ref ... | CSharp7.cs:193:17:193:27 | SSA def(r1) | +| CSharp7.cs:193:26:193:27 | access to local variable v1 | CSharp7.cs:199:21:199:22 | access to local variable v1 | +| CSharp7.cs:194:13:194:31 | SSA def(array) | CSharp7.cs:196:14:196:18 | access to local variable array | +| CSharp7.cs:194:21:194:31 | array creation of type Int32[] | CSharp7.cs:194:13:194:31 | SSA def(array) | +| CSharp7.cs:195:14:195:14 | 3 | CSharp7.cs:195:9:195:14 | SSA def(r1) | +| CSharp7.cs:196:9:196:21 | SSA def(r1) | CSharp7.cs:198:26:198:27 | access to local variable r1 | +| CSharp7.cs:196:14:196:18 | access to local variable array | CSharp7.cs:196:14:196:21 | access to array element | +| CSharp7.cs:196:14:196:18 | access to local variable array | CSharp7.cs:197:26:197:30 | access to local variable array | +| CSharp7.cs:196:14:196:21 | access to array element | CSharp7.cs:196:9:196:21 | SSA def(r1) | +| CSharp7.cs:197:26:197:30 | access to local variable array | CSharp7.cs:197:26:197:33 | access to array element | +| CSharp7.cs:198:26:198:27 | access to local variable r1 | CSharp7.cs:200:33:200:34 | access to local variable r1 | +| CSharp7.cs:199:14:199:23 | [post] this access | CSharp7.cs:200:26:200:35 | this access | +| CSharp7.cs:199:14:199:23 | this access | CSharp7.cs:200:26:200:35 | this access | +| CSharp7.cs:200:26:200:35 | [post] this access | CSharp7.cs:201:9:201:18 | this access | +| CSharp7.cs:200:26:200:35 | this access | CSharp7.cs:201:9:201:18 | this access | +| CSharp7.cs:200:33:200:34 | access to local variable r1 | CSharp7.cs:201:16:201:17 | access to local variable r1 | +| CSharp7.cs:204:24:204:24 | p | CSharp7.cs:207:20:207:20 | access to parameter p | +| CSharp7.cs:206:28:206:28 | q | CSharp7.cs:206:44:206:44 | access to parameter q | +| CSharp7.cs:217:13:217:17 | false | CSharp7.cs:217:9:217:17 | SSA def(x) | +| CSharp7.cs:218:17:218:17 | 0 | CSharp7.cs:218:16:218:23 | (..., ...) | +| CSharp7.cs:218:20:218:22 | 0 | CSharp7.cs:218:16:218:23 | (..., ...) | +| CSharp7.cs:221:10:221:13 | this | CSharp7.cs:223:13:223:20 | this access | +| CSharp7.cs:223:13:223:20 | [post] this access | CSharp7.cs:224:18:224:25 | this access | +| CSharp7.cs:223:13:223:20 | this access | CSharp7.cs:224:18:224:25 | this access | +| CSharp7.cs:224:18:224:25 | [post] this access | CSharp7.cs:225:22:225:29 | this access | +| CSharp7.cs:224:18:224:25 | this access | CSharp7.cs:225:22:225:29 | this access | +| CSharp7.cs:225:22:225:29 | [post] this access | CSharp7.cs:226:22:226:33 | this access | +| CSharp7.cs:225:22:225:29 | this access | CSharp7.cs:226:22:226:33 | this access | +| CSharp7.cs:234:16:234:23 | SSA def(o) | CSharp7.cs:235:13:235:13 | access to local variable o | +| CSharp7.cs:234:20:234:23 | null | CSharp7.cs:234:16:234:23 | SSA def(o) | +| CSharp7.cs:235:13:235:13 | access to local variable o | CSharp7.cs:235:18:235:23 | SSA def(i1) | +| CSharp7.cs:235:13:235:13 | access to local variable o | CSharp7.cs:239:18:239:18 | access to local variable o | +| CSharp7.cs:235:13:235:13 | access to local variable o | CSharp7.cs:250:17:250:17 | access to local variable o | +| CSharp7.cs:235:13:235:23 | ... is ... | CSharp7.cs:235:13:235:33 | ... && ... | +| CSharp7.cs:235:18:235:23 | SSA def(i1) | CSharp7.cs:235:28:235:29 | access to local variable i1 | +| CSharp7.cs:235:28:235:29 | access to local variable i1 | CSharp7.cs:235:28:235:33 | ... > ... | +| CSharp7.cs:235:28:235:29 | access to local variable i1 | CSharp7.cs:237:38:237:39 | access to local variable i1 | +| CSharp7.cs:235:28:235:33 | ... > ... | CSharp7.cs:235:13:235:33 | ... && ... | +| CSharp7.cs:237:33:237:36 | "int " | CSharp7.cs:237:31:237:41 | $"..." | +| CSharp7.cs:237:38:237:39 | access to local variable i1 | CSharp7.cs:237:31:237:41 | $"..." | +| CSharp7.cs:239:18:239:18 | access to local variable o | CSharp7.cs:239:23:239:31 | SSA def(s1) | +| CSharp7.cs:239:18:239:18 | access to local variable o | CSharp7.cs:243:18:243:18 | access to local variable o | +| CSharp7.cs:239:18:239:18 | access to local variable o | CSharp7.cs:250:17:250:17 | access to local variable o | +| CSharp7.cs:239:23:239:31 | SSA def(s1) | CSharp7.cs:241:41:241:42 | access to local variable s1 | +| CSharp7.cs:241:33:241:39 | "string " | CSharp7.cs:241:31:241:44 | $"..." | +| CSharp7.cs:241:41:241:42 | access to local variable s1 | CSharp7.cs:241:31:241:44 | $"..." | +| CSharp7.cs:243:18:243:18 | access to local variable o | CSharp7.cs:246:18:246:18 | access to local variable o | +| CSharp7.cs:243:18:243:18 | access to local variable o | CSharp7.cs:250:17:250:17 | access to local variable o | +| CSharp7.cs:246:18:246:18 | access to local variable o | CSharp7.cs:250:17:250:17 | access to local variable o | +| CSharp7.cs:250:17:250:17 | access to local variable o | CSharp7.cs:256:27:256:27 | access to local variable o | +| CSharp7.cs:250:17:250:17 | access to local variable o | CSharp7.cs:259:18:259:23 | SSA def(i2) | +| CSharp7.cs:250:17:250:17 | access to local variable o | CSharp7.cs:262:18:262:23 | SSA def(i3) | +| CSharp7.cs:250:17:250:17 | access to local variable o | CSharp7.cs:265:18:265:26 | SSA def(s2) | +| CSharp7.cs:254:26:254:26 | 1 | CSharp7.cs:254:26:254:30 | ... < ... | +| CSharp7.cs:254:30:254:30 | 2 | CSharp7.cs:254:26:254:30 | ... < ... | +| CSharp7.cs:256:27:256:27 | access to local variable o | CSharp7.cs:256:32:256:40 | SSA def(s4) | +| CSharp7.cs:256:32:256:40 | SSA def(s4) | CSharp7.cs:257:40:257:41 | access to local variable s4 | +| CSharp7.cs:257:37:257:38 | "x " | CSharp7.cs:257:35:257:43 | $"..." | +| CSharp7.cs:257:40:257:41 | access to local variable s4 | CSharp7.cs:257:35:257:43 | $"..." | +| CSharp7.cs:259:18:259:23 | SSA def(i2) | CSharp7.cs:259:30:259:31 | access to local variable i2 | +| CSharp7.cs:259:30:259:31 | access to local variable i2 | CSharp7.cs:259:30:259:35 | ... > ... | +| CSharp7.cs:259:30:259:31 | access to local variable i2 | CSharp7.cs:260:47:260:48 | access to local variable i2 | +| CSharp7.cs:260:37:260:45 | "positive " | CSharp7.cs:260:35:260:50 | $"..." | +| CSharp7.cs:260:47:260:48 | access to local variable i2 | CSharp7.cs:260:35:260:50 | $"..." | +| CSharp7.cs:262:18:262:23 | SSA def(i3) | CSharp7.cs:263:42:263:43 | access to local variable i3 | +| CSharp7.cs:263:37:263:40 | "int " | CSharp7.cs:263:35:263:45 | $"..." | +| CSharp7.cs:263:42:263:43 | access to local variable i3 | CSharp7.cs:263:35:263:45 | $"..." | +| CSharp7.cs:265:18:265:26 | SSA def(s2) | CSharp7.cs:266:45:266:46 | access to local variable s2 | +| CSharp7.cs:266:37:266:43 | "string " | CSharp7.cs:266:35:266:48 | $"..." | +| CSharp7.cs:266:45:266:46 | access to local variable s2 | CSharp7.cs:266:35:266:48 | $"..." | +| CSharp7.cs:284:13:284:48 | SSA def(dict) | CSharp7.cs:285:20:285:23 | access to local variable dict | +| CSharp7.cs:284:20:284:48 | object creation of type Dictionary | CSharp7.cs:284:13:284:48 | SSA def(dict) | +| CSharp7.cs:285:13:285:62 | SSA def(list) | CSharp7.cs:287:39:287:42 | access to local variable list | +| CSharp7.cs:285:20:285:62 | call to method Select | CSharp7.cs:285:13:285:62 | SSA def(list) | +| CSharp7.cs:285:32:285:35 | item | CSharp7.cs:285:41:285:44 | access to parameter item | +| CSharp7.cs:285:41:285:44 | access to parameter item | CSharp7.cs:285:51:285:54 | access to parameter item | +| CSharp7.cs:285:41:285:48 | access to property Key | CSharp7.cs:285:40:285:61 | (..., ...) | +| CSharp7.cs:285:51:285:60 | access to property Value | CSharp7.cs:285:40:285:61 | (..., ...) | +| CSharp7.cs:287:39:287:42 | access to local variable list | CSharp7.cs:289:36:289:39 | access to local variable list | +| CSharp7.cs:289:36:289:39 | access to local variable list | CSharp7.cs:291:32:291:35 | access to local variable list | +| CSharp7.cs:299:18:299:22 | SSA def(x) | CSharp7.cs:299:25:299:44 | SSA phi(x) | +| CSharp7.cs:299:22:299:22 | 0 | CSharp7.cs:299:18:299:22 | SSA def(x) | +| CSharp7.cs:299:25:299:25 | access to local variable x | CSharp7.cs:299:25:299:30 | ... < ... | +| CSharp7.cs:299:25:299:25 | access to local variable x | CSharp7.cs:299:35:299:35 | access to local variable x | +| CSharp7.cs:299:25:299:30 | ... < ... | CSharp7.cs:299:25:299:44 | ... && ... | +| CSharp7.cs:299:25:299:44 | SSA phi(x) | CSharp7.cs:299:25:299:25 | access to local variable x | +| CSharp7.cs:299:35:299:35 | access to local variable x | CSharp7.cs:299:40:299:44 | SSA def(y) | +| CSharp7.cs:299:35:299:35 | access to local variable x | CSharp7.cs:299:49:299:49 | access to local variable x | +| CSharp7.cs:299:35:299:44 | ... is ... | CSharp7.cs:299:25:299:44 | ... && ... | +| CSharp7.cs:299:40:299:44 | SSA def(y) | CSharp7.cs:301:31:301:31 | access to local variable y | +| CSharp7.cs:299:47:299:49 | SSA def(x) | CSharp7.cs:299:25:299:44 | SSA phi(x) | diff --git a/csharp/ql/test/library-tests/csharp7/LocalVariables.expected b/csharp/ql/test/library-tests/csharp7/LocalVariables.expected index 06f61a52f91e..7e0793d3b434 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalVariables.expected +++ b/csharp/ql/test/library-tests/csharp7/LocalVariables.expected @@ -36,35 +36,35 @@ | CSharp7.cs:135:19:135:20 | f4 | Func | | CSharp7.cs:139:24:139:25 | f5 | Func | | CSharp7.cs:151:16:151:16 | a | Action | -| CSharp7.cs:176:16:176:18 | src | string | -| CSharp7.cs:181:13:181:17 | sink1 | string | -| CSharp7.cs:182:13:182:17 | sink2 | string | -| CSharp7.cs:183:13:183:17 | sink3 | string | -| CSharp7.cs:191:13:191:14 | v1 | int | -| CSharp7.cs:192:17:192:18 | r1 | int | -| CSharp7.cs:193:13:193:17 | array | Int32[] | -| CSharp7.cs:196:17:196:18 | r2 | int | -| CSharp7.cs:197:17:197:18 | r3 | int | -| CSharp7.cs:199:17:199:18 | r4 | int | -| CSharp7.cs:224:14:224:14 | x | int | -| CSharp7.cs:225:17:225:17 | y | double | -| CSharp7.cs:225:32:225:32 | z | bool | -| CSharp7.cs:233:16:233:16 | o | object | -| CSharp7.cs:234:22:234:23 | i1 | int | -| CSharp7.cs:238:30:238:31 | s1 | string | -| CSharp7.cs:245:27:245:28 | v1 | object | -| CSharp7.cs:255:39:255:40 | s4 | string | -| CSharp7.cs:258:22:258:23 | i2 | int | -| CSharp7.cs:261:22:261:23 | i3 | int | -| CSharp7.cs:264:25:264:26 | s2 | string | -| CSharp7.cs:270:22:270:23 | v2 | object | -| CSharp7.cs:283:13:283:16 | dict | Dictionary | -| CSharp7.cs:284:13:284:16 | list | IEnumerable<(Int32,String)> | -| CSharp7.cs:286:23:286:23 | a | int | -| CSharp7.cs:286:33:286:33 | b | string | -| CSharp7.cs:288:23:288:23 | a | int | -| CSharp7.cs:288:30:288:30 | b | string | -| CSharp7.cs:290:23:290:23 | a | int | -| CSharp7.cs:290:26:290:26 | b | string | -| CSharp7.cs:298:17:298:17 | x | int | -| CSharp7.cs:298:39:298:39 | y | int | +| CSharp7.cs:177:16:177:18 | src | string | +| CSharp7.cs:182:13:182:17 | sink1 | string | +| CSharp7.cs:183:13:183:17 | sink2 | string | +| CSharp7.cs:184:13:184:17 | sink3 | string | +| CSharp7.cs:192:13:192:14 | v1 | int | +| CSharp7.cs:193:17:193:18 | r1 | int | +| CSharp7.cs:194:13:194:17 | array | Int32[] | +| CSharp7.cs:197:17:197:18 | r2 | int | +| CSharp7.cs:198:17:198:18 | r3 | int | +| CSharp7.cs:200:17:200:18 | r4 | int | +| CSharp7.cs:225:14:225:14 | x | int | +| CSharp7.cs:226:17:226:17 | y | double | +| CSharp7.cs:226:32:226:32 | z | bool | +| CSharp7.cs:234:16:234:16 | o | object | +| CSharp7.cs:235:22:235:23 | i1 | int | +| CSharp7.cs:239:30:239:31 | s1 | string | +| CSharp7.cs:246:27:246:28 | v1 | object | +| CSharp7.cs:256:39:256:40 | s4 | string | +| CSharp7.cs:259:22:259:23 | i2 | int | +| CSharp7.cs:262:22:262:23 | i3 | int | +| CSharp7.cs:265:25:265:26 | s2 | string | +| CSharp7.cs:271:22:271:23 | v2 | object | +| CSharp7.cs:284:13:284:16 | dict | Dictionary | +| CSharp7.cs:285:13:285:16 | list | IEnumerable<(Int32,String)> | +| CSharp7.cs:287:23:287:23 | a | int | +| CSharp7.cs:287:33:287:33 | b | string | +| CSharp7.cs:289:23:289:23 | a | int | +| CSharp7.cs:289:30:289:30 | b | string | +| CSharp7.cs:291:23:291:23 | a | int | +| CSharp7.cs:291:26:291:26 | b | string | +| CSharp7.cs:299:18:299:18 | x | int | +| CSharp7.cs:299:44:299:44 | y | int | diff --git a/csharp/ql/test/library-tests/csharp7/PrintAst.expected b/csharp/ql/test/library-tests/csharp7/PrintAst.expected new file mode 100644 index 000000000000..a3d0e25e815e --- /dev/null +++ b/csharp/ql/test/library-tests/csharp7/PrintAst.expected @@ -0,0 +1,793 @@ +CSharp7.cs: +# 7| [Class] Literals +# 9| 5: [Field] x +# 9| 1: [AssignExpr] ... = ... +# 9| 0: [IntLiteral] 11 +# 9| 1: [FieldAccess] access to field x +# 10| 6: [Field] y +# 10| 1: [AssignExpr] ... = ... +# 10| 0: [IntLiteral] 123456 +# 10| 1: [FieldAccess] access to field y +# 11| 7: [Field] z +# 11| 1: [AssignExpr] ... = ... +# 11| 0: [IntLiteral] 128 +# 11| 1: [FieldAccess] access to field z +# 14| [Class] ExpressionBodiedMembers +# 16| 4: [Field] field +# 16| 1: [AssignExpr] ... = ... +# 16| 0: [IntLiteral] 0 +# 16| 1: [FieldAccess] access to field field +# 17| 5: [Method] Foo +# 17| 4: [FieldAccess] access to field field +# 18| 6: [Property] P +# 18| 3: [Getter] get_P +# 18| 4: [IntLiteral] 5 +# 19| 7: [Property] Q +# 21| 3: [Getter] get_Q +# 21| 4: [MethodCall] call to method Foo +# 22| 4: [Setter] set_Q +#-----| 2: (Parameters) +# 22| 0: [Parameter] value +# 22| 4: [AssignExpr] ... = ... +# 22| 0: [ParameterAccess] access to parameter value +# 22| 1: [FieldAccess] access to field field +# 24| 8: [InstanceConstructor] ExpressionBodiedMembers +# 24| 3: [ConstructorInitializer] call to constructor ExpressionBodiedMembers +# 24| 0: [IntLiteral] 1 +# 24| 4: [BlockStmt] {...} +# 25| 9: [InstanceConstructor] ExpressionBodiedMembers +#-----| 2: (Parameters) +# 25| 0: [Parameter] x +# 25| 4: [MethodCall] call to method Foo +# 26| 10: [Destructor] ~ExpressionBodiedMembers +# 26| 4: [MethodCall] call to method Foo +# 29| [Class] ThrowExpr +# 31| 5: [Method] Throw +#-----| 2: (Parameters) +# 31| 0: [Parameter] i +# 32| 4: [BlockStmt] {...} +# 33| 0: [ReturnStmt] return ...; +# 33| 0: [ConditionalExpr] ... ? ... : ... +# 33| 0: [GTExpr] ... > ... +# 33| 0: [ParameterAccess] access to parameter i +# 33| 1: [IntLiteral] 0 +# 33| 1: [ParameterAccess] access to parameter i +# 33| 2: [ThrowExpr] throw ... +# 33| 0: [ObjectCreation] object creation of type ArgumentException +# 33| 0: [StringLiteral] "i" +# 37| [Class] OutVariables +# 39| 5: [Method] F +#-----| 2: (Parameters) +# 39| 0: [Parameter] x +# 40| 4: [BlockStmt] {...} +# 41| 0: [ExprStmt] ...; +# 41| 0: [AssignExpr] ... = ... +# 41| 0: [StringLiteral] "tainted" +# 41| 1: [ParameterAccess] access to parameter x +# 44| 6: [Method] G +#-----| 2: (Parameters) +# 44| 0: [Parameter] x +# 44| 1: [Parameter] y +# 45| 4: [BlockStmt] {...} +# 46| 0: [ExprStmt] ...; +# 46| 0: [AssignExpr] ... = ... +# 46| 0: [ParameterAccess] access to parameter x +# 46| 1: [ParameterAccess] access to parameter y +# 49| 7: [Method] G +# 50| 4: [BlockStmt] {...} +# 51| 0: [ExprStmt] ...; +# 51| 0: [MethodCall] call to method F +# 51| 0: [LocalVariableAccess,LocalVariableDeclExpr] String t1 +# 52| 1: [ExprStmt] ...; +# 52| 0: [MethodCall] call to method F +# 52| 0: [LocalVariableAccess,LocalVariableDeclExpr] String t2 +# 53| 2: [LocalVariableDeclStmt] ... ...; +# 53| 0: [LocalVariableDeclAndInitExpr] String t3 = ... +# 53| 0: [LocalVariableAccess] access to local variable t1 +# 53| 1: [LocalVariableAccess] access to local variable t3 +# 54| 3: [ExprStmt] ...; +# 54| 0: [MethodCall] call to method F +# 54| 0: [LocalVariableAccess] access to local variable t1 +# 55| 4: [ExprStmt] ...; +# 55| 0: [AssignExpr] ... = ... +# 55| 0: [LocalVariableAccess] access to local variable t1 +# 55| 1: [LocalVariableAccess] access to local variable t3 +# 56| 5: [ExprStmt] ...; +# 56| 0: [AssignExpr] ... = ... +# 56| 0: [LocalVariableAccess] access to local variable t2 +# 56| 1: [LocalVariableAccess] access to local variable t3 +# 57| 6: [ExprStmt] ...; +# 57| 0: [MethodCall] call to method G +# 57| 0: [StringLiteral] "tainted" +# 57| 1: [LocalVariableAccess,LocalVariableDeclExpr] String t4 +# 58| 7: [LocalVariableDeclStmt] ... ...; +# 58| 0: [LocalVariableDeclAndInitExpr] String t5 = ... +# 58| 0: [LocalVariableAccess] access to local variable t4 +# 58| 1: [LocalVariableAccess] access to local variable t5 +# 62| [Class] Tuples +# 64| 5: [Method] F +# 65| 4: [BlockStmt] {...} +# 66| 0: [ReturnStmt] return ...; +# 66| 0: [TupleExpr] (..., ...) +# 66| 0: [IntLiteral] 1 +# 66| 1: [IntLiteral] 2 +# 69| 6: [Method] Expressions +# 70| 4: [BlockStmt] {...} +# 71| 0: [ExprStmt] ...; +# 71| 0: [AssignExpr] ... = ... +# 71| 0: [MethodCall] call to method F +# 71| 1: [TupleExpr] (..., ...) +# 71| 0: [LocalVariableDeclExpr] Int32 x +# 71| 1: [LocalVariableDeclExpr] Int32 y +# 72| 1: [LocalVariableDeclStmt] ... ...; +# 72| 0: [LocalVariableDeclAndInitExpr] (Int32,Int32) z = ... +# 72| 0: [MethodCall] call to method F +# 72| 1: [LocalVariableAccess] access to local variable z +# 73| 2: [ExprStmt] ...; +# 73| 0: [AssignExpr] ... = ... +# 73| 0: [MethodCall] call to method F +# 73| 1: [TupleExpr] (..., ...) +# 73| 0: [LocalVariableAccess] access to local variable x +# 73| 1: [LocalVariableAccess] access to local variable y +# 74| 3: [ExprStmt] ...; +# 74| 0: [AssignExpr] ... = ... +# 74| 0: [FieldAccess] access to field A +# 74| -1: [MethodCall] call to method F +# 74| 1: [LocalVariableAccess] access to local variable x +# 75| 4: [ExprStmt] ...; +# 75| 0: [AssignExpr] ... = ... +# 75| 0: [TupleExpr] (..., ...) +# 75| 0: [IntLiteral] 1 +# 75| 1: [IntLiteral] 2 +# 75| 2: [IntLiteral] 3 +# 75| 1: [TupleExpr] (..., ...) +# 75| 0: [LocalVariableAccess] access to local variable x +# 75| 1: [LocalVariableAccess] access to local variable y +# 75| 2: [FieldAccess] access to field Item1 +# 75| -1: [LocalVariableAccess] access to local variable z +# 76| 5: [ExprStmt] ...; +# 76| 0: [AssignExpr] ... = ... +# 76| 0: [AssignExpr] ... = ... +# 76| 0: [TupleExpr] (..., ...) +# 76| 0: [IntLiteral] 1 +# 76| 1: [IntLiteral] 2 +# 76| 1: [TupleExpr] (..., ...) +# 76| 0: [LocalVariableAccess] access to local variable x +# 76| 1: [LocalVariableAccess] access to local variable y +# 76| 1: [TupleExpr] (..., ...) +# 76| 0: [LocalVariableAccess] access to local variable x +# 76| 1: [LocalVariableAccess] access to local variable y +# 77| 6: [ExprStmt] ...; +# 77| 0: [AssignExpr] ... = ... +# 77| 0: [TupleExpr] (..., ...) +# 77| 0: [IntLiteral] 1 +# 77| 1: [LocalVariableAccess] access to local variable z +# 77| 1: [TupleExpr] (..., ...) +# 77| 0: [LocalVariableDeclExpr] Int32 a +# 77| 1: [TupleExpr] (..., ...) +# 77| 0: [LocalVariableDeclExpr] Int32 b +# 77| 1: [LocalVariableDeclExpr] Int32 c +# 78| 7: [ExprStmt] ...; +# 78| 0: [AssignExpr] ... = ... +# 78| 0: [TupleExpr] (..., ...) +# 78| 0: [LocalVariableAccess] access to local variable b +# 78| 1: [TupleExpr] (..., ...) +# 78| 0: [LocalVariableAccess] access to local variable c +# 78| 1: [LocalVariableAccess] access to local variable a +# 78| 1: [TupleExpr] (..., ...) +# 78| 0: [LocalVariableAccess] access to local variable a +# 78| 1: [TupleExpr] (..., ...) +# 78| 0: [LocalVariableAccess] access to local variable b +# 78| 1: [LocalVariableAccess] access to local variable c +# 79| 8: [ExprStmt] ...; +# 79| 0: [AssignExpr] ... = ... +# 79| 0: [TupleExpr] (..., ...) +# 79| 0: [StringLiteral] "" +# 79| 1: [LocalVariableAccess] access to local variable x +# 79| 1: [TupleExpr] (..., ...) +# 79| 0: [LocalVariableDeclExpr] String i +# 79| 1: [LocalVariableDeclExpr] Int32 j +# 82| 7: [Method] I +#-----| 2: (Parameters) +# 82| 0: [Parameter] x +# 83| 4: [BlockStmt] {...} +# 84| 0: [ReturnStmt] return ...; +# 84| 0: [FieldAccess] access to field a +# 84| -1: [TupleExpr] (..., ...) +# 84| 0: [ParameterAccess] access to parameter x +# 84| 1: [IntLiteral] 2 +# 87| 8: [Method] TaintFlow +# 88| 4: [BlockStmt] {...} +# 89| 0: [LocalVariableDeclStmt] ... ...; +# 89| 0: [LocalVariableDeclAndInitExpr] (String,String) t1 = ... +# 89| 0: [TupleExpr] (..., ...) +# 89| 0: [StringLiteral] "tainted" +# 89| 1: [StringLiteral] "X2" +# 89| 1: [LocalVariableAccess] access to local variable t1 +# 90| 1: [ExprStmt] ...; +# 90| 0: [AssignExpr] ... = ... +# 90| 0: [LocalVariableAccess] access to local variable t1 +# 90| 1: [TupleExpr] (..., ...) +# 90| 0: [LocalVariableDeclExpr] String t2 +# 90| 1: [LocalVariableDeclExpr] String t3 +# 91| 2: [LocalVariableDeclStmt] ... ...; +# 91| 0: [LocalVariableDeclAndInitExpr] String t4 = ... +# 91| 0: [LocalVariableAccess] access to local variable t3 +# 91| 1: [LocalVariableAccess] access to local variable t4 +# 92| 3: [LocalVariableDeclStmt] ... ...; +# 92| 0: [LocalVariableDeclAndInitExpr] String t5 = ... +# 92| 0: [MethodCall] call to method I +# 92| 0: [FieldAccess] access to field Item1 +# 92| -1: [LocalVariableAccess] access to local variable t1 +# 92| 1: [LocalVariableAccess] access to local variable t5 +# 95| 9: [Method] TupleExprNode +# 96| 4: [BlockStmt] {...} +# 97| 0: [LocalVariableDeclStmt] ... ...; +# 97| 0: [LocalVariableDeclAndInitExpr] (Int32,String) m1 = ... +# 97| 0: [TupleExpr] (..., ...) +# 97| 0: [IntLiteral] 1 +# 97| 1: [StringLiteral] "TupleExprNode1" +# 97| 1: [LocalVariableAccess] access to local variable m1 +# 98| 1: [LocalVariableDeclStmt] ... ...; +# 98| 0: [LocalVariableDeclAndInitExpr] (Int32,(String,Int32)) m2 = ... +# 98| 0: [TupleExpr] (..., ...) +# 98| 0: [IntLiteral] 1 +# 98| 1: [TupleExpr] (..., ...) +# 98| 0: [StringLiteral] "TupleExprNode2" +# 98| 1: [IntLiteral] 2 +# 98| 1: [LocalVariableAccess] access to local variable m2 +# 101| 10: [Method] TupleMemberAccess +# 102| 4: [BlockStmt] {...} +# 103| 0: [LocalVariableDeclStmt] ... ...; +# 103| 0: [LocalVariableDeclAndInitExpr] String m1 = ... +# 103| 0: [FieldAccess] access to field Item1 +# 103| -1: [TupleExpr] (..., ...) +# 103| 0: [StringLiteral] "TupleMemberAccess1" +# 103| 1: [IntLiteral] 0 +# 103| 1: [LocalVariableAccess] access to local variable m1 +# 104| 1: [LocalVariableDeclStmt] ... ...; +# 104| 0: [LocalVariableDeclAndInitExpr] (String,Int32) m2 = ... +# 104| 0: [FieldAccess] access to field Item2 +# 104| -1: [TupleExpr] (..., ...) +# 104| 0: [IntLiteral] 0 +# 104| 1: [TupleExpr] (..., ...) +# 104| 0: [StringLiteral] "TupleMemberAccess2" +# 104| 1: [IntLiteral] 1 +# 104| 1: [LocalVariableAccess] access to local variable m2 +# 107| 11: [Method] DefUse +# 108| 4: [BlockStmt] {...} +# 109| 0: [ExprStmt] ...; +# 109| 0: [AssignExpr] ... = ... +# 109| 0: [TupleExpr] (..., ...) +# 109| 0: [StringLiteral] "DefUse1" +# 109| 1: [TupleExpr] (..., ...) +# 109| 0: [IntLiteral] 0 +# 109| 1: [IntLiteral] 1 +# 109| 1: [TupleExpr] (..., ...) +# 109| 0: [LocalVariableDeclExpr] String m1 +# 109| 1: [LocalVariableDeclExpr] (Int32,Int32) m2 +# 110| 1: [LocalVariableDeclStmt] ... ...; +# 110| 0: [LocalVariableDeclExpr] String m3 +# 111| 2: [LocalVariableDeclStmt] ... ...; +# 111| 0: [LocalVariableDeclExpr] Int32 m4 +# 111| 1: [LocalVariableDeclExpr] Int32 m5 +# 112| 3: [ExprStmt] ...; +# 112| 0: [AssignExpr] ... = ... +# 112| 0: [TupleExpr] (..., ...) +# 112| 0: [LocalVariableAccess] access to local variable m1 +# 112| 1: [LocalVariableAccess] access to local variable m2 +# 112| 1: [TupleExpr] (..., ...) +# 112| 0: [LocalVariableAccess] access to local variable m3 +# 112| 1: [TupleExpr] (..., ...) +# 112| 0: [LocalVariableAccess] access to local variable m4 +# 112| 1: [LocalVariableAccess] access to local variable m5 +# 113| 4: [LocalVariableDeclStmt] ... ...; +# 113| 0: [LocalVariableDeclAndInitExpr] Int32 m6 = ... +# 113| 0: [LocalVariableAccess] access to local variable m4 +# 113| 1: [LocalVariableAccess] access to local variable m6 +# 114| 5: [ExprStmt] ...; +# 114| 0: [AssignExpr] ... = ... +# 114| 0: [AssignExpr] ... = ... +# 114| 0: [TupleExpr] (..., ...) +# 114| 0: [StringLiteral] "DefUse2" +# 114| 1: [TupleExpr] (..., ...) +# 114| 0: [IntLiteral] 0 +# 114| 1: [IntLiteral] 1 +# 114| 1: [TupleExpr] (..., ...) +# 114| 0: [LocalVariableAccess] access to local variable m1 +# 114| 1: [LocalVariableAccess] access to local variable m2 +# 114| 1: [TupleExpr] (..., ...) +# 114| 0: [LocalVariableDeclExpr] String m7 +# 114| 1: [TupleExpr] (..., ...) +# 114| 0: [LocalVariableDeclExpr] Int32 m8 +# 114| 1: [LocalVariableDeclExpr] Int32 m9 +# 115| 6: [LocalVariableDeclStmt] ... ...; +# 115| 0: [LocalVariableDeclAndInitExpr] Int32 m10 = ... +# 115| 0: [LocalVariableAccess] access to local variable m9 +# 115| 1: [LocalVariableAccess] access to local variable m10 +# 118| 7: [ExprStmt] ...; +# 118| 0: [AssignExpr] ... = ... +# 118| 0: [IntLiteral] 0 +# 118| 1: [FieldAccess] access to field Item2 +# 118| -1: [LocalVariableAccess] access to local variable m2 +# 119| 8: [LocalVariableDeclStmt] ... ...; +# 119| 0: [LocalVariableDeclAndInitExpr] Int32 m11 = ... +# 119| 0: [FieldAccess] access to field Item1 +# 119| -1: [LocalVariableAccess] access to local variable m2 +# 119| 1: [LocalVariableAccess] access to local variable m11 +# 122| 9: [LocalVariableDeclStmt] ... ...; +# 122| 0: [LocalVariableDeclExpr] String m12 +# 123| 10: [LocalVariableDeclStmt] ... ...; +# 123| 0: [LocalVariableDeclAndInitExpr] String m13 = ... +# 123| 0: [AssignExpr] ... = ... +# 123| 0: [StringLiteral] "DefUse3" +# 123| 1: [LocalVariableAccess] access to local variable m12 +# 123| 1: [LocalVariableAccess] access to local variable m13 +# 127| [Class] LocalFunctions +# 129| 5: [Method] Main +# 130| 4: [BlockStmt] {...} +# 131| 0: [LocalFunctionStmt] f1(...) +# 131| 0: [LocalFunction] f1 +#-----| 2: (Parameters) +# 131| 0: [Parameter] x +# 131| 4: [BlockStmt] {...} +# 131| 0: [ReturnStmt] return ...; +# 131| 0: [AddExpr] ... + ... +# 131| 0: [ParameterAccess] access to parameter x +# 131| 1: [IntLiteral] 1 +# 133| 1: [LocalFunctionStmt] f2(...) +# 133| 0: [LocalFunction] f2 +#-----| 1: (Type parameters) +# 133| 0: [TypeParameter] T +# 133| 1: [TypeParameter] U +#-----| 2: (Parameters) +# 133| 0: [Parameter] t +# 133| 1: [Parameter] u +# 133| 4: [BlockStmt] {...} +# 133| 0: [ReturnStmt] return ...; +# 133| 0: [ParameterAccess] access to parameter t +# 135| 2: [LocalVariableDeclStmt] ... ...; +# 135| 0: [LocalVariableDeclAndInitExpr] Func f4 = ... +# 135| 0: [ImplicitDelegateCreation] delegate creation of type Func +# 135| 0: [LocalFunctionAccess] access to local function f3 +# 135| 1: [LocalVariableAccess] access to local variable f4 +# 137| 3: [LocalFunctionStmt] f3(...) +# 137| 0: [LocalFunction] f3 +# 137| 4: [IntLiteral] 2 +# 139| 4: [LocalVariableDeclStmt] ... ...; +# 139| 0: [LocalVariableDeclAndInitExpr] Func f5 = ... +# 139| 0: [LambdaExpr] (...) => ... +#-----| 2: (Parameters) +# 139| 0: [Parameter] x +# 139| 4: [AddExpr] ... + ... +# 139| 0: [ParameterAccess] access to parameter x +# 139| 1: [IntLiteral] 1 +# 139| 1: [LocalVariableAccess] access to local variable f5 +# 141| 5: [LocalFunctionStmt] f6(...) +# 141| 0: [LocalFunction] f6 +#-----| 2: (Parameters) +# 141| 0: [Parameter] x +# 141| 4: [ConditionalExpr] ... ? ... : ... +# 141| 0: [GTExpr] ... > ... +# 141| 0: [ParameterAccess] access to parameter x +# 141| 1: [IntLiteral] 0 +# 141| 1: [AddExpr] ... + ... +# 141| 0: [IntLiteral] 1 +# 141| 1: [LocalFunctionCall] call to local function f7 +# 141| -1: [LocalFunctionAccess] access to local function f7 +# 141| 0: [SubExpr] ... - ... +# 141| 0: [ParameterAccess] access to parameter x +# 141| 1: [IntLiteral] 1 +# 141| 2: [IntLiteral] 0 +# 143| 6: [LocalFunctionStmt] f7(...) +# 143| 0: [LocalFunction] f7 +#-----| 2: (Parameters) +# 143| 0: [Parameter] x +# 143| 4: [LocalFunctionCall] call to local function f6 +# 143| -1: [LocalFunctionAccess] access to local function f6 +# 143| 0: [ParameterAccess] access to parameter x +# 145| 7: [LocalFunctionStmt] f8(...) +# 145| 0: [LocalFunction] f8 +# 146| 4: [BlockStmt] {...} +# 147| 0: [LocalFunctionStmt] f9(...) +# 147| 0: [LocalFunction] f9 +#-----| 2: (Parameters) +# 147| 0: [Parameter] x +# 147| 4: [LocalFunctionCall] call to local function f7 +# 147| -1: [LocalFunctionAccess] access to local function f7 +# 147| 0: [ParameterAccess] access to parameter x +# 148| 1: [ReturnStmt] return ...; +# 148| 0: [LocalFunctionCall] call to local function f9 +# 148| -1: [LocalFunctionAccess] access to local function f9 +# 148| 0: [IntLiteral] 1 +# 151| 8: [LocalVariableDeclStmt] ... ...; +# 151| 0: [LocalVariableDeclAndInitExpr] Action a = ... +# 151| 0: [LambdaExpr] (...) => ... +# 152| 4: [BlockStmt] {...} +# 153| 0: [LocalFunctionStmt] f9(...) +# 153| 0: [LocalFunction] f9 +# 153| 4: [IntLiteral] 0 +# 151| 1: [LocalVariableAccess] access to local variable a +# 156| 9: [ReturnStmt] return ...; +# 156| 0: [LocalFunctionCall] call to local function f1 +# 156| -1: [LocalFunctionAccess] access to local function f1 +# 156| 0: [IntLiteral] 2 +# 159| 6: [Method] Generics +# 160| 4: [BlockStmt] {...} +# 161| 0: [LocalFunctionStmt] f(...) +# 161| 0: [LocalFunction] f +#-----| 1: (Type parameters) +# 161| 0: [TypeParameter] T +# 161| 4: [IntLiteral] 1 +# 162| 1: [LocalFunctionStmt] g(...) +# 162| 0: [LocalFunction] g +#-----| 1: (Type parameters) +# 162| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 162| 0: [Parameter] t +# 162| 4: [ParameterAccess] access to parameter t +# 164| 2: [LocalFunctionStmt] h(...) +# 164| 0: [LocalFunction] h +#-----| 1: (Type parameters) +# 164| 0: [TypeParameter] T +# 164| 1: [TypeParameter] U +#-----| 2: (Parameters) +# 164| 0: [Parameter] t +# 164| 1: [Parameter] u +# 165| 4: [BlockStmt] {...} +# 166| 0: [LocalFunctionStmt] f2(...) +# 166| 0: [LocalFunction] f2 +#-----| 1: (Type parameters) +# 166| 0: [TypeParameter] S +#-----| 2: (Parameters) +# 166| 0: [Parameter] s +# 166| 1: [Parameter] _t +# 166| 4: [LocalFunctionCall] call to local function f +# 166| -1: [LocalFunctionAccess] access to local function f +# 167| 1: [ExprStmt] ...; +# 167| 0: [LocalFunctionCall] call to local function f +# 167| -1: [LocalFunctionAccess] access to local function f +# 168| 2: [ReturnStmt] return ...; +# 168| 0: [LocalFunctionCall] call to local function g +# 168| -1: [LocalFunctionAccess] access to local function g +# 168| 0: [ParameterAccess] access to parameter u +# 171| 3: [ExprStmt] ...; +# 171| 0: [LocalFunctionCall] call to local function h +# 171| -1: [LocalFunctionAccess] access to local function h +# 171| 0: [IntLiteral] 0 +# 171| 1: [IntLiteral] 0 +# 172| 4: [ExprStmt] ...; +# 172| 0: [LocalFunctionCall] call to local function h +# 172| -1: [LocalFunctionAccess] access to local function h +# 172| 0: [StringLiteral] "" +# 172| 1: [BoolLiteral] true +# 175| 7: [Method] GlobalFlow +# 176| 4: [BlockStmt] {...} +# 177| 0: [LocalVariableDeclStmt] ... ...; +# 177| 0: [LocalVariableDeclAndInitExpr] String src = ... +# 177| 0: [StringLiteral] "tainted" +# 177| 1: [LocalVariableAccess] access to local variable src +# 178| 1: [LocalFunctionStmt] f(...) +# 178| 0: [LocalFunction] f +#-----| 2: (Parameters) +# 178| 0: [Parameter] s +# 178| 4: [AddExpr] ... + ... +# 178| 0: [LocalFunctionCall] call to local function g +# 178| -1: [LocalFunctionAccess] access to local function g +# 178| 0: [ParameterAccess] access to parameter s +# 178| 1: [StringLiteral] "" +# 179| 2: [LocalFunctionStmt] g(...) +# 179| 0: [LocalFunction] g +#-----| 2: (Parameters) +# 179| 0: [Parameter] s +# 179| 4: [ParameterAccess] access to parameter s +# 180| 3: [LocalFunctionStmt] h(...) +# 180| 0: [LocalFunction] h +#-----| 2: (Parameters) +# 180| 0: [Parameter] s +# 180| 4: [BlockStmt] {...} +# 180| 0: [ReturnStmt] return ...; +# 180| 0: [ParameterAccess] access to parameter s +# 182| 4: [LocalVariableDeclStmt] ... ...; +# 182| 0: [LocalVariableDeclAndInitExpr] String sink1 = ... +# 182| 0: [LocalFunctionCall] call to local function f +# 182| -1: [LocalFunctionAccess] access to local function f +# 182| 0: [LocalVariableAccess] access to local variable src +# 182| 1: [LocalVariableAccess] access to local variable sink1 +# 183| 5: [LocalVariableDeclStmt] ... ...; +# 183| 0: [LocalVariableDeclAndInitExpr] String sink2 = ... +# 183| 0: [LocalFunctionCall] call to local function g +# 183| -1: [LocalFunctionAccess] access to local function g +# 183| 0: [LocalVariableAccess] access to local variable src +# 183| 1: [LocalVariableAccess] access to local variable sink2 +# 184| 6: [LocalVariableDeclStmt] ... ...; +# 184| 0: [LocalVariableDeclAndInitExpr] String sink3 = ... +# 184| 0: [LocalFunctionCall] call to local function h +# 184| -1: [LocalFunctionAccess] access to local function h +# 184| 0: [LocalVariableAccess] access to local variable src +# 184| 1: [LocalVariableAccess] access to local variable sink3 +# 188| [Class] Refs +# 190| 5: [Method] F1 +# 191| 4: [BlockStmt] {...} +# 192| 0: [LocalVariableDeclStmt] ... ...; +# 192| 0: [LocalVariableDeclAndInitExpr] Int32 v1 = ... +# 192| 0: [IntLiteral] 2 +# 192| 1: [LocalVariableAccess] access to local variable v1 +# 193| 1: [LocalVariableDeclStmt] ... ...; +# 193| 0: [LocalVariableDeclAndInitExpr] Int32 r1 = ... +# 193| 0: [RefExpr] ref ... +# 193| 0: [LocalVariableAccess] access to local variable v1 +# 193| 1: [LocalVariableAccess] access to local variable r1 +# 194| 2: [LocalVariableDeclStmt] ... ...; +# 194| 0: [LocalVariableDeclAndInitExpr] Int32[] array = ... +# 194| 0: [ArrayCreation] array creation of type Int32[] +# 194| 0: [IntLiteral] 10 +# 194| 1: [LocalVariableAccess] access to local variable array +# 195| 3: [ExprStmt] ...; +# 195| 0: [AssignExpr] ... = ... +# 195| 0: [IntLiteral] 3 +# 195| 1: [LocalVariableAccess] access to local variable r1 +# 196| 4: [ExprStmt] ...; +# 196| 0: [AssignExpr] ... = ... +# 196| 0: [ArrayAccess] access to array element +# 196| -1: [LocalVariableAccess] access to local variable array +# 196| 0: [IntLiteral] 1 +# 196| 1: [LocalVariableAccess] access to local variable r1 +# 197| 5: [LocalVariableDeclStmt] ... ...; +# 197| 0: [LocalVariableDeclAndInitExpr] Int32 r2 = ... +# 197| 0: [RefExpr] ref ... +# 197| 0: [ArrayAccess] access to array element +# 197| -1: [LocalVariableAccess] access to local variable array +# 197| 0: [IntLiteral] 3 +# 197| 1: [LocalVariableAccess] access to local variable r2 +# 198| 6: [LocalVariableDeclStmt] ... ...; +# 198| 0: [LocalVariableDeclAndInitExpr] Int32 r3 = ... +# 198| 0: [RefExpr] ref ... +# 198| 0: [LocalVariableAccess] access to local variable r1 +# 198| 1: [LocalVariableAccess] access to local variable r3 +# 199| 7: [ExprStmt] ...; +# 199| 0: [AssignExpr] ... = ... +# 199| 0: [MethodCall] call to method F2 +# 199| 0: [LocalVariableAccess] access to local variable v1 +# 199| 1: [LocalVariableAccess] access to local variable v1 +# 200| 8: [LocalVariableDeclStmt] ... ...; +# 200| 0: [LocalVariableDeclAndInitExpr] Int32 r4 = ... +# 200| 0: [RefExpr] ref ... +# 200| 0: [MethodCall] call to method F2 +# 200| 0: [LocalVariableAccess] access to local variable r1 +# 200| 1: [LocalVariableAccess] access to local variable r4 +# 201| 9: [ExprStmt] ...; +# 201| 0: [AssignExpr] ... = ... +# 201| 0: [IntLiteral] 3 +# 201| 1: [MethodCall] call to method F2 +# 201| 0: [LocalVariableAccess] access to local variable r1 +# 204| 6: [Method] F2 +#-----| 2: (Parameters) +# 204| 0: [Parameter] p +# 205| 4: [BlockStmt] {...} +# 206| 0: [LocalFunctionStmt] F3(...) +# 206| 0: [LocalFunction] F3 +#-----| 2: (Parameters) +# 206| 0: [Parameter] q +# 206| 4: [BlockStmt] {...} +# 206| 0: [ReturnStmt] return ...; +# 206| 0: [RefExpr] ref ... +# 206| 0: [ParameterAccess] access to parameter q +# 207| 1: [ReturnStmt] return ...; +# 207| 0: [RefExpr] ref ... +# 207| 0: [ParameterAccess] access to parameter p +# 210| 7: [DelegateType] RefFn +#-----| 2: (Parameters) +# 210| 0: [Parameter] p +# 213| [Class] Discards +# 215| 5: [Method] f +#-----| 2: (Parameters) +# 215| 0: [Parameter] x +# 216| 4: [BlockStmt] {...} +# 217| 0: [ExprStmt] ...; +# 217| 0: [AssignExpr] ... = ... +# 217| 0: [BoolLiteral] false +# 217| 1: [ParameterAccess] access to parameter x +# 218| 1: [ReturnStmt] return ...; +# 218| 0: [TupleExpr] (..., ...) +# 218| 0: [IntLiteral] 0 +# 218| 1: [DoubleLiteral] 0 +# 221| 6: [Method] Test +# 222| 4: [BlockStmt] {...} +# 223| 0: [ExprStmt] ...; +# 223| 0: [AssignExpr] ... = ... +# 223| 0: [MethodCall] call to method f +# 223| 0: [DiscardExpr] _ +# 223| 1: [DiscardExpr] _ +# 224| 1: [ExprStmt] ...; +# 224| 0: [AssignExpr] ... = ... +# 224| 0: [MethodCall] call to method f +# 224| 0: [DiscardExpr] _ +# 224| 1: [TupleExpr] (..., ...) +# 224| 0: [DiscardExpr] _ +# 224| 1: [DiscardExpr] _ +# 225| 2: [ExprStmt] ...; +# 225| 0: [AssignExpr] ... = ... +# 225| 0: [MethodCall] call to method f +# 225| 0: [DiscardExpr] _ +# 225| 1: [TupleExpr] (..., ...) +# 225| 0: [LocalVariableDeclExpr] Int32 x +# 225| 1: [DiscardExpr] _ +# 226| 3: [ExprStmt] ...; +# 226| 0: [AssignExpr] ... = ... +# 226| 0: [MethodCall] call to method f +# 226| 0: [LocalVariableAccess,LocalVariableDeclExpr] Boolean z +# 226| 1: [TupleExpr] (..., ...) +# 226| 0: [DiscardExpr] _ +# 226| 1: [LocalVariableDeclExpr] Double y +# 230| [Class] Patterns +# 232| 5: [Method] Test +# 233| 4: [BlockStmt] {...} +# 234| 0: [LocalVariableDeclStmt] ... ...; +# 234| 0: [LocalVariableDeclAndInitExpr] Object o = ... +# 234| 0: [NullLiteral] null +# 234| 1: [LocalVariableAccess] access to local variable o +# 235| 1: [IfStmt] if (...) ... +# 235| 0: [LogicalAndExpr] ... && ... +# 235| 0: [IsExpr] ... is ... +# 235| 0: [LocalVariableAccess] access to local variable o +# 235| 1: [VariablePatternExpr] Int32 i1 +# 235| 1: [GTExpr] ... > ... +# 235| 0: [LocalVariableAccess] access to local variable i1 +# 235| 1: [IntLiteral] 0 +# 236| 1: [BlockStmt] {...} +# 237| 0: [ExprStmt] ...; +# 237| 0: [MethodCall] call to method WriteLine +# 237| -1: [TypeAccess] access to type Console +# 237| 0: [InterpolatedStringExpr] $"..." +# 237| 0: [StringLiteral] "int " +# 237| 1: [LocalVariableAccess] access to local variable i1 +# 239| 2: [IfStmt] if (...) ... +# 239| 0: [IsExpr] ... is ... +# 239| 0: [LocalVariableAccess] access to local variable o +# 239| 1: [VariablePatternExpr] String s1 +# 240| 1: [BlockStmt] {...} +# 241| 0: [ExprStmt] ...; +# 241| 0: [MethodCall] call to method WriteLine +# 241| -1: [TypeAccess] access to type Console +# 241| 0: [InterpolatedStringExpr] $"..." +# 241| 0: [StringLiteral] "string " +# 241| 1: [LocalVariableAccess] access to local variable s1 +# 243| 2: [IfStmt] if (...) ... +# 243| 0: [IsExpr] ... is ... +# 243| 0: [LocalVariableAccess] access to local variable o +# 243| 1: [TypeAccessPatternExpr] access to type Double +# 244| 1: [BlockStmt] {...} +# 246| 2: [IfStmt] if (...) ... +# 246| 0: [IsExpr] ... is ... +# 246| 0: [LocalVariableAccess] access to local variable o +# 246| 1: [VariablePatternExpr] Object v1 +# 247| 1: [BlockStmt] {...} +# 250| 2: [SwitchStmt] switch (...) {...} +# 250| 0: [LocalVariableAccess] access to local variable o +# 252| 0: [ConstCase] case ...: +# 252| 0: [ConstantPatternExpr,StringLiteral] "xyz" +# 253| 1: [BreakStmt] break; +# 254| 2: [ConstCase] case ...: +# 254| 0: [ConstantPatternExpr,StringLiteral] "" +# 254| 1: [LTExpr] ... < ... +# 254| 0: [IntLiteral] 1 +# 254| 1: [IntLiteral] 2 +# 255| 3: [BreakStmt] break; +# 256| 4: [ConstCase] case ...: +# 256| 0: [ConstantPatternExpr,StringLiteral] "x" +# 256| 1: [IsExpr] ... is ... +# 256| 0: [LocalVariableAccess] access to local variable o +# 256| 1: [VariablePatternExpr] String s4 +# 257| 5: [ExprStmt] ...; +# 257| 0: [MethodCall] call to method WriteLine +# 257| -1: [TypeAccess] access to type Console +# 257| 0: [InterpolatedStringExpr] $"..." +# 257| 0: [StringLiteral] "x " +# 257| 1: [LocalVariableAccess] access to local variable s4 +# 258| 6: [BreakStmt] break; +# 259| 7: [CaseStmt] case ...: +# 259| 0: [VariablePatternExpr] Int32 i2 +# 259| 1: [GTExpr] ... > ... +# 259| 0: [LocalVariableAccess] access to local variable i2 +# 259| 1: [IntLiteral] 0 +# 260| 8: [ExprStmt] ...; +# 260| 0: [MethodCall] call to method WriteLine +# 260| -1: [TypeAccess] access to type Console +# 260| 0: [InterpolatedStringExpr] $"..." +# 260| 0: [StringLiteral] "positive " +# 260| 1: [LocalVariableAccess] access to local variable i2 +# 261| 9: [BreakStmt] break; +# 262| 10: [CaseStmt] case ...: +# 262| 0: [VariablePatternExpr] Int32 i3 +# 263| 11: [ExprStmt] ...; +# 263| 0: [MethodCall] call to method WriteLine +# 263| -1: [TypeAccess] access to type Console +# 263| 0: [InterpolatedStringExpr] $"..." +# 263| 0: [StringLiteral] "int " +# 263| 1: [LocalVariableAccess] access to local variable i3 +# 264| 12: [BreakStmt] break; +# 265| 13: [CaseStmt] case ...: +# 265| 0: [VariablePatternExpr] String s2 +# 266| 14: [ExprStmt] ...; +# 266| 0: [MethodCall] call to method WriteLine +# 266| -1: [TypeAccess] access to type Console +# 266| 0: [InterpolatedStringExpr] $"..." +# 266| 0: [StringLiteral] "string " +# 266| 1: [LocalVariableAccess] access to local variable s2 +# 267| 15: [BreakStmt] break; +# 268| 16: [CaseStmt] case ...: +# 268| 0: [TypeAccessPatternExpr] access to type Double +# 269| 17: [ExprStmt] ...; +# 269| 0: [MethodCall] call to method WriteLine +# 269| -1: [TypeAccess] access to type Console +# 269| 0: [StringLiteral] "Double" +# 270| 18: [BreakStmt] break; +# 271| 19: [CaseStmt] case ...: +# 271| 0: [VariablePatternExpr] Object v2 +# 272| 20: [BreakStmt] break; +# 273| 21: [DefaultCase] default: +# 274| 22: [ExprStmt] ...; +# 274| 0: [MethodCall] call to method WriteLine +# 274| -1: [TypeAccess] access to type Console +# 274| 0: [StringLiteral] "Something else" +# 275| 23: [BreakStmt] break; +# 280| [Class] ForeachStatements +# 282| 5: [Method] Test +# 283| 4: [BlockStmt] {...} +# 284| 0: [LocalVariableDeclStmt] ... ...; +# 284| 0: [LocalVariableDeclAndInitExpr] Dictionary dict = ... +# 284| 0: [ObjectCreation] object creation of type Dictionary +# 284| 1: [LocalVariableAccess] access to local variable dict +# 285| 1: [LocalVariableDeclStmt] ... ...; +# 285| 0: [LocalVariableDeclAndInitExpr] IEnumerable<(Int32,String)> list = ... +# 285| 0: [MethodCall] call to method Select +# 285| -1: [LocalVariableAccess] access to local variable dict +# 285| 0: [LambdaExpr] (...) => ... +#-----| 2: (Parameters) +# 285| 0: [Parameter] item +# 285| 4: [TupleExpr] (..., ...) +# 285| 0: [PropertyCall] access to property Key +# 285| -1: [ParameterAccess] access to parameter item +# 285| 1: [PropertyCall] access to property Value +# 285| -1: [ParameterAccess] access to parameter item +# 285| 1: [LocalVariableAccess] access to local variable list +# 287| 2: [ForeachStmt] foreach (... ... in ...) ... +# 287| 0: [TupleExpr] (..., ...) +# 287| 0: [LocalVariableDeclExpr] Int32 a +# 287| 1: [LocalVariableDeclExpr] String b +# 287| 1: [LocalVariableAccess] access to local variable list +# 287| 2: [BlockStmt] {...} +# 289| 3: [ForeachStmt] foreach (... ... in ...) ... +# 289| 0: [TupleExpr] (..., ...) +# 289| 0: [LocalVariableDeclExpr] Int32 a +# 289| 1: [LocalVariableDeclExpr] String b +# 289| 1: [LocalVariableAccess] access to local variable list +# 289| 2: [BlockStmt] {...} +# 291| 4: [ForeachStmt] foreach (... ... in ...) ... +# 291| 0: [TupleExpr] (..., ...) +# 291| 0: [LocalVariableDeclExpr] Int32 a +# 291| 1: [LocalVariableDeclExpr] String b +# 291| 1: [LocalVariableAccess] access to local variable list +# 291| 2: [BlockStmt] {...} +# 295| [Class] ForLoops +# 297| 5: [Method] Test +# 298| 4: [BlockStmt] {...} +# 299| 0: [ForStmt] for (...;...;...) ... +# 299| -1: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 299| 0: [IntLiteral] 0 +# 299| 1: [LocalVariableAccess] access to local variable x +# 299| 0: [LogicalAndExpr] ... && ... +# 299| 0: [LTExpr] ... < ... +# 299| 0: [LocalVariableAccess] access to local variable x +# 299| 1: [IntLiteral] 10 +# 299| 1: [IsExpr] ... is ... +# 299| 0: [LocalVariableAccess] access to local variable x +# 299| 1: [VariablePatternExpr] Int32 y +# 299| 1: [PreIncrExpr] ++... +# 299| 0: [LocalVariableAccess] access to local variable x +# 300| 2: [BlockStmt] {...} +# 301| 0: [ExprStmt] ...; +# 301| 0: [MethodCall] call to method WriteLine +# 301| -1: [TypeAccess] access to type Console +# 301| 0: [LocalVariableAccess] access to local variable y diff --git a/csharp/ql/test/library-tests/csharp7/PrintAst.qlref b/csharp/ql/test/library-tests/csharp7/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/csharp7/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/csharp7/RefDelegates.expected b/csharp/ql/test/library-tests/csharp7/RefDelegates.expected index b84804d88c36..b26c77efc50d 100644 --- a/csharp/ql/test/library-tests/csharp7/RefDelegates.expected +++ b/csharp/ql/test/library-tests/csharp7/RefDelegates.expected @@ -1 +1 @@ -| CSharp7.cs:209:22:209:26 | RefFn | +| CSharp7.cs:210:22:210:26 | RefFn | diff --git a/csharp/ql/test/library-tests/csharp7/RefExprs.expected b/csharp/ql/test/library-tests/csharp7/RefExprs.expected index f8882edbe6b6..bb1a22d1e19f 100644 --- a/csharp/ql/test/library-tests/csharp7/RefExprs.expected +++ b/csharp/ql/test/library-tests/csharp7/RefExprs.expected @@ -1,6 +1,6 @@ -| CSharp7.cs:192:22:192:27 | ref ... | CSharp7.cs:192:26:192:27 | access to local variable v1 | Int32 | -| CSharp7.cs:196:22:196:33 | ref ... | CSharp7.cs:196:26:196:33 | access to array element | Int32 | -| CSharp7.cs:197:22:197:27 | ref ... | CSharp7.cs:197:26:197:27 | access to local variable r1 | Int32 | -| CSharp7.cs:199:22:199:35 | ref ... | CSharp7.cs:199:26:199:35 | call to method F2 | Int32 | -| CSharp7.cs:205:40:205:44 | ref ... | CSharp7.cs:205:44:205:44 | access to parameter q | Int32 | -| CSharp7.cs:206:16:206:20 | ref ... | CSharp7.cs:206:20:206:20 | access to parameter p | Int32 | +| CSharp7.cs:193:22:193:27 | ref ... | CSharp7.cs:193:26:193:27 | access to local variable v1 | Int32 | +| CSharp7.cs:197:22:197:33 | ref ... | CSharp7.cs:197:26:197:33 | access to array element | Int32 | +| CSharp7.cs:198:22:198:27 | ref ... | CSharp7.cs:198:26:198:27 | access to local variable r1 | Int32 | +| CSharp7.cs:200:22:200:35 | ref ... | CSharp7.cs:200:26:200:35 | call to method F2 | Int32 | +| CSharp7.cs:206:40:206:44 | ref ... | CSharp7.cs:206:44:206:44 | access to parameter q | Int32 | +| CSharp7.cs:207:16:207:20 | ref ... | CSharp7.cs:207:20:207:20 | access to parameter p | Int32 | diff --git a/csharp/ql/test/library-tests/csharp7/RefFunctions.expected b/csharp/ql/test/library-tests/csharp7/RefFunctions.expected index a9740121ee4b..c2899f5443fd 100644 --- a/csharp/ql/test/library-tests/csharp7/RefFunctions.expected +++ b/csharp/ql/test/library-tests/csharp7/RefFunctions.expected @@ -1,2 +1,2 @@ -| CSharp7.cs:203:13:203:14 | F2 | -| CSharp7.cs:205:9:205:47 | F3 | +| CSharp7.cs:204:13:204:14 | F2 | +| CSharp7.cs:206:9:206:47 | F3 | diff --git a/csharp/ql/test/library-tests/csharp7/RefVariables.expected b/csharp/ql/test/library-tests/csharp7/RefVariables.expected index 5d065352dd2a..ed8f2d59b450 100644 --- a/csharp/ql/test/library-tests/csharp7/RefVariables.expected +++ b/csharp/ql/test/library-tests/csharp7/RefVariables.expected @@ -1,4 +1,4 @@ -| CSharp7.cs:192:17:192:18 | r1 | -| CSharp7.cs:196:17:196:18 | r2 | -| CSharp7.cs:197:17:197:18 | r3 | -| CSharp7.cs:199:17:199:18 | r4 | +| CSharp7.cs:193:17:193:18 | r1 | +| CSharp7.cs:197:17:197:18 | r2 | +| CSharp7.cs:198:17:198:18 | r3 | +| CSharp7.cs:200:17:200:18 | r4 | diff --git a/csharp/ql/test/library-tests/csharp7/TaintReaches.expected b/csharp/ql/test/library-tests/csharp7/TaintReaches.expected index d5d232488ad0..88c531506d9d 100644 --- a/csharp/ql/test/library-tests/csharp7/TaintReaches.expected +++ b/csharp/ql/test/library-tests/csharp7/TaintReaches.expected @@ -25,14 +25,14 @@ | CSharp7.cs:114:50:114:58 | "DefUse2" | CSharp7.cs:114:38:114:67 | ... = ... | | CSharp7.cs:114:50:114:58 | "DefUse2" | CSharp7.cs:114:49:114:67 | (..., ...) | | CSharp7.cs:123:28:123:36 | "DefUse3" | CSharp7.cs:123:22:123:36 | ... = ... | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:176:16:176:30 | SSA def(src) | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:181:23:181:25 | access to local variable src | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:182:23:182:25 | access to local variable src | -| CSharp7.cs:176:22:176:30 | "tainted" | CSharp7.cs:183:23:183:25 | access to local variable src | -| CSharp7.cs:177:38:177:39 | "" | CSharp7.cs:177:31:177:39 | ... + ... | -| CSharp7.cs:236:33:236:36 | "int " | CSharp7.cs:236:31:236:41 | $"..." | -| CSharp7.cs:240:33:240:39 | "string " | CSharp7.cs:240:31:240:44 | $"..." | -| CSharp7.cs:256:37:256:38 | "x " | CSharp7.cs:256:35:256:43 | $"..." | -| CSharp7.cs:259:37:259:45 | "positive " | CSharp7.cs:259:35:259:50 | $"..." | -| CSharp7.cs:262:37:262:40 | "int " | CSharp7.cs:262:35:262:45 | $"..." | -| CSharp7.cs:265:37:265:43 | "string " | CSharp7.cs:265:35:265:48 | $"..." | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:177:16:177:30 | SSA def(src) | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:182:23:182:25 | access to local variable src | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:183:23:183:25 | access to local variable src | +| CSharp7.cs:177:22:177:30 | "tainted" | CSharp7.cs:184:23:184:25 | access to local variable src | +| CSharp7.cs:178:38:178:39 | "" | CSharp7.cs:178:31:178:39 | ... + ... | +| CSharp7.cs:237:33:237:36 | "int " | CSharp7.cs:237:31:237:41 | $"..." | +| CSharp7.cs:241:33:241:39 | "string " | CSharp7.cs:241:31:241:44 | $"..." | +| CSharp7.cs:257:37:257:38 | "x " | CSharp7.cs:257:35:257:43 | $"..." | +| CSharp7.cs:260:37:260:45 | "positive " | CSharp7.cs:260:35:260:50 | $"..." | +| CSharp7.cs:263:37:263:40 | "int " | CSharp7.cs:263:35:263:45 | $"..." | +| CSharp7.cs:266:37:266:43 | "string " | CSharp7.cs:266:35:266:48 | $"..." | diff --git a/csharp/ql/test/library-tests/csharp7/TupleAccess.expected b/csharp/ql/test/library-tests/csharp7/TupleAccess.expected index 437ee46820d1..978a9b041c0d 100644 --- a/csharp/ql/test/library-tests/csharp7/TupleAccess.expected +++ b/csharp/ql/test/library-tests/csharp7/TupleAccess.expected @@ -1,4 +1,4 @@ -| CSharp7.cs:66:16:66:21 | (..., ...) | read | +| CSharp7.cs:66:16:66:27 | (..., ...) | read | | CSharp7.cs:71:9:71:22 | (..., ...) | write | | CSharp7.cs:73:9:73:14 | (..., ...) | write | | CSharp7.cs:75:9:75:23 | (..., ...) | write | @@ -35,11 +35,11 @@ | CSharp7.cs:114:38:114:45 | (..., ...) | write | | CSharp7.cs:114:49:114:67 | (..., ...) | read | | CSharp7.cs:114:61:114:66 | (..., ...) | read | -| CSharp7.cs:217:16:217:23 | (..., ...) | read | -| CSharp7.cs:223:9:223:14 | (..., ...) | write | -| CSharp7.cs:224:9:224:18 | (..., ...) | write | +| CSharp7.cs:218:16:218:23 | (..., ...) | read | +| CSharp7.cs:224:9:224:14 | (..., ...) | write | | CSharp7.cs:225:9:225:18 | (..., ...) | write | -| CSharp7.cs:284:40:284:61 | (..., ...) | read | -| CSharp7.cs:286:18:286:34 | (..., ...) | write | -| CSharp7.cs:288:18:288:31 | (..., ...) | write | -| CSharp7.cs:290:18:290:27 | (..., ...) | write | +| CSharp7.cs:226:9:226:18 | (..., ...) | write | +| CSharp7.cs:285:40:285:61 | (..., ...) | read | +| CSharp7.cs:287:18:287:34 | (..., ...) | write | +| CSharp7.cs:289:18:289:31 | (..., ...) | write | +| CSharp7.cs:291:18:291:27 | (..., ...) | write | diff --git a/csharp/ql/test/library-tests/csharp7/TupleExpr.expected b/csharp/ql/test/library-tests/csharp7/TupleExpr.expected index ec92d01d1eeb..7e3a52e86cde 100644 --- a/csharp/ql/test/library-tests/csharp7/TupleExpr.expected +++ b/csharp/ql/test/library-tests/csharp7/TupleExpr.expected @@ -1,5 +1,5 @@ -| CSharp7.cs:66:16:66:21 | (..., ...) | 0 | CSharp7.cs:66:17:66:17 | 1 | -| CSharp7.cs:66:16:66:21 | (..., ...) | 1 | CSharp7.cs:66:20:66:20 | 2 | +| CSharp7.cs:66:16:66:27 | (..., ...) | 0 | CSharp7.cs:66:20:66:20 | 1 | +| CSharp7.cs:66:16:66:27 | (..., ...) | 1 | CSharp7.cs:66:26:66:26 | 2 | | CSharp7.cs:71:9:71:22 | (..., ...) | 0 | CSharp7.cs:71:14:71:14 | Int32 x | | CSharp7.cs:71:9:71:22 | (..., ...) | 1 | CSharp7.cs:71:21:71:21 | Int32 y | | CSharp7.cs:73:9:73:14 | (..., ...) | 0 | CSharp7.cs:73:10:73:10 | access to local variable x | @@ -74,19 +74,19 @@ | CSharp7.cs:114:49:114:67 | (..., ...) | 1 | CSharp7.cs:114:61:114:66 | (..., ...) | | CSharp7.cs:114:61:114:66 | (..., ...) | 0 | CSharp7.cs:114:62:114:62 | 0 | | CSharp7.cs:114:61:114:66 | (..., ...) | 1 | CSharp7.cs:114:65:114:65 | 1 | -| CSharp7.cs:217:16:217:23 | (..., ...) | 0 | CSharp7.cs:217:17:217:17 | 0 | -| CSharp7.cs:217:16:217:23 | (..., ...) | 1 | CSharp7.cs:217:20:217:22 | 0 | -| CSharp7.cs:223:9:223:14 | (..., ...) | 0 | CSharp7.cs:223:10:223:10 | _ | -| CSharp7.cs:223:9:223:14 | (..., ...) | 1 | CSharp7.cs:223:13:223:13 | _ | -| CSharp7.cs:224:9:224:18 | (..., ...) | 0 | CSharp7.cs:224:14:224:14 | Int32 x | -| CSharp7.cs:224:9:224:18 | (..., ...) | 1 | CSharp7.cs:224:17:224:17 | _ | -| CSharp7.cs:225:9:225:18 | (..., ...) | 0 | CSharp7.cs:225:10:225:10 | _ | -| CSharp7.cs:225:9:225:18 | (..., ...) | 1 | CSharp7.cs:225:17:225:17 | Double y | -| CSharp7.cs:284:40:284:61 | (..., ...) | 0 | CSharp7.cs:284:41:284:48 | access to property Key | -| CSharp7.cs:284:40:284:61 | (..., ...) | 1 | CSharp7.cs:284:51:284:60 | access to property Value | -| CSharp7.cs:286:18:286:34 | (..., ...) | 0 | CSharp7.cs:286:23:286:23 | Int32 a | -| CSharp7.cs:286:18:286:34 | (..., ...) | 1 | CSharp7.cs:286:33:286:33 | String b | -| CSharp7.cs:288:18:288:31 | (..., ...) | 0 | CSharp7.cs:288:23:288:23 | Int32 a | -| CSharp7.cs:288:18:288:31 | (..., ...) | 1 | CSharp7.cs:288:30:288:30 | String b | -| CSharp7.cs:290:18:290:27 | (..., ...) | 0 | CSharp7.cs:290:23:290:23 | Int32 a | -| CSharp7.cs:290:18:290:27 | (..., ...) | 1 | CSharp7.cs:290:26:290:26 | String b | +| CSharp7.cs:218:16:218:23 | (..., ...) | 0 | CSharp7.cs:218:17:218:17 | 0 | +| CSharp7.cs:218:16:218:23 | (..., ...) | 1 | CSharp7.cs:218:20:218:22 | 0 | +| CSharp7.cs:224:9:224:14 | (..., ...) | 0 | CSharp7.cs:224:10:224:10 | _ | +| CSharp7.cs:224:9:224:14 | (..., ...) | 1 | CSharp7.cs:224:13:224:13 | _ | +| CSharp7.cs:225:9:225:18 | (..., ...) | 0 | CSharp7.cs:225:14:225:14 | Int32 x | +| CSharp7.cs:225:9:225:18 | (..., ...) | 1 | CSharp7.cs:225:17:225:17 | _ | +| CSharp7.cs:226:9:226:18 | (..., ...) | 0 | CSharp7.cs:226:10:226:10 | _ | +| CSharp7.cs:226:9:226:18 | (..., ...) | 1 | CSharp7.cs:226:17:226:17 | Double y | +| CSharp7.cs:285:40:285:61 | (..., ...) | 0 | CSharp7.cs:285:41:285:48 | access to property Key | +| CSharp7.cs:285:40:285:61 | (..., ...) | 1 | CSharp7.cs:285:51:285:60 | access to property Value | +| CSharp7.cs:287:18:287:34 | (..., ...) | 0 | CSharp7.cs:287:23:287:23 | Int32 a | +| CSharp7.cs:287:18:287:34 | (..., ...) | 1 | CSharp7.cs:287:33:287:33 | String b | +| CSharp7.cs:289:18:289:31 | (..., ...) | 0 | CSharp7.cs:289:23:289:23 | Int32 a | +| CSharp7.cs:289:18:289:31 | (..., ...) | 1 | CSharp7.cs:289:30:289:30 | String b | +| CSharp7.cs:291:18:291:27 | (..., ...) | 0 | CSharp7.cs:291:23:291:23 | Int32 a | +| CSharp7.cs:291:18:291:27 | (..., ...) | 1 | CSharp7.cs:291:26:291:26 | String b | diff --git a/csharp/ql/test/library-tests/csharp7/TupleTypes.expected b/csharp/ql/test/library-tests/csharp7/TupleTypes.expected index 7168e2b833f7..7a375de1fb8d 100644 --- a/csharp/ql/test/library-tests/csharp7/TupleTypes.expected +++ b/csharp/ql/test/library-tests/csharp7/TupleTypes.expected @@ -6,19 +6,19 @@ | (Int32,(Int32,Int32)) | (int, (int, int)) | ValueTuple | 2 | 1 | CSharp7.cs:78:27:78:32 | Item2 | | (Int32,(String,Int32)) | (int, (string, int)) | ValueTuple | 2 | 0 | CSharp7.cs:98:19:98:19 | Item1 | | (Int32,(String,Int32)) | (int, (string, int)) | ValueTuple | 2 | 1 | CSharp7.cs:98:22:98:42 | Item2 | -| (Int32,Double) | (int, double) | ValueTuple | 2 | 0 | CSharp7.cs:214:6:214:8 | Item1 | -| (Int32,Double) | (int, double) | ValueTuple | 2 | 0 | CSharp7.cs:224:10:224:14 | x | -| (Int32,Double) | (int, double) | ValueTuple | 2 | 0 | CSharp7.cs:225:10:225:10 | Item1 | -| (Int32,Double) | (int, double) | ValueTuple | 2 | 1 | CSharp7.cs:214:11:214:16 | Item2 | -| (Int32,Double) | (int, double) | ValueTuple | 2 | 1 | CSharp7.cs:224:17:224:17 | Item2 | -| (Int32,Double) | (int, double) | ValueTuple | 2 | 1 | CSharp7.cs:225:13:225:17 | y | -| (Int32,Int32) | (int, int) | ValueTuple | 2 | 0 | CSharp7.cs:64:6:64:8 | Item1 | +| (Int32,Double) | (int, double) | ValueTuple | 2 | 0 | CSharp7.cs:215:6:215:8 | Item1 | +| (Int32,Double) | (int, double) | ValueTuple | 2 | 0 | CSharp7.cs:225:10:225:14 | x | +| (Int32,Double) | (int, double) | ValueTuple | 2 | 0 | CSharp7.cs:226:10:226:10 | Item1 | +| (Int32,Double) | (int, double) | ValueTuple | 2 | 1 | CSharp7.cs:215:11:215:16 | Item2 | +| (Int32,Double) | (int, double) | ValueTuple | 2 | 1 | CSharp7.cs:225:17:225:17 | Item2 | +| (Int32,Double) | (int, double) | ValueTuple | 2 | 1 | CSharp7.cs:226:13:226:17 | y | +| (Int32,Int32) | (int, int) | ValueTuple | 2 | 0 | CSharp7.cs:64:10:64:10 | A | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 0 | CSharp7.cs:71:10:71:14 | x | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 0 | CSharp7.cs:77:18:77:22 | b | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 0 | CSharp7.cs:78:28:78:28 | c | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 0 | CSharp7.cs:112:15:112:16 | m4 | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 0 | CSharp7.cs:114:19:114:24 | m8 | -| (Int32,Int32) | (int, int) | ValueTuple | 2 | 1 | CSharp7.cs:64:11:64:13 | Item2 | +| (Int32,Int32) | (int, int) | ValueTuple | 2 | 1 | CSharp7.cs:64:17:64:17 | B | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 1 | CSharp7.cs:71:17:71:21 | y | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 1 | CSharp7.cs:77:25:77:29 | c | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 1 | CSharp7.cs:78:31:78:31 | a | @@ -27,10 +27,10 @@ | (Int32,Int32,Int32) | (int, int, int) | ValueTuple | 3 | 0 | CSharp7.cs:75:10:75:10 | x | | (Int32,Int32,Int32) | (int, int, int) | ValueTuple | 3 | 1 | CSharp7.cs:75:13:75:13 | y | | (Int32,Int32,Int32) | (int, int, int) | ValueTuple | 3 | 2 | CSharp7.cs:75:16:75:22 | Item3 | -| (Int32,String) | (int, string) | ValueTuple | 2 | 0 | CSharp7.cs:284:41:284:48 | Key | -| (Int32,String) | (int, string) | ValueTuple | 2 | 0 | CSharp7.cs:286:19:286:23 | a | -| (Int32,String) | (int, string) | ValueTuple | 2 | 1 | CSharp7.cs:284:51:284:60 | Value | -| (Int32,String) | (int, string) | ValueTuple | 2 | 1 | CSharp7.cs:286:26:286:33 | b | +| (Int32,String) | (int, string) | ValueTuple | 2 | 0 | CSharp7.cs:285:41:285:48 | Key | +| (Int32,String) | (int, string) | ValueTuple | 2 | 0 | CSharp7.cs:287:19:287:23 | a | +| (Int32,String) | (int, string) | ValueTuple | 2 | 1 | CSharp7.cs:285:51:285:60 | Value | +| (Int32,String) | (int, string) | ValueTuple | 2 | 1 | CSharp7.cs:287:26:287:33 | b | | (String,(Int32,Int32)) | (string, (int, int)) | ValueTuple | 2 | 0 | CSharp7.cs:109:10:109:15 | m1 | | (String,(Int32,Int32)) | (string, (int, int)) | ValueTuple | 2 | 0 | CSharp7.cs:112:10:112:11 | m3 | | (String,(Int32,Int32)) | (string, (int, int)) | ValueTuple | 2 | 0 | CSharp7.cs:114:10:114:15 | m7 | diff --git a/csharp/ql/test/library-tests/csharp7/TypeCase1.expected b/csharp/ql/test/library-tests/csharp7/TypeCase1.expected index 92c23564233f..f5feda6b24bf 100644 --- a/csharp/ql/test/library-tests/csharp7/TypeCase1.expected +++ b/csharp/ql/test/library-tests/csharp7/TypeCase1.expected @@ -1,9 +1,9 @@ -| CSharp7.cs:251:13:251:23 | case ...: | -| CSharp7.cs:253:13:253:31 | case ...: | -| CSharp7.cs:255:13:255:41 | case ...: | -| CSharp7.cs:258:13:258:36 | case ...: | -| CSharp7.cs:261:13:261:24 | case ...: | -| CSharp7.cs:264:13:264:27 | case ...: | -| CSharp7.cs:267:13:267:26 | case ...: | -| CSharp7.cs:270:13:270:24 | case ...: | -| CSharp7.cs:272:13:272:20 | default: | +| CSharp7.cs:252:13:252:23 | case ...: | +| CSharp7.cs:254:13:254:31 | case ...: | +| CSharp7.cs:256:13:256:41 | case ...: | +| CSharp7.cs:259:13:259:36 | case ...: | +| CSharp7.cs:262:13:262:24 | case ...: | +| CSharp7.cs:265:13:265:27 | case ...: | +| CSharp7.cs:268:13:268:26 | case ...: | +| CSharp7.cs:271:13:271:24 | case ...: | +| CSharp7.cs:273:13:273:20 | default: | diff --git a/csharp/ql/test/library-tests/csharp7/TypeCase2.expected b/csharp/ql/test/library-tests/csharp7/TypeCase2.expected index 3825dd93fe6e..bace5da4c04e 100644 --- a/csharp/ql/test/library-tests/csharp7/TypeCase2.expected +++ b/csharp/ql/test/library-tests/csharp7/TypeCase2.expected @@ -1,4 +1,4 @@ -| CSharp7.cs:258:13:258:36 | case ...: | CSharp7.cs:258:18:258:23 | Int32 i2 | Int32 | false | -| CSharp7.cs:261:13:261:24 | case ...: | CSharp7.cs:261:18:261:23 | Int32 i3 | Int32 | false | -| CSharp7.cs:264:13:264:27 | case ...: | CSharp7.cs:264:18:264:26 | String s2 | String | false | -| CSharp7.cs:270:13:270:24 | case ...: | CSharp7.cs:270:18:270:23 | Object v2 | Object | true | +| CSharp7.cs:259:13:259:36 | case ...: | CSharp7.cs:259:18:259:23 | Int32 i2 | Int32 | false | +| CSharp7.cs:262:13:262:24 | case ...: | CSharp7.cs:262:18:262:23 | Int32 i3 | Int32 | false | +| CSharp7.cs:265:13:265:27 | case ...: | CSharp7.cs:265:18:265:26 | String s2 | String | false | +| CSharp7.cs:271:13:271:24 | case ...: | CSharp7.cs:271:18:271:23 | Object v2 | Object | true | diff --git a/csharp/ql/test/library-tests/csharp7/TypeCase3.expected b/csharp/ql/test/library-tests/csharp7/TypeCase3.expected index 93eb7af459b0..abab42820f88 100644 --- a/csharp/ql/test/library-tests/csharp7/TypeCase3.expected +++ b/csharp/ql/test/library-tests/csharp7/TypeCase3.expected @@ -1 +1 @@ -| CSharp7.cs:258:13:258:36 | case ...: | CSharp7.cs:258:30:258:35 | ... > ... | +| CSharp7.cs:259:13:259:36 | case ...: | CSharp7.cs:259:30:259:35 | ... > ... | diff --git a/csharp/ql/test/library-tests/csharp7/UnboundLocalFunctions.expected b/csharp/ql/test/library-tests/csharp7/UnboundLocalFunctions.expected index 594f51bac569..8629d9d496c6 100644 --- a/csharp/ql/test/library-tests/csharp7/UnboundLocalFunctions.expected +++ b/csharp/ql/test/library-tests/csharp7/UnboundLocalFunctions.expected @@ -1,7 +1,7 @@ | CSharp7.cs:133:9:133:42 | f2 | 0 | CSharp7.cs:133:14:133:14 | T | | CSharp7.cs:133:9:133:42 | f2 | 1 | CSharp7.cs:133:17:133:17 | U | -| CSharp7.cs:160:9:160:24 | f | 0 | CSharp7.cs:160:15:160:15 | T | -| CSharp7.cs:161:9:161:25 | g | 0 | CSharp7.cs:161:13:161:13 | T | -| CSharp7.cs:163:9:168:9 | h | 0 | CSharp7.cs:163:13:163:13 | T | -| CSharp7.cs:163:9:168:9 | h | 1 | CSharp7.cs:163:16:163:16 | U | -| CSharp7.cs:165:13:165:43 | f2 | 0 | CSharp7.cs:165:20:165:20 | S | +| CSharp7.cs:161:9:161:24 | f | 0 | CSharp7.cs:161:15:161:15 | T | +| CSharp7.cs:162:9:162:25 | g | 0 | CSharp7.cs:162:13:162:13 | T | +| CSharp7.cs:164:9:169:9 | h | 0 | CSharp7.cs:164:13:164:13 | T | +| CSharp7.cs:164:9:169:9 | h | 1 | CSharp7.cs:164:16:164:16 | U | +| CSharp7.cs:166:13:166:43 | f2 | 0 | CSharp7.cs:166:20:166:20 | S | diff --git a/csharp/ql/test/library-tests/csharp8/NullableRefTypes.expected b/csharp/ql/test/library-tests/csharp8/NullableRefTypes.expected index db4f3c6fb5d8..891ba1602cb0 100644 --- a/csharp/ql/test/library-tests/csharp8/NullableRefTypes.expected +++ b/csharp/ql/test/library-tests/csharp8/NullableRefTypes.expected @@ -243,7 +243,7 @@ expressionTypes | NullableRefTypes.cs:19:33:19:36 | this access | MyClass! | | NullableRefTypes.cs:26:44:26:53 | throw ... | MyClass![]! | | NullableRefTypes.cs:26:50:26:53 | null | null | -| NullableRefTypes.cs:27:44:27:53 | throw ... | MyClass![]! | +| NullableRefTypes.cs:27:44:27:53 | throw ... | MyClass?[]! | | NullableRefTypes.cs:27:50:27:53 | null | null | | NullableRefTypes.cs:30:21:30:24 | null | null | | NullableRefTypes.cs:31:20:31:23 | this access | MyClass! | diff --git a/csharp/ql/test/library-tests/csharp8/PrintAst.expected b/csharp/ql/test/library-tests/csharp8/PrintAst.expected new file mode 100644 index 000000000000..0a9d89972e10 --- /dev/null +++ b/csharp/ql/test/library-tests/csharp8/PrintAst.expected @@ -0,0 +1,1021 @@ +AlternateInterpolatedStrings.cs: +# 3| [Class] AlternateInterpolatedStrings +# 5| 5: [Field] s1 +# 5| 1: [AssignExpr] ... = ... +# 5| 0: [InterpolatedStringExpr] $"..." +# 5| 0: [StringLiteral] "C:" +# 5| 1: [IntLiteral] 12 +# 5| 1: [FieldAccess] access to field s1 +# 6| 6: [Field] s2 +# 6| 1: [AssignExpr] ... = ... +# 6| 0: [InterpolatedStringExpr] $"..." +# 6| 0: [StringLiteral] "C:" +# 6| 1: [IntLiteral] 12 +# 6| 1: [FieldAccess] access to field s2 +AsyncStreams.cs: +# 8| [Class] AsyncStreams +# 10| 5: [Method] Items +# 10| 4: [BlockStmt] {...} +# 11| 0: [YieldReturnStmt] yield return ...; +# 11| 0: [IntLiteral] 1 +# 12| 1: [YieldReturnStmt] yield return ...; +# 12| 0: [IntLiteral] 2 +# 13| 2: [ExprStmt] ...; +# 13| 0: [AwaitExpr] await ... +# 13| 0: [MethodCall] call to method Delay +# 13| -1: [TypeAccess] access to type Task +# 13| 0: [IntLiteral] 1000 +# 14| 3: [YieldReturnStmt] yield return ...; +# 14| 0: [IntLiteral] 3 +# 17| 6: [Method] F +# 18| 4: [BlockStmt] {...} +# 19| 0: [ForeachStmt] foreach (... ... in ...) ... +# 19| 0: [LocalVariableDeclExpr] Int32 item +# 19| 1: [MethodCall] call to method Items +# 20| 2: [ExprStmt] ...; +# 20| 0: [MethodCall] call to method WriteLine +# 20| -1: [TypeAccess] access to type Console +# 20| 0: [LocalVariableAccess] access to local variable item +# 24| [NamespaceDeclaration] namespace ... { ... } +# 26| 1: [Interface] IAsyncDisposable +# 28| 4: [Method] DisposeAsync +# 32| [NamespaceDeclaration] namespace ... { ... } +# 34| 1: [Interface] IAsyncEnumerable<> +#-----| 1: (Type parameters) +# 34| 0: [TypeParameter] T +# 36| 4: [Method] GetAsyncEnumerator +#-----| 2: (Parameters) +# 36| 0: [Parameter] cancellationToken +# 36| 1: [DefaultValueExpr] default(...) +# 36| 0: [TypeAccess] access to type CancellationToken +# 39| 2: [Interface] IAsyncEnumerator<> +#-----| 1: (Type parameters) +# 39| 0: [TypeParameter] T +#-----| 3: (Base types) +# 39| 1: [Interface] IAsyncDisposable +# 41| 4: [Property] Current +# 41| 3: [Getter] get_Current +# 42| 5: [Method] MoveNextAsync +DefaultInterfaceMethods.cs: +# 3| [Interface] IPerson +# 5| 4: [IndexerProperty] Name +# 5| 3: [Getter] get_Name +# 7| 5: [IndexerProperty] Greeting +# 9| 3: [Getter] get_Greeting +# 9| 4: [StringLiteral] "Hello" +# 10| 4: [Setter] set_Greeting +#-----| 2: (Parameters) +# 10| 0: [Parameter] value +# 10| 4: [BlockStmt] {...} +# 13| 6: [Method] Greet +#-----| 2: (Parameters) +# 13| 0: [Parameter] name +# 13| 4: [AddExpr] ... + ... +# 13| 0: [AddExpr] ... + ... +# 13| 0: [PropertyCall] access to property Greeting +# 13| 1: [StringLiteral] " " +# 13| 1: [ParameterAccess] access to parameter name +# 15| 7: [IndexerProperty] GreetingString +# 15| 3: [Getter] get_GreetingString +# 15| 4: [MethodCall] call to method Greet +# 15| 0: [PropertyCall] access to property Name +# 17| 8: [Method] Greet +# 20| [Class] Person +#-----| 3: (Base types) +# 20| 1: [Interface] IPerson +# 22| 5: [IndexerProperty] Name +# 22| 3: [Getter] get_Name +# 22| 4: [StringLiteral] "Petra" +# 24| 6: [IndexerProperty] Greeting +# 24| 3: [Getter] get_Greeting +# 24| 4: [StringLiteral] "Howdy" +# 24| 4: [Setter] set_Greeting +#-----| 2: (Parameters) +# 24| 0: [Parameter] value +# 24| 4: [BlockStmt] {...} +# 26| 7: [Method] Greet +# 26| 4: [BlockStmt] {...} +NullCoalescingAssignment.cs: +# 3| [Class] NullCoalescingAssignment +# 5| 5: [Method] NullCoalescing +# 6| 4: [BlockStmt] {...} +# 7| 0: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclAndInitExpr] Object o = ... +# 7| 0: [NullLiteral] null +# 7| 1: [LocalVariableAccess] access to local variable o +# 8| 1: [ExprStmt] ...; +# 8| 0: [AssignCoalesceExpr] ... ??= ... +# 8| 0: [ThisAccess] this access +# 8| 1: [LocalVariableAccess] access to local variable o +NullableRefTypes.cs: +# 6| [Class] MyClass +# 9| 5: [Field] A +# 10| 6: [Field] B +# 13| 7: [IndexerProperty] C +# 13| 3: [Getter] get_C +# 13| 4: [NullLiteral] null +# 14| 8: [IndexerProperty] D +# 14| 3: [Getter] get_D +# 14| 4: [ThisAccess] this access +# 17| 9: [Indexer] Item +#-----| 1: (Parameters) +# 17| 0: [Parameter] i +# 17| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 17| 0: [Parameter] i +# 17| 4: [NullLiteral] null +# 18| 10: [Indexer] Item +#-----| 1: (Parameters) +# 18| 0: [Parameter] i +# 18| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 18| 0: [Parameter] i +# 18| 4: [ThisAccess] this access +# 19| 11: [Indexer] Item +#-----| 1: (Parameters) +# 19| 0: [Parameter] i +# 19| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 19| 0: [Parameter] i +# 19| 4: [ThisAccess] this access +# 22| 12: [Field] G1 +# 23| 13: [Field] G2 +# 24| 14: [Field] G3 +# 25| 15: [Field] H +# 26| 16: [Method] ArrayFn1 +#-----| 2: (Parameters) +# 26| 0: [Parameter] x +# 26| 4: [ThrowExpr] throw ... +# 26| 0: [NullLiteral] null +# 27| 17: [Method] ArrayFn2 +#-----| 2: (Parameters) +# 27| 0: [Parameter] x +# 27| 4: [ThrowExpr] throw ... +# 27| 0: [NullLiteral] null +# 30| 18: [Method] M +# 30| 4: [NullLiteral] null +# 31| 19: [Method] N +# 31| 4: [ThisAccess] this access +# 32| 20: [Method] O +#-----| 2: (Parameters) +# 32| 0: [Parameter] a +# 32| 1: [Parameter] b +# 32| 4: [BlockStmt] {...} +# 35| 21: [Method] Locals +# 36| 4: [BlockStmt] {...} +# 37| 0: [LocalVariableDeclStmt] ... ...; +# 37| 0: [LocalVariableDeclAndInitExpr] MyClass a = ... +# 37| 0: [ObjectCreation] object creation of type MyClass +# 37| 1: [LocalVariableAccess] access to local variable a +# 38| 1: [LocalVariableDeclStmt] ... ...; +# 38| 0: [LocalVariableDeclAndInitExpr] MyClass b = ... +# 38| 0: [NullLiteral] null +# 38| 1: [LocalVariableAccess] access to local variable b +# 39| 2: [LocalVariableDeclStmt] ... ...; +# 39| 0: [LocalVariableDeclAndInitExpr] MyClass c = ... +# 39| 0: [RefExpr] ref ... +# 39| 0: [LocalVariableAccess] access to local variable b +# 39| 1: [LocalVariableAccess] access to local variable c +# 40| 3: [LocalVariableDeclStmt] ... ...; +# 40| 0: [LocalVariableDeclAndInitExpr] MyClass d = ... +# 40| 0: [RefExpr] ref ... +# 40| 0: [LocalVariableAccess] access to local variable b +# 40| 1: [LocalVariableAccess] access to local variable d +# 44| 22: [DelegateType] Del1 +# 47| 23: [DelegateType] Del +#-----| 2: (Parameters) +# 47| 0: [Parameter] x +# 48| 24: [Event] P +# 48| 3: [AddEventAccessor] add_P +#-----| 2: (Parameters) +# 48| 0: [Parameter] value +# 48| 4: [RemoveEventAccessor] remove_P +#-----| 2: (Parameters) +# 48| 0: [Parameter] value +# 51| 25: [Method] Q +#-----| 1: (Type parameters) +# 51| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 51| 0: [Parameter] t +# 51| 4: [NullLiteral] null +# 54| 27: [Class] Generic<,,,> +#-----| 1: (Type parameters) +# 54| 0: [TypeParameter] T1 +# 54| 1: [TypeParameter] T2 +# 54| 2: [TypeParameter] T3 +# 54| 3: [TypeParameter] T4 +# 58| 30: [Class] Generic2<,> +#-----| 1: (Type parameters) +# 58| 0: [TypeParameter] T1 +# 58| 1: [TypeParameter] T2 +# 65| 31: [Field] items2 +# 67| 32: [Method] GenericFn +#-----| 1: (Type parameters) +# 67| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 67| 0: [Parameter] x +# 68| 4: [BlockStmt] {...} +# 71| 34: [Method] CallF +# 72| 4: [BlockStmt] {...} +# 73| 0: [LocalVariableDeclStmt] ... ...; +# 73| 0: [LocalVariableDeclAndInitExpr] MyClass x = ... +# 73| 0: [NullLiteral] null +# 73| 1: [LocalVariableAccess] access to local variable x +# 74| 1: [ExprStmt] ...; +# 74| 0: [MethodCall] call to method GenericFn +# 74| 0: [LocalVariableAccess] access to local variable x +# 75| 2: [ExprStmt] ...; +# 75| 0: [MethodCall] call to method Q +# 75| 0: [LocalVariableAccess] access to local variable x +# 76| 3: [ReturnStmt] return ...; +# 76| 0: [DefaultValueExpr] default(...) +# 76| 0: [TypeAccess] access to type MyStruct +# 80| [Class] NullableRefTypes +# 82| 5: [Method] TestSuppressNullableWarningExpr +# 83| 4: [BlockStmt] {...} +# 84| 0: [LocalVariableDeclStmt] ... ...; +# 84| 0: [LocalVariableDeclAndInitExpr] String x = ... +# 84| 0: [StringLiteral] "source" +# 84| 1: [LocalVariableAccess] access to local variable x +# 85| 1: [LocalVariableDeclStmt] ... ...; +# 85| 0: [LocalVariableDeclAndInitExpr] String y = ... +# 85| 0: [SuppressNullableWarningExpr] ...! +# 85| 0: [LocalVariableAccess] access to local variable x +# 85| 1: [LocalVariableAccess] access to local variable y +# 86| 2: [ExprStmt] ...; +# 86| 0: [AssignExpr] ... = ... +# 86| 0: [SuppressNullableWarningExpr] ...! +# 86| 0: [LocalVariableAccess] access to local variable x +# 86| 1: [LocalVariableAccess] access to local variable y +# 87| 3: [ExprStmt] ...; +# 87| 0: [AssignExpr] ... = ... +# 87| 0: [NullLiteral] null +# 87| 1: [LocalVariableAccess] access to local variable x +# 88| 4: [ExprStmt] ...; +# 88| 0: [AssignExpr] ... = ... +# 88| 0: [SuppressNullableWarningExpr] ...! +# 88| 0: [LocalVariableAccess] access to local variable x +# 88| 1: [LocalVariableAccess] access to local variable y +# 91| 6: [Method] FunctionInNullableContext +# 92| 4: [BlockStmt] {...} +# 93| 0: [LocalVariableDeclStmt] ... ...; +# 93| 0: [LocalVariableDeclAndInitExpr] String x = ... +# 93| 0: [StringLiteral] "source" +# 93| 1: [LocalVariableAccess] access to local variable x +# 94| 1: [LocalVariableDeclStmt] ... ...; +# 94| 0: [LocalVariableDeclAndInitExpr] String y = ... +# 94| 0: [NullCoalescingExpr] ... ?? ... +# 94| 0: [LocalVariableAccess] access to local variable x +# 94| 1: [NullLiteral] null +# 94| 1: [LocalVariableAccess] access to local variable y +# 95| 2: [LocalVariableDeclStmt] ... ...; +# 95| 0: [LocalVariableDeclAndInitExpr] String z = ... +# 95| 0: [LocalVariableAccess] access to local variable x +# 95| 1: [LocalVariableAccess] access to local variable z +# 96| 3: [ExprStmt] ...; +# 96| 0: [MethodCall] call to method WriteLine +# 96| -1: [TypeAccess] access to type Console +# 96| 0: [LocalVariableAccess] access to local variable x +# 100| [Class] RefTypes +# 103| 5: [Method] ReturnsRef1 +#-----| 2: (Parameters) +# 103| 0: [Parameter] r +# 103| 4: [RefExpr] ref ... +# 103| 0: [ParameterAccess] access to parameter r +# 104| 6: [Method] ReturnsRef2 +#-----| 2: (Parameters) +# 104| 0: [Parameter] r +# 104| 4: [RefExpr] ref ... +# 104| 0: [ParameterAccess] access to parameter r +# 105| 7: [Method] ReturnsRef3 +#-----| 2: (Parameters) +# 105| 0: [Parameter] r +# 105| 4: [RefExpr] ref ... +# 105| 0: [ParameterAccess] access to parameter r +# 106| 8: [Method] ReturnsRef4 +#-----| 2: (Parameters) +# 106| 0: [Parameter] r +# 106| 4: [RefExpr] ref ... +# 106| 0: [ParameterAccess] access to parameter r +# 107| 9: [Method] ReturnsRef5 +#-----| 2: (Parameters) +# 107| 0: [Parameter] r +# 107| 4: [RefExpr] ref ... +# 107| 0: [ParameterAccess] access to parameter r +# 108| 10: [Method] ReturnsRef6 +#-----| 2: (Parameters) +# 108| 0: [Parameter] r +# 108| 4: [RefExpr] ref ... +# 108| 0: [ParameterAccess] access to parameter r +# 110| 11: [Method] Parameters1 +#-----| 2: (Parameters) +# 110| 0: [Parameter] p1 +# 110| 1: [Parameter] p2 +# 110| 4: [ThrowExpr] throw ... +# 110| 0: [NullLiteral] null +# 112| 12: [Field] Property +# 113| 13: [IndexerProperty] RefProperty +# 113| 3: [Getter] get_RefProperty +# 113| 4: [RefExpr] ref ... +# 113| 0: [SuppressNullableWarningExpr] ...! +# 113| 0: [FieldAccess] access to field Property +# 116| [Class] ToStringWithTypes +# 118| 5: [Field] a +# 119| 6: [Field] b +# 120| 7: [Field] c +# 121| 8: [Field] d +# 123| 9: [Field] e +# 124| 10: [Field] f +# 125| 11: [Field] g +# 126| 12: [Field] h +# 128| 13: [Field] i +# 129| 14: [Field] j +# 130| 15: [Field] k +# 131| 16: [Field] l +# 136| [Class] ToStringWithTypes2 +# 138| 5: [Field] a +# 139| 6: [Field] b +# 140| 7: [Field] c +# 141| 8: [Field] d +# 143| 9: [Field] e +# 144| 10: [Field] f +# 145| 11: [Field] g +# 146| 12: [Field] h +# 148| 13: [Field] i +# 149| 14: [Field] j +# 150| 15: [Field] k +# 151| 16: [Field] l +# 154| [Class] DisabledNullability +# 156| 5: [Field] f1 +# 157| 6: [IndexerProperty] P +# 157| 3: [Getter] get_P +# 157| 4: [ObjectCreation] object creation of type MyClass +# 158| 7: [Method] Fn +#-----| 2: (Parameters) +# 158| 0: [Parameter] p +# 159| 4: [BlockStmt] {...} +# 160| 0: [LocalVariableDeclStmt] ... ...; +# 160| 0: [LocalVariableDeclAndInitExpr] MyClass a = ... +# 160| 0: [ParameterAccess] access to parameter p +# 160| 1: [LocalVariableAccess] access to local variable a +# 161| 1: [ReturnStmt] return ...; +# 161| 0: [LocalVariableAccess] access to local variable a +# 165| [Struct] MyStruct +# 171| [Class] TestNullableFlowStates +# 173| 5: [Method] MaybeNull +# 175| 6: [Method] Check +#-----| 2: (Parameters) +# 175| 0: [Parameter] isNull +# 177| 7: [Method] Count +# 179| 8: [Method] LoopUnrolling +# 180| 4: [BlockStmt] {...} +# 181| 0: [LocalVariableDeclStmt] ... ...; +# 181| 0: [LocalVariableDeclAndInitExpr] String x = ... +# 181| 0: [MethodCall] call to method MaybeNull +# 181| 1: [LocalVariableAccess] access to local variable x +# 183| 1: [ExprStmt] ...; +# 183| 0: [MethodCall] call to method Check +# 183| 0: [LocalVariableAccess] access to local variable x +# 185| 2: [ForStmt] for (...;...;...) ... +# 185| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 185| 0: [IntLiteral] 0 +# 185| 1: [LocalVariableAccess] access to local variable i +# 185| 0: [LTExpr] ... < ... +# 185| 0: [LocalVariableAccess] access to local variable i +# 185| 1: [IntLiteral] 10 +# 185| 1: [PreIncrExpr] ++... +# 185| 0: [LocalVariableAccess] access to local variable i +# 186| 2: [BlockStmt] {...} +# 187| 0: [ExprStmt] ...; +# 187| 0: [AssignExpr] ... = ... +# 187| 0: [StringLiteral] "not null any more" +# 187| 1: [LocalVariableAccess] access to local variable x +# 190| 3: [ExprStmt] ...; +# 190| 0: [MethodCall] call to method Check +# 190| 0: [LocalVariableAccess] access to local variable x +# 193| 9: [Method] ExceptionFlow +# 194| 4: [BlockStmt] {...} +# 195| 0: [LocalVariableDeclStmt] ... ...; +# 195| 0: [LocalVariableDeclAndInitExpr] String y = ... +# 195| 0: [MethodCall] call to method MaybeNull +# 195| 1: [LocalVariableAccess] access to local variable y +# 197| 1: [TryStmt] try {...} ... +# 202| -1: [BlockStmt] {...} +# 203| 0: [ExprStmt] ...; +# 203| 0: [AssignExpr] ... = ... +# 203| 0: [StringLiteral] "not null" +# 203| 1: [LocalVariableAccess] access to local variable y +# 198| 0: [BlockStmt] {...} +# 199| 0: [ThrowStmt] throw ...; +# 199| 0: [ObjectCreation] object creation of type ArgumentException +# 206| 2: [ExprStmt] ...; +# 206| 0: [MethodCall] call to method Check +# 206| 0: [LocalVariableAccess] access to local variable y +# 209| 10: [Method] InvocationTest +#-----| 2: (Parameters) +# 209| 0: [Parameter] o +# 210| 4: [BlockStmt] {...} +# 211| 0: [LocalVariableDeclStmt] ... ...; +# 211| 0: [LocalVariableDeclAndInitExpr] Type t = ... +# 211| 0: [MethodCall] call to method GetType +# 211| -1: [ParameterAccess] access to parameter o +# 211| 1: [LocalVariableAccess] access to local variable t +# 212| 1: [ReturnStmt] return ...; +# 212| 0: [MethodCall] call to method ToString +# 212| -1: [LocalVariableAccess] access to local variable t +# 215| 11: [Method] ElementTest +#-----| 2: (Parameters) +# 215| 0: [Parameter] list +# 216| 4: [BlockStmt] {...} +# 217| 0: [LocalVariableDeclStmt] ... ...; +# 217| 0: [LocalVariableDeclAndInitExpr] String a = ... +# 217| 0: [FieldAccess] access to field Field +# 217| -1: [MethodCall] call to method GetSelf +# 217| 1: [LocalVariableAccess] access to local variable a +# 218| 1: [LocalVariableDeclStmt] ... ...; +# 218| 0: [LocalVariableDeclAndInitExpr] String b = ... +# 218| 0: [IndexerCall] access to indexer +# 218| -1: [ParameterAccess] access to parameter list +# 218| 0: [IntLiteral] 0 +# 218| 1: [LocalVariableAccess] access to local variable b +# 219| 2: [LocalVariableDeclStmt] ... ...; +# 219| 0: [LocalVariableDeclAndInitExpr] String c = ... +# 219| 0: [IndexerCall] access to indexer +# 219| -1: [ParameterAccess] access to parameter list +# 219| 0: [IntLiteral] 0 +# 219| 1: [LocalVariableAccess] access to local variable c +# 220| 3: [LocalVariableDeclStmt] ... ...; +# 220| 0: [LocalVariableDeclAndInitExpr] String d = ... +# 220| 0: [FieldAccess] access to field Field +# 220| -1: [MethodCall] call to method GetSelf +# 220| 1: [LocalVariableAccess] access to local variable d +# 223| 12: [Method] GetSelf +# 225| 13: [Field] Field +StaticLocalFunctions.cs: +# 5| [Class] StaticLocalFunctions +# 7| 5: [Method] Fn +#-----| 2: (Parameters) +# 7| 0: [Parameter] x +# 8| 4: [BlockStmt] {...} +# 9| 0: [LocalFunctionStmt] I(...) +# 9| 0: [LocalFunction] I +#-----| 2: (Parameters) +# 9| 0: [Parameter] y +# 9| 4: [ParameterAccess] access to parameter y +# 10| 1: [LocalFunctionStmt] J(...) +# 10| 0: [LocalFunction] J +#-----| 2: (Parameters) +# 10| 0: [Parameter] y +# 10| 4: [AddExpr] ... + ... +# 10| 0: [ParameterAccess] access to parameter x +# 10| 1: [ParameterAccess] access to parameter y +# 11| 2: [ReturnStmt] return ...; +# 11| 0: [AddExpr] ... + ... +# 11| 0: [LocalFunctionCall] call to local function I +# 11| -1: [LocalFunctionAccess] access to local function I +# 11| 0: [ParameterAccess] access to parameter x +# 11| 1: [LocalFunctionCall] call to local function J +# 11| -1: [LocalFunctionAccess] access to local function J +# 11| 0: [ParameterAccess] access to parameter x +UnmanagedGenericStructs.cs: +# 3| [Struct] S<,> +#-----| 1: (Type parameters) +# 3| 0: [TypeParameter] T +# 3| 1: [TypeParameter] U +# 5| 5: [Field] id +# 6| 6: [Field] value1 +# 7| 7: [Field] value2 +UsingDeclarations.cs: +# 4| [Class] UsingDeclarations +# 6| 5: [Method] TestUsingDeclarations +# 7| 4: [BlockStmt] {...} +# 8| 0: [UsingDeclStmt] using ... ...; +# 8| 0: [LocalVariableDeclAndInitExpr] FileStream file1 = ... +# 8| 0: [ObjectCreation] object creation of type FileStream +# 8| 0: [StringLiteral] "..." +# 8| 1: [MemberConstantAccess] access to constant Open +# 8| -1: [TypeAccess] access to type FileMode +# 8| 1: [LocalVariableAccess] access to local variable file1 +# 8| 1: [LocalVariableDeclAndInitExpr] FileStream file2 = ... +# 8| 0: [ObjectCreation] object creation of type FileStream +# 8| 0: [StringLiteral] "..." +# 8| 1: [MemberConstantAccess] access to constant Open +# 8| -1: [TypeAccess] access to type FileMode +# 8| 1: [LocalVariableAccess] access to local variable file2 +# 10| 1: [UsingBlockStmt] using (...) {...} +# 10| -2: [LocalVariableDeclAndInitExpr] FileStream file4 = ... +# 10| 0: [ObjectCreation] object creation of type FileStream +# 10| 0: [StringLiteral] "..." +# 10| 1: [MemberConstantAccess] access to constant Open +# 10| -1: [TypeAccess] access to type FileMode +# 10| 1: [LocalVariableAccess] access to local variable file4 +# 10| -1: [LocalVariableDeclAndInitExpr] FileStream file3 = ... +# 10| 0: [ObjectCreation] object creation of type FileStream +# 10| 0: [StringLiteral] "..." +# 10| 1: [MemberConstantAccess] access to constant Open +# 10| -1: [TypeAccess] access to type FileMode +# 10| 1: [LocalVariableAccess] access to local variable file3 +# 11| 1: [BlockStmt] {...} +# 14| 2: [UsingBlockStmt] using (...) {...} +# 14| 0: [ObjectCreation] object creation of type FileStream +# 14| 0: [StringLiteral] "..." +# 14| 1: [MemberConstantAccess] access to constant Open +# 14| -1: [TypeAccess] access to type FileMode +# 15| 1: [EmptyStmt] ; +patterns.cs: +# 3| [Class] Patterns +# 5| 5: [Method] IsPatterns +# 6| 4: [BlockStmt] {...} +# 7| 0: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclAndInitExpr] Object o = ... +# 7| 0: [CastExpr] (...) ... +# 7| 0: [ObjectCreation] object creation of type MyStruct +# 7| -1: [ObjectInitializer] { ..., ... } +# 7| 0: [MemberInitializer] ... = ... +# 7| 0: [IntLiteral] 2 +# 7| 1: [FieldAccess] access to field X +# 7| 1: [LocalVariableAccess] access to local variable o +# 9| 1: [IfStmt] if (...) ... +# 9| 0: [IsExpr] ... is ... +# 9| 0: [LocalVariableAccess] access to local variable o +# 9| 1: [VariablePatternExpr] MyStruct ms1 +# 10| 1: [BlockStmt] {...} +# 13| 2: [IfStmt] if (...) ... +# 13| 0: [LogicalAndExpr] ... && ... +# 13| 0: [LogicalAndExpr] ... && ... +# 13| 0: [IsExpr] ... is ... +# 13| 0: [LocalVariableAccess] access to local variable o +# 13| 1: [RecursivePatternExpr] { ... } +# 13| 0: [LocalVariableDeclExpr] MyStruct s +# 13| 1: [TypeAccess] access to type MyStruct +# 13| 3: [PropertyPatternExpr] { ... } +# 13| 0: [LabeledPatternExpr,VariablePatternExpr] Int32 x +# 13| 1: [LTExpr] ... < ... +# 13| 0: [LocalVariableAccess] access to local variable x +# 13| 1: [IntLiteral] 4 +# 13| 1: [LTExpr] ... < ... +# 13| 0: [PropertyCall] access to property Y +# 13| -1: [LocalVariableAccess] access to local variable s +# 13| 1: [IntLiteral] 2 +# 14| 1: [BlockStmt] {...} +# 17| 3: [IfStmt] if (...) ... +# 17| 0: [IsExpr] ... is ... +# 17| 0: [LocalVariableAccess] access to local variable o +# 17| 1: [RecursivePatternExpr] { ... } +# 17| 0: [LocalVariableDeclExpr] Object p +# 17| 3: [PropertyPatternExpr] { ... } +# 18| 1: [BlockStmt] {...} +# 22| 4: [IfStmt] if (...) ... +# 22| 0: [IsExpr] ... is ... +# 22| 0: [LocalVariableAccess] access to local variable o +# 22| 1: [RecursivePatternExpr] { ... } +# 22| 1: [TypeAccess] access to type MyStruct +# 22| 3: [PropertyPatternExpr] { ... } +# 22| 0: [ConstantPatternExpr,IntLiteral,LabeledPatternExpr] 12 +# 22| 1: [LabeledPatternExpr,RecursivePatternExpr] { ... } +# 22| 3: [PropertyPatternExpr] { ... } +# 22| 0: [LabeledPatternExpr,VariablePatternExpr] Int32 subX +# 23| 1: [BlockStmt] {...} +# 27| 5: [IfStmt] if (...) ... +# 27| 0: [IsExpr] ... is ... +# 27| 0: [LocalVariableAccess] access to local variable o +# 27| 1: [RecursivePatternExpr] { ... } +# 27| 1: [TypeAccess] access to type MyStruct +# 27| 3: [PropertyPatternExpr] { ... } +# 27| 0: [ConstantPatternExpr,IntLiteral,LabeledPatternExpr] 12 +# 27| 1: [LabeledPatternExpr,RecursivePatternExpr] { ... } +# 27| 0: [LocalVariableDeclExpr] MyStruct ms +# 27| 1: [TypeAccess] access to type MyStruct +# 27| 3: [PropertyPatternExpr] { ... } +# 27| 0: [DiscardPatternExpr,LabeledPatternExpr] _ +# 28| 1: [BlockStmt] {...} +# 32| 6: [Method] SwitchStatements +# 33| 4: [BlockStmt] {...} +# 34| 0: [LocalVariableDeclStmt] ... ...; +# 34| 0: [LocalVariableDeclAndInitExpr] MyStruct s = ... +# 34| 0: [ObjectCreation] object creation of type MyStruct +# 34| -1: [ObjectInitializer] { ..., ... } +# 34| 0: [MemberInitializer] ... = ... +# 34| 0: [IntLiteral] 0 +# 34| 1: [FieldAccess] access to field X +# 34| 1: [LocalVariableAccess] access to local variable s +# 36| 1: [SwitchStmt] switch (...) {...} +# 36| 0: [LocalVariableAccess] access to local variable s +# 38| 0: [CaseStmt] case ...: +# 38| 0: [VariablePatternExpr] MyStruct ms1 +# 38| 1: [EQExpr] ... == ... +# 38| 0: [FieldAccess] access to field X +# 38| -1: [LocalVariableAccess] access to local variable ms1 +# 38| 1: [IntLiteral] 10 +# 39| 1: [ExprStmt] ...; +# 39| 0: [MethodCall] call to method WriteLine +# 39| -1: [TypeAccess] access to type Console +# 39| 0: [StringLiteral] "Hit the breakpoint" +# 40| 2: [BreakStmt] break; +# 41| 3: [CaseStmt] case ...: +# 41| 0: [VariablePatternExpr] MyStruct ms2 +# 41| 1: [LTExpr] ... < ... +# 41| 0: [FieldAccess] access to field X +# 41| -1: [LocalVariableAccess] access to local variable ms2 +# 41| 1: [IntLiteral] 10 +# 42| 4: [ExprStmt] ...; +# 42| 0: [MethodCall] call to method WriteLine +# 42| -1: [TypeAccess] access to type Console +# 42| 0: [StringLiteral] "Missed the breakpoint" +# 43| 5: [BreakStmt] break; +# 46| 2: [SwitchStmt] switch (...) {...} +# 46| 0: [LocalVariableAccess] access to local variable s +# 48| 0: [CaseStmt] case ...: +# 48| 0: [RecursivePatternExpr] { ... } +# 48| 1: [TypeAccess] access to type MyStruct +# 48| 3: [PropertyPatternExpr] { ... } +# 48| 0: [LabeledPatternExpr,VariablePatternExpr] Int32 x +# 48| 1: [GTExpr] ... > ... +# 48| 0: [LocalVariableAccess] access to local variable x +# 48| 1: [IntLiteral] 2 +# 49| 1: [ExprStmt] ...; +# 49| 0: [MethodCall] call to method WriteLine +# 49| -1: [TypeAccess] access to type Console +# 49| 0: [LocalVariableAccess] access to local variable x +# 50| 2: [BreakStmt] break; +# 51| 3: [CaseStmt] case ...: +# 51| 0: [RecursivePatternExpr] { ... } +# 51| 0: [LocalVariableDeclExpr] MyStruct ms +# 51| 1: [TypeAccess] access to type MyStruct +# 51| 3: [PropertyPatternExpr] { ... } +# 51| 0: [ConstantPatternExpr,IntLiteral,LabeledPatternExpr] 10 +# 52| 4: [ExprStmt] ...; +# 52| 0: [MethodCall] call to method WriteLine +# 52| -1: [TypeAccess] access to type Console +# 52| 0: [StringLiteral] "Hit the breakpoint" +# 53| 5: [BreakStmt] break; +# 54| 6: [CaseStmt] case ...: +# 54| 0: [RecursivePatternExpr] { ... } +# 54| 3: [PropertyPatternExpr] { ... } +# 54| 0: [LabeledPatternExpr,VariablePatternExpr] Int32 x2 +# 54| 1: [GTExpr] ... > ... +# 54| 0: [LocalVariableAccess] access to local variable x2 +# 54| 1: [IntLiteral] 2 +# 55| 7: [ExprStmt] ...; +# 55| 0: [MethodCall] call to method WriteLine +# 55| -1: [TypeAccess] access to type Console +# 55| 0: [LocalVariableAccess] access to local variable x2 +# 56| 8: [BreakStmt] break; +# 57| 9: [CaseStmt] case ...: +# 57| 0: [RecursivePatternExpr] { ... } +# 57| 2: [PositionalPatternExpr] ( ... ) +# 57| 0: [ConstantPatternExpr,IntLiteral] 1 +# 57| 1: [ConstantPatternExpr,IntLiteral] 2 +# 58| 10: [BreakStmt] break; +# 59| 11: [CaseStmt] case ...: +# 59| 0: [TupleExpr] (..., ...) +# 59| 0: [LocalVariableDeclExpr] Int32 x +# 59| 1: [LocalVariableDeclExpr] Int32 y +# 60| 12: [BreakStmt] break; +# 61| 13: [DefaultCase] default: +# 62| 14: [BreakStmt] break; +# 65| 3: [SwitchStmt] switch (...) {...} +# 65| 0: [LocalVariableAccess] access to local variable s +# 67| 0: [CaseStmt] case ...: +# 67| 0: [RecursivePatternExpr] { ... } +# 67| 1: [TypeAccess] access to type MyStruct +# 67| 3: [PropertyPatternExpr] { ... } +# 67| 0: [LabeledPatternExpr,VariablePatternExpr] Int32 x +# 67| 1: [GTExpr] ... > ... +# 67| 0: [LocalVariableAccess] access to local variable x +# 67| 1: [IntLiteral] 2 +# 68| 1: [ExprStmt] ...; +# 68| 0: [MethodCall] call to method WriteLine +# 68| -1: [TypeAccess] access to type Console +# 68| 0: [LocalVariableAccess] access to local variable x +# 69| 2: [BreakStmt] break; +# 70| 3: [CaseStmt] case ...: +# 70| 0: [RecursivePatternExpr] { ... } +# 70| 0: [LocalVariableDeclExpr] MyStruct ms +# 70| 1: [TypeAccess] access to type MyStruct +# 70| 3: [PropertyPatternExpr] { ... } +# 70| 0: [ConstantPatternExpr,IntLiteral,LabeledPatternExpr] 10 +# 70| 1: [EQExpr] ... == ... +# 70| 0: [FieldAccess] access to field X +# 70| -1: [LocalVariableAccess] access to local variable s +# 70| 1: [IntLiteral] 0 +# 71| 4: [ExprStmt] ...; +# 71| 0: [MethodCall] call to method WriteLine +# 71| -1: [TypeAccess] access to type Console +# 71| 0: [StringLiteral] "Hit the breakpoint" +# 72| 5: [BreakStmt] break; +# 76| 4: [SwitchStmt] switch (...) {...} +# 76| 0: [ObjectCreation] object creation of type Object +# 78| 0: [CaseStmt] case ...: +# 78| 0: [RecursivePatternExpr] { ... } +# 78| 2: [PositionalPatternExpr] ( ... ) +# 78| 0: [VariablePatternExpr] Int32 x +# 78| 1: [VariablePatternExpr] Single y +# 78| 1: [LTExpr] ... < ... +# 78| 0: [CastExpr] (...) ... +# 78| 0: [LocalVariableAccess] access to local variable x +# 78| 1: [LocalVariableAccess] access to local variable y +# 79| 1: [BreakStmt] break; +# 80| 2: [CaseStmt] case ...: +# 80| 0: [RecursivePatternExpr] { ... } +# 80| 2: [PositionalPatternExpr] ( ... ) +# 81| 3: [BreakStmt] break; +# 82| 4: [CaseStmt] case ...: +# 82| 0: [RecursivePatternExpr] { ... } +# 82| 3: [PropertyPatternExpr] { ... } +# 83| 5: [BreakStmt] break; +# 86| 5: [SwitchStmt] switch (...) {...} +# 86| 0: [TupleExpr] (..., ...) +# 86| 0: [IntLiteral] 1 +# 86| 1: [IntLiteral] 2 +# 88| 0: [CaseStmt] case ...: +# 88| 0: [RecursivePatternExpr] { ... } +# 88| 2: [PositionalPatternExpr] ( ... ) +# 88| 0: [ConstantPatternExpr,IntLiteral] 1 +# 88| 1: [ConstantPatternExpr,IntLiteral] 2 +# 88| 1: [BreakStmt] break; +# 91| 6: [SwitchStmt] switch (...) {...} +# 91| 0: [TupleExpr] (..., ...) +# 91| 0: [IntLiteral] 1 +# 91| 1: [IntLiteral] 2 +# 93| 0: [CaseStmt] case ...: +# 93| 0: [RecursivePatternExpr] { ... } +# 93| 2: [PositionalPatternExpr] ( ... ) +# 93| 0: [ConstantPatternExpr,IntLiteral] 1 +# 93| 1: [VariablePatternExpr] Int32 x +# 93| 1: [BreakStmt] break; +# 94| 2: [CaseStmt] case ...: +# 94| 0: [RecursivePatternExpr] { ... } +# 94| 2: [PositionalPatternExpr] ( ... ) +# 94| 0: [ConstantPatternExpr,IntLiteral] 2 +# 94| 1: [DiscardPatternExpr] _ +# 94| 3: [BreakStmt] break; +# 98| 7: [Method] Expressions +#-----| 2: (Parameters) +# 98| 0: [Parameter] x +# 99| 4: [BlockStmt] {...} +# 100| 0: [LocalVariableDeclStmt] ... ...; +# 100| 0: [LocalVariableDeclAndInitExpr] String size = ... +# 100| 0: [SwitchExpr] ... switch { ... } +# 100| -1: [ParameterAccess] access to parameter x +# 101| 0: [SwitchCaseExpr] ... => ... +# 101| 0: [VariablePatternExpr] Int32 y +# 101| 1: [GTExpr] ... > ... +# 101| 0: [LocalVariableAccess] access to local variable y +# 101| 1: [IntLiteral] 10 +# 101| 2: [StringLiteral] "large" +# 102| 1: [SwitchCaseExpr] ... => ... +# 102| 0: [DiscardPatternExpr] _ +# 102| 2: [StringLiteral] "small" +# 100| 1: [LocalVariableAccess] access to local variable size +# 105| 1: [LocalVariableDeclStmt] ... ...; +# 105| 0: [LocalVariableDeclAndInitExpr] Int32 x0 = ... +# 105| 0: [IntLiteral] 0 +# 105| 1: [LocalVariableAccess] access to local variable x0 +# 105| 1: [LocalVariableDeclAndInitExpr] Int32 y0 = ... +# 105| 0: [IntLiteral] 0 +# 105| 1: [LocalVariableAccess] access to local variable y0 +# 108| 2: [ExprStmt] ...; +# 108| 0: [AssignExpr] ... = ... +# 108| 0: [SwitchExpr] ... switch { ... } +# 108| -1: [TupleExpr] (..., ...) +# 108| 0: [LocalVariableAccess] access to local variable x0 +# 108| 1: [LocalVariableAccess] access to local variable y0 +# 110| 0: [SwitchCaseExpr] ... => ... +# 110| 0: [RecursivePatternExpr] { ... } +# 110| 2: [PositionalPatternExpr] ( ... ) +# 110| 0: [ConstantPatternExpr,IntLiteral] 0 +# 110| 1: [ConstantPatternExpr,IntLiteral] 1 +# 110| 2: [TupleExpr] (..., ...) +# 110| 0: [IntLiteral] 1 +# 110| 1: [IntLiteral] 0 +# 111| 1: [SwitchCaseExpr] ... => ... +# 111| 0: [RecursivePatternExpr] { ... } +# 111| 2: [PositionalPatternExpr] ( ... ) +# 111| 0: [ConstantPatternExpr,IntLiteral] 1 +# 111| 1: [ConstantPatternExpr,IntLiteral] 0 +# 111| 2: [TupleExpr] (..., ...) +# 111| 0: [IntLiteral] 0 +# 111| 1: [IntLiteral] 1 +# 108| 1: [TupleExpr] (..., ...) +# 108| 0: [LocalVariableDeclExpr] Int32 x1 +# 108| 1: [LocalVariableDeclExpr] Int32 y1 +# 115| 3: [ExprStmt] ...; +# 115| 0: [AssignExpr] ... = ... +# 115| 0: [SwitchExpr] ... switch { ... } +# 115| -1: [TupleExpr] (..., ...) +# 115| 0: [LocalVariableAccess] access to local variable x0 +# 115| 1: [LocalVariableAccess] access to local variable y0 +# 117| 0: [SwitchCaseExpr] ... => ... +# 117| 0: [RecursivePatternExpr] { ... } +# 117| 2: [PositionalPatternExpr] ( ... ) +# 117| 0: [ConstantPatternExpr,IntLiteral] 0 +# 117| 1: [VariablePatternExpr] Int32 y2 +# 117| 2: [TupleExpr] (..., ...) +# 117| 0: [LocalVariableAccess] access to local variable y2 +# 117| 1: [IntLiteral] 0 +# 118| 1: [SwitchCaseExpr] ... => ... +# 118| 0: [RecursivePatternExpr] { ... } +# 118| 2: [PositionalPatternExpr] ( ... ) +# 118| 0: [VariablePatternExpr] Int32 x2 +# 118| 1: [ConstantPatternExpr,IntLiteral] 0 +# 118| 2: [TupleExpr] (..., ...) +# 118| 0: [IntLiteral] 0 +# 118| 1: [LocalVariableAccess] access to local variable x2 +# 119| 2: [SwitchCaseExpr] ... => ... +# 119| 0: [RecursivePatternExpr] { ... } +# 119| 2: [PositionalPatternExpr] ( ... ) +# 119| 0: [VariablePatternExpr] Int32 x2 +# 119| 1: [VariablePatternExpr] Int32 y2 +# 119| 2: [TupleExpr] (..., ...) +# 119| 0: [IntLiteral] 0 +# 119| 1: [IntLiteral] 0 +# 115| 1: [TupleExpr] (..., ...) +# 115| 0: [LocalVariableAccess] access to local variable x1 +# 115| 1: [LocalVariableAccess] access to local variable y1 +# 123| 8: [Method] Expressions2 +#-----| 2: (Parameters) +# 123| 0: [Parameter] o +# 124| 4: [BlockStmt] {...} +# 125| 0: [LocalVariableDeclStmt] ... ...; +# 125| 0: [LocalVariableDeclAndInitExpr] MyStruct s = ... +# 125| 0: [ObjectCreation] object creation of type MyStruct +# 125| -1: [ObjectInitializer] { ..., ... } +# 125| 0: [MemberInitializer] ... = ... +# 125| 0: [IntLiteral] 0 +# 125| 1: [FieldAccess] access to field X +# 125| 1: [LocalVariableAccess] access to local variable s +# 126| 1: [LocalVariableDeclStmt] ... ...; +# 126| 0: [LocalVariableDeclAndInitExpr] Int32 r = ... +# 126| 0: [SwitchExpr] ... switch { ... } +# 126| -1: [LocalVariableAccess] access to local variable s +# 128| 0: [SwitchCaseExpr] ... => ... +# 128| 0: [RecursivePatternExpr] { ... } +# 128| 1: [TypeAccess] access to type MyStruct +# 128| 3: [PropertyPatternExpr] { ... } +# 128| 0: [LabeledPatternExpr,VariablePatternExpr] Int32 x +# 128| 1: [GTExpr] ... > ... +# 128| 0: [LocalVariableAccess] access to local variable x +# 128| 1: [IntLiteral] 2 +# 128| 2: [IntLiteral] 0 +# 129| 1: [SwitchCaseExpr] ... => ... +# 129| 0: [RecursivePatternExpr] { ... } +# 129| 0: [LocalVariableDeclExpr] MyStruct ms +# 129| 1: [TypeAccess] access to type MyStruct +# 129| 3: [PropertyPatternExpr] { ... } +# 129| 0: [ConstantPatternExpr,IntLiteral,LabeledPatternExpr] 10 +# 129| 2: [IntLiteral] 1 +# 130| 2: [SwitchCaseExpr] ... => ... +# 130| 0: [RecursivePatternExpr] { ... } +# 130| 2: [PositionalPatternExpr] ( ... ) +# 130| 0: [ConstantPatternExpr,IntLiteral] 1 +# 130| 1: [ConstantPatternExpr,IntLiteral] 2 +# 130| 2: [IntLiteral] 2 +# 131| 3: [SwitchCaseExpr] ... => ... +# 131| 0: [TupleExpr] (..., ...) +# 131| 0: [LocalVariableDeclExpr] Int32 x +# 131| 1: [DiscardExpr] _ +# 131| 2: [IntLiteral] 3 +# 126| 1: [LocalVariableAccess] access to local variable r +# 134| 2: [TryStmt] try {...} ... +# 135| 0: [BlockStmt] {...} +# 136| 0: [ExprStmt] ...; +# 136| 0: [AssignExpr] ... = ... +# 136| 0: [SwitchExpr] ... switch { ... } +# 136| -1: [ParameterAccess] access to parameter o +# 138| 0: [SwitchCaseExpr] ... => ... +# 138| 0: [ConstantPatternExpr,IntLiteral] 1 +# 138| 2: [ThrowExpr] throw ... +# 138| 0: [ObjectCreation] object creation of type ArgumentException +# 139| 1: [SwitchCaseExpr] ... => ... +# 139| 0: [ConstantPatternExpr,IntLiteral] 2 +# 139| 2: [IntLiteral] 3 +# 140| 2: [SwitchCaseExpr] ... => ... +# 140| 0: [VariablePatternExpr] Object y +# 140| 1: [IsExpr] ... is ... +# 140| 0: [LocalVariableAccess] access to local variable y +# 140| 1: [RecursivePatternExpr] { ... } +# 140| 3: [PropertyPatternExpr] { ... } +# 140| 2: [IntLiteral] 4 +# 141| 3: [SwitchCaseExpr] ... => ... +# 141| 0: [TypeAccessPatternExpr] access to type String +# 141| 2: [IntLiteral] 5 +# 142| 4: [SwitchCaseExpr] ... => ... +# 142| 0: [RecursivePatternExpr] { ... } +# 142| 1: [TypeAccess] access to type MyStruct +# 142| 3: [PropertyPatternExpr] { ... } +# 142| 0: [ConstantPatternExpr,IntLiteral,LabeledPatternExpr] 10 +# 142| 2: [IntLiteral] 6 +# 136| 1: [LocalVariableAccess] access to local variable r +# 145| 1: [SpecificCatchClause] catch (...) {...} +# 145| 0: [LocalVariableDeclExpr] InvalidOperationException ex +# 146| 1: [BlockStmt] {...} +# 147| 0: [ExprStmt] ...; +# 147| 0: [MethodCall] call to method WriteLine +# 147| -1: [TypeAccess] access to type Console +# 147| 0: [StringLiteral] "Invalid operation" +# 151| 9: [Struct] MyStruct +# 153| 5: [Field] X +# 154| 6: [Property] Y +# 154| 3: [Getter] get_Y +# 154| 4: [IntLiteral] 10 +# 156| 7: [Property] S +# 156| 3: [Getter] get_S +# 156| 4: [ThisAccess] this access +# 158| 8: [Method] Deconstruct +#-----| 2: (Parameters) +# 158| 0: [Parameter] x +# 158| 1: [Parameter] y +# 159| 4: [BlockStmt] {...} +# 160| 0: [ExprStmt] ...; +# 160| 0: [AssignExpr] ... = ... +# 160| 0: [FieldAccess] access to field X +# 160| 1: [ParameterAccess] access to parameter x +# 161| 1: [ExprStmt] ...; +# 161| 0: [AssignExpr] ... = ... +# 161| 0: [PropertyCall] access to property Y +# 161| 1: [ParameterAccess] access to parameter y +# 164| 9: [Method] Deconstruct +# 165| 4: [BlockStmt] {...} +ranges.cs: +# 5| [Class] Ranges +# 7| 5: [Method] F +# 8| 4: [BlockStmt] {...} +# 9| 0: [LocalVariableDeclStmt] ... ...; +# 9| 0: [LocalVariableDeclAndInitExpr] Int32[] array = ... +# 9| 0: [ArrayCreation] array creation of type Int32[] +# 9| -1: [ArrayInitializer] { ..., ... } +# 9| 0: [IntLiteral] 1 +# 9| 1: [IntLiteral] 2 +# 9| 2: [IntLiteral] 3 +# 9| 3: [IntLiteral] 4 +# 9| 1: [LocalVariableAccess] access to local variable array +# 11| 1: [LocalVariableDeclStmt] ... ...; +# 11| 0: [LocalVariableDeclAndInitExpr] Int32[] slice1 = ... +# 11| 0: [ArrayAccess] access to array element +# 11| -1: [LocalVariableAccess] access to local variable array +# 11| 0: [RangeExpr] ... .. ... +# 11| 0: [OperatorCall] call to operator implicit conversion +# 11| 0: [IntLiteral] 1 +# 11| 1: [OperatorCall] call to operator implicit conversion +# 11| 0: [IntLiteral] 3 +# 11| 1: [LocalVariableAccess] access to local variable slice1 +# 12| 2: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclAndInitExpr] Int32[] slice2 = ... +# 12| 0: [ArrayAccess] access to array element +# 12| -1: [LocalVariableAccess] access to local variable array +# 12| 0: [RangeExpr] ... .. ... +# 12| 0: [OperatorCall] call to operator implicit conversion +# 12| 0: [IntLiteral] 0 +# 12| 1: [IndexExpr] ^... +# 12| 0: [IntLiteral] 1 +# 12| 1: [LocalVariableAccess] access to local variable slice2 +# 13| 3: [LocalVariableDeclStmt] ... ...; +# 13| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 13| 0: [IntLiteral] 2 +# 13| 1: [LocalVariableAccess] access to local variable x +# 13| 1: [LocalVariableDeclAndInitExpr] Int32 y = ... +# 13| 0: [IntLiteral] 3 +# 13| 1: [LocalVariableAccess] access to local variable y +# 14| 4: [LocalVariableDeclStmt] ... ...; +# 14| 0: [LocalVariableDeclAndInitExpr] Int32[] slice3 = ... +# 14| 0: [ArrayAccess] access to array element +# 14| -1: [LocalVariableAccess] access to local variable array +# 14| 0: [RangeExpr] ... .. ... +# 14| 0: [OperatorCall] call to operator implicit conversion +# 14| 0: [LocalVariableAccess] access to local variable x +# 14| 1: [OperatorCall] call to operator implicit conversion +# 14| 0: [LocalVariableAccess] access to local variable y +# 14| 1: [LocalVariableAccess] access to local variable slice3 +# 15| 5: [LocalVariableDeclStmt] ... ...; +# 15| 0: [LocalVariableDeclAndInitExpr] Int32[] slice4 = ... +# 15| 0: [ArrayAccess] access to array element +# 15| -1: [LocalVariableAccess] access to local variable array +# 15| 0: [RangeExpr] ... .. ... +# 15| 1: [OperatorCall] call to operator implicit conversion +# 15| 0: [LocalVariableAccess] access to local variable y +# 15| 1: [LocalVariableAccess] access to local variable slice4 +# 16| 6: [LocalVariableDeclStmt] ... ...; +# 16| 0: [LocalVariableDeclAndInitExpr] Int32[] slice5 = ... +# 16| 0: [ArrayAccess] access to array element +# 16| -1: [LocalVariableAccess] access to local variable array +# 16| 0: [RangeExpr] ... .. ... +# 16| 0: [OperatorCall] call to operator implicit conversion +# 16| 0: [LocalVariableAccess] access to local variable x +# 16| 1: [LocalVariableAccess] access to local variable slice5 +# 17| 7: [LocalVariableDeclStmt] ... ...; +# 17| 0: [LocalVariableDeclAndInitExpr] Int32[] slice6 = ... +# 17| 0: [ArrayAccess] access to array element +# 17| -1: [LocalVariableAccess] access to local variable array +# 17| 0: [RangeExpr] ... .. ... +# 17| 1: [LocalVariableAccess] access to local variable slice6 +# 18| 8: [LocalVariableDeclStmt] ... ...; +# 18| 0: [LocalVariableDeclAndInitExpr] Int32[] slice7 = ... +# 18| 0: [ArrayAccess] access to array element +# 18| -1: [LocalVariableAccess] access to local variable array +# 18| 0: [RangeExpr] ... .. ... +# 18| 0: [IndexExpr] ^... +# 18| 0: [IntLiteral] 10 +# 18| 1: [IndexExpr] ^... +# 18| 0: [IntLiteral] 5 +# 18| 1: [LocalVariableAccess] access to local variable slice7 diff --git a/csharp/ql/test/library-tests/csharp8/PrintAst.qlref b/csharp/ql/test/library-tests/csharp8/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/csharp8/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/csharp8/ranges.cs b/csharp/ql/test/library-tests/csharp8/ranges.cs index 006e239f629b..e076b9ac4d2d 100644 --- a/csharp/ql/test/library-tests/csharp8/ranges.cs +++ b/csharp/ql/test/library-tests/csharp8/ranges.cs @@ -7,36 +7,14 @@ class Ranges void F() { var array = new int[] { 1, 2, 3, 4 }; - var array2 = new int[2, 3]; var slice1 = array[1..3]; var slice2 = array[0..^1]; - int x=2, y=3; + int x = 2, y = 3; var slice3 = array[x..y]; var slice4 = array[..y]; var slice5 = array[x..]; var slice6 = array[..]; var slice7 = array[^10..^5]; - var slice8 = array2[1..2, ..]; - } -} - -// These are temporary until qltest uses .NET Core 3.0. -namespace System -{ - public readonly struct Index - { - public Index(int value, bool fromEnd = false) { } - public static implicit operator Index(int value) => default(Index); - } - - public readonly struct Range - { - public Range(Index start, Index end) => throw null; - public static Range StartAt(System.Index start) => throw null; - public static Range EndAt(System.Index end) => throw null; - public static Range All => throw null; - public static Range Create(Index start, Index end) => throw null; - public static implicit operator int(Range r) => throw null; } } diff --git a/csharp/ql/test/library-tests/csharp8/ranges.expected b/csharp/ql/test/library-tests/csharp8/ranges.expected index 1e8411c6806f..914f8c82a357 100644 --- a/csharp/ql/test/library-tests/csharp8/ranges.expected +++ b/csharp/ql/test/library-tests/csharp8/ranges.expected @@ -1,28 +1,24 @@ indexes -| ranges.cs:13:31:13:32 | ^... | ranges.cs:13:32:13:32 | 1 | -| ranges.cs:19:28:19:30 | ^... | ranges.cs:19:29:19:30 | 10 | -| ranges.cs:19:33:19:34 | ^... | ranges.cs:19:34:19:34 | 5 | +| ranges.cs:12:31:12:32 | ^... | ranges.cs:12:32:12:32 | 1 | +| ranges.cs:18:28:18:30 | ^... | ranges.cs:18:29:18:30 | 10 | +| ranges.cs:18:33:18:34 | ^... | ranges.cs:18:34:18:34 | 5 | ranges -| ranges.cs:12:28:12:31 | ... .. ... | -| ranges.cs:13:28:13:32 | ... .. ... | -| ranges.cs:15:28:15:31 | ... .. ... | +| ranges.cs:11:28:11:31 | ... .. ... | +| ranges.cs:12:28:12:32 | ... .. ... | +| ranges.cs:14:28:14:31 | ... .. ... | +| ranges.cs:15:28:15:30 | ... .. ... | | ranges.cs:16:28:16:30 | ... .. ... | -| ranges.cs:17:28:17:30 | ... .. ... | -| ranges.cs:18:28:18:29 | ... .. ... | -| ranges.cs:19:28:19:34 | ... .. ... | -| ranges.cs:20:29:20:32 | ... .. ... | -| ranges.cs:20:35:20:36 | ... .. ... | +| ranges.cs:17:28:17:29 | ... .. ... | +| ranges.cs:18:28:18:34 | ... .. ... | rangeStart -| ranges.cs:12:28:12:31 | ... .. ... | ranges.cs:12:28:12:28 | 1 | -| ranges.cs:13:28:13:32 | ... .. ... | ranges.cs:13:28:13:28 | 0 | -| ranges.cs:15:28:15:31 | ... .. ... | ranges.cs:15:28:15:28 | access to local variable x | -| ranges.cs:17:28:17:30 | ... .. ... | ranges.cs:17:28:17:28 | access to local variable x | -| ranges.cs:19:28:19:34 | ... .. ... | ranges.cs:19:28:19:30 | ^... | -| ranges.cs:20:29:20:32 | ... .. ... | ranges.cs:20:29:20:29 | 1 | +| ranges.cs:11:28:11:31 | ... .. ... | ranges.cs:11:28:11:28 | 1 | +| ranges.cs:12:28:12:32 | ... .. ... | ranges.cs:12:28:12:28 | 0 | +| ranges.cs:14:28:14:31 | ... .. ... | ranges.cs:14:28:14:28 | access to local variable x | +| ranges.cs:16:28:16:30 | ... .. ... | ranges.cs:16:28:16:28 | access to local variable x | +| ranges.cs:18:28:18:34 | ... .. ... | ranges.cs:18:28:18:30 | ^... | rangeEnd -| ranges.cs:12:28:12:31 | ... .. ... | ranges.cs:12:31:12:31 | 3 | -| ranges.cs:13:28:13:32 | ... .. ... | ranges.cs:13:31:13:32 | ^... | -| ranges.cs:15:28:15:31 | ... .. ... | ranges.cs:15:31:15:31 | access to local variable y | -| ranges.cs:16:28:16:30 | ... .. ... | ranges.cs:16:30:16:30 | access to local variable y | -| ranges.cs:19:28:19:34 | ... .. ... | ranges.cs:19:33:19:34 | ^... | -| ranges.cs:20:29:20:32 | ... .. ... | ranges.cs:20:32:20:32 | 2 | +| ranges.cs:11:28:11:31 | ... .. ... | ranges.cs:11:31:11:31 | 3 | +| ranges.cs:12:28:12:32 | ... .. ... | ranges.cs:12:31:12:32 | ^... | +| ranges.cs:14:28:14:31 | ... .. ... | ranges.cs:14:31:14:31 | access to local variable y | +| ranges.cs:15:28:15:30 | ... .. ... | ranges.cs:15:30:15:30 | access to local variable y | +| ranges.cs:18:28:18:34 | ... .. ... | ranges.cs:18:33:18:34 | ^... | diff --git a/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected new file mode 100644 index 000000000000..06e2fd994c5a --- /dev/null +++ b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected @@ -0,0 +1,16 @@ +| patterns.cs:101:34:101:40 | "large" | String | +| patterns.cs:102:18:102:24 | "small" | String | +| patterns.cs:110:22:110:26 | (..., ...) | (Int32,Int32) | +| patterns.cs:111:22:111:26 | (..., ...) | (Int32,Int32) | +| patterns.cs:117:27:117:33 | (..., ...) | (Int32,Int32) | +| patterns.cs:118:28:118:34 | (..., ...) | (Int32,Int32) | +| patterns.cs:119:33:119:38 | (..., ...) | (Int32,Int32) | +| patterns.cs:128:49:128:49 | 0 | Int32 | +| patterns.cs:129:38:129:38 | 1 | Int32 | +| patterns.cs:130:23:130:23 | 2 | Int32 | +| patterns.cs:131:27:131:27 | 3 | Int32 | +| patterns.cs:138:22:138:50 | throw ... | null | +| patterns.cs:139:22:139:22 | 3 | Int32 | +| patterns.cs:140:42:140:42 | 4 | Int32 | +| patterns.cs:141:29:141:29 | 5 | Int32 | +| patterns.cs:142:41:142:41 | 6 | Int32 | diff --git a/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql new file mode 100644 index 000000000000..a9ad01da2f18 --- /dev/null +++ b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql @@ -0,0 +1,4 @@ +import csharp + +from SwitchCaseExpr case +select case.getBody(), case.getType().toString() diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs index db4cd4b643d4..948d0498983f 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs @@ -161,9 +161,14 @@ public static void Sink(object o) { } - public void M() + public virtual void M(object o) { + Sink(o); + } + public static void CallM(A2 a2, object o) + { + a2.M(o); } public void Callsite(InterfaceB intF) @@ -172,28 +177,31 @@ public void Callsite(InterfaceB intF) // in both possible implementations of foo, this callsite is relevant // in IntA, it improves virtual dispatch, // and in IntB, it improves the dataflow analysis. - intF.Foo(b, new object(), false); + intF.Foo(b, new object(), false); // no flow to `Sink()` via `A2.M()`, but flow via `IntA.Foo()` + + CallM(b, new object()); // no flow to `Sink()` + CallM(this, new object()); // flow to `Sink()` } - private class B : A2 + public class B : A2 { - public void M() + public override void M(object o) { } } - private class IntA : InterfaceB + public class IntA : InterfaceB { public void Foo(A2 obj, object o, bool cond) { - obj.M(); + obj.M(o); Sink(o); } } - private class IntB : InterfaceB + public class IntB : InterfaceB { public void Foo(A2 obj, object o, bool cond) diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected index 0a39a463c578..c17fa754c4fa 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected @@ -26,8 +26,12 @@ edges | CallSensitivityFlow.cs:124:43:124:43 | o : Object | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | | CallSensitivityFlow.cs:133:44:133:44 | o : Object | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | | CallSensitivityFlow.cs:142:49:142:49 | o : Object | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | -| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:189:40:189:40 | o : Object | -| CallSensitivityFlow.cs:189:40:189:40 | o : Object | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | +| CallSensitivityFlow.cs:164:34:164:34 | o : Object | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | +| CallSensitivityFlow.cs:169:44:169:44 | o : Object | CallSensitivityFlow.cs:171:14:171:14 | access to parameter o : Object | +| CallSensitivityFlow.cs:171:14:171:14 | access to parameter o : Object | CallSensitivityFlow.cs:164:34:164:34 | o : Object | +| CallSensitivityFlow.cs:180:21:180:32 | object creation of type Object : Object | CallSensitivityFlow.cs:197:40:197:40 | o : Object | +| CallSensitivityFlow.cs:183:21:183:32 | object creation of type Object : Object | CallSensitivityFlow.cs:169:44:169:44 | o : Object | +| CallSensitivityFlow.cs:197:40:197:40 | o : Object | CallSensitivityFlow.cs:200:18:200:18 | access to parameter o | nodes | CallSensitivityFlow.cs:19:39:19:39 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | semmle.label | access to parameter o | @@ -66,9 +70,14 @@ nodes | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | semmle.label | access to parameter o | | CallSensitivityFlow.cs:142:49:142:49 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | semmle.label | access to local variable o3 | -| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| CallSensitivityFlow.cs:189:40:189:40 | o : Object | semmle.label | o : Object | -| CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | semmle.label | access to parameter o | +| CallSensitivityFlow.cs:164:34:164:34 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | semmle.label | access to parameter o | +| CallSensitivityFlow.cs:169:44:169:44 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:171:14:171:14 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| CallSensitivityFlow.cs:180:21:180:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| CallSensitivityFlow.cs:183:21:183:32 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| CallSensitivityFlow.cs:197:40:197:40 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:200:18:200:18 | access to parameter o | semmle.label | access to parameter o | #select | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:78:24:78:35 | object creation of type Object : Object | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | $@ | CallSensitivityFlow.cs:23:18:23:18 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:79:25:79:36 | object creation of type Object : Object | CallSensitivityFlow.cs:79:25:79:36 | object creation of type Object : Object | CallSensitivityFlow.cs:31:18:31:18 | access to parameter o | $@ | CallSensitivityFlow.cs:31:18:31:18 | access to parameter o | access to parameter o | @@ -87,4 +96,5 @@ nodes | CallSensitivityFlow.cs:117:26:117:37 | object creation of type Object : Object | CallSensitivityFlow.cs:117:26:117:37 | object creation of type Object : Object | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | $@ | CallSensitivityFlow.cs:128:22:128:22 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:118:27:118:38 | object creation of type Object : Object | CallSensitivityFlow.cs:118:27:118:38 | object creation of type Object : Object | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | $@ | CallSensitivityFlow.cs:137:22:137:22 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:119:32:119:43 | object creation of type Object : Object | CallSensitivityFlow.cs:119:32:119:43 | object creation of type Object : Object | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | $@ | CallSensitivityFlow.cs:152:18:152:19 | access to local variable o3 | access to local variable o3 | -| CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:175:21:175:32 | object creation of type Object : Object | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | $@ | CallSensitivityFlow.cs:192:18:192:18 | access to parameter o | access to parameter o | +| CallSensitivityFlow.cs:180:21:180:32 | object creation of type Object : Object | CallSensitivityFlow.cs:180:21:180:32 | object creation of type Object : Object | CallSensitivityFlow.cs:200:18:200:18 | access to parameter o | $@ | CallSensitivityFlow.cs:200:18:200:18 | access to parameter o | access to parameter o | +| CallSensitivityFlow.cs:183:21:183:32 | object creation of type Object : Object | CallSensitivityFlow.cs:183:21:183:32 | object creation of type Object : Object | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | $@ | CallSensitivityFlow.cs:166:14:166:14 | access to parameter o | access to parameter o | diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.cs b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.cs index 3565ba0b6eae..5f1bf2a6731b 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.cs @@ -7,6 +7,8 @@ public class CollectionFlow { public class A { } + public A[] As; + public void ArrayInitializerFlow() { var a = new A(); @@ -25,6 +27,24 @@ public void ArrayInitializerNoFlow(A other) Sink(First(@as)); // no flow } + public void ArrayInitializerCSharp6Flow() + { + var a = new A(); + var c = new CollectionFlow() { As = { [0] = a } }; + Sink(c.As[0]); // flow + SinkElem(c.As); // flow + Sink(First(c.As)); // flow + } + + public void ArrayInitializerCSharp6NoFlow(A other) + { + var a = new A(); + var c = new CollectionFlow() { As = { [0] = other } }; + Sink(c.As[0]); // no flow + SinkElem(c.As); // no flow + Sink(First(c.As)); // no flow + } + public void ArrayAssignmentFlow() { var a = new A(); @@ -108,7 +128,7 @@ public void DictionaryAssignmentFlow() Sink(dict[0]); // flow SinkDictValue(dict); // flow Sink(DictIndexZero(dict)); // flow - Sink(DictFirstValue(dict)); // flow [MISSING] + Sink(DictFirstValue(dict)); // flow Sink(DictValuesFirst(dict)); // flow } @@ -130,7 +150,7 @@ public void DictionaryValueInitializerFlow() Sink(dict[0]); // flow SinkDictValue(dict); // flow Sink(DictIndexZero(dict)); // flow - Sink(DictFirstValue(dict)); // flow [MISSING] + Sink(DictFirstValue(dict)); // flow Sink(DictValuesFirst(dict)); // flow } @@ -144,14 +164,36 @@ public void DictionaryValueInitializerNoFlow(A other) Sink(DictValuesFirst(dict)); // no flow } + public void DictionaryValueInitializerCSharp6Flow() + { + var a = new A(); + var dict = new Dictionary() { [0] = a }; + Sink(dict[0]); // flow + SinkDictValue(dict); // flow + Sink(DictIndexZero(dict)); // flow + Sink(DictFirstValue(dict)); // flow + Sink(DictValuesFirst(dict)); // flow + } + + public void DictionaryValueInitializerCSharp6NoFlow(A other) + { + var a = new A(); + var dict = new Dictionary() { [0] = other }; + Sink(dict[0]); // no flow + SinkDictValue(dict); // no flow + Sink(DictIndexZero(dict)); // no flow + Sink(DictFirstValue(dict)); // no flow + Sink(DictValuesFirst(dict)); // no flow + } + public void DictionaryKeyInitializerFlow() { var a = new A(); var dict = new Dictionary() { { a, 0 } }; - Sink(dict.Keys.First()); // flow [MISSING] - SinkDictKey(dict); // flow [MISSING] - Sink(DictKeysFirst(dict)); // flow [MISSING] - Sink(DictFirstKey(dict)); // flow [MISSING] + Sink(dict.Keys.First()); // flow + SinkDictKey(dict); // flow + Sink(DictKeysFirst(dict)); // flow + Sink(DictFirstKey(dict)); // flow } public void DictionaryKeyInitializerNoFlow(A other) @@ -163,6 +205,25 @@ public void DictionaryKeyInitializerNoFlow(A other) Sink(DictFirstKey(dict)); // no flow } + public void DictionaryKeyInitializerCSharp6Flow() + { + var a = new A(); + var dict = new Dictionary() { [a] = 0 }; + Sink(dict.Keys.First()); // flow + SinkDictKey(dict); // flow + Sink(DictKeysFirst(dict)); // flow + Sink(DictFirstKey(dict)); // flow + } + + public void DictionaryKeyInitializerCSharp6NoFlow(A other) + { + var dict = new Dictionary() { [other] = 0 }; + Sink(dict.Keys.First()); // no flow + SinkDictKey(dict); // no flow + Sink(DictKeysFirst(dict)); // no flow + Sink(DictFirstKey(dict)); // no flow + } + public void ForeachFlow() { var a = new A(); @@ -202,7 +263,110 @@ public void ListGetEnumeratorFlow() list.Add(a); var enumerator = list.GetEnumerator(); while (enumerator.MoveNext()) - Sink(enumerator.Current); // flow [MISSING] + Sink(enumerator.Current); // flow + } + + public void ListGetEnumeratorNoFlow(A other) + { + var list = new List(); + list.Add(other); + var enumerator = list.GetEnumerator(); + while (enumerator.MoveNext()) + Sink(enumerator.Current); // no flow + } + + public void SelectFlow() + { + var a = new A(); + var list = new List>(); + list.Add(new KeyValuePair(a, 0)); + list.Select(kvp => + { + Sink(kvp.Key); // flow + return kvp.Value; + }); + } + + public void SelectNoFlow() + { + var a = new A(); + var list = new List>(); + list.Add(new KeyValuePair(a, 0)); + list.Select(kvp => + { + Sink(kvp.Value); // no flow + return kvp.Value; + }); + } + + void SetArray(A[] array, A element) => array[0] = element; + + public void ArraySetterFlow() + { + var a = new A(); + var @as = new A[1]; + SetArray(@as, a); + Sink(@as[0]); // flow + SinkElem(@as); // flow + Sink(First(@as)); // flow + } + + public void ArraySetterNoFlow(A other) + { + var a = new A(); + var @as = new A[1]; + SetArray(@as, other); + Sink(@as[0]); // no flow + SinkElem(@as); // no flow + Sink(First(@as)); // no flow + } + + void SetList(List list, A element) => list.Add(element); + + public void ListSetterFlow() + { + var a = new A(); + var list = new List(); + SetList(list, a); + Sink(list[0]); // flow + SinkListElem(list); // flow + Sink(ListFirst(list)); // flow + } + + public void ListSetterNoFlow(A other) + { + var list = new List(); + SetList(list, other); + Sink(list[0]); // no flow + SinkListElem(list); // no flow + Sink(ListFirst(list)); // no flow + } + + public void ParamsFlow() + { + SinkParams(new A()); // flow + SinkParams(null, new A()); // flow + SinkParams(null, new A(), null); // flow + SinkParams(new A[] { new A() }); // flow + } + + public void ParamsNoFlow(A other) + { + SinkParams(other); // no flow + SinkParams(null, other); // no flow + SinkParams(null, other, null); // no flow + SinkParams(new A[] { other }); // no flow + } + + public void ListAddClearNoFlow() + { + var a = new A(); + var list = new List(); + list.Add(a); + list.Clear(); + Sink(list[0]); // no flow + SinkListElem(list); // no flow + Sink(ListFirst(list)); // no flow } public static void Sink(T t) { } @@ -228,4 +392,6 @@ public static void Sink(T t) { } public static T DictKeysFirst(IDictionary dict) => dict.Keys.First(); public static T DictFirstKey(IDictionary dict) => dict.First().Key; + + public static void SinkParams(params T[] args) => Sink(args[0]); } diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected index c6cc298ea94b..3c27e96311bb 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected @@ -1,235 +1,421 @@ edges -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:14:14:14:19 | access to array element | -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:15:18:15:20 | access to local variable as : A[] | -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:16:20:16:22 | access to local variable as : A[] | -| CollectionFlow.cs:15:18:15:20 | access to local variable as : A[] | CollectionFlow.cs:210:40:210:41 | ts : A[] | -| CollectionFlow.cs:16:20:16:22 | access to local variable as : A[] | CollectionFlow.cs:16:14:16:23 | call to method First | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:33:14:33:19 | access to array element | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:34:18:34:20 | access to local variable as : A[] | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:35:20:35:22 | access to local variable as : A[] | -| CollectionFlow.cs:34:18:34:20 | access to local variable as : A[] | CollectionFlow.cs:210:40:210:41 | ts : A[] | -| CollectionFlow.cs:35:20:35:22 | access to local variable as : A[] | CollectionFlow.cs:35:14:35:23 | call to method First | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:53:14:53:20 | access to indexer | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:54:22:54:25 | access to local variable list : List | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:55:24:55:27 | access to local variable list : List | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:53:14:53:20 | access to indexer | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:54:22:54:25 | access to local variable list : List | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:55:24:55:27 | access to local variable list : List | -| CollectionFlow.cs:54:22:54:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:55:24:55:27 | access to local variable list : List | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:62:14:62:20 | access to indexer | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:63:22:63:25 | access to local variable list : List | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:64:24:64:27 | access to local variable list : List | -| CollectionFlow.cs:63:22:63:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:64:24:64:27 | access to local variable list : List | CollectionFlow.cs:64:14:64:28 | call to method ListFirst | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:71:14:71:20 | access to indexer | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:72:22:72:25 | access to local variable list : List | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:73:24:73:27 | access to local variable list : List | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:71:14:71:20 | access to indexer | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:72:22:72:25 | access to local variable list : List | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:73:24:73:27 | access to local variable list : List | -| CollectionFlow.cs:72:22:72:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:73:24:73:27 | access to local variable list : List | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:79:14:79:20 | access to indexer | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:80:22:80:25 | access to local variable list : List | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:81:24:81:27 | access to local variable list : List | -| CollectionFlow.cs:80:22:80:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:81:24:81:27 | access to local variable list : List | CollectionFlow.cs:81:14:81:28 | call to method ListFirst | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:89:14:89:20 | access to indexer | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:90:22:90:25 | access to local variable list : List | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:91:24:91:27 | access to local variable list : List | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:89:14:89:20 | access to indexer | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:90:22:90:25 | access to local variable list : List | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:91:24:91:27 | access to local variable list : List | -| CollectionFlow.cs:90:22:90:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:91:24:91:27 | access to local variable list : List | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:98:14:98:20 | access to indexer | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:99:22:99:25 | access to local variable list : List | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:100:24:100:27 | access to local variable list : List | -| CollectionFlow.cs:99:22:99:25 | access to local variable list : List | CollectionFlow.cs:212:49:212:52 | list : List | -| CollectionFlow.cs:100:24:100:27 | access to local variable list : List | CollectionFlow.cs:100:14:100:28 | call to method ListFirst | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:108:14:108:20 | access to indexer | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:109:23:109:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:110:28:110:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:112:30:112:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:108:14:108:20 | access to indexer | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:109:23:109:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:110:28:110:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:112:30:112:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:109:23:109:26 | access to local variable dict : Dictionary | CollectionFlow.cs:214:61:214:64 | dict : Dictionary | -| CollectionFlow.cs:110:28:110:31 | access to local variable dict : Dictionary | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | -| CollectionFlow.cs:112:30:112:33 | access to local variable dict : Dictionary | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:119:14:119:20 | access to indexer | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:120:23:120:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:121:28:121:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:123:30:123:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:120:23:120:26 | access to local variable dict : Dictionary | CollectionFlow.cs:214:61:214:64 | dict : Dictionary | -| CollectionFlow.cs:121:28:121:31 | access to local variable dict : Dictionary | CollectionFlow.cs:121:14:121:32 | call to method DictIndexZero | -| CollectionFlow.cs:123:30:123:33 | access to local variable dict : Dictionary | CollectionFlow.cs:123:14:123:34 | call to method DictValuesFirst | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:130:14:130:20 | access to indexer | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:131:23:131:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:132:28:132:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:134:30:134:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:130:14:130:20 | access to indexer | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:131:23:131:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:132:28:132:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:134:30:134:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:131:23:131:26 | access to local variable dict : Dictionary | CollectionFlow.cs:214:61:214:64 | dict : Dictionary | -| CollectionFlow.cs:132:28:132:31 | access to local variable dict : Dictionary | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | -| CollectionFlow.cs:134:30:134:33 | access to local variable dict : Dictionary | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:140:14:140:20 | access to indexer | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:141:23:141:26 | access to local variable dict : Dictionary | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:142:28:142:31 | access to local variable dict : Dictionary | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:144:30:144:33 | access to local variable dict : Dictionary | -| CollectionFlow.cs:141:23:141:26 | access to local variable dict : Dictionary | CollectionFlow.cs:214:61:214:64 | dict : Dictionary | -| CollectionFlow.cs:142:28:142:31 | access to local variable dict : Dictionary | CollectionFlow.cs:142:14:142:32 | call to method DictIndexZero | -| CollectionFlow.cs:144:30:144:33 | access to local variable dict : Dictionary | CollectionFlow.cs:144:14:144:34 | call to method DictValuesFirst | -| CollectionFlow.cs:168:17:168:23 | object creation of type A : A | CollectionFlow.cs:171:18:171:18 | access to local variable x | -| CollectionFlow.cs:183:17:183:23 | object creation of type A : A | CollectionFlow.cs:187:18:187:35 | access to property Current | -| CollectionFlow.cs:210:40:210:41 | ts : A[] | CollectionFlow.cs:210:52:210:56 | access to array element | -| CollectionFlow.cs:212:49:212:52 | list : List | CollectionFlow.cs:212:63:212:69 | access to indexer | -| CollectionFlow.cs:214:61:214:64 | dict : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:15:27:15:27 | access to local variable a : A | +| CollectionFlow.cs:15:25:15:29 | { ..., ... } [[]] : A | CollectionFlow.cs:16:14:16:16 | access to local variable as [[]] : A | +| CollectionFlow.cs:15:25:15:29 | { ..., ... } [[]] : A | CollectionFlow.cs:17:18:17:20 | access to local variable as [[]] : A | +| CollectionFlow.cs:15:25:15:29 | { ..., ... } [[]] : A | CollectionFlow.cs:18:20:18:22 | access to local variable as [[]] : A | +| CollectionFlow.cs:15:27:15:27 | access to local variable a : A | CollectionFlow.cs:15:25:15:29 | { ..., ... } [[]] : A | +| CollectionFlow.cs:16:14:16:16 | access to local variable as [[]] : A | CollectionFlow.cs:16:14:16:19 | access to array element | +| CollectionFlow.cs:17:18:17:20 | access to local variable as [[]] : A | CollectionFlow.cs:374:40:374:41 | ts [[]] : A | +| CollectionFlow.cs:18:20:18:22 | access to local variable as [[]] : A | CollectionFlow.cs:18:14:18:23 | call to method First | +| CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:33:53:33:53 | access to local variable a : A | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | +| CollectionFlow.cs:33:53:33:53 | access to local variable a : A | CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | +| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | +| CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | CollectionFlow.cs:34:14:34:20 | access to array element | +| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | +| CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | CollectionFlow.cs:374:40:374:41 | ts [[]] : A | +| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | +| CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | CollectionFlow.cs:36:14:36:24 | call to method First | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:52:18:52:18 | access to local variable a : A | +| CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | CollectionFlow.cs:53:14:53:16 | access to local variable as [[]] : A | +| CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | CollectionFlow.cs:54:18:54:20 | access to local variable as [[]] : A | +| CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | CollectionFlow.cs:55:20:55:22 | access to local variable as [[]] : A | +| CollectionFlow.cs:52:18:52:18 | access to local variable a : A | CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | +| CollectionFlow.cs:53:14:53:16 | access to local variable as [[]] : A | CollectionFlow.cs:53:14:53:19 | access to array element | +| CollectionFlow.cs:54:18:54:20 | access to local variable as [[]] : A | CollectionFlow.cs:374:40:374:41 | ts [[]] : A | +| CollectionFlow.cs:55:20:55:22 | access to local variable as [[]] : A | CollectionFlow.cs:55:14:55:23 | call to method First | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:72:19:72:19 | access to local variable a : A | +| CollectionFlow.cs:72:9:72:12 | [post] access to local variable list [[]] : A | CollectionFlow.cs:73:14:73:17 | access to local variable list [[]] : A | +| CollectionFlow.cs:72:9:72:12 | [post] access to local variable list [[]] : A | CollectionFlow.cs:74:22:74:25 | access to local variable list [[]] : A | +| CollectionFlow.cs:72:9:72:12 | [post] access to local variable list [[]] : A | CollectionFlow.cs:75:24:75:27 | access to local variable list [[]] : A | +| CollectionFlow.cs:72:19:72:19 | access to local variable a : A | CollectionFlow.cs:72:9:72:12 | [post] access to local variable list [[]] : A | +| CollectionFlow.cs:73:14:73:17 | access to local variable list [[]] : A | CollectionFlow.cs:73:14:73:20 | access to indexer | +| CollectionFlow.cs:74:22:74:25 | access to local variable list [[]] : A | CollectionFlow.cs:376:49:376:52 | list [[]] : A | +| CollectionFlow.cs:75:24:75:27 | access to local variable list [[]] : A | CollectionFlow.cs:75:14:75:28 | call to method ListFirst | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:90:36:90:36 | access to local variable a : A | +| CollectionFlow.cs:90:34:90:38 | { ..., ... } [[]] : A | CollectionFlow.cs:91:14:91:17 | access to local variable list [[]] : A | +| CollectionFlow.cs:90:34:90:38 | { ..., ... } [[]] : A | CollectionFlow.cs:92:22:92:25 | access to local variable list [[]] : A | +| CollectionFlow.cs:90:34:90:38 | { ..., ... } [[]] : A | CollectionFlow.cs:93:24:93:27 | access to local variable list [[]] : A | +| CollectionFlow.cs:90:36:90:36 | access to local variable a : A | CollectionFlow.cs:90:34:90:38 | { ..., ... } [[]] : A | +| CollectionFlow.cs:91:14:91:17 | access to local variable list [[]] : A | CollectionFlow.cs:91:14:91:20 | access to indexer | +| CollectionFlow.cs:92:22:92:25 | access to local variable list [[]] : A | CollectionFlow.cs:376:49:376:52 | list [[]] : A | +| CollectionFlow.cs:93:24:93:27 | access to local variable list [[]] : A | CollectionFlow.cs:93:14:93:28 | call to method ListFirst | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:108:18:108:18 | access to local variable a : A | +| CollectionFlow.cs:108:9:108:12 | [post] access to local variable list [[]] : A | CollectionFlow.cs:109:14:109:17 | access to local variable list [[]] : A | +| CollectionFlow.cs:108:9:108:12 | [post] access to local variable list [[]] : A | CollectionFlow.cs:110:22:110:25 | access to local variable list [[]] : A | +| CollectionFlow.cs:108:9:108:12 | [post] access to local variable list [[]] : A | CollectionFlow.cs:111:24:111:27 | access to local variable list [[]] : A | +| CollectionFlow.cs:108:18:108:18 | access to local variable a : A | CollectionFlow.cs:108:9:108:12 | [post] access to local variable list [[]] : A | +| CollectionFlow.cs:109:14:109:17 | access to local variable list [[]] : A | CollectionFlow.cs:109:14:109:20 | access to indexer | +| CollectionFlow.cs:110:22:110:25 | access to local variable list [[]] : A | CollectionFlow.cs:376:49:376:52 | list [[]] : A | +| CollectionFlow.cs:111:24:111:27 | access to local variable list [[]] : A | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:127:19:127:19 | access to local variable a : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:19:127:19 | access to local variable a : A | CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | +| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:128:14:128:20 | access to indexer | +| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | +| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | +| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:149:52:149:52 | access to local variable a : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:52:149:52 | access to local variable a : A | CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:150:14:150:20 | access to indexer | +| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | +| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | +| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | +| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:170:53:170:53 | access to local variable a : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:53:170:53 | access to local variable a : A | CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:171:14:171:20 | access to indexer | +| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | +| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | +| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | +| CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:192:49:192:49 | access to local variable a : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:49:192:49 | access to local variable a : A | CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | +| CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | CollectionFlow.cs:193:14:193:30 | call to method First | +| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | +| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | +| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | +| CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:211:48:211:48 | access to local variable a : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:48:211:48 | access to local variable a : A | CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | +| CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | CollectionFlow.cs:212:14:212:30 | call to method First | +| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | +| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | +| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | +| CollectionFlow.cs:229:17:229:23 | object creation of type A : A | CollectionFlow.cs:230:27:230:27 | access to local variable a : A | +| CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | CollectionFlow.cs:231:27:231:29 | access to local variable as [[]] : A | +| CollectionFlow.cs:230:27:230:27 | access to local variable a : A | CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | +| CollectionFlow.cs:231:22:231:22 | SSA def(x) : A | CollectionFlow.cs:232:18:232:18 | access to local variable x | +| CollectionFlow.cs:231:27:231:29 | access to local variable as [[]] : A | CollectionFlow.cs:231:22:231:22 | SSA def(x) : A | +| CollectionFlow.cs:244:17:244:23 | object creation of type A : A | CollectionFlow.cs:245:27:245:27 | access to local variable a : A | +| CollectionFlow.cs:245:25:245:29 | { ..., ... } [[]] : A | CollectionFlow.cs:246:26:246:28 | access to local variable as [[]] : A | +| CollectionFlow.cs:245:27:245:27 | access to local variable a : A | CollectionFlow.cs:245:25:245:29 | { ..., ... } [[]] : A | +| CollectionFlow.cs:246:26:246:28 | access to local variable as [[]] : A | CollectionFlow.cs:246:26:246:44 | call to method GetEnumerator [Current] : A | +| CollectionFlow.cs:246:26:246:44 | call to method GetEnumerator [Current] : A | CollectionFlow.cs:248:18:248:27 | access to local variable enumerator [Current] : A | +| CollectionFlow.cs:248:18:248:27 | access to local variable enumerator [Current] : A | CollectionFlow.cs:248:18:248:35 | access to property Current | +| CollectionFlow.cs:261:17:261:23 | object creation of type A : A | CollectionFlow.cs:263:18:263:18 | access to local variable a : A | +| CollectionFlow.cs:263:9:263:12 | [post] access to local variable list [[]] : A | CollectionFlow.cs:264:26:264:29 | access to local variable list [[]] : A | +| CollectionFlow.cs:263:18:263:18 | access to local variable a : A | CollectionFlow.cs:263:9:263:12 | [post] access to local variable list [[]] : A | +| CollectionFlow.cs:264:26:264:29 | access to local variable list [[]] : A | CollectionFlow.cs:264:26:264:45 | call to method GetEnumerator [Current] : A | +| CollectionFlow.cs:264:26:264:45 | call to method GetEnumerator [Current] : A | CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | +| CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | CollectionFlow.cs:266:18:266:35 | access to property Current | +| CollectionFlow.cs:280:17:280:23 | object creation of type A : A | CollectionFlow.cs:282:43:282:43 | access to local variable a : A | +| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | +| CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | +| CollectionFlow.cs:282:43:282:43 | access to local variable a : A | CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | +| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | +| CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | +| CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | CollectionFlow.cs:285:18:285:24 | access to property Key | +| CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:308:23:308:23 | access to local variable a : A | +| CollectionFlow.cs:308:18:308:20 | [post] access to local variable as [[]] : A | CollectionFlow.cs:309:14:309:16 | access to local variable as [[]] : A | +| CollectionFlow.cs:308:18:308:20 | [post] access to local variable as [[]] : A | CollectionFlow.cs:310:18:310:20 | access to local variable as [[]] : A | +| CollectionFlow.cs:308:18:308:20 | [post] access to local variable as [[]] : A | CollectionFlow.cs:311:20:311:22 | access to local variable as [[]] : A | +| CollectionFlow.cs:308:23:308:23 | access to local variable a : A | CollectionFlow.cs:308:18:308:20 | [post] access to local variable as [[]] : A | +| CollectionFlow.cs:309:14:309:16 | access to local variable as [[]] : A | CollectionFlow.cs:309:14:309:19 | access to array element | +| CollectionFlow.cs:310:18:310:20 | access to local variable as [[]] : A | CollectionFlow.cs:374:40:374:41 | ts [[]] : A | +| CollectionFlow.cs:311:20:311:22 | access to local variable as [[]] : A | CollectionFlow.cs:311:14:311:23 | call to method First | +| CollectionFlow.cs:328:17:328:23 | object creation of type A : A | CollectionFlow.cs:330:23:330:23 | access to local variable a : A | +| CollectionFlow.cs:330:17:330:20 | [post] access to local variable list [[]] : A | CollectionFlow.cs:331:14:331:17 | access to local variable list [[]] : A | +| CollectionFlow.cs:330:17:330:20 | [post] access to local variable list [[]] : A | CollectionFlow.cs:332:22:332:25 | access to local variable list [[]] : A | +| CollectionFlow.cs:330:17:330:20 | [post] access to local variable list [[]] : A | CollectionFlow.cs:333:24:333:27 | access to local variable list [[]] : A | +| CollectionFlow.cs:330:23:330:23 | access to local variable a : A | CollectionFlow.cs:330:17:330:20 | [post] access to local variable list [[]] : A | +| CollectionFlow.cs:331:14:331:17 | access to local variable list [[]] : A | CollectionFlow.cs:331:14:331:20 | access to indexer | +| CollectionFlow.cs:332:22:332:25 | access to local variable list [[]] : A | CollectionFlow.cs:376:49:376:52 | list [[]] : A | +| CollectionFlow.cs:333:24:333:27 | access to local variable list [[]] : A | CollectionFlow.cs:333:14:333:28 | call to method ListFirst | +| CollectionFlow.cs:347:20:347:26 | object creation of type A : A | CollectionFlow.cs:396:49:396:52 | args [[]] : A | +| CollectionFlow.cs:348:26:348:32 | object creation of type A : A | CollectionFlow.cs:396:49:396:52 | args [[]] : A | +| CollectionFlow.cs:349:26:349:32 | object creation of type A : A | CollectionFlow.cs:396:49:396:52 | args [[]] : A | +| CollectionFlow.cs:350:20:350:38 | array creation of type A[] [[]] : A | CollectionFlow.cs:396:49:396:52 | args [[]] : A | +| CollectionFlow.cs:350:28:350:38 | { ..., ... } [[]] : A | CollectionFlow.cs:350:20:350:38 | array creation of type A[] [[]] : A | +| CollectionFlow.cs:350:30:350:36 | object creation of type A : A | CollectionFlow.cs:350:28:350:38 | { ..., ... } [[]] : A | +| CollectionFlow.cs:374:40:374:41 | ts [[]] : A | CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | +| CollectionFlow.cs:374:40:374:41 | ts [[]] : A | CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | +| CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | CollectionFlow.cs:374:52:374:56 | access to array element | +| CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | CollectionFlow.cs:374:52:374:56 | access to array element | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | +| CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | +| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | CollectionFlow.cs:378:75:378:81 | access to indexer | +| CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | +| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | +| CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | CollectionFlow.cs:380:73:380:89 | call to method First | +| CollectionFlow.cs:396:49:396:52 | args [[]] : A | CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | +| CollectionFlow.cs:396:49:396:52 | args [[]] : A | CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | +| CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | CollectionFlow.cs:396:63:396:69 | access to array element | +| CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | CollectionFlow.cs:396:63:396:69 | access to array element | nodes -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:14:14:14:19 | access to array element | semmle.label | access to array element | -| CollectionFlow.cs:15:18:15:20 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | -| CollectionFlow.cs:16:14:16:23 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:16:20:16:22 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:33:14:33:19 | access to array element | semmle.label | access to array element | -| CollectionFlow.cs:34:18:34:20 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | -| CollectionFlow.cs:35:14:35:23 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:35:20:35:22 | access to local variable as : A[] | semmle.label | access to local variable as : A[] | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:15:25:15:29 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | +| CollectionFlow.cs:15:27:15:27 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:16:14:16:16 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:16:14:16:19 | access to array element | semmle.label | access to array element | +| CollectionFlow.cs:17:18:17:20 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:18:14:18:23 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:18:20:18:22 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:32:17:32:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | semmle.label | { ..., ... } [As, []] : A | +| CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | +| CollectionFlow.cs:33:53:33:53 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | +| CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | +| CollectionFlow.cs:34:14:34:20 | access to array element | semmle.label | access to array element | +| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | +| CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | +| CollectionFlow.cs:36:14:36:24 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | +| CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:53:14:53:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:54:22:54:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:55:14:55:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:55:24:55:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:62:14:62:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:63:22:63:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:64:14:64:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:64:24:64:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:71:14:71:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:72:22:72:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:73:14:73:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:73:24:73:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:79:14:79:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:80:22:80:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:81:14:81:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:81:24:81:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:89:14:89:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:90:22:90:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:91:14:91:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:91:24:91:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | semmle.label | object creation of type List : List | -| CollectionFlow.cs:98:14:98:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:99:22:99:25 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:100:14:100:28 | call to method ListFirst | semmle.label | call to method ListFirst | -| CollectionFlow.cs:100:24:100:27 | access to local variable list : List | semmle.label | access to local variable list : List | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | -| CollectionFlow.cs:108:14:108:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:109:23:109:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:110:28:110:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:112:30:112:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | -| CollectionFlow.cs:119:14:119:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:120:23:120:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:121:14:121:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:121:28:121:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:123:14:123:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:123:30:123:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | -| CollectionFlow.cs:130:14:130:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:131:23:131:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:132:28:132:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:134:30:134:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | semmle.label | object creation of type Dictionary : Dictionary | -| CollectionFlow.cs:140:14:140:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:141:23:141:26 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:142:14:142:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:142:28:142:31 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:144:14:144:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:144:30:144:33 | access to local variable dict : Dictionary | semmle.label | access to local variable dict : Dictionary | -| CollectionFlow.cs:168:17:168:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:171:18:171:18 | access to local variable x | semmle.label | access to local variable x | -| CollectionFlow.cs:183:17:183:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:187:18:187:35 | access to property Current | semmle.label | access to property Current | -| CollectionFlow.cs:210:40:210:41 | ts : A[] | semmle.label | ts : A[] | -| CollectionFlow.cs:210:52:210:56 | access to array element | semmle.label | access to array element | -| CollectionFlow.cs:212:49:212:52 | list : List | semmle.label | list : List | -| CollectionFlow.cs:212:63:212:69 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:214:61:214:64 | dict : Dictionary | semmle.label | dict : Dictionary | -| CollectionFlow.cs:214:75:214:81 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | semmle.label | [post] access to local variable as [[]] : A | +| CollectionFlow.cs:52:18:52:18 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:53:14:53:16 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:53:14:53:19 | access to array element | semmle.label | access to array element | +| CollectionFlow.cs:54:18:54:20 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:55:14:55:23 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:55:20:55:22 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:72:9:72:12 | [post] access to local variable list [[]] : A | semmle.label | [post] access to local variable list [[]] : A | +| CollectionFlow.cs:72:19:72:19 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:73:14:73:17 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:73:14:73:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:74:22:74:25 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:75:14:75:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:75:24:75:27 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:90:34:90:38 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | +| CollectionFlow.cs:90:36:90:36 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:91:14:91:17 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:91:14:91:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:92:22:92:25 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:93:14:93:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:93:24:93:27 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:108:9:108:12 | [post] access to local variable list [[]] : A | semmle.label | [post] access to local variable list [[]] : A | +| CollectionFlow.cs:108:18:108:18 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:109:14:109:17 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:109:14:109:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:110:22:110:25 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:111:14:111:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:111:24:111:27 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | semmle.label | [post] access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:19:127:19 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:128:14:128:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | +| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | semmle.label | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:149:52:149:52 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:150:14:150:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | +| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | semmle.label | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:170:53:170:53 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:171:14:171:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | +| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | +| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | +| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:191:17:191:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | semmle.label | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:192:49:192:49 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | +| CollectionFlow.cs:193:14:193:30 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | semmle.label | call to method DictKeysFirst | +| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | semmle.label | call to method DictFirstKey | +| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:210:17:210:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | semmle.label | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:211:48:211:48 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | +| CollectionFlow.cs:212:14:212:30 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | semmle.label | call to method DictKeysFirst | +| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | semmle.label | call to method DictFirstKey | +| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:229:17:229:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | +| CollectionFlow.cs:230:27:230:27 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:231:22:231:22 | SSA def(x) : A | semmle.label | SSA def(x) : A | +| CollectionFlow.cs:231:27:231:29 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:232:18:232:18 | access to local variable x | semmle.label | access to local variable x | +| CollectionFlow.cs:244:17:244:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:245:25:245:29 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | +| CollectionFlow.cs:245:27:245:27 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:246:26:246:28 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:246:26:246:44 | call to method GetEnumerator [Current] : A | semmle.label | call to method GetEnumerator [Current] : A | +| CollectionFlow.cs:248:18:248:27 | access to local variable enumerator [Current] : A | semmle.label | access to local variable enumerator [Current] : A | +| CollectionFlow.cs:248:18:248:35 | access to property Current | semmle.label | access to property Current | +| CollectionFlow.cs:261:17:261:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:263:9:263:12 | [post] access to local variable list [[]] : A | semmle.label | [post] access to local variable list [[]] : A | +| CollectionFlow.cs:263:18:263:18 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:264:26:264:29 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:264:26:264:45 | call to method GetEnumerator [Current] : A | semmle.label | call to method GetEnumerator [Current] : A | +| CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | semmle.label | access to local variable enumerator [Current] : A | +| CollectionFlow.cs:266:18:266:35 | access to property Current | semmle.label | access to property Current | +| CollectionFlow.cs:280:17:280:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | semmle.label | [post] access to local variable list [[], Key] : A | +| CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | semmle.label | object creation of type KeyValuePair [Key] : A | +| CollectionFlow.cs:282:43:282:43 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | semmle.label | access to local variable list [[], Key] : A | +| CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | semmle.label | kvp [Key] : A | +| CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | semmle.label | access to parameter kvp [Key] : A | +| CollectionFlow.cs:285:18:285:24 | access to property Key | semmle.label | access to property Key | +| CollectionFlow.cs:306:17:306:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:308:18:308:20 | [post] access to local variable as [[]] : A | semmle.label | [post] access to local variable as [[]] : A | +| CollectionFlow.cs:308:23:308:23 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:309:14:309:16 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:309:14:309:19 | access to array element | semmle.label | access to array element | +| CollectionFlow.cs:310:18:310:20 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:311:14:311:23 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:311:20:311:22 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | +| CollectionFlow.cs:328:17:328:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:330:17:330:20 | [post] access to local variable list [[]] : A | semmle.label | [post] access to local variable list [[]] : A | +| CollectionFlow.cs:330:23:330:23 | access to local variable a : A | semmle.label | access to local variable a : A | +| CollectionFlow.cs:331:14:331:17 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:331:14:331:20 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:332:22:332:25 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:333:14:333:28 | call to method ListFirst | semmle.label | call to method ListFirst | +| CollectionFlow.cs:333:24:333:27 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | +| CollectionFlow.cs:347:20:347:26 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:348:26:348:32 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:349:26:349:32 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:350:20:350:38 | array creation of type A[] [[]] : A | semmle.label | array creation of type A[] [[]] : A | +| CollectionFlow.cs:350:28:350:38 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | +| CollectionFlow.cs:350:30:350:36 | object creation of type A : A | semmle.label | object creation of type A : A | +| CollectionFlow.cs:374:40:374:41 | ts [[]] : A | semmle.label | ts [[]] : A | +| CollectionFlow.cs:374:40:374:41 | ts [[]] : A | semmle.label | ts [[]] : A | +| CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | semmle.label | access to parameter ts [[]] : A | +| CollectionFlow.cs:374:52:374:53 | access to parameter ts [[]] : A | semmle.label | access to parameter ts [[]] : A | +| CollectionFlow.cs:374:52:374:56 | access to array element | semmle.label | access to array element | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | semmle.label | list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | semmle.label | list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | semmle.label | list [[]] : A | +| CollectionFlow.cs:376:49:376:52 | list [[]] : A | semmle.label | list [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | +| CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | +| CollectionFlow.cs:376:63:376:69 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | semmle.label | dict [[], Value] : A | +| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | semmle.label | access to parameter dict [[], Value] : A | +| CollectionFlow.cs:378:75:378:81 | access to indexer | semmle.label | access to indexer | +| CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | semmle.label | dict [[], Key] : A | +| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | semmle.label | access to parameter dict [[], Key] : A | +| CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | +| CollectionFlow.cs:380:73:380:89 | call to method First | semmle.label | call to method First | +| CollectionFlow.cs:396:49:396:52 | args [[]] : A | semmle.label | args [[]] : A | +| CollectionFlow.cs:396:49:396:52 | args [[]] : A | semmle.label | args [[]] : A | +| CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | semmle.label | access to parameter args [[]] : A | +| CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | semmle.label | access to parameter args [[]] : A | +| CollectionFlow.cs:396:63:396:69 | access to array element | semmle.label | access to array element | #select -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:14:14:14:19 | access to array element | $@ | CollectionFlow.cs:14:14:14:19 | access to array element | access to array element | -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:16:14:16:23 | call to method First | $@ | CollectionFlow.cs:16:14:16:23 | call to method First | call to method First | -| CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:12:17:12:23 | object creation of type A : A | CollectionFlow.cs:210:52:210:56 | access to array element | $@ | CollectionFlow.cs:210:52:210:56 | access to array element | access to array element | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:33:14:33:19 | access to array element | $@ | CollectionFlow.cs:33:14:33:19 | access to array element | access to array element | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:35:14:35:23 | call to method First | $@ | CollectionFlow.cs:35:14:35:23 | call to method First | call to method First | -| CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:30:17:30:23 | object creation of type A : A | CollectionFlow.cs:210:52:210:56 | access to array element | $@ | CollectionFlow.cs:210:52:210:56 | access to array element | access to array element | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:53:14:53:20 | access to indexer | $@ | CollectionFlow.cs:53:14:53:20 | access to indexer | access to indexer | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | $@ | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:53:14:53:20 | access to indexer | $@ | CollectionFlow.cs:53:14:53:20 | access to indexer | access to indexer | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | $@ | CollectionFlow.cs:55:14:55:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:51:20:51:32 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:62:14:62:20 | access to indexer | $@ | CollectionFlow.cs:62:14:62:20 | access to indexer | access to indexer | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:64:14:64:28 | call to method ListFirst | $@ | CollectionFlow.cs:64:14:64:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:60:20:60:32 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:71:14:71:20 | access to indexer | $@ | CollectionFlow.cs:71:14:71:20 | access to indexer | access to indexer | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | $@ | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:69:17:69:23 | object creation of type A : A | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:71:14:71:20 | access to indexer | $@ | CollectionFlow.cs:71:14:71:20 | access to indexer | access to indexer | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | $@ | CollectionFlow.cs:73:14:73:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:70:20:70:38 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:79:14:79:20 | access to indexer | $@ | CollectionFlow.cs:79:14:79:20 | access to indexer | access to indexer | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:81:14:81:28 | call to method ListFirst | $@ | CollectionFlow.cs:81:14:81:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:78:20:78:42 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:89:14:89:20 | access to indexer | $@ | CollectionFlow.cs:89:14:89:20 | access to indexer | access to indexer | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | $@ | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:86:17:86:23 | object creation of type A : A | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:89:14:89:20 | access to indexer | $@ | CollectionFlow.cs:89:14:89:20 | access to indexer | access to indexer | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | $@ | CollectionFlow.cs:91:14:91:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:87:20:87:32 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:98:14:98:20 | access to indexer | $@ | CollectionFlow.cs:98:14:98:20 | access to indexer | access to indexer | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:100:14:100:28 | call to method ListFirst | $@ | CollectionFlow.cs:100:14:100:28 | call to method ListFirst | call to method ListFirst | -| CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:96:20:96:32 | object creation of type List : List | CollectionFlow.cs:212:63:212:69 | access to indexer | $@ | CollectionFlow.cs:212:63:212:69 | access to indexer | access to indexer | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:108:14:108:20 | access to indexer | $@ | CollectionFlow.cs:108:14:108:20 | access to indexer | access to indexer | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:105:17:105:23 | object creation of type A : A | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:108:14:108:20 | access to indexer | $@ | CollectionFlow.cs:108:14:108:20 | access to indexer | access to indexer | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:110:14:110:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:112:14:112:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:106:20:106:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:119:14:119:20 | access to indexer | $@ | CollectionFlow.cs:119:14:119:20 | access to indexer | access to indexer | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:121:14:121:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:121:14:121:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:123:14:123:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:123:14:123:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:117:20:117:43 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:130:14:130:20 | access to indexer | $@ | CollectionFlow.cs:130:14:130:20 | access to indexer | access to indexer | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:128:17:128:23 | object creation of type A : A | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:130:14:130:20 | access to indexer | $@ | CollectionFlow.cs:130:14:130:20 | access to indexer | access to indexer | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:132:14:132:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:134:14:134:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:129:20:129:56 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:140:14:140:20 | access to indexer | $@ | CollectionFlow.cs:140:14:140:20 | access to indexer | access to indexer | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:142:14:142:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:142:14:142:32 | call to method DictIndexZero | call to method DictIndexZero | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:144:14:144:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:144:14:144:34 | call to method DictValuesFirst | call to method DictValuesFirst | -| CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:139:20:139:60 | object creation of type Dictionary : Dictionary | CollectionFlow.cs:214:75:214:81 | access to indexer | $@ | CollectionFlow.cs:214:75:214:81 | access to indexer | access to indexer | -| CollectionFlow.cs:168:17:168:23 | object creation of type A : A | CollectionFlow.cs:168:17:168:23 | object creation of type A : A | CollectionFlow.cs:171:18:171:18 | access to local variable x | $@ | CollectionFlow.cs:171:18:171:18 | access to local variable x | access to local variable x | -| CollectionFlow.cs:183:17:183:23 | object creation of type A : A | CollectionFlow.cs:183:17:183:23 | object creation of type A : A | CollectionFlow.cs:187:18:187:35 | access to property Current | $@ | CollectionFlow.cs:187:18:187:35 | access to property Current | access to property Current | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:16:14:16:19 | access to array element | $@ | CollectionFlow.cs:16:14:16:19 | access to array element | access to array element | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:18:14:18:23 | call to method First | $@ | CollectionFlow.cs:18:14:18:23 | call to method First | call to method First | +| CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:14:17:14:23 | object creation of type A : A | CollectionFlow.cs:374:52:374:56 | access to array element | $@ | CollectionFlow.cs:374:52:374:56 | access to array element | access to array element | +| CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:34:14:34:20 | access to array element | $@ | CollectionFlow.cs:34:14:34:20 | access to array element | access to array element | +| CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:36:14:36:24 | call to method First | $@ | CollectionFlow.cs:36:14:36:24 | call to method First | call to method First | +| CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:374:52:374:56 | access to array element | $@ | CollectionFlow.cs:374:52:374:56 | access to array element | access to array element | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:53:14:53:19 | access to array element | $@ | CollectionFlow.cs:53:14:53:19 | access to array element | access to array element | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:55:14:55:23 | call to method First | $@ | CollectionFlow.cs:55:14:55:23 | call to method First | call to method First | +| CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:374:52:374:56 | access to array element | $@ | CollectionFlow.cs:374:52:374:56 | access to array element | access to array element | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:73:14:73:20 | access to indexer | $@ | CollectionFlow.cs:73:14:73:20 | access to indexer | access to indexer | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:75:14:75:28 | call to method ListFirst | $@ | CollectionFlow.cs:75:14:75:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:70:17:70:23 | object creation of type A : A | CollectionFlow.cs:376:63:376:69 | access to indexer | $@ | CollectionFlow.cs:376:63:376:69 | access to indexer | access to indexer | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:91:14:91:20 | access to indexer | $@ | CollectionFlow.cs:91:14:91:20 | access to indexer | access to indexer | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:93:14:93:28 | call to method ListFirst | $@ | CollectionFlow.cs:93:14:93:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:89:17:89:23 | object creation of type A : A | CollectionFlow.cs:376:63:376:69 | access to indexer | $@ | CollectionFlow.cs:376:63:376:69 | access to indexer | access to indexer | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:109:14:109:20 | access to indexer | $@ | CollectionFlow.cs:109:14:109:20 | access to indexer | access to indexer | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | $@ | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:106:17:106:23 | object creation of type A : A | CollectionFlow.cs:376:63:376:69 | access to indexer | $@ | CollectionFlow.cs:376:63:376:69 | access to indexer | access to indexer | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:128:14:128:20 | access to indexer | $@ | CollectionFlow.cs:128:14:128:20 | access to indexer | access to indexer | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | $@ | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | call to method DictFirstValue | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:378:75:378:81 | access to indexer | $@ | CollectionFlow.cs:378:75:378:81 | access to indexer | access to indexer | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:150:14:150:20 | access to indexer | $@ | CollectionFlow.cs:150:14:150:20 | access to indexer | access to indexer | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | $@ | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | call to method DictFirstValue | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:378:75:378:81 | access to indexer | $@ | CollectionFlow.cs:378:75:378:81 | access to indexer | access to indexer | +| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:171:14:171:20 | access to indexer | $@ | CollectionFlow.cs:171:14:171:20 | access to indexer | access to indexer | +| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | $@ | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | call to method DictIndexZero | +| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | $@ | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | call to method DictFirstValue | +| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | $@ | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | call to method DictValuesFirst | +| CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:378:75:378:81 | access to indexer | $@ | CollectionFlow.cs:378:75:378:81 | access to indexer | access to indexer | +| CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:193:14:193:30 | call to method First | $@ | CollectionFlow.cs:193:14:193:30 | call to method First | call to method First | +| CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | $@ | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | call to method DictKeysFirst | +| CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | $@ | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | call to method DictFirstKey | +| CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:380:73:380:89 | call to method First | $@ | CollectionFlow.cs:380:73:380:89 | call to method First | call to method First | +| CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:212:14:212:30 | call to method First | $@ | CollectionFlow.cs:212:14:212:30 | call to method First | call to method First | +| CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | $@ | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | call to method DictKeysFirst | +| CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | $@ | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | call to method DictFirstKey | +| CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:380:73:380:89 | call to method First | $@ | CollectionFlow.cs:380:73:380:89 | call to method First | call to method First | +| CollectionFlow.cs:229:17:229:23 | object creation of type A : A | CollectionFlow.cs:229:17:229:23 | object creation of type A : A | CollectionFlow.cs:232:18:232:18 | access to local variable x | $@ | CollectionFlow.cs:232:18:232:18 | access to local variable x | access to local variable x | +| CollectionFlow.cs:244:17:244:23 | object creation of type A : A | CollectionFlow.cs:244:17:244:23 | object creation of type A : A | CollectionFlow.cs:248:18:248:35 | access to property Current | $@ | CollectionFlow.cs:248:18:248:35 | access to property Current | access to property Current | +| CollectionFlow.cs:261:17:261:23 | object creation of type A : A | CollectionFlow.cs:261:17:261:23 | object creation of type A : A | CollectionFlow.cs:266:18:266:35 | access to property Current | $@ | CollectionFlow.cs:266:18:266:35 | access to property Current | access to property Current | +| CollectionFlow.cs:280:17:280:23 | object creation of type A : A | CollectionFlow.cs:280:17:280:23 | object creation of type A : A | CollectionFlow.cs:285:18:285:24 | access to property Key | $@ | CollectionFlow.cs:285:18:285:24 | access to property Key | access to property Key | +| CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:309:14:309:19 | access to array element | $@ | CollectionFlow.cs:309:14:309:19 | access to array element | access to array element | +| CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:311:14:311:23 | call to method First | $@ | CollectionFlow.cs:311:14:311:23 | call to method First | call to method First | +| CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:374:52:374:56 | access to array element | $@ | CollectionFlow.cs:374:52:374:56 | access to array element | access to array element | +| CollectionFlow.cs:328:17:328:23 | object creation of type A : A | CollectionFlow.cs:328:17:328:23 | object creation of type A : A | CollectionFlow.cs:331:14:331:20 | access to indexer | $@ | CollectionFlow.cs:331:14:331:20 | access to indexer | access to indexer | +| CollectionFlow.cs:328:17:328:23 | object creation of type A : A | CollectionFlow.cs:328:17:328:23 | object creation of type A : A | CollectionFlow.cs:333:14:333:28 | call to method ListFirst | $@ | CollectionFlow.cs:333:14:333:28 | call to method ListFirst | call to method ListFirst | +| CollectionFlow.cs:328:17:328:23 | object creation of type A : A | CollectionFlow.cs:328:17:328:23 | object creation of type A : A | CollectionFlow.cs:376:63:376:69 | access to indexer | $@ | CollectionFlow.cs:376:63:376:69 | access to indexer | access to indexer | +| CollectionFlow.cs:347:20:347:26 | object creation of type A : A | CollectionFlow.cs:347:20:347:26 | object creation of type A : A | CollectionFlow.cs:396:63:396:69 | access to array element | $@ | CollectionFlow.cs:396:63:396:69 | access to array element | access to array element | +| CollectionFlow.cs:348:26:348:32 | object creation of type A : A | CollectionFlow.cs:348:26:348:32 | object creation of type A : A | CollectionFlow.cs:396:63:396:69 | access to array element | $@ | CollectionFlow.cs:396:63:396:69 | access to array element | access to array element | +| CollectionFlow.cs:349:26:349:32 | object creation of type A : A | CollectionFlow.cs:349:26:349:32 | object creation of type A : A | CollectionFlow.cs:396:63:396:69 | access to array element | $@ | CollectionFlow.cs:396:63:396:69 | access to array element | access to array element | +| CollectionFlow.cs:350:30:350:36 | object creation of type A : A | CollectionFlow.cs:350:30:350:36 | object creation of type A : A | CollectionFlow.cs:396:63:396:69 | access to array element | $@ | CollectionFlow.cs:396:63:396:69 | access to array element | access to array element | diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql index d1290562252c..7d02ffa7bcb1 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql @@ -5,7 +5,7 @@ import csharp import DataFlow::PathGraph -class Conf extends TaintTracking::Configuration { +class Conf extends DataFlow::Configuration { Conf() { this = "ArrayFlowConf" } override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation } @@ -16,6 +16,8 @@ class Conf extends TaintTracking::Configuration { mc.getAnArgument() = sink.asExpr() ) } + + override int fieldFlowBranchLimit() { result = 10 } } from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs index 7a50bcc057d9..9479cca940d7 100644 --- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs @@ -99,5 +99,13 @@ public void M14() M2(LocalFunction); } + public void M15() + { + Func f = () => 42; + new Lazy(f); + f = () => 43; + new Lazy(f); + } + public delegate void MyDelegate(); } diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected index ecdd7f9e5f82..c8893ec9b0e2 100644 --- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected @@ -1,3 +1,7 @@ +summaryDelegateCall +| file://:0:0:0:0 | [summary] valueFactory | DelegateFlow.cs:104:23:104:30 | (...) => ... | DelegateFlow.cs:105:23:105:23 | access to local variable f | +| file://:0:0:0:0 | [summary] valueFactory | DelegateFlow.cs:106:13:106:20 | (...) => ... | DelegateFlow.cs:107:23:107:23 | access to local variable f | +delegateCall | DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:17:12:17:13 | delegate creation of type Action | | DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:5:10:5:11 | M1 | DelegateFlow.cs:22:12:22:12 | access to parameter a | | DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:12:16:19 | (...) => ... | DelegateFlow.cs:16:12:16:19 | (...) => ... | diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql index 2b0386491eac..c01feabc2f97 100644 --- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.ql @@ -1,5 +1,22 @@ import csharp +import semmle.code.csharp.dataflow.internal.DataFlowPrivate +import semmle.code.csharp.dataflow.internal.DelegateDataFlow -from DelegateCall dc, Callable c, CallContext::CallContext cc -where c = dc.getARuntimeTarget(cc) -select dc, c, cc +private class NodeAdjusted extends TNode { + string toString() { result = this.(DataFlow::Node).toString() } + + Location getLocation() { + exists(Location l | + l = this.(DataFlow::Node).getLocation() and + if l instanceof SourceLocation then result = l else result instanceof EmptyLocation + ) + } +} + +query predicate summaryDelegateCall(NodeAdjusted sink, Callable c, CallContext::CallContext cc) { + c = sink.(SummaryDelegateParameterSink).getARuntimeTarget(cc) +} + +query predicate delegateCall(DelegateCall dc, Callable c, CallContext::CallContext cc) { + c = dc.getARuntimeTarget(cc) +} diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index 3d0a0a83ed54..8f1a95d58c75 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -27,49 +27,49 @@ edges | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | A.cs:98:22:98:36 | ... ? ... : ... [c] : C | | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | | A.cs:97:19:97:25 | object creation of type C : C | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | -| A.cs:98:13:98:16 | [post] this access [b, c] | A.cs:105:17:105:29 | object creation of type D [b, c] | +| A.cs:98:13:98:16 | [post] this access [b, c] : C | A.cs:105:17:105:29 | object creation of type D [b, c] : C | | A.cs:98:13:98:16 | [post] this access [b] : B | A.cs:105:17:105:29 | object creation of type D [b] : B | | A.cs:98:22:98:36 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [b] : B | -| A.cs:98:22:98:36 | ... ? ... : ... [c] : C | A.cs:98:13:98:16 | [post] this access [b, c] | +| A.cs:98:22:98:36 | ... ? ... : ... [c] : C | A.cs:98:13:98:16 | [post] this access [b, c] : C | | A.cs:98:30:98:36 | object creation of type B : B | A.cs:98:22:98:36 | ... ? ... : ... : B | | A.cs:104:17:104:23 | object creation of type B : B | A.cs:105:23:105:23 | access to local variable b : B | -| A.cs:105:17:105:29 | object creation of type D [b, c] | A.cs:107:14:107:14 | access to local variable d [b, c] | +| A.cs:105:17:105:29 | object creation of type D [b, c] : C | A.cs:107:14:107:14 | access to local variable d [b, c] : C | | A.cs:105:17:105:29 | object creation of type D [b] : B | A.cs:106:14:106:14 | access to local variable d [b] : B | | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | A.cs:108:14:108:14 | access to local variable b [c] : C | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:105:17:105:29 | object creation of type D [b] : B | | A.cs:106:14:106:14 | access to local variable d [b] : B | A.cs:106:14:106:16 | access to field b | -| A.cs:107:14:107:14 | access to local variable d [b, c] | A.cs:107:14:107:16 | access to field b [c] : C | +| A.cs:107:14:107:14 | access to local variable d [b, c] : C | A.cs:107:14:107:16 | access to field b [c] : C | | A.cs:107:14:107:16 | access to field b [c] : C | A.cs:107:14:107:18 | access to field c | | A.cs:108:14:108:14 | access to local variable b [c] : C | A.cs:108:14:108:16 | access to field c | | A.cs:113:17:113:23 | object creation of type B : B | A.cs:114:29:114:29 | access to local variable b : B | | A.cs:114:18:114:54 | object creation of type MyList [head] : B | A.cs:115:35:115:36 | access to local variable l1 [head] : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:114:18:114:54 | object creation of type MyList [head] : B | -| A.cs:115:18:115:37 | object creation of type MyList [next, head] | A.cs:116:35:116:36 | access to local variable l2 [next, head] | -| A.cs:115:35:115:36 | access to local variable l1 [head] : B | A.cs:115:18:115:37 | object creation of type MyList [next, head] | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | -| A.cs:116:35:116:36 | access to local variable l2 [next, head] | A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | -| A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | A.cs:119:14:119:20 | access to field next [next, head] | -| A.cs:119:14:119:20 | access to field next [next, head] | A.cs:119:14:119:25 | access to field next [head] : B | +| A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | +| A.cs:115:35:115:36 | access to local variable l1 [head] : B | A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | +| A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | A.cs:119:14:119:20 | access to field next [next, head] : B | +| A.cs:119:14:119:20 | access to field next [next, head] : B | A.cs:119:14:119:25 | access to field next [head] : B | | A.cs:119:14:119:25 | access to field next [head] : B | A.cs:119:14:119:30 | access to field head | -| A.cs:121:41:121:41 | access to local variable l [next, head] | A.cs:121:41:121:46 | access to field next [head] : B | -| A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | A.cs:121:41:121:46 | access to field next [next, head] | +| A.cs:121:41:121:41 | access to local variable l [next, head] : B | A.cs:121:41:121:46 | access to field next [head] : B | +| A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | A.cs:121:41:121:46 | access to field next [next, head] : B | | A.cs:121:41:121:46 | access to field next [head] : B | A.cs:123:18:123:18 | access to local variable l [head] : B | -| A.cs:121:41:121:46 | access to field next [next, head] | A.cs:121:41:121:41 | access to local variable l [next, head] | +| A.cs:121:41:121:46 | access to field next [next, head] : B | A.cs:121:41:121:41 | access to local variable l [next, head] : B | | A.cs:123:18:123:18 | access to local variable l [head] : B | A.cs:123:18:123:23 | access to field head | | B.cs:5:17:5:26 | object creation of type Elem : Elem | B.cs:6:27:6:27 | access to local variable e : Elem | | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | -| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | -| B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | -| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | +| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | +| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | B.cs:8:14:8:26 | access to field elem1 | | B.cs:14:17:14:26 | object creation of type Elem : Elem | B.cs:15:33:15:33 | access to local variable e : Elem | | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | -| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | -| B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | -| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | +| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | +| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | B.cs:18:14:18:26 | access to field elem2 | | C.cs:3:18:3:19 | [post] this access [s1] : Elem | C.cs:12:15:12:21 | object creation of type C [s1] : Elem | | C.cs:3:23:3:32 | object creation of type Elem : Elem | C.cs:3:18:3:19 | [post] this access [s1] : Elem | @@ -124,51 +124,51 @@ edges | F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:11:24:11:24 | access to local variable o : Object | | F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:15:26:15:26 | access to local variable o : Object | | F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:19:32:19:32 | access to local variable o : Object | +| F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:23:32:23:32 | access to local variable o : Object | | F.cs:11:17:11:31 | call to method Create [Field1] : Object | F.cs:12:14:12:14 | access to local variable f [Field1] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | F.cs:11:17:11:31 | call to method Create [Field1] : Object | | F.cs:12:14:12:14 | access to local variable f [Field1] : Object | F.cs:12:14:12:21 | access to field Field1 | | F.cs:15:13:15:27 | call to method Create [Field2] : Object | F.cs:17:14:17:14 | access to local variable f [Field2] : Object | | F.cs:15:26:15:26 | access to local variable o : Object | F.cs:15:13:15:27 | call to method Create [Field2] : Object | | F.cs:17:14:17:14 | access to local variable f [Field2] : Object | F.cs:17:14:17:21 | access to field Field2 | -| F.cs:19:13:19:34 | object creation of type F [Field1] : Object | F.cs:20:14:20:14 | access to local variable f [Field1] : Object | -| F.cs:19:32:19:32 | access to local variable o : Object | F.cs:19:13:19:34 | object creation of type F [Field1] : Object | -| F.cs:19:32:19:32 | access to local variable o : Object | F.cs:23:32:23:32 | access to local variable o : Object | +| F.cs:19:21:19:34 | { ..., ... } [Field1] : Object | F.cs:20:14:20:14 | access to local variable f [Field1] : Object | +| F.cs:19:32:19:32 | access to local variable o : Object | F.cs:19:21:19:34 | { ..., ... } [Field1] : Object | | F.cs:20:14:20:14 | access to local variable f [Field1] : Object | F.cs:20:14:20:21 | access to field Field1 | -| F.cs:23:13:23:34 | object creation of type F [Field2] : Object | F.cs:25:14:25:14 | access to local variable f [Field2] : Object | -| F.cs:23:32:23:32 | access to local variable o : Object | F.cs:23:13:23:34 | object creation of type F [Field2] : Object | +| F.cs:23:21:23:34 | { ..., ... } [Field2] : Object | F.cs:25:14:25:14 | access to local variable f [Field2] : Object | +| F.cs:23:32:23:32 | access to local variable o : Object | F.cs:23:21:23:34 | { ..., ... } [Field2] : Object | | F.cs:25:14:25:14 | access to local variable f [Field2] : Object | F.cs:25:14:25:21 | access to field Field2 | | G.cs:7:18:7:27 | object creation of type Elem : Elem | G.cs:9:23:9:23 | access to local variable e : Elem | -| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | -| G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | -| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:15:18:15:27 | object creation of type Elem : Elem | G.cs:17:24:17:24 | access to local variable e : Elem | -| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | -| G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | -| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:23:18:23:27 | object creation of type Elem : Elem | G.cs:25:28:25:28 | access to local variable e : Elem | -| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | -| G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | -| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:31:18:31:27 | object creation of type Elem : Elem | G.cs:33:29:33:29 | access to local variable e : Elem | -| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | -| G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | -| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | -| G.cs:37:38:37:39 | b2 [Box1, Elem] | G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | -| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | +| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | +| G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | G.cs:39:14:39:35 | call to method GetElem | | G.cs:44:18:44:27 | object creation of type Elem : Elem | G.cs:46:30:46:30 | access to local variable e : Elem | -| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | -| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | -| G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | +| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | +| G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | -| G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | -| G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | -| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | -| G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | +| G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | +| G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | +| G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | G.cs:52:14:52:31 | access to field Elem | | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | H.cs:24:27:24:27 | access to local variable a [FieldA] : Object | | H.cs:23:20:23:31 | object creation of type Object : Object | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | @@ -201,20 +201,43 @@ edges | H.cs:131:18:131:18 | access to local variable a [FieldA] : Object | H.cs:131:14:131:19 | call to method Get | | H.cs:147:17:147:32 | call to method Through : A | H.cs:148:14:148:14 | access to local variable a | | H.cs:147:25:147:31 | object creation of type A : A | H.cs:147:17:147:32 | call to method Through : A | -| H.cs:155:17:155:23 | object creation of type B : B | H.cs:157:20:157:20 | access to local variable b : B | +| H.cs:155:17:155:23 | object creation of type B : B | H.cs:156:9:156:9 | access to local variable b : B | +| H.cs:156:9:156:9 | access to local variable b : B | H.cs:157:20:157:20 | access to local variable b : B | | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | | H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:164:22:164:22 | access to local variable o : Object | -| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | +| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | -| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | | H.cs:165:17:165:28 | (...) ... : B | H.cs:166:14:166:14 | access to local variable b | | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | +| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | H.cs:165:21:165:28 | access to field FieldA : B | | H.cs:165:21:165:28 | access to field FieldA : B | H.cs:165:17:165:28 | (...) ... : B | | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | H.cs:167:14:167:21 | access to field FieldB | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:21:13:21:19 | object creation of type I [Field1] : Object | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | I.cs:26:13:26:37 | [pre-initializer] object creation of type I [Field1] : Object | +| I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:7:9:7:14 | [post] this access [Field1] : Object | +| I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:15:20:15:20 | access to local variable o : Object | +| I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | I.cs:16:9:16:9 | access to local variable i [Field1] : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | +| I.cs:16:9:16:9 | access to local variable i [Field1] : Object | I.cs:17:9:17:9 | access to local variable i [Field1] : Object | +| I.cs:17:9:17:9 | access to local variable i [Field1] : Object | I.cs:18:14:18:14 | access to local variable i [Field1] : Object | +| I.cs:18:14:18:14 | access to local variable i [Field1] : Object | I.cs:18:14:18:21 | access to field Field1 | +| I.cs:21:13:21:19 | object creation of type I [Field1] : Object | I.cs:22:9:22:9 | access to local variable i [Field1] : Object | +| I.cs:22:9:22:9 | access to local variable i [Field1] : Object | I.cs:23:14:23:14 | access to local variable i [Field1] : Object | +| I.cs:23:14:23:14 | access to local variable i [Field1] : Object | I.cs:23:14:23:21 | access to field Field1 | +| I.cs:26:13:26:37 | [pre-initializer] object creation of type I [Field1] : Object | I.cs:27:14:27:14 | access to local variable i [Field1] : Object | +| I.cs:27:14:27:14 | access to local variable i [Field1] : Object | I.cs:27:14:27:21 | access to field Field1 | +| I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:32:20:32:20 | access to local variable o : Object | +| I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | I.cs:33:9:33:9 | access to local variable i [Field1] : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | +| I.cs:33:9:33:9 | access to local variable i [Field1] : Object | I.cs:34:12:34:12 | access to local variable i [Field1] : Object | +| I.cs:34:12:34:12 | access to local variable i [Field1] : Object | I.cs:37:23:37:23 | i [Field1] : Object | +| I.cs:37:23:37:23 | i [Field1] : Object | I.cs:39:9:39:9 | access to parameter i [Field1] : Object | +| I.cs:39:9:39:9 | access to parameter i [Field1] : Object | I.cs:40:14:40:14 | access to parameter i [Field1] : Object | +| I.cs:40:14:40:14 | access to parameter i [Field1] : Object | I.cs:40:14:40:21 | access to field Field1 | nodes | A.cs:5:17:5:23 | object creation of type C : C | semmle.label | object creation of type C : C | | A.cs:6:17:6:25 | call to method Make [c] : C | semmle.label | call to method Make [c] : C | @@ -250,19 +273,19 @@ nodes | A.cs:89:14:89:16 | access to field c | semmle.label | access to field c | | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | semmle.label | [post] access to parameter b [c] : C | | A.cs:97:19:97:25 | object creation of type C : C | semmle.label | object creation of type C : C | -| A.cs:98:13:98:16 | [post] this access [b, c] | semmle.label | [post] this access [b, c] | +| A.cs:98:13:98:16 | [post] this access [b, c] : C | semmle.label | [post] this access [b, c] : C | | A.cs:98:13:98:16 | [post] this access [b] : B | semmle.label | [post] this access [b] : B | | A.cs:98:22:98:36 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | | A.cs:98:22:98:36 | ... ? ... : ... [c] : C | semmle.label | ... ? ... : ... [c] : C | | A.cs:98:30:98:36 | object creation of type B : B | semmle.label | object creation of type B : B | | A.cs:104:17:104:23 | object creation of type B : B | semmle.label | object creation of type B : B | -| A.cs:105:17:105:29 | object creation of type D [b, c] | semmle.label | object creation of type D [b, c] | +| A.cs:105:17:105:29 | object creation of type D [b, c] : C | semmle.label | object creation of type D [b, c] : C | | A.cs:105:17:105:29 | object creation of type D [b] : B | semmle.label | object creation of type D [b] : B | | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | semmle.label | [post] access to local variable b [c] : C | | A.cs:105:23:105:23 | access to local variable b : B | semmle.label | access to local variable b : B | | A.cs:106:14:106:14 | access to local variable d [b] : B | semmle.label | access to local variable d [b] : B | | A.cs:106:14:106:16 | access to field b | semmle.label | access to field b | -| A.cs:107:14:107:14 | access to local variable d [b, c] | semmle.label | access to local variable d [b, c] | +| A.cs:107:14:107:14 | access to local variable d [b, c] : C | semmle.label | access to local variable d [b, c] : C | | A.cs:107:14:107:16 | access to field b [c] : C | semmle.label | access to field b [c] : C | | A.cs:107:14:107:18 | access to field c | semmle.label | access to field c | | A.cs:108:14:108:14 | access to local variable b [c] : C | semmle.label | access to local variable b [c] : C | @@ -270,34 +293,34 @@ nodes | A.cs:113:17:113:23 | object creation of type B : B | semmle.label | object creation of type B : B | | A.cs:114:18:114:54 | object creation of type MyList [head] : B | semmle.label | object creation of type MyList [head] : B | | A.cs:114:29:114:29 | access to local variable b : B | semmle.label | access to local variable b : B | -| A.cs:115:18:115:37 | object creation of type MyList [next, head] | semmle.label | object creation of type MyList [next, head] | +| A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | semmle.label | object creation of type MyList [next, head] : B | | A.cs:115:35:115:36 | access to local variable l1 [head] : B | semmle.label | access to local variable l1 [head] : B | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | semmle.label | object creation of type MyList [next, next, ... (3)] | -| A.cs:116:35:116:36 | access to local variable l2 [next, head] | semmle.label | access to local variable l2 [next, head] | -| A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | semmle.label | access to local variable l3 [next, next, ... (3)] | -| A.cs:119:14:119:20 | access to field next [next, head] | semmle.label | access to field next [next, head] | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | semmle.label | object creation of type MyList [next, next, head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | semmle.label | access to local variable l2 [next, head] : B | +| A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | semmle.label | access to local variable l3 [next, next, head] : B | +| A.cs:119:14:119:20 | access to field next [next, head] : B | semmle.label | access to field next [next, head] : B | | A.cs:119:14:119:25 | access to field next [head] : B | semmle.label | access to field next [head] : B | | A.cs:119:14:119:30 | access to field head | semmle.label | access to field head | -| A.cs:121:41:121:41 | access to local variable l [next, head] | semmle.label | access to local variable l [next, head] | -| A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | semmle.label | access to local variable l [next, next, ... (3)] | +| A.cs:121:41:121:41 | access to local variable l [next, head] : B | semmle.label | access to local variable l [next, head] : B | +| A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | semmle.label | access to local variable l [next, next, head] : B | | A.cs:121:41:121:46 | access to field next [head] : B | semmle.label | access to field next [head] : B | -| A.cs:121:41:121:46 | access to field next [next, head] | semmle.label | access to field next [next, head] | +| A.cs:121:41:121:46 | access to field next [next, head] : B | semmle.label | access to field next [next, head] : B | | A.cs:123:18:123:18 | access to local variable l [head] : B | semmle.label | access to local variable l [head] : B | | A.cs:123:18:123:23 | access to field head | semmle.label | access to field head | | B.cs:5:17:5:26 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | semmle.label | object creation of type Box1 [elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | semmle.label | object creation of type Box2 [box1, elem1] | +| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | semmle.label | object creation of type Box2 [box1, elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | semmle.label | access to local variable b1 [elem1] : Elem | -| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | semmle.label | access to local variable b2 [box1, elem1] | +| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | semmle.label | access to local variable b2 [box1, elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | semmle.label | access to field box1 [elem1] : Elem | | B.cs:8:14:8:26 | access to field elem1 | semmle.label | access to field elem1 | | B.cs:14:17:14:26 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | semmle.label | object creation of type Box1 [elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | semmle.label | object creation of type Box2 [box1, elem2] | +| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | semmle.label | object creation of type Box2 [box1, elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | semmle.label | access to local variable b1 [elem2] : Elem | -| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | semmle.label | access to local variable b2 [box1, elem2] | +| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | semmle.label | access to local variable b2 [box1, elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | semmle.label | access to field box1 [elem2] : Elem | | B.cs:18:14:18:26 | access to field elem2 | semmle.label | access to field elem2 | | C.cs:3:18:3:19 | [post] this access [s1] : Elem | semmle.label | [post] this access [s1] : Elem | @@ -367,47 +390,47 @@ nodes | F.cs:15:26:15:26 | access to local variable o : Object | semmle.label | access to local variable o : Object | | F.cs:17:14:17:14 | access to local variable f [Field2] : Object | semmle.label | access to local variable f [Field2] : Object | | F.cs:17:14:17:21 | access to field Field2 | semmle.label | access to field Field2 | -| F.cs:19:13:19:34 | object creation of type F [Field1] : Object | semmle.label | object creation of type F [Field1] : Object | +| F.cs:19:21:19:34 | { ..., ... } [Field1] : Object | semmle.label | { ..., ... } [Field1] : Object | | F.cs:19:32:19:32 | access to local variable o : Object | semmle.label | access to local variable o : Object | | F.cs:20:14:20:14 | access to local variable f [Field1] : Object | semmle.label | access to local variable f [Field1] : Object | | F.cs:20:14:20:21 | access to field Field1 | semmle.label | access to field Field1 | -| F.cs:23:13:23:34 | object creation of type F [Field2] : Object | semmle.label | object creation of type F [Field2] : Object | +| F.cs:23:21:23:34 | { ..., ... } [Field2] : Object | semmle.label | { ..., ... } [Field2] : Object | | F.cs:23:32:23:32 | access to local variable o : Object | semmle.label | access to local variable o : Object | | F.cs:25:14:25:14 | access to local variable f [Field2] : Object | semmle.label | access to local variable f [Field2] : Object | | F.cs:25:14:25:21 | access to field Field2 | semmle.label | access to field Field2 | | G.cs:7:18:7:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:15:18:15:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:23:18:23:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | semmle.label | [post] call to method GetBox1 [Elem] : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:31:18:31:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | semmle.label | [post] call to method GetBox1 [Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | -| G.cs:37:38:37:39 | b2 [Box1, Elem] | semmle.label | b2 [Box1, Elem] | -| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | semmle.label | access to parameter b2 [Box1, Elem] | +| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | +| G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | semmle.label | b2 [Box1, Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | semmle.label | access to parameter b2 [Box1, Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | semmle.label | call to method GetBox1 [Elem] : Elem | | G.cs:39:14:39:35 | call to method GetElem | semmle.label | call to method GetElem | | G.cs:44:18:44:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | semmle.label | [post] access to field boxfield [Box1, Elem] | -| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | semmle.label | [post] this access [boxfield, Box1, ... (3)] | +| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | semmle.label | [post] access to field boxfield [Box1, Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | semmle.label | [post] this access [boxfield, Box1, Elem] : Elem | | G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | semmle.label | this access [boxfield, Box1, ... (3)] | -| G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | semmle.label | this [boxfield, Box1, ... (3)] | -| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | semmle.label | access to field boxfield [Box1, Elem] | -| G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | semmle.label | this access [boxfield, Box1, ... (3)] | +| G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | semmle.label | this access [boxfield, Box1, Elem] : Elem | +| G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | semmle.label | this [boxfield, Box1, Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | semmle.label | access to field boxfield [Box1, Elem] : Elem | +| G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | semmle.label | this access [boxfield, Box1, Elem] : Elem | | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | semmle.label | access to field Box1 [Elem] : Elem | | G.cs:52:14:52:31 | access to field Elem | semmle.label | access to field Elem | | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object | @@ -449,21 +472,47 @@ nodes | H.cs:147:25:147:31 | object creation of type A : A | semmle.label | object creation of type A : A | | H.cs:148:14:148:14 | access to local variable a | semmle.label | access to local variable a | | H.cs:155:17:155:23 | object creation of type B : B | semmle.label | object creation of type B : B | +| H.cs:156:9:156:9 | access to local variable b : B | semmle.label | access to local variable b : B | | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | semmle.label | [post] access to parameter a [FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | semmle.label | access to local variable b : B | | H.cs:163:17:163:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | semmle.label | [post] access to local variable a [FieldA, FieldB] | +| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | semmle.label | [post] access to local variable a [FieldA, FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | semmle.label | [post] access to local variable a [FieldA] : B | | H.cs:164:22:164:22 | access to local variable o : Object | semmle.label | access to local variable o : Object | | H.cs:165:17:165:28 | (...) ... : B | semmle.label | (...) ... : B | | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | semmle.label | (...) ... [FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | semmle.label | access to local variable a [FieldA, FieldB] | +| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | semmle.label | access to local variable a [FieldA, FieldB] : Object | | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | semmle.label | access to local variable a [FieldA] : B | | H.cs:165:21:165:28 | access to field FieldA : B | semmle.label | access to field FieldA : B | | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | semmle.label | access to field FieldA [FieldB] : Object | | H.cs:166:14:166:14 | access to local variable b | semmle.label | access to local variable b | | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | semmle.label | access to local variable b [FieldB] : Object | | H.cs:167:14:167:21 | access to field FieldB | semmle.label | access to field FieldB | +| I.cs:7:9:7:14 | [post] this access [Field1] : Object | semmle.label | [post] this access [Field1] : Object | +| I.cs:7:18:7:29 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:13:17:13:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:15:9:15:9 | [post] access to local variable i [Field1] : Object | semmle.label | [post] access to local variable i [Field1] : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:16:9:16:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:17:9:17:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:18:14:18:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:18:14:18:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:21:13:21:19 | object creation of type I [Field1] : Object | semmle.label | object creation of type I [Field1] : Object | +| I.cs:22:9:22:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:23:14:23:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:23:14:23:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:26:13:26:37 | [pre-initializer] object creation of type I [Field1] : Object | semmle.label | [pre-initializer] object creation of type I [Field1] : Object | +| I.cs:27:14:27:14 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:27:14:27:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:31:13:31:24 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:32:9:32:9 | [post] access to local variable i [Field1] : Object | semmle.label | [post] access to local variable i [Field1] : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:33:9:33:9 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:34:12:34:12 | access to local variable i [Field1] : Object | semmle.label | access to local variable i [Field1] : Object | +| I.cs:37:23:37:23 | i [Field1] : Object | semmle.label | i [Field1] : Object | +| I.cs:39:9:39:9 | access to parameter i [Field1] : Object | semmle.label | access to parameter i [Field1] : Object | +| I.cs:40:14:40:14 | access to parameter i [Field1] : Object | semmle.label | access to parameter i [Field1] : Object | +| I.cs:40:14:40:21 | access to field Field1 | semmle.label | access to field Field1 | #select | A.cs:7:14:7:16 | access to field c | A.cs:5:17:5:23 | object creation of type C : C | A.cs:7:14:7:16 | access to field c | $@ | A.cs:5:17:5:23 | object creation of type C : C | object creation of type C : C | | A.cs:14:14:14:20 | call to method Get | A.cs:13:15:13:22 | object creation of type C1 : C1 | A.cs:14:14:14:20 | call to method Get | $@ | A.cs:13:15:13:22 | object creation of type C1 : C1 | object creation of type C1 : C1 | @@ -513,3 +562,7 @@ nodes | H.cs:148:14:148:14 | access to local variable a | H.cs:147:25:147:31 | object creation of type A : A | H.cs:148:14:148:14 | access to local variable a | $@ | H.cs:147:25:147:31 | object creation of type A : A | object creation of type A : A | | H.cs:166:14:166:14 | access to local variable b | H.cs:155:17:155:23 | object creation of type B : B | H.cs:166:14:166:14 | access to local variable b | $@ | H.cs:155:17:155:23 | object creation of type B : B | object creation of type B : B | | H.cs:167:14:167:21 | access to field FieldB | H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:167:14:167:21 | access to field FieldB | $@ | H.cs:163:17:163:28 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:18:14:18:21 | access to field Field1 | I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:13:17:13:28 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:23:14:23:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:23:14:23:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:27:14:27:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:27:14:27:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | +| I.cs:40:14:40:21 | access to field Field1 | I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:31:13:31:24 | object creation of type Object : Object | object creation of type Object : Object | diff --git a/csharp/ql/test/library-tests/dataflow/fields/I.cs b/csharp/ql/test/library-tests/dataflow/fields/I.cs new file mode 100644 index 000000000000..99aed3b0df7b --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/fields/I.cs @@ -0,0 +1,46 @@ +public class I +{ + object Field1; + object Field2; + public I() + { + Field1 = new object(); + Field2 = new object(); + } + + private void M() + { + var o = new object(); + var i = new I(); + i.Field1 = o; + i.Field2 = o; + i.Field2 = null; + Sink(i.Field1); // flow + Sink(i.Field2); // no flow + + i = new I(); + i.Field2 = null; + Sink(i.Field1); // flow + Sink(i.Field2); // no flow + + i = new I() { Field2 = null }; + Sink(i.Field1); // flow + Sink(i.Field2); // no flow + + i = new I(); + o = new object(); + i.Field1 = o; + i.Field2 = o; + M2(i); + } + + private void M2(I i) + { + i.Field2 = null; + Sink(i.Field1); // flow + Sink(i.Field2); // no flow + + } + + public static void Sink(object o) { } +} diff --git a/csharp/ql/test/library-tests/dataflow/global/Common.qll b/csharp/ql/test/library-tests/dataflow/global/Common.qll index 1ecaebd9f949..17d4b2208763 100644 --- a/csharp/ql/test/library-tests/dataflow/global/Common.qll +++ b/csharp/ql/test/library-tests/dataflow/global/Common.qll @@ -10,10 +10,9 @@ class Config extends DataFlow::Configuration { } override predicate isSink(DataFlow::Node sink) { - sink.asExpr() instanceof Access and exists(MethodCall mc | mc.getTarget().getName() = "Check" and - mc.getAnArgument() = sink.asExpr().getParent*() + mc.getAnArgument() = sink.asExpr() ) } } diff --git a/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected b/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected index ca81fb559045..596f6f532743 100644 --- a/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/global/DataFlow.expected @@ -12,35 +12,53 @@ | Capture.cs:161:15:161:20 | access to local variable sink36 | | Capture.cs:169:15:169:20 | access to local variable sink37 | | Capture.cs:195:15:195:20 | access to local variable sink38 | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | +| GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | +| GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | +| GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | +| GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | -| Splitting.cs:21:28:21:32 | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | +| Splitting.cs:41:19:41:19 | access to local variable s | +| Splitting.cs:43:19:43:19 | access to local variable s | +| Splitting.cs:50:19:50:19 | access to local variable s | +| Splitting.cs:52:19:52:19 | access to local variable s | diff --git a/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected b/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected index a46026ba3da8..c13ef765d937 100644 --- a/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected +++ b/csharp/ql/test/library-tests/dataflow/global/DataFlowPath.expected @@ -1,189 +1,226 @@ edges -| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:14:9:14:20 | [implicit argument] tainted : String | -| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:25:9:25:20 | [implicit argument] tainted : String | -| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:33:9:33:40 | [implicit argument] tainted : String | +| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:12:19:12:24 | access to local variable sink27 | +| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:21:23:21:28 | access to local variable sink28 | +| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:30:19:30:24 | access to local variable sink29 | | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:61:36:61:42 | access to parameter tainted : String | -| Capture.cs:9:9:13:9 | SSA capture def(tainted) : String | Capture.cs:12:19:12:24 | access to local variable sink27 | -| Capture.cs:14:9:14:20 | [implicit argument] tainted : String | Capture.cs:9:9:13:9 | SSA capture def(tainted) : String | -| Capture.cs:18:13:22:13 | SSA capture def(tainted) : String | Capture.cs:21:23:21:28 | access to local variable sink28 | -| Capture.cs:25:9:25:20 | [implicit argument] tainted : String | Capture.cs:18:13:22:13 | SSA capture def(tainted) : String | -| Capture.cs:27:43:32:9 | SSA capture def(tainted) : String | Capture.cs:30:19:30:24 | access to local variable sink29 | -| Capture.cs:33:9:33:40 | [implicit argument] tainted : String | Capture.cs:27:43:32:9 | SSA capture def(tainted) : String | -| Capture.cs:50:50:50:55 | sink39 : String | Capture.cs:52:13:59:14 | [implicit argument] sink39 : String | -| Capture.cs:52:13:59:14 | [implicit argument] sink39 : String | Capture.cs:55:27:58:17 | SSA capture def(sink39) : String | -| Capture.cs:55:27:58:17 | SSA capture def(sink39) : String | Capture.cs:57:27:57:32 | access to parameter sink39 | +| Capture.cs:50:50:50:55 | sink39 : String | Capture.cs:57:27:57:32 | access to parameter sink39 | | Capture.cs:61:36:61:42 | access to parameter tainted : String | Capture.cs:50:50:50:55 | sink39 : String | -| Capture.cs:69:13:69:35 | SSA def(sink30) : String | Capture.cs:71:9:71:21 | SSA call def(sink30) : String | +| Capture.cs:69:13:69:35 | SSA def(sink30) : String | Capture.cs:72:15:72:20 | access to local variable sink30 | | Capture.cs:69:22:69:35 | "taint source" : String | Capture.cs:69:13:69:35 | SSA def(sink30) : String | -| Capture.cs:71:9:71:21 | SSA call def(sink30) : String | Capture.cs:72:15:72:20 | access to local variable sink30 | -| Capture.cs:79:17:79:39 | SSA def(sink31) : String | Capture.cs:83:9:83:21 | SSA call def(sink31) : String | +| Capture.cs:79:17:79:39 | SSA def(sink31) : String | Capture.cs:84:15:84:20 | access to local variable sink31 | | Capture.cs:79:26:79:39 | "taint source" : String | Capture.cs:79:17:79:39 | SSA def(sink31) : String | -| Capture.cs:83:9:83:21 | SSA call def(sink31) : String | Capture.cs:84:15:84:20 | access to local variable sink31 | -| Capture.cs:89:13:89:35 | SSA def(sink32) : String | Capture.cs:92:9:92:41 | SSA call def(sink32) : String | +| Capture.cs:89:13:89:35 | SSA def(sink32) : String | Capture.cs:93:15:93:20 | access to local variable sink32 | | Capture.cs:89:22:89:35 | "taint source" : String | Capture.cs:89:13:89:35 | SSA def(sink32) : String | -| Capture.cs:92:9:92:41 | SSA call def(sink32) : String | Capture.cs:93:15:93:20 | access to local variable sink32 | -| Capture.cs:115:17:115:39 | SSA def(sink40) : String | Capture.cs:121:9:121:35 | SSA call def(sink40) : String | +| Capture.cs:115:17:115:39 | SSA def(sink40) : String | Capture.cs:122:15:122:20 | access to local variable sink40 | | Capture.cs:115:26:115:39 | "taint source" : String | Capture.cs:115:17:115:39 | SSA def(sink40) : String | -| Capture.cs:121:9:121:35 | SSA call def(sink40) : String | Capture.cs:122:15:122:20 | access to local variable sink40 | -| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:132:9:132:25 | [implicit argument] tainted : String | -| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:144:9:144:25 | [implicit argument] tainted : String | -| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:153:9:153:45 | [implicit argument] tainted : String | -| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:160:22:160:38 | [implicit argument] tainted : String | +| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:133:15:133:20 | access to local variable sink33 | +| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:145:15:145:20 | access to local variable sink34 | +| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:154:15:154:20 | access to local variable sink35 | +| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 : String | | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:168:25:168:31 | access to parameter tainted : String | | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:194:25:194:31 | access to parameter tainted : String | -| Capture.cs:132:9:132:25 | SSA call def(sink33) : String | Capture.cs:133:15:133:20 | access to local variable sink33 | -| Capture.cs:132:9:132:25 | [implicit argument] tainted : String | Capture.cs:132:9:132:25 | SSA call def(sink33) : String | -| Capture.cs:144:9:144:25 | SSA call def(sink34) : String | Capture.cs:145:15:145:20 | access to local variable sink34 | -| Capture.cs:144:9:144:25 | [implicit argument] tainted : String | Capture.cs:144:9:144:25 | SSA call def(sink34) : String | -| Capture.cs:153:9:153:45 | SSA call def(sink35) : String | Capture.cs:154:15:154:20 | access to local variable sink35 | -| Capture.cs:153:9:153:45 | [implicit argument] tainted : String | Capture.cs:153:9:153:45 | SSA call def(sink35) : String | -| Capture.cs:160:22:160:38 | [implicit argument] tainted : String | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 : String | | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 : String | Capture.cs:161:15:161:20 | access to local variable sink36 | -| Capture.cs:168:9:168:32 | SSA call def(sink37) : String | Capture.cs:169:15:169:20 | access to local variable sink37 | -| Capture.cs:168:25:168:31 | access to parameter tainted : String | Capture.cs:168:9:168:32 | SSA call def(sink37) : String | +| Capture.cs:168:25:168:31 | access to parameter tainted : String | Capture.cs:169:15:169:20 | access to local variable sink37 | | Capture.cs:194:22:194:32 | call to local function Id : String | Capture.cs:195:15:195:20 | access to local variable sink38 | | Capture.cs:194:25:194:31 | access to parameter tainted : String | Capture.cs:194:22:194:32 | call to local function Id : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | -| GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:361:41:361:41 | x : String | -| GlobalDataFlow.cs:53:15:53:15 | x : String | GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | -| GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:361:41:361:41 | x : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:56:37:56:37 | x : String | GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | -| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:406:9:406:11 | value : String | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | -| GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | -| GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | -| GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | -| GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | GlobalDataFlow.cs:135:21:135:34 | delegate call : String | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | -| GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | -| GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | -| GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | -| GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | GlobalDataFlow.cs:180:21:180:26 | delegate call : String | -| GlobalDataFlow.cs:180:21:180:26 | delegate call : String | GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | -| GlobalDataFlow.cs:189:22:189:42 | [library code] object creation of type Lazy : String | GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | -| GlobalDataFlow.cs:189:39:189:41 | [output] delegate creation of type Func : String | GlobalDataFlow.cs:189:22:189:42 | [library code] object creation of type Lazy : String | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | -| GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:189:39:189:41 | [output] delegate creation of type Func : String | -| GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | -| GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | -| GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | -| GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | GlobalDataFlow.cs:53:15:53:15 | x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:56:37:56:37 | x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | -| GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | -| GlobalDataFlow.cs:406:9:406:11 | value : String | GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | -| GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:260:26:260:35 | sinkParam1 : String | +| GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:379:41:379:41 | x : String | +| GlobalDataFlow.cs:54:15:54:15 | x : String | GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | +| GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | GlobalDataFlow.cs:270:26:270:35 | sinkParam4 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:379:41:379:41 | x : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:393:52:393:52 | x : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:393:52:393:52 | x : String | +| GlobalDataFlow.cs:57:37:57:37 | x : String | GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | +| GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | GlobalDataFlow.cs:285:26:285:35 | sinkParam7 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:393:52:393:52 | x : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:424:9:424:11 | value : String | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | +| GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | +| GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | +| GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:81:59:81:63 | access to local variable sink3 : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven [[]] : String | GlobalDataFlow.cs:81:22:81:93 | call to method First : String | +| GlobalDataFlow.cs:81:22:81:93 | call to method First : String | GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | +| GlobalDataFlow.cs:81:22:81:93 | call to method First : String | GlobalDataFlow.cs:83:59:83:64 | access to local variable sink13 : String | +| GlobalDataFlow.cs:81:23:81:65 | (...) ... [[]] : String | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven [[]] : String | +| GlobalDataFlow.cs:81:57:81:65 | { ..., ... } [[]] : String | GlobalDataFlow.cs:81:23:81:65 | (...) ... [[]] : String | +| GlobalDataFlow.cs:81:59:81:63 | access to local variable sink3 : String | GlobalDataFlow.cs:81:57:81:65 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:83:22:83:87 | call to method Select [[]] : String | GlobalDataFlow.cs:83:22:83:95 | call to method First : String | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:85:59:85:64 | access to local variable sink14 : String | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | GlobalDataFlow.cs:83:22:83:87 | call to method Select [[]] : String | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | GlobalDataFlow.cs:312:31:312:40 | sinkParam8 : String | +| GlobalDataFlow.cs:83:57:83:66 | { ..., ... } [[]] : String | GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | +| GlobalDataFlow.cs:83:59:83:64 | access to local variable sink13 : String | GlobalDataFlow.cs:83:57:83:66 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:85:22:85:128 | call to method Zip [[]] : String | GlobalDataFlow.cs:85:22:85:136 | call to method First : String | +| GlobalDataFlow.cs:85:22:85:136 | call to method First : String | GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | +| GlobalDataFlow.cs:85:22:85:136 | call to method First : String | GlobalDataFlow.cs:87:106:87:111 | access to local variable sink15 : String | +| GlobalDataFlow.cs:85:23:85:66 | (...) ... [[]] : String | GlobalDataFlow.cs:85:22:85:128 | call to method Zip [[]] : String | +| GlobalDataFlow.cs:85:57:85:66 | { ..., ... } [[]] : String | GlobalDataFlow.cs:85:23:85:66 | (...) ... [[]] : String | +| GlobalDataFlow.cs:85:59:85:64 | access to local variable sink14 : String | GlobalDataFlow.cs:85:57:85:66 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:87:22:87:128 | call to method Zip [[]] : String | GlobalDataFlow.cs:87:22:87:136 | call to method First : String | +| GlobalDataFlow.cs:87:22:87:136 | call to method First : String | GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | +| GlobalDataFlow.cs:87:70:87:113 | (...) ... [[]] : String | GlobalDataFlow.cs:87:22:87:128 | call to method Zip [[]] : String | +| GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | GlobalDataFlow.cs:87:70:87:113 | (...) ... [[]] : String | +| GlobalDataFlow.cs:87:106:87:111 | access to local variable sink15 : String | GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | +| GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | GlobalDataFlow.cs:136:21:136:34 | delegate call : String | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | +| GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | +| GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | +| GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield [[]] : String | GlobalDataFlow.cs:162:22:162:39 | call to method First : String | +| GlobalDataFlow.cs:162:22:162:39 | call to method First : String | GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | +| GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:181:21:181:26 | delegate call : String | +| GlobalDataFlow.cs:181:21:181:26 | delegate call : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | +| GlobalDataFlow.cs:208:38:208:61 | array creation of type String[] [[]] : String | GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | GlobalDataFlow.cs:213:22:213:28 | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | GlobalDataFlow.cs:215:22:215:28 | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | GlobalDataFlow.cs:217:22:217:28 | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:208:44:208:61 | { ..., ... } [[]] : String | GlobalDataFlow.cs:208:38:208:61 | array creation of type String[] [[]] : String | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:208:44:208:61 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:212:71:212:71 | x : String | GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | +| GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | GlobalDataFlow.cs:318:32:318:41 | sinkParam9 : String | +| GlobalDataFlow.cs:213:22:213:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | +| GlobalDataFlow.cs:213:22:213:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:213:22:213:39 | call to method Select [[]] : String | +| GlobalDataFlow.cs:213:22:213:39 | call to method Select [[]] : String | GlobalDataFlow.cs:213:22:213:47 | call to method First : String | +| GlobalDataFlow.cs:213:22:213:47 | call to method First : String | GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | +| GlobalDataFlow.cs:215:22:215:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:212:71:212:71 | x : String | +| GlobalDataFlow.cs:215:22:215:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:215:22:215:39 | call to method Select [[]] : String | +| GlobalDataFlow.cs:215:22:215:39 | call to method Select [[]] : String | GlobalDataFlow.cs:215:22:215:47 | call to method First : String | +| GlobalDataFlow.cs:215:22:215:47 | call to method First : String | GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | +| GlobalDataFlow.cs:217:22:217:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:217:22:217:49 | call to method Select [[]] : String | +| GlobalDataFlow.cs:217:22:217:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:324:32:324:42 | sinkParam11 : String | +| GlobalDataFlow.cs:217:22:217:49 | call to method Select [[]] : String | GlobalDataFlow.cs:217:22:217:57 | call to method First : String | +| GlobalDataFlow.cs:217:22:217:57 | call to method First : String | GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | +| GlobalDataFlow.cs:238:20:238:49 | call to method Run [Result] : String | GlobalDataFlow.cs:239:22:239:25 | access to local variable task [Result] : String | +| GlobalDataFlow.cs:238:20:238:49 | call to method Run [Result] : String | GlobalDataFlow.cs:241:28:241:31 | access to local variable task [Result] : String | +| GlobalDataFlow.cs:238:35:238:48 | "taint source" : String | GlobalDataFlow.cs:238:20:238:49 | call to method Run [Result] : String | +| GlobalDataFlow.cs:239:22:239:25 | access to local variable task [Result] : String | GlobalDataFlow.cs:239:22:239:32 | access to property Result : String | +| GlobalDataFlow.cs:239:22:239:32 | access to property Result : String | GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | +| GlobalDataFlow.cs:241:22:241:31 | await ... : String | GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | +| GlobalDataFlow.cs:241:28:241:31 | access to local variable task [Result] : String | GlobalDataFlow.cs:241:22:241:31 | await ... : String | +| GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | GlobalDataFlow.cs:256:16:256:25 | access to parameter sinkParam0 : String | +| GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:256:16:256:25 | access to parameter sinkParam0 : String | GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | +| GlobalDataFlow.cs:260:26:260:35 | sinkParam1 : String | GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:265:26:265:35 | sinkParam3 : String | GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:270:26:270:35 | sinkParam4 : String | GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:275:26:275:35 | sinkParam5 : String | GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:280:26:280:35 | sinkParam6 : String | GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:285:26:285:35 | sinkParam7 : String | GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:312:31:312:40 | sinkParam8 : String | GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:318:32:318:41 | sinkParam9 : String | GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:324:32:324:42 | sinkParam11 : String | GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | +| GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | +| GlobalDataFlow.cs:343:9:343:26 | SSA def(x) : String | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | +| GlobalDataFlow.cs:343:13:343:26 | "taint source" : String | GlobalDataFlow.cs:343:9:343:26 | SSA def(x) : String | +| GlobalDataFlow.cs:348:9:348:26 | SSA def(x) : String | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | +| GlobalDataFlow.cs:348:13:348:26 | "taint source" : String | GlobalDataFlow.cs:348:9:348:26 | SSA def(x) : String | +| GlobalDataFlow.cs:354:22:354:35 | "taint source" : String | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield [[]] : String | +| GlobalDataFlow.cs:379:41:379:41 | x : String | GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | +| GlobalDataFlow.cs:379:41:379:41 | x : String | GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | +| GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | GlobalDataFlow.cs:54:15:54:15 | x : String | +| GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | GlobalDataFlow.cs:265:26:265:35 | sinkParam3 : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | GlobalDataFlow.cs:57:37:57:37 | x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | GlobalDataFlow.cs:275:26:275:35 | sinkParam5 : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | GlobalDataFlow.cs:280:26:280:35 | sinkParam6 : String | +| GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | +| GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:402:16:402:21 | access to local variable sink11 : String | +| GlobalDataFlow.cs:402:16:402:21 | access to local variable sink11 : String | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | +| GlobalDataFlow.cs:424:9:424:11 | value : String | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | +| GlobalDataFlow.cs:435:22:435:35 | "taint source" : String | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | @@ -191,7 +228,8 @@ edges | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | Splitting.cs:11:19:11:19 | access to local variable x | | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | -| Splitting.cs:21:9:21:11 | value : String | Splitting.cs:21:28:21:32 | access to parameter value | +| Splitting.cs:21:9:21:11 | value : String | Splitting.cs:21:28:21:32 | access to parameter value : String | +| Splitting.cs:21:28:21:32 | access to parameter value : String | Splitting.cs:21:21:21:33 | call to method Return | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:31:19:31:25 | [b (line 24): false] access to parameter tainted : String | @@ -203,149 +241,193 @@ edges | Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element : String | Splitting.cs:34:19:34:19 | access to local variable x | | Splitting.cs:31:19:31:25 | [b (line 24): false] access to parameter tainted : String | Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element : String | | Splitting.cs:31:19:31:25 | [b (line 24): true] access to parameter tainted : String | Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element : String | +| Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | Splitting.cs:41:19:41:19 | access to local variable s | +| Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | Splitting.cs:43:19:43:19 | access to local variable s | +| Splitting.cs:48:36:48:49 | "taint source" : String | Splitting.cs:50:19:50:19 | access to local variable s | +| Splitting.cs:48:36:48:49 | "taint source" : String | Splitting.cs:52:19:52:19 | access to local variable s | nodes | Capture.cs:7:20:7:26 | tainted : String | semmle.label | tainted : String | -| Capture.cs:9:9:13:9 | SSA capture def(tainted) : String | semmle.label | SSA capture def(tainted) : String | | Capture.cs:12:19:12:24 | access to local variable sink27 | semmle.label | access to local variable sink27 | -| Capture.cs:14:9:14:20 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | -| Capture.cs:18:13:22:13 | SSA capture def(tainted) : String | semmle.label | SSA capture def(tainted) : String | | Capture.cs:21:23:21:28 | access to local variable sink28 | semmle.label | access to local variable sink28 | -| Capture.cs:25:9:25:20 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | -| Capture.cs:27:43:32:9 | SSA capture def(tainted) : String | semmle.label | SSA capture def(tainted) : String | | Capture.cs:30:19:30:24 | access to local variable sink29 | semmle.label | access to local variable sink29 | -| Capture.cs:33:9:33:40 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:50:50:50:55 | sink39 : String | semmle.label | sink39 : String | -| Capture.cs:52:13:59:14 | [implicit argument] sink39 : String | semmle.label | [implicit argument] sink39 : String | -| Capture.cs:55:27:58:17 | SSA capture def(sink39) : String | semmle.label | SSA capture def(sink39) : String | | Capture.cs:57:27:57:32 | access to parameter sink39 | semmle.label | access to parameter sink39 | | Capture.cs:61:36:61:42 | access to parameter tainted : String | semmle.label | access to parameter tainted : String | | Capture.cs:69:13:69:35 | SSA def(sink30) : String | semmle.label | SSA def(sink30) : String | | Capture.cs:69:22:69:35 | "taint source" : String | semmle.label | "taint source" : String | -| Capture.cs:71:9:71:21 | SSA call def(sink30) : String | semmle.label | SSA call def(sink30) : String | | Capture.cs:72:15:72:20 | access to local variable sink30 | semmle.label | access to local variable sink30 | | Capture.cs:79:17:79:39 | SSA def(sink31) : String | semmle.label | SSA def(sink31) : String | | Capture.cs:79:26:79:39 | "taint source" : String | semmle.label | "taint source" : String | -| Capture.cs:83:9:83:21 | SSA call def(sink31) : String | semmle.label | SSA call def(sink31) : String | | Capture.cs:84:15:84:20 | access to local variable sink31 | semmle.label | access to local variable sink31 | | Capture.cs:89:13:89:35 | SSA def(sink32) : String | semmle.label | SSA def(sink32) : String | | Capture.cs:89:22:89:35 | "taint source" : String | semmle.label | "taint source" : String | -| Capture.cs:92:9:92:41 | SSA call def(sink32) : String | semmle.label | SSA call def(sink32) : String | | Capture.cs:93:15:93:20 | access to local variable sink32 | semmle.label | access to local variable sink32 | | Capture.cs:115:17:115:39 | SSA def(sink40) : String | semmle.label | SSA def(sink40) : String | | Capture.cs:115:26:115:39 | "taint source" : String | semmle.label | "taint source" : String | -| Capture.cs:121:9:121:35 | SSA call def(sink40) : String | semmle.label | SSA call def(sink40) : String | | Capture.cs:122:15:122:20 | access to local variable sink40 | semmle.label | access to local variable sink40 | | Capture.cs:125:25:125:31 | tainted : String | semmle.label | tainted : String | -| Capture.cs:132:9:132:25 | SSA call def(sink33) : String | semmle.label | SSA call def(sink33) : String | -| Capture.cs:132:9:132:25 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:133:15:133:20 | access to local variable sink33 | semmle.label | access to local variable sink33 | -| Capture.cs:144:9:144:25 | SSA call def(sink34) : String | semmle.label | SSA call def(sink34) : String | -| Capture.cs:144:9:144:25 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:145:15:145:20 | access to local variable sink34 | semmle.label | access to local variable sink34 | -| Capture.cs:153:9:153:45 | SSA call def(sink35) : String | semmle.label | SSA call def(sink35) : String | -| Capture.cs:153:9:153:45 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:154:15:154:20 | access to local variable sink35 | semmle.label | access to local variable sink35 | -| Capture.cs:160:22:160:38 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 : String | semmle.label | call to local function CaptureThrough4 : String | | Capture.cs:161:15:161:20 | access to local variable sink36 | semmle.label | access to local variable sink36 | -| Capture.cs:168:9:168:32 | SSA call def(sink37) : String | semmle.label | SSA call def(sink37) : String | | Capture.cs:168:25:168:31 | access to parameter tainted : String | semmle.label | access to parameter tainted : String | | Capture.cs:169:15:169:20 | access to local variable sink37 | semmle.label | access to local variable sink37 | | Capture.cs:194:22:194:32 | call to local function Id : String | semmle.label | call to local function Id : String | | Capture.cs:194:25:194:31 | access to parameter tainted : String | semmle.label | access to parameter tainted : String | | Capture.cs:195:15:195:20 | access to local variable sink38 | semmle.label | access to local variable sink38 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | semmle.label | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | semmle.label | access to property SinkProperty0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | semmle.label | sinkParam2 : String | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | semmle.label | access to parameter sinkParam2 | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:15:53:15 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:56:37:56:37 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | semmle.label | call to method Return : String | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | semmle.label | access to local variable sink0 | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | semmle.label | (...) ... : String | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | semmle.label | call to method Invoke : String | -| GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | semmle.label | access to local variable sink0 : String | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | -| GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | semmle.label | access to local variable sink1 : String | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | semmle.label | SSA def(sink2) : String | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | semmle.label | access to local variable sink2 | -| GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | semmle.label | SSA def(sink3) : String | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | semmle.label | access to local variable sink3 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | semmle.label | delegate call : String | -| GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | semmle.label | access to local variable sink4 | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | semmle.label | call to method ApplyFunc : String | -| GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | semmle.label | access to local variable sink4 : String | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | semmle.label | access to local variable sink5 | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | semmle.label | call to method Out : String | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | semmle.label | access to local variable sink6 | -| GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | semmle.label | SSA def(sink7) : String | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | semmle.label | access to local variable sink7 | -| GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | semmle.label | SSA def(sink8) : String | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | semmle.label | access to local variable sink8 | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | semmle.label | call to method TaintedParam : String | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | semmle.label | access to local variable sink23 | -| GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:180:21:180:26 | delegate call : String | semmle.label | delegate call : String | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | semmle.label | access to local variable sink9 | -| GlobalDataFlow.cs:189:22:189:42 | [library code] object creation of type Lazy : String | semmle.label | [library code] object creation of type Lazy : String | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | semmle.label | object creation of type Lazy [Value] : String | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | semmle.label | access to property Value : String | -| GlobalDataFlow.cs:189:39:189:41 | [output] delegate creation of type Func : String | semmle.label | [output] delegate creation of type Func : String | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | semmle.label | access to local variable sink10 | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | semmle.label | access to property OutProperty : String | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | semmle.label | access to local variable sink19 | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | semmle.label | sinkParam0 : String | -| GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | semmle.label | access to parameter sinkParam0 : String | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | semmle.label | access to parameter sinkParam0 | -| GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | semmle.label | sinkParam1 : String | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | semmle.label | access to parameter sinkParam1 | -| GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | semmle.label | sinkParam3 : String | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | semmle.label | access to parameter sinkParam3 | -| GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | semmle.label | sinkParam4 : String | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | semmle.label | access to parameter sinkParam4 | -| GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | semmle.label | sinkParam5 : String | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | semmle.label | access to parameter sinkParam5 | -| GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | semmle.label | sinkParam6 : String | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | semmle.label | access to parameter sinkParam6 | -| GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | semmle.label | sinkParam7 : String | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | semmle.label | access to parameter sinkParam7 | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | -| GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | -| GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | semmle.label | tainted : String | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | semmle.label | access to local variable sink11 | -| GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | semmle.label | access to local variable sink11 : String | -| GlobalDataFlow.cs:406:9:406:11 | value : String | semmle.label | value : String | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | -| GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | semmle.label | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | semmle.label | access to property SinkProperty0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | semmle.label | sinkParam2 : String | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | semmle.label | access to parameter sinkParam2 | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:15:54:15 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:57:37:57:37 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | semmle.label | call to method Return : String | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | semmle.label | access to local variable sink0 | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | semmle.label | (...) ... : String | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | semmle.label | call to method Invoke : String | +| GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | semmle.label | access to local variable sink0 : String | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | +| GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | semmle.label | access to local variable sink1 : String | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | semmle.label | SSA def(sink2) : String | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | semmle.label | access to local variable sink2 | +| GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | semmle.label | SSA def(sink3) : String | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | semmle.label | access to local variable sink3 | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven [[]] : String | semmle.label | call to method SelectEven [[]] : String | +| GlobalDataFlow.cs:81:22:81:93 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:81:23:81:65 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:81:57:81:65 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:81:59:81:63 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | semmle.label | access to local variable sink13 | +| GlobalDataFlow.cs:83:22:83:87 | call to method Select [[]] : String | semmle.label | call to method Select [[]] : String | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:83:57:83:66 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:83:59:83:64 | access to local variable sink13 : String | semmle.label | access to local variable sink13 : String | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | semmle.label | access to local variable sink14 | +| GlobalDataFlow.cs:85:22:85:128 | call to method Zip [[]] : String | semmle.label | call to method Zip [[]] : String | +| GlobalDataFlow.cs:85:22:85:136 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:85:23:85:66 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:85:57:85:66 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:85:59:85:64 | access to local variable sink14 : String | semmle.label | access to local variable sink14 : String | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | semmle.label | access to local variable sink15 | +| GlobalDataFlow.cs:87:22:87:128 | call to method Zip [[]] : String | semmle.label | call to method Zip [[]] : String | +| GlobalDataFlow.cs:87:22:87:136 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:87:70:87:113 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:87:106:87:111 | access to local variable sink15 : String | semmle.label | access to local variable sink15 : String | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | semmle.label | access to local variable sink16 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | semmle.label | delegate call : String | +| GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | semmle.label | access to local variable sink4 | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | semmle.label | call to method ApplyFunc : String | +| GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | semmle.label | access to local variable sink4 : String | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | semmle.label | access to local variable sink5 | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | semmle.label | call to method Out : String | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | semmle.label | access to local variable sink6 | +| GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | semmle.label | SSA def(sink7) : String | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | semmle.label | access to local variable sink7 | +| GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | semmle.label | SSA def(sink8) : String | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | semmle.label | access to local variable sink8 | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield [[]] : String | semmle.label | call to method OutYield [[]] : String | +| GlobalDataFlow.cs:162:22:162:39 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | semmle.label | access to local variable sink12 | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | semmle.label | call to method TaintedParam : String | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | semmle.label | access to local variable sink23 | +| GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:181:21:181:26 | delegate call : String | semmle.label | delegate call : String | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | semmle.label | access to local variable sink9 | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | semmle.label | object creation of type Lazy [Value] : String | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | semmle.label | access to property Value : String | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | semmle.label | access to local variable sink10 | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | semmle.label | access to property OutProperty : String | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | semmle.label | access to local variable sink19 | +| GlobalDataFlow.cs:208:38:208:61 | array creation of type String[] [[]] : String | semmle.label | array creation of type String[] [[]] : String | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | semmle.label | call to method AsQueryable [[]] : String | +| GlobalDataFlow.cs:208:44:208:61 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | semmle.label | sinkParam10 : String | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | semmle.label | access to parameter sinkParam10 | +| GlobalDataFlow.cs:212:71:212:71 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:213:22:213:28 | access to local variable tainted [[]] : String | semmle.label | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:213:22:213:39 | call to method Select [[]] : String | semmle.label | call to method Select [[]] : String | +| GlobalDataFlow.cs:213:22:213:47 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | semmle.label | access to local variable sink24 | +| GlobalDataFlow.cs:215:22:215:28 | access to local variable tainted [[]] : String | semmle.label | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:215:22:215:39 | call to method Select [[]] : String | semmle.label | call to method Select [[]] : String | +| GlobalDataFlow.cs:215:22:215:47 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | semmle.label | access to local variable sink25 | +| GlobalDataFlow.cs:217:22:217:28 | access to local variable tainted [[]] : String | semmle.label | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:217:22:217:49 | call to method Select [[]] : String | semmle.label | call to method Select [[]] : String | +| GlobalDataFlow.cs:217:22:217:57 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | semmle.label | access to local variable sink26 | +| GlobalDataFlow.cs:238:20:238:49 | call to method Run [Result] : String | semmle.label | call to method Run [Result] : String | +| GlobalDataFlow.cs:238:35:238:48 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:239:22:239:25 | access to local variable task [Result] : String | semmle.label | access to local variable task [Result] : String | +| GlobalDataFlow.cs:239:22:239:32 | access to property Result : String | semmle.label | access to property Result : String | +| GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | semmle.label | access to local variable sink41 | +| GlobalDataFlow.cs:241:22:241:31 | await ... : String | semmle.label | await ... : String | +| GlobalDataFlow.cs:241:28:241:31 | access to local variable task [Result] : String | semmle.label | access to local variable task [Result] : String | +| GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | semmle.label | access to local variable sink42 | +| GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | semmle.label | sinkParam0 : String | +| GlobalDataFlow.cs:256:16:256:25 | access to parameter sinkParam0 : String | semmle.label | access to parameter sinkParam0 : String | +| GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | semmle.label | access to parameter sinkParam0 | +| GlobalDataFlow.cs:260:26:260:35 | sinkParam1 : String | semmle.label | sinkParam1 : String | +| GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | semmle.label | access to parameter sinkParam1 | +| GlobalDataFlow.cs:265:26:265:35 | sinkParam3 : String | semmle.label | sinkParam3 : String | +| GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | semmle.label | access to parameter sinkParam3 | +| GlobalDataFlow.cs:270:26:270:35 | sinkParam4 : String | semmle.label | sinkParam4 : String | +| GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | semmle.label | access to parameter sinkParam4 | +| GlobalDataFlow.cs:275:26:275:35 | sinkParam5 : String | semmle.label | sinkParam5 : String | +| GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | semmle.label | access to parameter sinkParam5 | +| GlobalDataFlow.cs:280:26:280:35 | sinkParam6 : String | semmle.label | sinkParam6 : String | +| GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | semmle.label | access to parameter sinkParam6 | +| GlobalDataFlow.cs:285:26:285:35 | sinkParam7 : String | semmle.label | sinkParam7 : String | +| GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | semmle.label | access to parameter sinkParam7 | +| GlobalDataFlow.cs:312:31:312:40 | sinkParam8 : String | semmle.label | sinkParam8 : String | +| GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | semmle.label | access to parameter sinkParam8 | +| GlobalDataFlow.cs:318:32:318:41 | sinkParam9 : String | semmle.label | sinkParam9 : String | +| GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | semmle.label | access to parameter sinkParam9 | +| GlobalDataFlow.cs:324:32:324:42 | sinkParam11 : String | semmle.label | sinkParam11 : String | +| GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | semmle.label | access to parameter sinkParam11 | +| GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:343:9:343:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | +| GlobalDataFlow.cs:343:13:343:26 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:348:9:348:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | +| GlobalDataFlow.cs:348:13:348:26 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:354:22:354:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:379:41:379:41 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:379:41:379:41 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:398:39:398:45 | tainted : String | semmle.label | tainted : String | +| GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | semmle.label | access to local variable sink11 | +| GlobalDataFlow.cs:402:16:402:21 | access to local variable sink11 : String | semmle.label | access to local variable sink11 : String | +| GlobalDataFlow.cs:424:9:424:11 | value : String | semmle.label | value : String | +| GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | +| GlobalDataFlow.cs:435:22:435:35 | "taint source" : String | semmle.label | "taint source" : String | | Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String | | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String | @@ -355,7 +437,8 @@ nodes | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | semmle.label | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | semmle.label | access to local variable x | | Splitting.cs:21:9:21:11 | value : String | semmle.label | value : String | -| Splitting.cs:21:28:21:32 | access to parameter value | semmle.label | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | semmle.label | call to method Return | +| Splitting.cs:21:28:21:32 | access to parameter value : String | semmle.label | access to parameter value : String | | Splitting.cs:24:28:24:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | semmle.label | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | semmle.label | [b (line 24): true] access to parameter tainted : String | @@ -366,24 +449,42 @@ nodes | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | semmle.label | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | semmle.label | [b (line 24): true] access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | semmle.label | access to local variable x | +| Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | semmle.label | [b (line 37): true] "taint source" : String | +| Splitting.cs:41:19:41:19 | access to local variable s | semmle.label | access to local variable s | +| Splitting.cs:43:19:43:19 | access to local variable s | semmle.label | access to local variable s | +| Splitting.cs:48:36:48:49 | "taint source" : String | semmle.label | "taint source" : String | +| Splitting.cs:50:19:50:19 | access to local variable s | semmle.label | access to local variable s | +| Splitting.cs:52:19:52:19 | access to local variable s | semmle.label | access to local variable s | #select | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | [b (line 24): true] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | [b (line 3): true] access to local variable x | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | access to field SinkField0 | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | access to local variable sink0 | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | access to local variable sink1 | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | access to local variable sink10 | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | access to local variable sink11 | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | access to local variable sink19 | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | access to local variable sink2 | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | access to local variable sink20 | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | access to local variable sink23 | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | access to field SinkField0 | +| Splitting.cs:41:19:41:19 | access to local variable s | Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | Splitting.cs:41:19:41:19 | access to local variable s | access to local variable s | +| Splitting.cs:43:19:43:19 | access to local variable s | Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | Splitting.cs:43:19:43:19 | access to local variable s | access to local variable s | +| Splitting.cs:50:19:50:19 | access to local variable s | Splitting.cs:48:36:48:49 | "taint source" : String | Splitting.cs:50:19:50:19 | access to local variable s | access to local variable s | +| Splitting.cs:52:19:52:19 | access to local variable s | Splitting.cs:48:36:48:49 | "taint source" : String | Splitting.cs:52:19:52:19 | access to local variable s | access to local variable s | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | access to local variable sink0 | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | access to local variable sink1 | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | access to local variable sink10 | +| GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | access to local variable sink11 | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | GlobalDataFlow.cs:354:22:354:35 | "taint source" : String | GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | access to local variable sink12 | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | access to local variable sink13 | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | access to local variable sink14 | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | access to local variable sink15 | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | access to local variable sink16 | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | GlobalDataFlow.cs:435:22:435:35 | "taint source" : String | GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | access to local variable sink19 | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | access to local variable sink2 | +| GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | access to local variable sink20 | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | access to local variable sink23 | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | access to local variable sink24 | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | access to local variable sink25 | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | access to local variable sink26 | | Capture.cs:12:19:12:24 | access to local variable sink27 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:12:19:12:24 | access to local variable sink27 | access to local variable sink27 | | Capture.cs:21:23:21:28 | access to local variable sink28 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:21:23:21:28 | access to local variable sink28 | access to local variable sink28 | | Capture.cs:30:19:30:24 | access to local variable sink29 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:30:19:30:24 | access to local variable sink29 | access to local variable sink29 | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | access to local variable sink3 | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | access to local variable sink3 | | Capture.cs:72:15:72:20 | access to local variable sink30 | Capture.cs:69:22:69:35 | "taint source" : String | Capture.cs:72:15:72:20 | access to local variable sink30 | access to local variable sink30 | | Capture.cs:84:15:84:20 | access to local variable sink31 | Capture.cs:79:26:79:39 | "taint source" : String | Capture.cs:84:15:84:20 | access to local variable sink31 | access to local variable sink31 | | Capture.cs:93:15:93:20 | access to local variable sink32 | Capture.cs:89:22:89:35 | "taint source" : String | Capture.cs:93:15:93:20 | access to local variable sink32 | access to local variable sink32 | @@ -393,23 +494,29 @@ nodes | Capture.cs:161:15:161:20 | access to local variable sink36 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:161:15:161:20 | access to local variable sink36 | access to local variable sink36 | | Capture.cs:169:15:169:20 | access to local variable sink37 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:169:15:169:20 | access to local variable sink37 | access to local variable sink37 | | Capture.cs:195:15:195:20 | access to local variable sink38 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:195:15:195:20 | access to local variable sink38 | access to local variable sink38 | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | access to local variable sink4 | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | access to local variable sink4 | | Capture.cs:122:15:122:20 | access to local variable sink40 | Capture.cs:115:26:115:39 | "taint source" : String | Capture.cs:122:15:122:20 | access to local variable sink40 | access to local variable sink40 | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | access to local variable sink5 | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | access to local variable sink6 | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | access to local variable sink7 | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | access to local variable sink8 | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | access to local variable sink9 | +| GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | GlobalDataFlow.cs:238:35:238:48 | "taint source" : String | GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | access to local variable sink41 | +| GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | GlobalDataFlow.cs:238:35:238:48 | "taint source" : String | GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | access to local variable sink42 | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | access to local variable sink5 | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | access to local variable sink6 | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | GlobalDataFlow.cs:343:13:343:26 | "taint source" : String | GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | access to local variable sink7 | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | GlobalDataFlow.cs:348:13:348:26 | "taint source" : String | GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | access to local variable sink8 | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | access to local variable sink9 | | Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:34:19:34:19 | access to local variable x | access to local variable x | | Capture.cs:57:27:57:32 | access to parameter sink39 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:57:27:57:32 | access to parameter sink39 | access to parameter sink39 | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | -| Splitting.cs:21:28:21:32 | access to parameter value | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:28:21:32 | access to parameter value | access to parameter value | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | access to property SinkProperty0 | +| GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | access to property SinkProperty0 | +| Splitting.cs:21:21:21:33 | call to method Return | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:21:21:33 | call to method Return | call to method Return | diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected index 2b712d5c2f8c..1acaa3082567 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected +++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected @@ -1,204 +1,359 @@ | Capture.cs:33:9:33:40 | call to method Select | return | Capture.cs:33:9:33:40 | call to method Select | | Capture.cs:33:9:33:40 | call to method Select | yield return | Capture.cs:33:9:33:40 | call to method Select | | Capture.cs:33:9:33:50 | call to method ToArray | return | Capture.cs:33:9:33:50 | call to method ToArray | -| Capture.cs:33:30:33:39 | [implicit call] access to local variable captureIn3 | return | Capture.cs:33:30:33:39 | [output] access to local variable captureIn3 | | Capture.cs:71:9:71:21 | call to local function CaptureOut1 | captured sink30 | Capture.cs:71:9:71:21 | SSA call def(sink30) | | Capture.cs:83:9:83:21 | [transitive] call to local function CaptureOut2 | captured sink31 | Capture.cs:83:9:83:21 | SSA call def(sink31) | -| Capture.cs:92:9:92:41 | call to method Select | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) | +| Capture.cs:92:9:92:41 | [transitive] call to method Select | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) | | Capture.cs:92:9:92:41 | call to method Select | return | Capture.cs:92:9:92:41 | call to method Select | | Capture.cs:92:9:92:41 | call to method Select | yield return | Capture.cs:92:9:92:41 | call to method Select | | Capture.cs:92:9:92:51 | call to method ToArray | return | Capture.cs:92:9:92:51 | call to method ToArray | -| Capture.cs:92:30:92:40 | [implicit call] access to local variable captureOut3 | captured sink32 | Capture.cs:92:9:92:41 | SSA call def(sink32) | -| Capture.cs:92:30:92:40 | [implicit call] access to local variable captureOut3 | return | Capture.cs:92:30:92:40 | [output] access to local variable captureOut3 | | Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured nonSink0 | Capture.cs:121:9:121:35 | SSA call def(nonSink0) | | Capture.cs:121:9:121:35 | [transitive] call to local function CaptureOutMultipleLambdas | captured sink40 | Capture.cs:121:9:121:35 | SSA call def(sink40) | | Capture.cs:132:9:132:25 | call to local function CaptureThrough1 | captured sink33 | Capture.cs:132:9:132:25 | SSA call def(sink33) | | Capture.cs:144:9:144:25 | [transitive] call to local function CaptureThrough2 | captured sink34 | Capture.cs:144:9:144:25 | SSA call def(sink34) | -| Capture.cs:153:9:153:45 | call to method Select | captured sink35 | Capture.cs:153:9:153:45 | SSA call def(sink35) | +| Capture.cs:153:9:153:45 | [transitive] call to method Select | captured sink35 | Capture.cs:153:9:153:45 | SSA call def(sink35) | | Capture.cs:153:9:153:45 | call to method Select | return | Capture.cs:153:9:153:45 | call to method Select | | Capture.cs:153:9:153:45 | call to method Select | yield return | Capture.cs:153:9:153:45 | call to method Select | | Capture.cs:153:9:153:55 | call to method ToArray | return | Capture.cs:153:9:153:55 | call to method ToArray | -| Capture.cs:153:30:153:44 | [implicit call] access to local variable captureThrough3 | captured sink35 | Capture.cs:153:9:153:45 | SSA call def(sink35) | -| Capture.cs:153:30:153:44 | [implicit call] access to local variable captureThrough3 | return | Capture.cs:153:30:153:44 | [output] access to local variable captureThrough3 | | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 | return | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 | | Capture.cs:168:9:168:32 | call to local function CaptureThrough5 | captured sink37 | Capture.cs:168:9:168:32 | SSA call def(sink37) | | Capture.cs:191:20:191:22 | call to local function M | return | Capture.cs:191:20:191:22 | call to local function M | | Capture.cs:194:22:194:32 | call to local function Id | return | Capture.cs:194:22:194:32 | call to local function Id | | Capture.cs:196:20:196:25 | call to local function Id | return | Capture.cs:196:20:196:25 | call to local function Id | -| GlobalDataFlow.cs:25:9:25:26 | access to property SinkProperty0 | return | GlobalDataFlow.cs:25:9:25:26 | access to property SinkProperty0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | return | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:29:9:29:29 | access to property NonSinkProperty0 | return | GlobalDataFlow.cs:29:9:29:29 | access to property NonSinkProperty0 | -| GlobalDataFlow.cs:30:15:30:35 | access to property NonSinkProperty0 | return | GlobalDataFlow.cs:30:15:30:35 | access to property NonSinkProperty0 | -| GlobalDataFlow.cs:31:9:31:29 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:31:9:31:29 | access to property NonSinkProperty1 | -| GlobalDataFlow.cs:32:15:32:35 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:32:15:32:35 | access to property NonSinkProperty1 | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 | -| GlobalDataFlow.cs:36:26:36:58 | call to method GetMethod | return | GlobalDataFlow.cs:36:26:36:58 | call to method GetMethod | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 | -| GlobalDataFlow.cs:38:9:38:37 | call to method Invoke | return | GlobalDataFlow.cs:38:9:38:37 | call to method Invoke | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 | return | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 | return | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 | -| GlobalDataFlow.cs:64:9:64:18 | access to property InProperty | return | GlobalDataFlow.cs:64:9:64:18 | access to property InProperty | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 | return | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 | -| GlobalDataFlow.cs:67:9:67:21 | access to property NonInProperty | return | GlobalDataFlow.cs:67:9:67:21 | access to property NonInProperty | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return | return | GlobalDataFlow.cs:70:21:70:46 | call to method Return | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 | -| GlobalDataFlow.cs:72:29:72:64 | call to method GetMethod | return | GlobalDataFlow.cs:72:29:72:64 | call to method GetMethod | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke | return | GlobalDataFlow.cs:72:29:72:101 | call to method Invoke | -| GlobalDataFlow.cs:75:9:75:46 | call to method ReturnOut | out | GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) | -| GlobalDataFlow.cs:75:9:75:46 | call to method ReturnOut | ref | GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) | -| GlobalDataFlow.cs:78:9:78:46 | call to method ReturnRef | out | GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) | -| GlobalDataFlow.cs:78:9:78:46 | call to method ReturnRef | ref | GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven | return | GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven | yield return | GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven | -| GlobalDataFlow.cs:80:22:80:93 | call to method First | return | GlobalDataFlow.cs:80:22:80:93 | call to method First | -| GlobalDataFlow.cs:82:22:82:87 | call to method Select | return | GlobalDataFlow.cs:82:22:82:87 | call to method Select | -| GlobalDataFlow.cs:82:22:82:87 | call to method Select | yield return | GlobalDataFlow.cs:82:22:82:87 | call to method Select | -| GlobalDataFlow.cs:82:22:82:95 | call to method First | return | GlobalDataFlow.cs:82:22:82:95 | call to method First | -| GlobalDataFlow.cs:82:76:82:86 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:82:76:82:86 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:84:22:84:128 | call to method Zip | return | GlobalDataFlow.cs:84:22:84:128 | call to method Zip | -| GlobalDataFlow.cs:84:22:84:128 | call to method Zip | yield return | GlobalDataFlow.cs:84:22:84:128 | call to method Zip | -| GlobalDataFlow.cs:84:22:84:136 | call to method First | return | GlobalDataFlow.cs:84:22:84:136 | call to method First | -| GlobalDataFlow.cs:84:117:84:127 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:84:117:84:127 | [output] (...) => ... | -| GlobalDataFlow.cs:86:22:86:128 | call to method Zip | return | GlobalDataFlow.cs:86:22:86:128 | call to method Zip | -| GlobalDataFlow.cs:86:22:86:128 | call to method Zip | yield return | GlobalDataFlow.cs:86:22:86:128 | call to method Zip | -| GlobalDataFlow.cs:86:22:86:136 | call to method First | return | GlobalDataFlow.cs:86:22:86:136 | call to method First | -| GlobalDataFlow.cs:86:117:86:127 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:86:117:86:127 | [output] (...) => ... | -| GlobalDataFlow.cs:88:22:88:110 | call to method Aggregate | return | GlobalDataFlow.cs:88:22:88:110 | call to method Aggregate | -| GlobalDataFlow.cs:88:83:88:101 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:88:83:88:101 | [output] (...) => ... | -| GlobalDataFlow.cs:88:104:88:109 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:88:104:88:109 | [output] (...) => ... | -| GlobalDataFlow.cs:90:22:90:110 | call to method Aggregate | return | GlobalDataFlow.cs:90:22:90:110 | call to method Aggregate | -| GlobalDataFlow.cs:90:83:90:101 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:90:83:90:101 | [output] (...) => ... | -| GlobalDataFlow.cs:90:104:90:109 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:90:104:90:109 | [output] (...) => ... | -| GlobalDataFlow.cs:93:9:93:42 | call to method TryParse | out | GlobalDataFlow.cs:93:36:93:41 | SSA def(sink21) | -| GlobalDataFlow.cs:93:9:93:42 | call to method TryParse | ref | GlobalDataFlow.cs:93:36:93:41 | SSA def(sink21) | -| GlobalDataFlow.cs:93:9:93:42 | call to method TryParse | return | GlobalDataFlow.cs:93:9:93:42 | call to method TryParse | -| GlobalDataFlow.cs:96:9:96:41 | call to method TryParse | out | GlobalDataFlow.cs:96:35:96:40 | SSA def(sink22) | -| GlobalDataFlow.cs:96:9:96:41 | call to method TryParse | ref | GlobalDataFlow.cs:96:35:96:40 | SSA def(sink22) | -| GlobalDataFlow.cs:96:9:96:41 | call to method TryParse | return | GlobalDataFlow.cs:96:9:96:41 | call to method TryParse | -| GlobalDataFlow.cs:100:24:100:33 | call to method Return | return | GlobalDataFlow.cs:100:24:100:33 | call to method Return | -| GlobalDataFlow.cs:102:28:102:63 | call to method GetMethod | return | GlobalDataFlow.cs:102:28:102:63 | call to method GetMethod | -| GlobalDataFlow.cs:102:28:102:103 | call to method Invoke | return | GlobalDataFlow.cs:102:28:102:103 | call to method Invoke | -| GlobalDataFlow.cs:104:9:104:49 | call to method ReturnOut | out | GlobalDataFlow.cs:104:27:104:34 | SSA def(nonSink0) | -| GlobalDataFlow.cs:104:9:104:49 | call to method ReturnOut | ref | GlobalDataFlow.cs:104:27:104:34 | SSA def(nonSink0) | -| GlobalDataFlow.cs:106:9:106:49 | call to method ReturnOut | out | GlobalDataFlow.cs:106:41:106:48 | SSA def(nonSink0) | -| GlobalDataFlow.cs:108:9:108:49 | call to method ReturnRef | out | GlobalDataFlow.cs:108:27:108:34 | SSA def(nonSink0) | -| GlobalDataFlow.cs:108:9:108:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:108:27:108:34 | SSA def(nonSink0) | -| GlobalDataFlow.cs:110:9:110:49 | call to method ReturnRef | out | GlobalDataFlow.cs:110:30:110:34 | SSA def(sink1) | -| GlobalDataFlow.cs:110:9:110:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:110:30:110:34 | SSA def(sink1) | -| GlobalDataFlow.cs:112:20:112:86 | call to method SelectEven | return | GlobalDataFlow.cs:112:20:112:86 | call to method SelectEven | -| GlobalDataFlow.cs:112:20:112:86 | call to method SelectEven | yield return | GlobalDataFlow.cs:112:20:112:86 | call to method SelectEven | -| GlobalDataFlow.cs:112:20:112:94 | call to method First | return | GlobalDataFlow.cs:112:20:112:94 | call to method First | -| GlobalDataFlow.cs:114:20:114:82 | call to method Select | return | GlobalDataFlow.cs:114:20:114:82 | call to method Select | -| GlobalDataFlow.cs:114:20:114:82 | call to method Select | yield return | GlobalDataFlow.cs:114:20:114:82 | call to method Select | -| GlobalDataFlow.cs:114:20:114:90 | call to method First | return | GlobalDataFlow.cs:114:20:114:90 | call to method First | -| GlobalDataFlow.cs:114:76:114:81 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:114:76:114:81 | [output] (...) => ... | -| GlobalDataFlow.cs:116:20:116:126 | call to method Zip | return | GlobalDataFlow.cs:116:20:116:126 | call to method Zip | -| GlobalDataFlow.cs:116:20:116:126 | call to method Zip | yield return | GlobalDataFlow.cs:116:20:116:126 | call to method Zip | -| GlobalDataFlow.cs:116:20:116:134 | call to method First | return | GlobalDataFlow.cs:116:20:116:134 | call to method First | -| GlobalDataFlow.cs:116:115:116:125 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:116:115:116:125 | [output] (...) => ... | -| GlobalDataFlow.cs:118:20:118:126 | call to method Zip | return | GlobalDataFlow.cs:118:20:118:126 | call to method Zip | -| GlobalDataFlow.cs:118:20:118:126 | call to method Zip | yield return | GlobalDataFlow.cs:118:20:118:126 | call to method Zip | -| GlobalDataFlow.cs:118:20:118:134 | call to method First | return | GlobalDataFlow.cs:118:20:118:134 | call to method First | -| GlobalDataFlow.cs:118:115:118:125 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:118:115:118:125 | [output] (...) => ... | -| GlobalDataFlow.cs:120:20:120:104 | call to method Aggregate | return | GlobalDataFlow.cs:120:20:120:104 | call to method Aggregate | -| GlobalDataFlow.cs:120:81:120:95 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:120:81:120:95 | [output] (...) => ... | -| GlobalDataFlow.cs:120:98:120:103 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:120:98:120:103 | [output] (...) => ... | -| GlobalDataFlow.cs:122:20:122:109 | call to method Aggregate | return | GlobalDataFlow.cs:122:20:122:109 | call to method Aggregate | -| GlobalDataFlow.cs:122:81:122:99 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:122:81:122:99 | [output] (...) => ... | -| GlobalDataFlow.cs:122:102:122:108 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:122:102:122:108 | [output] (...) => ... | -| GlobalDataFlow.cs:124:20:124:107 | call to method Aggregate | return | GlobalDataFlow.cs:124:20:124:107 | call to method Aggregate | -| GlobalDataFlow.cs:124:86:124:98 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:124:86:124:98 | [output] (...) => ... | -| GlobalDataFlow.cs:124:101:124:106 | [implicit call] (...) => ... | return | GlobalDataFlow.cs:124:101:124:106 | [output] (...) => ... | -| GlobalDataFlow.cs:127:9:127:46 | call to method TryParse | out | GlobalDataFlow.cs:127:38:127:45 | SSA def(nonSink2) | -| GlobalDataFlow.cs:127:9:127:46 | call to method TryParse | ref | GlobalDataFlow.cs:127:38:127:45 | SSA def(nonSink2) | -| GlobalDataFlow.cs:127:9:127:46 | call to method TryParse | return | GlobalDataFlow.cs:127:9:127:46 | call to method TryParse | -| GlobalDataFlow.cs:130:9:130:45 | call to method TryParse | out | GlobalDataFlow.cs:130:37:130:44 | SSA def(nonSink3) | -| GlobalDataFlow.cs:130:9:130:45 | call to method TryParse | ref | GlobalDataFlow.cs:130:37:130:44 | SSA def(nonSink3) | -| GlobalDataFlow.cs:130:9:130:45 | call to method TryParse | return | GlobalDataFlow.cs:130:9:130:45 | call to method TryParse | -| GlobalDataFlow.cs:134:45:134:64 | call to method ApplyFunc | return | GlobalDataFlow.cs:134:45:134:64 | call to method ApplyFunc | -| GlobalDataFlow.cs:135:21:135:34 | delegate call | return | GlobalDataFlow.cs:135:21:135:34 | delegate call | -| GlobalDataFlow.cs:139:20:139:36 | delegate call | return | GlobalDataFlow.cs:139:20:139:36 | delegate call | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc | -| GlobalDataFlow.cs:147:20:147:40 | call to method ApplyFunc | return | GlobalDataFlow.cs:147:20:147:40 | call to method ApplyFunc | -| GlobalDataFlow.cs:149:20:149:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:149:20:149:44 | call to method ApplyFunc | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out | return | GlobalDataFlow.cs:153:21:153:25 | call to method Out | -| GlobalDataFlow.cs:156:9:156:25 | call to method OutOut | out | GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) | -| GlobalDataFlow.cs:156:9:156:25 | call to method OutOut | ref | GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) | -| GlobalDataFlow.cs:159:9:159:25 | call to method OutRef | out | GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) | -| GlobalDataFlow.cs:159:9:159:25 | call to method OutRef | ref | GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) | -| GlobalDataFlow.cs:161:22:161:31 | call to method OutYield | return | GlobalDataFlow.cs:161:22:161:31 | call to method OutYield | -| GlobalDataFlow.cs:161:22:161:31 | call to method OutYield | yield return | GlobalDataFlow.cs:161:22:161:31 | call to method OutYield | -| GlobalDataFlow.cs:161:22:161:39 | call to method First | return | GlobalDataFlow.cs:161:22:161:39 | call to method First | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam | return | GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam | -| GlobalDataFlow.cs:167:20:167:27 | call to method NonOut | return | GlobalDataFlow.cs:167:20:167:27 | call to method NonOut | -| GlobalDataFlow.cs:169:9:169:31 | call to method NonOutOut | out | GlobalDataFlow.cs:169:23:169:30 | SSA def(nonSink0) | -| GlobalDataFlow.cs:169:9:169:31 | call to method NonOutOut | ref | GlobalDataFlow.cs:169:23:169:30 | SSA def(nonSink0) | -| GlobalDataFlow.cs:171:9:171:31 | call to method NonOutRef | out | GlobalDataFlow.cs:171:23:171:30 | SSA def(nonSink0) | -| GlobalDataFlow.cs:171:9:171:31 | call to method NonOutRef | ref | GlobalDataFlow.cs:171:23:171:30 | SSA def(nonSink0) | -| GlobalDataFlow.cs:173:20:173:32 | call to method NonOutYield | return | GlobalDataFlow.cs:173:20:173:32 | call to method NonOutYield | -| GlobalDataFlow.cs:173:20:173:32 | call to method NonOutYield | yield return | GlobalDataFlow.cs:173:20:173:32 | call to method NonOutYield | -| GlobalDataFlow.cs:173:20:173:40 | call to method First | return | GlobalDataFlow.cs:173:20:173:40 | call to method First | -| GlobalDataFlow.cs:175:20:175:44 | call to method NonTaintedParam | return | GlobalDataFlow.cs:175:20:175:44 | call to method NonTaintedParam | -| GlobalDataFlow.cs:180:21:180:26 | delegate call | return | GlobalDataFlow.cs:180:21:180:26 | delegate call | -| GlobalDataFlow.cs:185:20:185:27 | delegate call | return | GlobalDataFlow.cs:185:20:185:27 | delegate call | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy | return | GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value | return | GlobalDataFlow.cs:189:22:189:48 | access to property Value | -| GlobalDataFlow.cs:189:39:189:41 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:189:39:189:41 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:193:20:193:43 | object creation of type Lazy | return | GlobalDataFlow.cs:193:20:193:43 | object creation of type Lazy | -| GlobalDataFlow.cs:193:20:193:49 | access to property Value | return | GlobalDataFlow.cs:193:20:193:49 | access to property Value | -| GlobalDataFlow.cs:193:37:193:42 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:193:37:193:42 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty | return | GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty | -| GlobalDataFlow.cs:201:20:201:33 | access to property NonOutProperty | return | GlobalDataFlow.cs:201:20:201:33 | access to property NonOutProperty | -| GlobalDataFlow.cs:207:38:207:75 | call to method AsQueryable | return | GlobalDataFlow.cs:207:38:207:75 | call to method AsQueryable | -| GlobalDataFlow.cs:208:41:208:77 | call to method AsQueryable | return | GlobalDataFlow.cs:208:41:208:77 | call to method AsQueryable | -| GlobalDataFlow.cs:211:76:211:90 | call to method ReturnCheck2 | return | GlobalDataFlow.cs:211:76:211:90 | call to method ReturnCheck2 | -| GlobalDataFlow.cs:212:22:212:39 | call to method Select | return | GlobalDataFlow.cs:212:22:212:39 | call to method Select | -| GlobalDataFlow.cs:212:22:212:39 | call to method Select | yield return | GlobalDataFlow.cs:212:22:212:39 | call to method Select | -| GlobalDataFlow.cs:212:22:212:47 | call to method First | return | GlobalDataFlow.cs:212:22:212:47 | call to method First | -| GlobalDataFlow.cs:212:37:212:38 | [implicit call] access to local variable f1 | return | GlobalDataFlow.cs:212:37:212:38 | [output] access to local variable f1 | -| GlobalDataFlow.cs:214:22:214:39 | call to method Select | return | GlobalDataFlow.cs:214:22:214:39 | call to method Select | -| GlobalDataFlow.cs:214:22:214:47 | call to method First | return | GlobalDataFlow.cs:214:22:214:47 | call to method First | -| GlobalDataFlow.cs:214:37:214:38 | [implicit call] access to local variable f2 | return | GlobalDataFlow.cs:214:37:214:38 | [output] access to local variable f2 | -| GlobalDataFlow.cs:216:22:216:49 | call to method Select | return | GlobalDataFlow.cs:216:22:216:49 | call to method Select | -| GlobalDataFlow.cs:216:22:216:49 | call to method Select | yield return | GlobalDataFlow.cs:216:22:216:49 | call to method Select | -| GlobalDataFlow.cs:216:22:216:57 | call to method First | return | GlobalDataFlow.cs:216:22:216:57 | call to method First | -| GlobalDataFlow.cs:216:37:216:48 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:216:37:216:48 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:221:76:221:92 | call to method NonReturnCheck | return | GlobalDataFlow.cs:221:76:221:92 | call to method NonReturnCheck | -| GlobalDataFlow.cs:222:23:222:43 | call to method Select | return | GlobalDataFlow.cs:222:23:222:43 | call to method Select | -| GlobalDataFlow.cs:222:23:222:43 | call to method Select | yield return | GlobalDataFlow.cs:222:23:222:43 | call to method Select | -| GlobalDataFlow.cs:222:23:222:51 | call to method First | return | GlobalDataFlow.cs:222:23:222:51 | call to method First | -| GlobalDataFlow.cs:222:41:222:42 | [implicit call] access to local variable f1 | return | GlobalDataFlow.cs:222:41:222:42 | [output] access to local variable f1 | -| GlobalDataFlow.cs:224:19:224:39 | call to method Select | return | GlobalDataFlow.cs:224:19:224:39 | call to method Select | -| GlobalDataFlow.cs:224:19:224:47 | call to method First | return | GlobalDataFlow.cs:224:19:224:47 | call to method First | -| GlobalDataFlow.cs:224:37:224:38 | [implicit call] access to local variable f2 | return | GlobalDataFlow.cs:224:37:224:38 | [output] access to local variable f2 | -| GlobalDataFlow.cs:226:19:226:39 | call to method Select | return | GlobalDataFlow.cs:226:19:226:39 | call to method Select | -| GlobalDataFlow.cs:226:19:226:39 | call to method Select | yield return | GlobalDataFlow.cs:226:19:226:39 | call to method Select | -| GlobalDataFlow.cs:226:19:226:47 | call to method First | return | GlobalDataFlow.cs:226:19:226:47 | call to method First | -| GlobalDataFlow.cs:226:37:226:38 | [implicit call] access to local variable f3 | return | GlobalDataFlow.cs:226:37:226:38 | [output] access to local variable f3 | -| GlobalDataFlow.cs:228:19:228:39 | call to method Select | return | GlobalDataFlow.cs:228:19:228:39 | call to method Select | -| GlobalDataFlow.cs:228:19:228:47 | call to method First | return | GlobalDataFlow.cs:228:19:228:47 | call to method First | -| GlobalDataFlow.cs:228:37:228:38 | [implicit call] access to local variable f4 | return | GlobalDataFlow.cs:228:37:228:38 | [output] access to local variable f4 | -| GlobalDataFlow.cs:230:19:230:49 | call to method Select | return | GlobalDataFlow.cs:230:19:230:49 | call to method Select | -| GlobalDataFlow.cs:230:19:230:49 | call to method Select | yield return | GlobalDataFlow.cs:230:19:230:49 | call to method Select | -| GlobalDataFlow.cs:230:19:230:57 | call to method First | return | GlobalDataFlow.cs:230:19:230:57 | call to method First | -| GlobalDataFlow.cs:230:37:230:48 | [implicit call] delegate creation of type Func | return | GlobalDataFlow.cs:230:37:230:48 | [output] delegate creation of type Func | -| GlobalDataFlow.cs:279:17:279:38 | call to method ApplyFunc | return | GlobalDataFlow.cs:279:17:279:38 | call to method ApplyFunc | -| GlobalDataFlow.cs:368:16:368:19 | delegate call | return | GlobalDataFlow.cs:368:16:368:19 | delegate call | -| GlobalDataFlow.cs:433:44:433:47 | delegate call | return | GlobalDataFlow.cs:433:44:433:47 | delegate call | +| GlobalDataFlow.cs:26:9:26:26 | access to property SinkProperty0 | return | GlobalDataFlow.cs:26:9:26:26 | access to property SinkProperty0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | return | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:30:9:30:29 | access to property NonSinkProperty0 | return | GlobalDataFlow.cs:30:9:30:29 | access to property NonSinkProperty0 | +| GlobalDataFlow.cs:31:15:31:35 | access to property NonSinkProperty0 | return | GlobalDataFlow.cs:31:15:31:35 | access to property NonSinkProperty0 | +| GlobalDataFlow.cs:32:9:32:29 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:32:9:32:29 | access to property NonSinkProperty1 | +| GlobalDataFlow.cs:33:15:33:35 | access to property NonSinkProperty1 | return | GlobalDataFlow.cs:33:15:33:35 | access to property NonSinkProperty1 | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 | +| GlobalDataFlow.cs:37:26:37:58 | call to method GetMethod | qualifier | GlobalDataFlow.cs:37:26:37:41 | [post] typeof(...) | +| GlobalDataFlow.cs:37:26:37:58 | call to method GetMethod | return | GlobalDataFlow.cs:37:26:37:58 | call to method GetMethod | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 | +| GlobalDataFlow.cs:39:9:39:37 | call to method Invoke | return | GlobalDataFlow.cs:39:9:39:37 | call to method Invoke | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 | return | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 | return | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 | return | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 | return | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 | +| GlobalDataFlow.cs:65:9:65:18 | access to property InProperty | qualifier | GlobalDataFlow.cs:65:9:65:18 | [post] this access | +| GlobalDataFlow.cs:65:9:65:18 | access to property InProperty | return | GlobalDataFlow.cs:65:9:65:18 | access to property InProperty | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 | return | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 | +| GlobalDataFlow.cs:68:9:68:21 | access to property NonInProperty | qualifier | GlobalDataFlow.cs:68:9:68:21 | [post] this access | +| GlobalDataFlow.cs:68:9:68:21 | access to property NonInProperty | return | GlobalDataFlow.cs:68:9:68:21 | access to property NonInProperty | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return | return | GlobalDataFlow.cs:71:21:71:46 | call to method Return | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 | return | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 | +| GlobalDataFlow.cs:73:29:73:64 | call to method GetMethod | qualifier | GlobalDataFlow.cs:73:29:73:44 | [post] typeof(...) | +| GlobalDataFlow.cs:73:29:73:64 | call to method GetMethod | return | GlobalDataFlow.cs:73:29:73:64 | call to method GetMethod | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke | return | GlobalDataFlow.cs:73:29:73:101 | call to method Invoke | +| GlobalDataFlow.cs:76:9:76:46 | call to method ReturnOut | out | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) | +| GlobalDataFlow.cs:76:9:76:46 | call to method ReturnOut | ref | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) | +| GlobalDataFlow.cs:79:9:79:46 | call to method ReturnRef | out | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) | +| GlobalDataFlow.cs:79:9:79:46 | call to method ReturnRef | ref | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | return | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | yield return | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven | +| GlobalDataFlow.cs:81:22:81:93 | call to method First | return | GlobalDataFlow.cs:81:22:81:93 | call to method First | +| GlobalDataFlow.cs:83:22:83:87 | call to method Select | return | GlobalDataFlow.cs:83:22:83:87 | call to method Select | +| GlobalDataFlow.cs:83:22:83:87 | call to method Select | yield return | GlobalDataFlow.cs:83:22:83:87 | call to method Select | +| GlobalDataFlow.cs:83:22:83:95 | call to method First | return | GlobalDataFlow.cs:83:22:83:95 | call to method First | +| GlobalDataFlow.cs:85:22:85:128 | call to method Zip | return | GlobalDataFlow.cs:85:22:85:128 | call to method Zip | +| GlobalDataFlow.cs:85:22:85:128 | call to method Zip | yield return | GlobalDataFlow.cs:85:22:85:128 | call to method Zip | +| GlobalDataFlow.cs:85:22:85:136 | call to method First | return | GlobalDataFlow.cs:85:22:85:136 | call to method First | +| GlobalDataFlow.cs:87:22:87:128 | call to method Zip | return | GlobalDataFlow.cs:87:22:87:128 | call to method Zip | +| GlobalDataFlow.cs:87:22:87:128 | call to method Zip | yield return | GlobalDataFlow.cs:87:22:87:128 | call to method Zip | +| GlobalDataFlow.cs:87:22:87:136 | call to method First | return | GlobalDataFlow.cs:87:22:87:136 | call to method First | +| GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate | return | GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate | return | GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate | +| GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | out | GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) | +| GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | ref | GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) | +| GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | return | GlobalDataFlow.cs:94:9:94:42 | call to method TryParse | +| GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | out | GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) | +| GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | ref | GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) | +| GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | return | GlobalDataFlow.cs:97:9:97:41 | call to method TryParse | +| GlobalDataFlow.cs:101:24:101:33 | call to method Return | return | GlobalDataFlow.cs:101:24:101:33 | call to method Return | +| GlobalDataFlow.cs:103:28:103:63 | call to method GetMethod | qualifier | GlobalDataFlow.cs:103:28:103:43 | [post] typeof(...) | +| GlobalDataFlow.cs:103:28:103:63 | call to method GetMethod | return | GlobalDataFlow.cs:103:28:103:63 | call to method GetMethod | +| GlobalDataFlow.cs:103:28:103:103 | call to method Invoke | return | GlobalDataFlow.cs:103:28:103:103 | call to method Invoke | +| GlobalDataFlow.cs:105:9:105:49 | call to method ReturnOut | out | GlobalDataFlow.cs:105:27:105:34 | SSA def(nonSink0) | +| GlobalDataFlow.cs:105:9:105:49 | call to method ReturnOut | ref | GlobalDataFlow.cs:105:27:105:34 | SSA def(nonSink0) | +| GlobalDataFlow.cs:107:9:107:49 | call to method ReturnOut | out | GlobalDataFlow.cs:107:41:107:48 | SSA def(nonSink0) | +| GlobalDataFlow.cs:109:9:109:49 | call to method ReturnRef | out | GlobalDataFlow.cs:109:27:109:34 | SSA def(nonSink0) | +| GlobalDataFlow.cs:109:9:109:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:109:27:109:34 | SSA def(nonSink0) | +| GlobalDataFlow.cs:111:9:111:49 | call to method ReturnRef | out | GlobalDataFlow.cs:111:30:111:34 | SSA def(sink1) | +| GlobalDataFlow.cs:111:9:111:49 | call to method ReturnRef | ref | GlobalDataFlow.cs:111:30:111:34 | SSA def(sink1) | +| GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | return | GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | +| GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | yield return | GlobalDataFlow.cs:113:20:113:86 | call to method SelectEven | +| GlobalDataFlow.cs:113:20:113:94 | call to method First | return | GlobalDataFlow.cs:113:20:113:94 | call to method First | +| GlobalDataFlow.cs:115:20:115:82 | call to method Select | return | GlobalDataFlow.cs:115:20:115:82 | call to method Select | +| GlobalDataFlow.cs:115:20:115:82 | call to method Select | yield return | GlobalDataFlow.cs:115:20:115:82 | call to method Select | +| GlobalDataFlow.cs:115:20:115:90 | call to method First | return | GlobalDataFlow.cs:115:20:115:90 | call to method First | +| GlobalDataFlow.cs:117:20:117:126 | call to method Zip | return | GlobalDataFlow.cs:117:20:117:126 | call to method Zip | +| GlobalDataFlow.cs:117:20:117:126 | call to method Zip | yield return | GlobalDataFlow.cs:117:20:117:126 | call to method Zip | +| GlobalDataFlow.cs:117:20:117:134 | call to method First | return | GlobalDataFlow.cs:117:20:117:134 | call to method First | +| GlobalDataFlow.cs:119:20:119:126 | call to method Zip | return | GlobalDataFlow.cs:119:20:119:126 | call to method Zip | +| GlobalDataFlow.cs:119:20:119:126 | call to method Zip | yield return | GlobalDataFlow.cs:119:20:119:126 | call to method Zip | +| GlobalDataFlow.cs:119:20:119:134 | call to method First | return | GlobalDataFlow.cs:119:20:119:134 | call to method First | +| GlobalDataFlow.cs:121:20:121:104 | call to method Aggregate | return | GlobalDataFlow.cs:121:20:121:104 | call to method Aggregate | +| GlobalDataFlow.cs:123:20:123:109 | call to method Aggregate | return | GlobalDataFlow.cs:123:20:123:109 | call to method Aggregate | +| GlobalDataFlow.cs:125:20:125:107 | call to method Aggregate | return | GlobalDataFlow.cs:125:20:125:107 | call to method Aggregate | +| GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | out | GlobalDataFlow.cs:128:38:128:45 | SSA def(nonSink2) | +| GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | ref | GlobalDataFlow.cs:128:38:128:45 | SSA def(nonSink2) | +| GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | return | GlobalDataFlow.cs:128:9:128:46 | call to method TryParse | +| GlobalDataFlow.cs:131:9:131:45 | call to method TryParse | out | GlobalDataFlow.cs:131:37:131:44 | SSA def(nonSink3) | +| GlobalDataFlow.cs:131:9:131:45 | call to method TryParse | ref | GlobalDataFlow.cs:131:37:131:44 | SSA def(nonSink3) | +| GlobalDataFlow.cs:131:9:131:45 | call to method TryParse | return | GlobalDataFlow.cs:131:9:131:45 | call to method TryParse | +| GlobalDataFlow.cs:135:45:135:64 | call to method ApplyFunc | return | GlobalDataFlow.cs:135:45:135:64 | call to method ApplyFunc | +| GlobalDataFlow.cs:136:21:136:34 | delegate call | return | GlobalDataFlow.cs:136:21:136:34 | delegate call | +| GlobalDataFlow.cs:140:20:140:36 | delegate call | return | GlobalDataFlow.cs:140:20:140:36 | delegate call | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc | +| GlobalDataFlow.cs:148:20:148:40 | call to method ApplyFunc | return | GlobalDataFlow.cs:148:20:148:40 | call to method ApplyFunc | +| GlobalDataFlow.cs:150:20:150:44 | call to method ApplyFunc | return | GlobalDataFlow.cs:150:20:150:44 | call to method ApplyFunc | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out | qualifier | GlobalDataFlow.cs:154:21:154:25 | [post] this access | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out | return | GlobalDataFlow.cs:154:21:154:25 | call to method Out | +| GlobalDataFlow.cs:157:9:157:25 | call to method OutOut | out | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) | +| GlobalDataFlow.cs:157:9:157:25 | call to method OutOut | qualifier | GlobalDataFlow.cs:157:9:157:25 | [post] this access | +| GlobalDataFlow.cs:157:9:157:25 | call to method OutOut | ref | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) | +| GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | out | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) | +| GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | qualifier | GlobalDataFlow.cs:160:9:160:25 | [post] this access | +| GlobalDataFlow.cs:160:9:160:25 | call to method OutRef | ref | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | qualifier | GlobalDataFlow.cs:162:22:162:31 | [post] this access | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | return | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | yield return | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield | +| GlobalDataFlow.cs:162:22:162:39 | call to method First | return | GlobalDataFlow.cs:162:22:162:39 | call to method First | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam | return | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam | +| GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | qualifier | GlobalDataFlow.cs:168:20:168:27 | [post] this access | +| GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | return | GlobalDataFlow.cs:168:20:168:27 | call to method NonOut | +| GlobalDataFlow.cs:170:9:170:31 | call to method NonOutOut | out | GlobalDataFlow.cs:170:23:170:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:170:9:170:31 | call to method NonOutOut | qualifier | GlobalDataFlow.cs:170:9:170:31 | [post] this access | +| GlobalDataFlow.cs:170:9:170:31 | call to method NonOutOut | ref | GlobalDataFlow.cs:170:23:170:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | out | GlobalDataFlow.cs:172:23:172:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | qualifier | GlobalDataFlow.cs:172:9:172:31 | [post] this access | +| GlobalDataFlow.cs:172:9:172:31 | call to method NonOutRef | ref | GlobalDataFlow.cs:172:23:172:30 | SSA def(nonSink0) | +| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | qualifier | GlobalDataFlow.cs:174:20:174:32 | [post] this access | +| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | return | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | +| GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | yield return | GlobalDataFlow.cs:174:20:174:32 | call to method NonOutYield | +| GlobalDataFlow.cs:174:20:174:40 | call to method First | return | GlobalDataFlow.cs:174:20:174:40 | call to method First | +| GlobalDataFlow.cs:176:20:176:44 | call to method NonTaintedParam | return | GlobalDataFlow.cs:176:20:176:44 | call to method NonTaintedParam | +| GlobalDataFlow.cs:181:21:181:26 | delegate call | return | GlobalDataFlow.cs:181:21:181:26 | delegate call | +| GlobalDataFlow.cs:186:20:186:27 | delegate call | return | GlobalDataFlow.cs:186:20:186:27 | delegate call | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy | return | GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value | qualifier | GlobalDataFlow.cs:190:22:190:42 | [post] object creation of type Lazy | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value | return | GlobalDataFlow.cs:190:22:190:48 | access to property Value | +| GlobalDataFlow.cs:194:20:194:43 | object creation of type Lazy | return | GlobalDataFlow.cs:194:20:194:43 | object creation of type Lazy | +| GlobalDataFlow.cs:194:20:194:49 | access to property Value | qualifier | GlobalDataFlow.cs:194:20:194:43 | [post] object creation of type Lazy | +| GlobalDataFlow.cs:194:20:194:49 | access to property Value | return | GlobalDataFlow.cs:194:20:194:49 | access to property Value | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty | qualifier | GlobalDataFlow.cs:198:22:198:32 | [post] this access | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty | return | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty | +| GlobalDataFlow.cs:202:20:202:33 | access to property NonOutProperty | qualifier | GlobalDataFlow.cs:202:20:202:33 | [post] this access | +| GlobalDataFlow.cs:202:20:202:33 | access to property NonOutProperty | return | GlobalDataFlow.cs:202:20:202:33 | access to property NonOutProperty | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable | return | GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable | +| GlobalDataFlow.cs:209:41:209:77 | call to method AsQueryable | return | GlobalDataFlow.cs:209:41:209:77 | call to method AsQueryable | +| GlobalDataFlow.cs:212:76:212:90 | call to method ReturnCheck2 | return | GlobalDataFlow.cs:212:76:212:90 | call to method ReturnCheck2 | +| GlobalDataFlow.cs:213:22:213:39 | call to method Select | return | GlobalDataFlow.cs:213:22:213:39 | call to method Select | +| GlobalDataFlow.cs:213:22:213:39 | call to method Select | yield return | GlobalDataFlow.cs:213:22:213:39 | call to method Select | +| GlobalDataFlow.cs:213:22:213:47 | call to method First | return | GlobalDataFlow.cs:213:22:213:47 | call to method First | +| GlobalDataFlow.cs:215:22:215:39 | call to method Select | return | GlobalDataFlow.cs:215:22:215:39 | call to method Select | +| GlobalDataFlow.cs:215:22:215:47 | call to method First | return | GlobalDataFlow.cs:215:22:215:47 | call to method First | +| GlobalDataFlow.cs:217:22:217:49 | call to method Select | return | GlobalDataFlow.cs:217:22:217:49 | call to method Select | +| GlobalDataFlow.cs:217:22:217:49 | call to method Select | yield return | GlobalDataFlow.cs:217:22:217:49 | call to method Select | +| GlobalDataFlow.cs:217:22:217:57 | call to method First | return | GlobalDataFlow.cs:217:22:217:57 | call to method First | +| GlobalDataFlow.cs:222:76:222:92 | call to method NonReturnCheck | return | GlobalDataFlow.cs:222:76:222:92 | call to method NonReturnCheck | +| GlobalDataFlow.cs:223:23:223:43 | call to method Select | return | GlobalDataFlow.cs:223:23:223:43 | call to method Select | +| GlobalDataFlow.cs:223:23:223:43 | call to method Select | yield return | GlobalDataFlow.cs:223:23:223:43 | call to method Select | +| GlobalDataFlow.cs:223:23:223:51 | call to method First | return | GlobalDataFlow.cs:223:23:223:51 | call to method First | +| GlobalDataFlow.cs:225:19:225:39 | call to method Select | return | GlobalDataFlow.cs:225:19:225:39 | call to method Select | +| GlobalDataFlow.cs:225:19:225:47 | call to method First | return | GlobalDataFlow.cs:225:19:225:47 | call to method First | +| GlobalDataFlow.cs:227:19:227:39 | call to method Select | return | GlobalDataFlow.cs:227:19:227:39 | call to method Select | +| GlobalDataFlow.cs:227:19:227:39 | call to method Select | yield return | GlobalDataFlow.cs:227:19:227:39 | call to method Select | +| GlobalDataFlow.cs:227:19:227:47 | call to method First | return | GlobalDataFlow.cs:227:19:227:47 | call to method First | +| GlobalDataFlow.cs:229:19:229:39 | call to method Select | return | GlobalDataFlow.cs:229:19:229:39 | call to method Select | +| GlobalDataFlow.cs:229:19:229:47 | call to method First | return | GlobalDataFlow.cs:229:19:229:47 | call to method First | +| GlobalDataFlow.cs:231:19:231:49 | call to method Select | return | GlobalDataFlow.cs:231:19:231:49 | call to method Select | +| GlobalDataFlow.cs:231:19:231:49 | call to method Select | yield return | GlobalDataFlow.cs:231:19:231:49 | call to method Select | +| GlobalDataFlow.cs:231:19:231:57 | call to method First | return | GlobalDataFlow.cs:231:19:231:57 | call to method First | +| GlobalDataFlow.cs:238:20:238:49 | call to method Run | return | GlobalDataFlow.cs:238:20:238:49 | call to method Run | +| GlobalDataFlow.cs:239:22:239:32 | access to property Result | qualifier | GlobalDataFlow.cs:239:22:239:25 | [post] access to local variable task | +| GlobalDataFlow.cs:239:22:239:32 | access to property Result | return | GlobalDataFlow.cs:239:22:239:32 | access to property Result | +| GlobalDataFlow.cs:245:16:245:33 | call to method Run | return | GlobalDataFlow.cs:245:16:245:33 | call to method Run | +| GlobalDataFlow.cs:246:24:246:34 | access to property Result | qualifier | GlobalDataFlow.cs:246:24:246:27 | [post] access to local variable task | +| GlobalDataFlow.cs:246:24:246:34 | access to property Result | return | GlobalDataFlow.cs:246:24:246:34 | access to property Result | +| GlobalDataFlow.cs:297:17:297:38 | call to method ApplyFunc | return | GlobalDataFlow.cs:297:17:297:38 | call to method ApplyFunc | +| GlobalDataFlow.cs:386:16:386:19 | delegate call | return | GlobalDataFlow.cs:386:16:386:19 | delegate call | +| GlobalDataFlow.cs:445:9:445:20 | call to method Append | qualifier | GlobalDataFlow.cs:445:9:445:10 | [post] access to parameter sb | +| GlobalDataFlow.cs:445:9:445:20 | call to method Append | return | GlobalDataFlow.cs:445:9:445:20 | call to method Append | +| GlobalDataFlow.cs:450:18:450:36 | object creation of type StringBuilder | return | GlobalDataFlow.cs:450:18:450:36 | object creation of type StringBuilder | +| GlobalDataFlow.cs:452:22:452:34 | call to method ToString | qualifier | GlobalDataFlow.cs:452:22:452:23 | [post] access to local variable sb | +| GlobalDataFlow.cs:452:22:452:34 | call to method ToString | return | GlobalDataFlow.cs:452:22:452:34 | call to method ToString | +| GlobalDataFlow.cs:455:9:455:18 | call to method Clear | qualifier | GlobalDataFlow.cs:455:9:455:10 | [post] access to local variable sb | +| GlobalDataFlow.cs:455:9:455:18 | call to method Clear | return | GlobalDataFlow.cs:455:9:455:18 | call to method Clear | +| GlobalDataFlow.cs:456:23:456:35 | call to method ToString | qualifier | GlobalDataFlow.cs:456:23:456:24 | [post] access to local variable sb | +| GlobalDataFlow.cs:456:23:456:35 | call to method ToString | return | GlobalDataFlow.cs:456:23:456:35 | call to method ToString | +| GlobalDataFlow.cs:462:22:462:65 | call to method Join | return | GlobalDataFlow.cs:462:22:462:65 | call to method Join | +| GlobalDataFlow.cs:465:23:465:65 | call to method Join | return | GlobalDataFlow.cs:465:23:465:65 | call to method Join | +| GlobalDataFlow.cs:477:44:477:47 | delegate call | return | GlobalDataFlow.cs:477:44:477:47 | delegate call | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return | | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return | return | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return | | Splitting.cs:20:22:20:30 | call to method Return | return | Splitting.cs:20:22:20:30 | call to method Return | | Splitting.cs:21:21:21:33 | call to method Return | return | Splitting.cs:21:21:21:33 | call to method Return | +| Splitting.cs:30:9:30:13 | [b (line 24): false] dynamic access to element | qualifier | Splitting.cs:30:9:30:9 | [post] [b (line 24): false] access to local variable d | | Splitting.cs:30:9:30:13 | [b (line 24): false] dynamic access to element | return | Splitting.cs:30:9:30:13 | [b (line 24): false] dynamic access to element | +| Splitting.cs:30:9:30:13 | [b (line 24): true] dynamic access to element | qualifier | Splitting.cs:30:9:30:9 | [post] [b (line 24): true] access to local variable d | | Splitting.cs:30:9:30:13 | [b (line 24): true] dynamic access to element | return | Splitting.cs:30:9:30:13 | [b (line 24): true] dynamic access to element | +| Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element | qualifier | Splitting.cs:31:17:31:17 | [post] [b (line 24): false] access to local variable d | | Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element | return | Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element | +| Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element | qualifier | Splitting.cs:31:17:31:17 | [post] [b (line 24): true] access to local variable d | | Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element | return | Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element | | Splitting.cs:32:9:32:16 | [b (line 24): false] dynamic call to method Check | return | Splitting.cs:32:9:32:16 | [b (line 24): false] dynamic call to method Check | | Splitting.cs:32:9:32:16 | [b (line 24): true] dynamic call to method Check | return | Splitting.cs:32:9:32:16 | [b (line 24): true] dynamic call to method Check | | Splitting.cs:34:13:34:20 | dynamic call to method Check | return | Splitting.cs:34:13:34:20 | dynamic call to method Check | +| This.cs:12:9:12:20 | call to method M | qualifier | This.cs:12:9:12:12 | [post] this access | +| This.cs:13:9:13:15 | call to method M | qualifier | This.cs:13:9:13:15 | [post] this access | +| This.cs:15:9:15:21 | call to method M | qualifier | This.cs:15:9:15:12 | [post] this access | +| This.cs:16:9:16:16 | call to method M | qualifier | This.cs:16:9:16:16 | [post] this access | | This.cs:17:9:17:18 | object creation of type This | return | This.cs:17:9:17:18 | object creation of type This | +| This.cs:26:13:26:24 | call to method M | qualifier | This.cs:26:13:26:16 | [post] this access | +| This.cs:27:13:27:24 | call to method M | qualifier | This.cs:27:13:27:16 | [post] base access | | This.cs:28:13:28:21 | object creation of type Sub | return | This.cs:28:13:28:21 | object creation of type Sub | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of ContinueWith | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of ContinueWith] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Lazy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Lazy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Lazy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Lazy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Run | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Run] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Run | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Run] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Run | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Run] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Run | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Run] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of StartNew | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of StartNew] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 0 of Task | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 0 of Task] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAll | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAll] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of ContinueWhenAny | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of ContinueWhenAny] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Select | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Select] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Select | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Select] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Select | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Select] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of Select | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of Select] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 1 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 1 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of SelectMany | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of SelectMany] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of ToDictionary | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of ToDictionary] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of ToDictionary | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of ToDictionary] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of ToLookup | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of ToLookup] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of ToLookup | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of ToLookup] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Zip | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Zip] | +| file://:0:0:0:0 | [summary] delegate call, parameter 2 of Zip | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 2 of Zip] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of Aggregate | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of Aggregate] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 3 of GroupBy | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 3 of GroupBy] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of GroupJoin | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of GroupJoin] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of GroupJoin | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of GroupJoin] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of GroupJoin | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of GroupJoin] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of GroupJoin | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of GroupJoin] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of Join | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of Join] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of Join | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of Join] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of Join | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of Join] | +| file://:0:0:0:0 | [summary] delegate call, parameter 4 of Join | return | file://:0:0:0:0 | [summary] output from delegate call, parameter 4 of Join] | diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.ql b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.ql index e3cee19f7245..a850a6af8d53 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.ql +++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.ql @@ -1,5 +1,29 @@ import csharp import semmle.code.csharp.dataflow.internal.DataFlowDispatch +import semmle.code.csharp.dataflow.internal.DataFlowPrivate -from DataFlowCall call, ReturnKind kind -select call, kind, getAnOutNode(call, kind) +private class DataFlowCallAdjusted extends TDataFlowCall { + string toString() { result = this.(DataFlowCall).toString() } + + Location getLocation() { + exists(Location l | + l = this.(DataFlowCall).getLocation() and + if l instanceof SourceLocation then result = l else result instanceof EmptyLocation + ) + } +} + +private class NodeAdjusted extends TNode { + string toString() { result = this.(DataFlow::Node).toString() } + + Location getLocation() { + exists(Location l | + l = this.(DataFlow::Node).getLocation() and + if l instanceof SourceLocation then result = l else result instanceof EmptyLocation + ) + } +} + +from DataFlowCallAdjusted call, NodeAdjusted n, ReturnKind kind +where n = getAnOutNode(call, kind) +select call, kind, n diff --git a/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs b/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs index 83003aaea668..35c38fb809b1 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/global/GlobalDataFlow.cs @@ -2,6 +2,7 @@ using System.Text; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; /// /// All (tainted) sinks are named `sink[Param|Field|Property]N`, for some N, and all @@ -231,6 +232,23 @@ public void M2() Check(nonSink); } + public async void M3() + { + // async await, tainted + var task = Task.Run(() => "taint source"); + var sink41 = task.Result; + Check(sink41); + var sink42 = await task; + Check(sink42); + + // async await, not tainted + task = Task.Run(() => ""); + var nonSink0 = task.Result; + Check(nonSink0); + var nonSink1 = await task; + Check(nonSink1); + } + static void Check(T x) { } static void In0(T sinkParam0) @@ -421,6 +439,32 @@ string NonOutProperty { get { return ""; } } + + static void AppendToStringBuilder(StringBuilder sb, string s) + { + sb.Append(s); + } + + void TestStringBuilderFlow() + { + var sb = new StringBuilder(); + AppendToStringBuilder(sb, "taint source"); + var sink43 = sb.ToString(); + Check(sink43); + + sb.Clear(); + var nonSink = sb.ToString(); + Check(nonSink); + } + + void TestStringFlow() + { + var sink44 = string.Join(",", "whatever", "taint source"); + Check(sink44); + + var nonSink = string.Join(",", "whatever", "not tainted"); + Check(nonSink); + } } static class IEnumerableExtensions diff --git a/csharp/ql/test/library-tests/dataflow/global/Splitting.cs b/csharp/ql/test/library-tests/dataflow/global/Splitting.cs index 8749c4c44c79..30b9a1b9d719 100644 --- a/csharp/ql/test/library-tests/dataflow/global/Splitting.cs +++ b/csharp/ql/test/library-tests/dataflow/global/Splitting.cs @@ -33,4 +33,22 @@ void M2(bool b, string tainted) if (b) Check(x); } + + void M3(bool b) + { + var s = b ? "taint source" : "not tainted"; + if (b) + Check(s); // flow + else + Check(s); // no flow [FALSE POSITIVE] + } + + void M4(bool b) + { + var s = b switch { true => "taint source", false => "not tainted" }; + if (b) + Check(s); // flow + else + Check(s); // no flow [FALSE POSITIVE] + } } diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected index 57fd2c9e0c3d..aee973d855b7 100644 --- a/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected +++ b/csharp/ql/test/library-tests/dataflow/global/TaintTracking.expected @@ -12,51 +12,59 @@ | Capture.cs:161:15:161:20 | access to local variable sink36 | | Capture.cs:169:15:169:20 | access to local variable sink37 | | Capture.cs:195:15:195:20 | access to local variable sink38 | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | -| GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | -| GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | -| GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | -| GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | -| GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | -| GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | -| GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | -| GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | -| GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | -| GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | -| GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | -| GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | -| GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | -| GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | -| GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | +| GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | +| GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | +| GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | +| GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | +| GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | +| GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | +| GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | +| GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | +| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | +| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | -| Splitting.cs:21:28:21:32 | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | +| Splitting.cs:41:19:41:19 | access to local variable s | +| Splitting.cs:43:19:43:19 | access to local variable s | +| Splitting.cs:50:19:50:19 | access to local variable s | +| Splitting.cs:52:19:52:19 | access to local variable s | diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected index c0422a5f0f57..a0ac519a6c65 100644 --- a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected +++ b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected @@ -1,239 +1,246 @@ edges -| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:14:9:14:20 | [implicit argument] tainted : String | -| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:25:9:25:20 | [implicit argument] tainted : String | -| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:33:9:33:40 | [implicit argument] tainted : String | +| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:12:19:12:24 | access to local variable sink27 | +| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:21:23:21:28 | access to local variable sink28 | +| Capture.cs:7:20:7:26 | tainted : String | Capture.cs:30:19:30:24 | access to local variable sink29 | | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:61:36:61:42 | access to parameter tainted : String | -| Capture.cs:9:9:13:9 | SSA capture def(tainted) : String | Capture.cs:12:19:12:24 | access to local variable sink27 | -| Capture.cs:14:9:14:20 | [implicit argument] tainted : String | Capture.cs:9:9:13:9 | SSA capture def(tainted) : String | -| Capture.cs:18:13:22:13 | SSA capture def(tainted) : String | Capture.cs:21:23:21:28 | access to local variable sink28 | -| Capture.cs:25:9:25:20 | [implicit argument] tainted : String | Capture.cs:18:13:22:13 | SSA capture def(tainted) : String | -| Capture.cs:27:43:32:9 | SSA capture def(tainted) : String | Capture.cs:30:19:30:24 | access to local variable sink29 | -| Capture.cs:33:9:33:40 | [implicit argument] tainted : String | Capture.cs:27:43:32:9 | SSA capture def(tainted) : String | -| Capture.cs:50:50:50:55 | sink39 : String | Capture.cs:52:13:59:14 | [implicit argument] sink39 : String | -| Capture.cs:52:13:59:14 | [implicit argument] sink39 : String | Capture.cs:55:27:58:17 | SSA capture def(sink39) : String | -| Capture.cs:55:27:58:17 | SSA capture def(sink39) : String | Capture.cs:57:27:57:32 | access to parameter sink39 | +| Capture.cs:50:50:50:55 | sink39 : String | Capture.cs:57:27:57:32 | access to parameter sink39 | | Capture.cs:61:36:61:42 | access to parameter tainted : String | Capture.cs:50:50:50:55 | sink39 : String | -| Capture.cs:69:13:69:35 | SSA def(sink30) : String | Capture.cs:71:9:71:21 | SSA call def(sink30) : String | +| Capture.cs:69:13:69:35 | SSA def(sink30) : String | Capture.cs:72:15:72:20 | access to local variable sink30 | | Capture.cs:69:22:69:35 | "taint source" : String | Capture.cs:69:13:69:35 | SSA def(sink30) : String | -| Capture.cs:71:9:71:21 | SSA call def(sink30) : String | Capture.cs:72:15:72:20 | access to local variable sink30 | -| Capture.cs:79:17:79:39 | SSA def(sink31) : String | Capture.cs:83:9:83:21 | SSA call def(sink31) : String | +| Capture.cs:79:17:79:39 | SSA def(sink31) : String | Capture.cs:84:15:84:20 | access to local variable sink31 | | Capture.cs:79:26:79:39 | "taint source" : String | Capture.cs:79:17:79:39 | SSA def(sink31) : String | -| Capture.cs:83:9:83:21 | SSA call def(sink31) : String | Capture.cs:84:15:84:20 | access to local variable sink31 | -| Capture.cs:89:13:89:35 | SSA def(sink32) : String | Capture.cs:92:9:92:41 | SSA call def(sink32) : String | +| Capture.cs:89:13:89:35 | SSA def(sink32) : String | Capture.cs:93:15:93:20 | access to local variable sink32 | | Capture.cs:89:22:89:35 | "taint source" : String | Capture.cs:89:13:89:35 | SSA def(sink32) : String | -| Capture.cs:92:9:92:41 | SSA call def(sink32) : String | Capture.cs:93:15:93:20 | access to local variable sink32 | -| Capture.cs:115:17:115:39 | SSA def(sink40) : String | Capture.cs:121:9:121:35 | SSA call def(sink40) : String | +| Capture.cs:115:17:115:39 | SSA def(sink40) : String | Capture.cs:122:15:122:20 | access to local variable sink40 | | Capture.cs:115:26:115:39 | "taint source" : String | Capture.cs:115:17:115:39 | SSA def(sink40) : String | -| Capture.cs:121:9:121:35 | SSA call def(sink40) : String | Capture.cs:122:15:122:20 | access to local variable sink40 | -| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:132:9:132:25 | [implicit argument] tainted : String | -| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:144:9:144:25 | [implicit argument] tainted : String | -| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:153:9:153:45 | [implicit argument] tainted : String | -| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:160:22:160:38 | [implicit argument] tainted : String | +| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:133:15:133:20 | access to local variable sink33 | +| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:145:15:145:20 | access to local variable sink34 | +| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:154:15:154:20 | access to local variable sink35 | +| Capture.cs:125:25:125:31 | tainted : String | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 : String | | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:168:25:168:31 | access to parameter tainted : String | | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:194:25:194:31 | access to parameter tainted : String | -| Capture.cs:132:9:132:25 | SSA call def(sink33) : String | Capture.cs:133:15:133:20 | access to local variable sink33 | -| Capture.cs:132:9:132:25 | [implicit argument] tainted : String | Capture.cs:132:9:132:25 | SSA call def(sink33) : String | -| Capture.cs:144:9:144:25 | SSA call def(sink34) : String | Capture.cs:145:15:145:20 | access to local variable sink34 | -| Capture.cs:144:9:144:25 | [implicit argument] tainted : String | Capture.cs:144:9:144:25 | SSA call def(sink34) : String | -| Capture.cs:153:9:153:45 | SSA call def(sink35) : String | Capture.cs:154:15:154:20 | access to local variable sink35 | -| Capture.cs:153:9:153:45 | [implicit argument] tainted : String | Capture.cs:153:9:153:45 | SSA call def(sink35) : String | -| Capture.cs:160:22:160:38 | [implicit argument] tainted : String | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 : String | | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 : String | Capture.cs:161:15:161:20 | access to local variable sink36 | -| Capture.cs:168:9:168:32 | SSA call def(sink37) : String | Capture.cs:169:15:169:20 | access to local variable sink37 | -| Capture.cs:168:25:168:31 | access to parameter tainted : String | Capture.cs:168:9:168:32 | SSA call def(sink37) : String | +| Capture.cs:168:25:168:31 | access to parameter tainted : String | Capture.cs:169:15:169:20 | access to local variable sink37 | | Capture.cs:194:22:194:32 | call to local function Id : String | Capture.cs:195:15:195:20 | access to local variable sink38 | | Capture.cs:194:25:194:31 | access to parameter tainted : String | Capture.cs:194:22:194:32 | call to local function Id : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | -| GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:361:41:361:41 | x : String | -| GlobalDataFlow.cs:53:15:53:15 | x : String | GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | -| GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:361:41:361:41 | x : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:56:37:56:37 | x : String | GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | -| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:375:52:375:52 | x : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:406:9:406:11 | value : String | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | -| GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | -| GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | -| GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:80:23:80:65 | (...) ... : String[] | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven : IEnumerable | GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven : IEnumerable | GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | -| GlobalDataFlow.cs:80:23:80:65 | (...) ... : String[] | GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven : IEnumerable | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | GlobalDataFlow.cs:82:76:82:86 | [implicit argument 0] delegate creation of type Func : String | -| GlobalDataFlow.cs:82:76:82:86 | [implicit argument 0] delegate creation of type Func : String | GlobalDataFlow.cs:82:76:82:86 | [output] delegate creation of type Func : String | -| GlobalDataFlow.cs:82:76:82:86 | [implicit argument 0] delegate creation of type Func : String | GlobalDataFlow.cs:294:31:294:40 | sinkParam8 : String | -| GlobalDataFlow.cs:82:76:82:86 | [output] delegate creation of type Func : String | GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | -| GlobalDataFlow.cs:82:76:82:86 | [output] delegate creation of type Func : String | GlobalDataFlow.cs:84:23:84:66 | (...) ... : String[] | -| GlobalDataFlow.cs:82:76:82:86 | [output] delegate creation of type Func : String | GlobalDataFlow.cs:88:23:88:66 | (...) ... : String[] | -| GlobalDataFlow.cs:82:76:82:86 | [output] delegate creation of type Func : String | GlobalDataFlow.cs:90:83:90:101 | [implicit argument 0] (...) => ... : String | -| GlobalDataFlow.cs:84:23:84:66 | (...) ... : String[] | GlobalDataFlow.cs:84:117:84:127 | [implicit argument 0] (...) => ... : String | -| GlobalDataFlow.cs:84:117:84:127 | [implicit argument 0] (...) => ... : String | GlobalDataFlow.cs:84:117:84:127 | [output] (...) => ... : String | -| GlobalDataFlow.cs:84:117:84:127 | [output] (...) => ... : String | GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | -| GlobalDataFlow.cs:84:117:84:127 | [output] (...) => ... : String | GlobalDataFlow.cs:86:70:86:113 | (...) ... : String[] | -| GlobalDataFlow.cs:86:70:86:113 | (...) ... : String[] | GlobalDataFlow.cs:86:117:86:127 | [implicit argument 1] (...) => ... : String | -| GlobalDataFlow.cs:86:117:86:127 | [implicit argument 1] (...) => ... : String | GlobalDataFlow.cs:86:117:86:127 | [output] (...) => ... : String | -| GlobalDataFlow.cs:86:117:86:127 | [output] (...) => ... : String | GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | -| GlobalDataFlow.cs:88:23:88:66 | (...) ... : String[] | GlobalDataFlow.cs:88:83:88:101 | [implicit argument 1] (...) => ... : String | -| GlobalDataFlow.cs:88:83:88:101 | [implicit argument 1] (...) => ... : String | GlobalDataFlow.cs:88:83:88:101 | [output] (...) => ... : String | -| GlobalDataFlow.cs:88:83:88:101 | [output] (...) => ... : String | GlobalDataFlow.cs:88:104:88:109 | [implicit argument 0] (...) => ... : String | -| GlobalDataFlow.cs:88:104:88:109 | [implicit argument 0] (...) => ... : String | GlobalDataFlow.cs:88:104:88:109 | [output] (...) => ... : String | -| GlobalDataFlow.cs:88:104:88:109 | [output] (...) => ... : String | GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | -| GlobalDataFlow.cs:90:83:90:101 | [implicit argument 0] (...) => ... : String | GlobalDataFlow.cs:90:83:90:101 | [output] (...) => ... : String | -| GlobalDataFlow.cs:90:83:90:101 | [output] (...) => ... : String | GlobalDataFlow.cs:90:104:90:109 | [implicit argument 0] (...) => ... : String | -| GlobalDataFlow.cs:90:104:90:109 | [implicit argument 0] (...) => ... : String | GlobalDataFlow.cs:90:104:90:109 | [output] (...) => ... : String | -| GlobalDataFlow.cs:90:104:90:109 | [output] (...) => ... : String | GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | -| GlobalDataFlow.cs:90:104:90:109 | [output] (...) => ... : String | GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | -| GlobalDataFlow.cs:90:104:90:109 | [output] (...) => ... : String | GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | -| GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | GlobalDataFlow.cs:135:21:135:34 | delegate call : String | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | -| GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | -| GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | -| GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | -| GlobalDataFlow.cs:161:22:161:31 | call to method OutYield : IEnumerable | GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | -| GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | GlobalDataFlow.cs:180:21:180:26 | delegate call : String | -| GlobalDataFlow.cs:180:21:180:26 | delegate call : String | GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | -| GlobalDataFlow.cs:189:22:189:42 | [library code] object creation of type Lazy : String | GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | -| GlobalDataFlow.cs:189:39:189:41 | [output] delegate creation of type Func : String | GlobalDataFlow.cs:189:22:189:42 | [library code] object creation of type Lazy : String | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:212:37:212:38 | [implicit argument 0] access to local variable f1 : String | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:214:37:214:38 | [implicit argument 0] access to local variable f2 : String | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:216:37:216:48 | [implicit argument 0] delegate creation of type Func : String | -| GlobalDataFlow.cs:210:35:210:45 | sinkParam10 : String | GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | -| GlobalDataFlow.cs:211:71:211:71 | x : String | GlobalDataFlow.cs:211:89:211:89 | access to parameter x : String | -| GlobalDataFlow.cs:211:89:211:89 | access to parameter x : String | GlobalDataFlow.cs:300:32:300:41 | sinkParam9 : String | -| GlobalDataFlow.cs:212:37:212:38 | [implicit argument 0] access to local variable f1 : String | GlobalDataFlow.cs:210:35:210:45 | sinkParam10 : String | -| GlobalDataFlow.cs:212:37:212:38 | [implicit argument 0] access to local variable f1 : String | GlobalDataFlow.cs:212:37:212:38 | [output] access to local variable f1 : String | -| GlobalDataFlow.cs:212:37:212:38 | [output] access to local variable f1 : String | GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | -| GlobalDataFlow.cs:214:37:214:38 | [implicit argument 0] access to local variable f2 : String | GlobalDataFlow.cs:211:71:211:71 | x : String | -| GlobalDataFlow.cs:214:37:214:38 | [implicit argument 0] access to local variable f2 : String | GlobalDataFlow.cs:214:37:214:38 | [output] access to local variable f2 : String | -| GlobalDataFlow.cs:214:37:214:38 | [output] access to local variable f2 : String | GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | -| GlobalDataFlow.cs:216:37:216:48 | [implicit argument 0] delegate creation of type Func : String | GlobalDataFlow.cs:216:37:216:48 | [output] delegate creation of type Func : String | -| GlobalDataFlow.cs:216:37:216:48 | [implicit argument 0] delegate creation of type Func : String | GlobalDataFlow.cs:306:32:306:42 | sinkParam11 : String | -| GlobalDataFlow.cs:216:37:216:48 | [output] delegate creation of type Func : String | GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | -| GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:294:31:294:40 | sinkParam8 : String | GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | -| GlobalDataFlow.cs:300:32:300:41 | sinkParam9 : String | GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | -| GlobalDataFlow.cs:306:32:306:42 | sinkParam11 : String | GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:189:39:189:41 | [output] delegate creation of type Func : String | -| GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | -| GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | -| GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | -| GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | -| GlobalDataFlow.cs:336:9:336:36 | yield return ...; : IEnumerable | GlobalDataFlow.cs:161:22:161:31 | call to method OutYield : IEnumerable | -| GlobalDataFlow.cs:336:22:336:35 | "taint source" : String | GlobalDataFlow.cs:336:9:336:36 | yield return ...; : IEnumerable | -| GlobalDataFlow.cs:361:41:361:41 | x : String | GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | GlobalDataFlow.cs:53:15:53:15 | x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:56:37:56:37 | x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | -| GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | -| GlobalDataFlow.cs:406:9:406:11 | value : String | GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | -| GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:260:26:260:35 | sinkParam1 : String | +| GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | GlobalDataFlow.cs:379:41:379:41 | x : String | +| GlobalDataFlow.cs:54:15:54:15 | x : String | GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | +| GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | GlobalDataFlow.cs:270:26:270:35 | sinkParam4 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:379:41:379:41 | x : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | GlobalDataFlow.cs:393:52:393:52 | x : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:393:52:393:52 | x : String | +| GlobalDataFlow.cs:57:37:57:37 | x : String | GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | +| GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | GlobalDataFlow.cs:285:26:285:35 | sinkParam7 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | GlobalDataFlow.cs:393:52:393:52 | x : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | GlobalDataFlow.cs:424:9:424:11 | value : String | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | +| GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | +| GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | +| GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:81:59:81:63 | access to local variable sink3 : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven [[]] : String | GlobalDataFlow.cs:81:22:81:93 | call to method First : String | +| GlobalDataFlow.cs:81:22:81:93 | call to method First : String | GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | +| GlobalDataFlow.cs:81:22:81:93 | call to method First : String | GlobalDataFlow.cs:83:59:83:64 | access to local variable sink13 : String | +| GlobalDataFlow.cs:81:23:81:65 | (...) ... [[]] : String | GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven [[]] : String | +| GlobalDataFlow.cs:81:57:81:65 | { ..., ... } [[]] : String | GlobalDataFlow.cs:81:23:81:65 | (...) ... [[]] : String | +| GlobalDataFlow.cs:81:59:81:63 | access to local variable sink3 : String | GlobalDataFlow.cs:81:57:81:65 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:83:22:83:87 | call to method Select [[]] : String | GlobalDataFlow.cs:83:22:83:95 | call to method First : String | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:85:59:85:64 | access to local variable sink14 : String | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:89:59:89:64 | access to local variable sink14 : String | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | GlobalDataFlow.cs:91:75:91:80 | access to local variable sink14 : String | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | GlobalDataFlow.cs:83:22:83:87 | call to method Select [[]] : String | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | GlobalDataFlow.cs:312:31:312:40 | sinkParam8 : String | +| GlobalDataFlow.cs:83:57:83:66 | { ..., ... } [[]] : String | GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | +| GlobalDataFlow.cs:83:59:83:64 | access to local variable sink13 : String | GlobalDataFlow.cs:83:57:83:66 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:85:22:85:128 | call to method Zip [[]] : String | GlobalDataFlow.cs:85:22:85:136 | call to method First : String | +| GlobalDataFlow.cs:85:22:85:136 | call to method First : String | GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | +| GlobalDataFlow.cs:85:22:85:136 | call to method First : String | GlobalDataFlow.cs:87:106:87:111 | access to local variable sink15 : String | +| GlobalDataFlow.cs:85:23:85:66 | (...) ... [[]] : String | GlobalDataFlow.cs:85:22:85:128 | call to method Zip [[]] : String | +| GlobalDataFlow.cs:85:57:85:66 | { ..., ... } [[]] : String | GlobalDataFlow.cs:85:23:85:66 | (...) ... [[]] : String | +| GlobalDataFlow.cs:85:59:85:64 | access to local variable sink14 : String | GlobalDataFlow.cs:85:57:85:66 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:87:22:87:128 | call to method Zip [[]] : String | GlobalDataFlow.cs:87:22:87:136 | call to method First : String | +| GlobalDataFlow.cs:87:22:87:136 | call to method First : String | GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | +| GlobalDataFlow.cs:87:70:87:113 | (...) ... [[]] : String | GlobalDataFlow.cs:87:22:87:128 | call to method Zip [[]] : String | +| GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | GlobalDataFlow.cs:87:70:87:113 | (...) ... [[]] : String | +| GlobalDataFlow.cs:87:106:87:111 | access to local variable sink15 : String | GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate : String | GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | +| GlobalDataFlow.cs:89:23:89:66 | (...) ... [[]] : String | GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate : String | +| GlobalDataFlow.cs:89:57:89:66 | { ..., ... } [[]] : String | GlobalDataFlow.cs:89:23:89:66 | (...) ... [[]] : String | +| GlobalDataFlow.cs:89:59:89:64 | access to local variable sink14 : String | GlobalDataFlow.cs:89:57:89:66 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | GlobalDataFlow.cs:94:24:94:29 | access to local variable sink18 : String | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | GlobalDataFlow.cs:97:23:97:28 | access to local variable sink18 : String | +| GlobalDataFlow.cs:91:75:91:80 | access to local variable sink14 : String | GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | +| GlobalDataFlow.cs:94:24:94:29 | access to local variable sink18 : String | GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) : Int32 | +| GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) : Int32 | GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | +| GlobalDataFlow.cs:97:23:97:28 | access to local variable sink18 : String | GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) : Boolean | +| GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) : Boolean | GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | +| GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | GlobalDataFlow.cs:136:21:136:34 | delegate call : String | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | +| GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | +| GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | +| GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield [[]] : String | GlobalDataFlow.cs:162:22:162:39 | call to method First : String | +| GlobalDataFlow.cs:162:22:162:39 | call to method First : String | GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | +| GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:181:21:181:26 | delegate call : String | +| GlobalDataFlow.cs:181:21:181:26 | delegate call : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | +| GlobalDataFlow.cs:208:38:208:61 | array creation of type String[] [[]] : String | GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | GlobalDataFlow.cs:213:22:213:28 | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | GlobalDataFlow.cs:215:22:215:28 | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | GlobalDataFlow.cs:217:22:217:28 | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:208:44:208:61 | { ..., ... } [[]] : String | GlobalDataFlow.cs:208:38:208:61 | array creation of type String[] [[]] : String | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:208:44:208:61 | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:212:71:212:71 | x : String | GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | +| GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | GlobalDataFlow.cs:318:32:318:41 | sinkParam9 : String | +| GlobalDataFlow.cs:213:22:213:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | +| GlobalDataFlow.cs:213:22:213:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:213:22:213:39 | call to method Select [[]] : String | +| GlobalDataFlow.cs:213:22:213:39 | call to method Select [[]] : String | GlobalDataFlow.cs:213:22:213:47 | call to method First : String | +| GlobalDataFlow.cs:213:22:213:47 | call to method First : String | GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | +| GlobalDataFlow.cs:215:22:215:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:212:71:212:71 | x : String | +| GlobalDataFlow.cs:215:22:215:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:215:22:215:39 | call to method Select [[]] : String | +| GlobalDataFlow.cs:215:22:215:39 | call to method Select [[]] : String | GlobalDataFlow.cs:215:22:215:47 | call to method First : String | +| GlobalDataFlow.cs:215:22:215:47 | call to method First : String | GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | +| GlobalDataFlow.cs:217:22:217:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:217:22:217:49 | call to method Select [[]] : String | +| GlobalDataFlow.cs:217:22:217:28 | access to local variable tainted [[]] : String | GlobalDataFlow.cs:324:32:324:42 | sinkParam11 : String | +| GlobalDataFlow.cs:217:22:217:49 | call to method Select [[]] : String | GlobalDataFlow.cs:217:22:217:57 | call to method First : String | +| GlobalDataFlow.cs:217:22:217:57 | call to method First : String | GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | +| GlobalDataFlow.cs:238:20:238:49 | call to method Run [Result] : String | GlobalDataFlow.cs:239:22:239:25 | access to local variable task [Result] : String | +| GlobalDataFlow.cs:238:20:238:49 | call to method Run [Result] : String | GlobalDataFlow.cs:241:28:241:31 | access to local variable task [Result] : String | +| GlobalDataFlow.cs:238:35:238:48 | "taint source" : String | GlobalDataFlow.cs:238:20:238:49 | call to method Run [Result] : String | +| GlobalDataFlow.cs:239:22:239:25 | access to local variable task [Result] : String | GlobalDataFlow.cs:239:22:239:32 | access to property Result : String | +| GlobalDataFlow.cs:239:22:239:32 | access to property Result : String | GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | +| GlobalDataFlow.cs:241:22:241:31 | await ... : String | GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | +| GlobalDataFlow.cs:241:28:241:31 | access to local variable task [Result] : String | GlobalDataFlow.cs:241:22:241:31 | await ... : String | +| GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | GlobalDataFlow.cs:256:16:256:25 | access to parameter sinkParam0 : String | +| GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:256:16:256:25 | access to parameter sinkParam0 : String | GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | +| GlobalDataFlow.cs:260:26:260:35 | sinkParam1 : String | GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:265:26:265:35 | sinkParam3 : String | GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:270:26:270:35 | sinkParam4 : String | GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:275:26:275:35 | sinkParam5 : String | GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:280:26:280:35 | sinkParam6 : String | GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:285:26:285:35 | sinkParam7 : String | GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:312:31:312:40 | sinkParam8 : String | GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:318:32:318:41 | sinkParam9 : String | GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:324:32:324:42 | sinkParam11 : String | GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | +| GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | +| GlobalDataFlow.cs:343:9:343:26 | SSA def(x) : String | GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | +| GlobalDataFlow.cs:343:13:343:26 | "taint source" : String | GlobalDataFlow.cs:343:9:343:26 | SSA def(x) : String | +| GlobalDataFlow.cs:348:9:348:26 | SSA def(x) : String | GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | +| GlobalDataFlow.cs:348:13:348:26 | "taint source" : String | GlobalDataFlow.cs:348:9:348:26 | SSA def(x) : String | +| GlobalDataFlow.cs:354:22:354:35 | "taint source" : String | GlobalDataFlow.cs:162:22:162:31 | call to method OutYield [[]] : String | +| GlobalDataFlow.cs:379:41:379:41 | x : String | GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | +| GlobalDataFlow.cs:379:41:379:41 | x : String | GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | +| GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | GlobalDataFlow.cs:54:15:54:15 | x : String | +| GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | GlobalDataFlow.cs:265:26:265:35 | sinkParam3 : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | GlobalDataFlow.cs:57:37:57:37 | x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | GlobalDataFlow.cs:275:26:275:35 | sinkParam5 : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | GlobalDataFlow.cs:280:26:280:35 | sinkParam6 : String | +| GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | +| GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:402:16:402:21 | access to local variable sink11 : String | +| GlobalDataFlow.cs:402:16:402:21 | access to local variable sink11 : String | GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | +| GlobalDataFlow.cs:424:9:424:11 | value : String | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | +| GlobalDataFlow.cs:435:22:435:35 | "taint source" : String | GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | +| GlobalDataFlow.cs:451:31:451:32 | [post] access to local variable sb [[]] : String | GlobalDataFlow.cs:452:22:452:23 | access to local variable sb [[]] : String | +| GlobalDataFlow.cs:451:35:451:48 | "taint source" : String | GlobalDataFlow.cs:451:31:451:32 | [post] access to local variable sb [[]] : String | +| GlobalDataFlow.cs:452:22:452:23 | access to local variable sb [[]] : String | GlobalDataFlow.cs:452:22:452:34 | call to method ToString : String | +| GlobalDataFlow.cs:452:22:452:34 | call to method ToString : String | GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | +| GlobalDataFlow.cs:462:22:462:65 | call to method Join : String | GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | +| GlobalDataFlow.cs:462:51:462:64 | "taint source" : String | GlobalDataFlow.cs:462:22:462:65 | call to method Join : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | @@ -241,14 +248,12 @@ edges | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | Splitting.cs:11:19:11:19 | access to local variable x | | Splitting.cs:8:24:8:30 | [b (line 3): false] access to parameter tainted : String | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | | Splitting.cs:8:24:8:30 | [b (line 3): true] access to parameter tainted : String | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | -| Splitting.cs:21:9:21:11 | value : String | Splitting.cs:21:28:21:32 | access to parameter value | +| Splitting.cs:21:9:21:11 | value : String | Splitting.cs:21:28:21:32 | access to parameter value : String | +| Splitting.cs:21:28:21:32 | access to parameter value : String | Splitting.cs:21:21:21:33 | call to method Return | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:31:19:31:25 | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:31:19:31:25 | [b (line 24): true] access to parameter tainted : String | -| Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | -| Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | -| Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:34:19:34:19 | access to local variable x | | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | Splitting.cs:21:9:21:11 | value : String | | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | Splitting.cs:21:9:21:11 | value : String | | Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element : String | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | @@ -256,201 +261,215 @@ edges | Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element : String | Splitting.cs:34:19:34:19 | access to local variable x | | Splitting.cs:31:19:31:25 | [b (line 24): false] access to parameter tainted : String | Splitting.cs:31:17:31:26 | [b (line 24): false] dynamic access to element : String | | Splitting.cs:31:19:31:25 | [b (line 24): true] access to parameter tainted : String | Splitting.cs:31:17:31:26 | [b (line 24): true] dynamic access to element : String | +| Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | Splitting.cs:41:19:41:19 | access to local variable s | +| Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | Splitting.cs:43:19:43:19 | access to local variable s | +| Splitting.cs:48:36:48:49 | "taint source" : String | Splitting.cs:50:19:50:19 | access to local variable s | +| Splitting.cs:48:36:48:49 | "taint source" : String | Splitting.cs:52:19:52:19 | access to local variable s | nodes | Capture.cs:7:20:7:26 | tainted : String | semmle.label | tainted : String | -| Capture.cs:9:9:13:9 | SSA capture def(tainted) : String | semmle.label | SSA capture def(tainted) : String | | Capture.cs:12:19:12:24 | access to local variable sink27 | semmle.label | access to local variable sink27 | -| Capture.cs:14:9:14:20 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | -| Capture.cs:18:13:22:13 | SSA capture def(tainted) : String | semmle.label | SSA capture def(tainted) : String | | Capture.cs:21:23:21:28 | access to local variable sink28 | semmle.label | access to local variable sink28 | -| Capture.cs:25:9:25:20 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | -| Capture.cs:27:43:32:9 | SSA capture def(tainted) : String | semmle.label | SSA capture def(tainted) : String | | Capture.cs:30:19:30:24 | access to local variable sink29 | semmle.label | access to local variable sink29 | -| Capture.cs:33:9:33:40 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:50:50:50:55 | sink39 : String | semmle.label | sink39 : String | -| Capture.cs:52:13:59:14 | [implicit argument] sink39 : String | semmle.label | [implicit argument] sink39 : String | -| Capture.cs:55:27:58:17 | SSA capture def(sink39) : String | semmle.label | SSA capture def(sink39) : String | | Capture.cs:57:27:57:32 | access to parameter sink39 | semmle.label | access to parameter sink39 | | Capture.cs:61:36:61:42 | access to parameter tainted : String | semmle.label | access to parameter tainted : String | | Capture.cs:69:13:69:35 | SSA def(sink30) : String | semmle.label | SSA def(sink30) : String | | Capture.cs:69:22:69:35 | "taint source" : String | semmle.label | "taint source" : String | -| Capture.cs:71:9:71:21 | SSA call def(sink30) : String | semmle.label | SSA call def(sink30) : String | | Capture.cs:72:15:72:20 | access to local variable sink30 | semmle.label | access to local variable sink30 | | Capture.cs:79:17:79:39 | SSA def(sink31) : String | semmle.label | SSA def(sink31) : String | | Capture.cs:79:26:79:39 | "taint source" : String | semmle.label | "taint source" : String | -| Capture.cs:83:9:83:21 | SSA call def(sink31) : String | semmle.label | SSA call def(sink31) : String | | Capture.cs:84:15:84:20 | access to local variable sink31 | semmle.label | access to local variable sink31 | | Capture.cs:89:13:89:35 | SSA def(sink32) : String | semmle.label | SSA def(sink32) : String | | Capture.cs:89:22:89:35 | "taint source" : String | semmle.label | "taint source" : String | -| Capture.cs:92:9:92:41 | SSA call def(sink32) : String | semmle.label | SSA call def(sink32) : String | | Capture.cs:93:15:93:20 | access to local variable sink32 | semmle.label | access to local variable sink32 | | Capture.cs:115:17:115:39 | SSA def(sink40) : String | semmle.label | SSA def(sink40) : String | | Capture.cs:115:26:115:39 | "taint source" : String | semmle.label | "taint source" : String | -| Capture.cs:121:9:121:35 | SSA call def(sink40) : String | semmle.label | SSA call def(sink40) : String | | Capture.cs:122:15:122:20 | access to local variable sink40 | semmle.label | access to local variable sink40 | | Capture.cs:125:25:125:31 | tainted : String | semmle.label | tainted : String | -| Capture.cs:132:9:132:25 | SSA call def(sink33) : String | semmle.label | SSA call def(sink33) : String | -| Capture.cs:132:9:132:25 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:133:15:133:20 | access to local variable sink33 | semmle.label | access to local variable sink33 | -| Capture.cs:144:9:144:25 | SSA call def(sink34) : String | semmle.label | SSA call def(sink34) : String | -| Capture.cs:144:9:144:25 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:145:15:145:20 | access to local variable sink34 | semmle.label | access to local variable sink34 | -| Capture.cs:153:9:153:45 | SSA call def(sink35) : String | semmle.label | SSA call def(sink35) : String | -| Capture.cs:153:9:153:45 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:154:15:154:20 | access to local variable sink35 | semmle.label | access to local variable sink35 | -| Capture.cs:160:22:160:38 | [implicit argument] tainted : String | semmle.label | [implicit argument] tainted : String | | Capture.cs:160:22:160:38 | call to local function CaptureThrough4 : String | semmle.label | call to local function CaptureThrough4 : String | | Capture.cs:161:15:161:20 | access to local variable sink36 | semmle.label | access to local variable sink36 | -| Capture.cs:168:9:168:32 | SSA call def(sink37) : String | semmle.label | SSA call def(sink37) : String | | Capture.cs:168:25:168:31 | access to parameter tainted : String | semmle.label | access to parameter tainted : String | | Capture.cs:169:15:169:20 | access to local variable sink37 | semmle.label | access to local variable sink37 | | Capture.cs:194:22:194:32 | call to local function Id : String | semmle.label | call to local function Id : String | | Capture.cs:194:25:194:31 | access to parameter tainted : String | semmle.label | access to parameter tainted : String | | Capture.cs:195:15:195:20 | access to local variable sink38 | semmle.label | access to local variable sink38 | -| GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | semmle.label | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | semmle.label | access to property SinkProperty0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:35:13:35:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:37:35:37:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:44:30:44:39 | sinkParam2 : String | semmle.label | sinkParam2 : String | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | semmle.label | access to parameter sinkParam2 | -| GlobalDataFlow.cs:45:13:45:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:52:20:52:37 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:53:15:53:15 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:53:24:53:24 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:53:28:53:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:54:44:54:61 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:55:28:55:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:56:37:56:37 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:56:46:56:46 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:57:35:57:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:64:22:64:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:70:21:70:46 | call to method Return : String | semmle.label | call to method Return : String | -| GlobalDataFlow.cs:70:28:70:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | semmle.label | access to local variable sink0 | -| GlobalDataFlow.cs:72:21:72:101 | (...) ... : String | semmle.label | (...) ... : String | -| GlobalDataFlow.cs:72:29:72:101 | call to method Invoke : String | semmle.label | call to method Invoke : String | -| GlobalDataFlow.cs:72:94:72:98 | access to local variable sink0 : String | semmle.label | access to local variable sink0 : String | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | -| GlobalDataFlow.cs:75:19:75:23 | access to local variable sink1 : String | semmle.label | access to local variable sink1 : String | -| GlobalDataFlow.cs:75:30:75:34 | SSA def(sink2) : String | semmle.label | SSA def(sink2) : String | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | semmle.label | access to local variable sink2 | -| GlobalDataFlow.cs:78:19:78:23 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | -| GlobalDataFlow.cs:78:30:78:34 | SSA def(sink3) : String | semmle.label | SSA def(sink3) : String | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | semmle.label | access to local variable sink3 | -| GlobalDataFlow.cs:80:22:80:85 | call to method SelectEven : IEnumerable | semmle.label | call to method SelectEven : IEnumerable | -| GlobalDataFlow.cs:80:23:80:65 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | semmle.label | access to local variable sink13 | -| GlobalDataFlow.cs:82:23:82:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:82:76:82:86 | [implicit argument 0] delegate creation of type Func : String | semmle.label | [implicit argument 0] delegate creation of type Func : String | -| GlobalDataFlow.cs:82:76:82:86 | [output] delegate creation of type Func : String | semmle.label | [output] delegate creation of type Func : String | -| GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | semmle.label | access to local variable sink14 | -| GlobalDataFlow.cs:84:23:84:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:84:117:84:127 | [implicit argument 0] (...) => ... : String | semmle.label | [implicit argument 0] (...) => ... : String | -| GlobalDataFlow.cs:84:117:84:127 | [output] (...) => ... : String | semmle.label | [output] (...) => ... : String | -| GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | semmle.label | access to local variable sink15 | -| GlobalDataFlow.cs:86:70:86:113 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:86:117:86:127 | [implicit argument 1] (...) => ... : String | semmle.label | [implicit argument 1] (...) => ... : String | -| GlobalDataFlow.cs:86:117:86:127 | [output] (...) => ... : String | semmle.label | [output] (...) => ... : String | -| GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | semmle.label | access to local variable sink16 | -| GlobalDataFlow.cs:88:23:88:66 | (...) ... : String[] | semmle.label | (...) ... : String[] | -| GlobalDataFlow.cs:88:83:88:101 | [implicit argument 1] (...) => ... : String | semmle.label | [implicit argument 1] (...) => ... : String | -| GlobalDataFlow.cs:88:83:88:101 | [output] (...) => ... : String | semmle.label | [output] (...) => ... : String | -| GlobalDataFlow.cs:88:104:88:109 | [implicit argument 0] (...) => ... : String | semmle.label | [implicit argument 0] (...) => ... : String | -| GlobalDataFlow.cs:88:104:88:109 | [output] (...) => ... : String | semmle.label | [output] (...) => ... : String | -| GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | semmle.label | access to local variable sink17 | -| GlobalDataFlow.cs:90:83:90:101 | [implicit argument 0] (...) => ... : String | semmle.label | [implicit argument 0] (...) => ... : String | -| GlobalDataFlow.cs:90:83:90:101 | [output] (...) => ... : String | semmle.label | [output] (...) => ... : String | -| GlobalDataFlow.cs:90:104:90:109 | [implicit argument 0] (...) => ... : String | semmle.label | [implicit argument 0] (...) => ... : String | -| GlobalDataFlow.cs:90:104:90:109 | [output] (...) => ... : String | semmle.label | [output] (...) => ... : String | -| GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | semmle.label | access to local variable sink18 | -| GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | semmle.label | access to local variable sink21 | -| GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | semmle.label | access to local variable sink22 | -| GlobalDataFlow.cs:135:21:135:34 | delegate call : String | semmle.label | delegate call : String | -| GlobalDataFlow.cs:135:29:135:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | semmle.label | access to local variable sink4 | -| GlobalDataFlow.cs:143:21:143:44 | call to method ApplyFunc : String | semmle.label | call to method ApplyFunc : String | -| GlobalDataFlow.cs:143:39:143:43 | access to local variable sink4 : String | semmle.label | access to local variable sink4 : String | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | semmle.label | access to local variable sink5 | -| GlobalDataFlow.cs:153:21:153:25 | call to method Out : String | semmle.label | call to method Out : String | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | semmle.label | access to local variable sink6 | -| GlobalDataFlow.cs:156:20:156:24 | SSA def(sink7) : String | semmle.label | SSA def(sink7) : String | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | semmle.label | access to local variable sink7 | -| GlobalDataFlow.cs:159:20:159:24 | SSA def(sink8) : String | semmle.label | SSA def(sink8) : String | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | semmle.label | access to local variable sink8 | -| GlobalDataFlow.cs:161:22:161:31 | call to method OutYield : IEnumerable | semmle.label | call to method OutYield : IEnumerable | -| GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | semmle.label | access to local variable sink12 | -| GlobalDataFlow.cs:163:22:163:43 | call to method TaintedParam : String | semmle.label | call to method TaintedParam : String | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | semmle.label | access to local variable sink23 | -| GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:180:21:180:26 | delegate call : String | semmle.label | delegate call : String | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | semmle.label | access to local variable sink9 | -| GlobalDataFlow.cs:189:22:189:42 | [library code] object creation of type Lazy : String | semmle.label | [library code] object creation of type Lazy : String | -| GlobalDataFlow.cs:189:22:189:42 | object creation of type Lazy [Value] : String | semmle.label | object creation of type Lazy [Value] : String | -| GlobalDataFlow.cs:189:22:189:48 | access to property Value : String | semmle.label | access to property Value : String | -| GlobalDataFlow.cs:189:39:189:41 | [output] delegate creation of type Func : String | semmle.label | [output] delegate creation of type Func : String | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | semmle.label | access to local variable sink10 | -| GlobalDataFlow.cs:197:22:197:32 | access to property OutProperty : String | semmle.label | access to property OutProperty : String | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | semmle.label | access to local variable sink19 | -| GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:210:35:210:45 | sinkParam10 : String | semmle.label | sinkParam10 : String | -| GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | semmle.label | access to parameter sinkParam10 | -| GlobalDataFlow.cs:211:71:211:71 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:211:89:211:89 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:212:37:212:38 | [implicit argument 0] access to local variable f1 : String | semmle.label | [implicit argument 0] access to local variable f1 : String | -| GlobalDataFlow.cs:212:37:212:38 | [output] access to local variable f1 : String | semmle.label | [output] access to local variable f1 : String | -| GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | semmle.label | access to local variable sink24 | -| GlobalDataFlow.cs:214:37:214:38 | [implicit argument 0] access to local variable f2 : String | semmle.label | [implicit argument 0] access to local variable f2 : String | -| GlobalDataFlow.cs:214:37:214:38 | [output] access to local variable f2 : String | semmle.label | [output] access to local variable f2 : String | -| GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | semmle.label | access to local variable sink25 | -| GlobalDataFlow.cs:216:37:216:48 | [implicit argument 0] delegate creation of type Func : String | semmle.label | [implicit argument 0] delegate creation of type Func : String | -| GlobalDataFlow.cs:216:37:216:48 | [output] delegate creation of type Func : String | semmle.label | [output] delegate creation of type Func : String | -| GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | semmle.label | access to local variable sink26 | -| GlobalDataFlow.cs:236:26:236:35 | sinkParam0 : String | semmle.label | sinkParam0 : String | -| GlobalDataFlow.cs:238:16:238:25 | access to parameter sinkParam0 : String | semmle.label | access to parameter sinkParam0 : String | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | semmle.label | access to parameter sinkParam0 | -| GlobalDataFlow.cs:242:26:242:35 | sinkParam1 : String | semmle.label | sinkParam1 : String | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | semmle.label | access to parameter sinkParam1 | -| GlobalDataFlow.cs:247:26:247:35 | sinkParam3 : String | semmle.label | sinkParam3 : String | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | semmle.label | access to parameter sinkParam3 | -| GlobalDataFlow.cs:252:26:252:35 | sinkParam4 : String | semmle.label | sinkParam4 : String | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | semmle.label | access to parameter sinkParam4 | -| GlobalDataFlow.cs:257:26:257:35 | sinkParam5 : String | semmle.label | sinkParam5 : String | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | semmle.label | access to parameter sinkParam5 | -| GlobalDataFlow.cs:262:26:262:35 | sinkParam6 : String | semmle.label | sinkParam6 : String | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | semmle.label | access to parameter sinkParam6 | -| GlobalDataFlow.cs:267:26:267:35 | sinkParam7 : String | semmle.label | sinkParam7 : String | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | semmle.label | access to parameter sinkParam7 | -| GlobalDataFlow.cs:294:31:294:40 | sinkParam8 : String | semmle.label | sinkParam8 : String | -| GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | semmle.label | access to parameter sinkParam8 | -| GlobalDataFlow.cs:300:32:300:41 | sinkParam9 : String | semmle.label | sinkParam9 : String | -| GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | semmle.label | access to parameter sinkParam9 | -| GlobalDataFlow.cs:306:32:306:42 | sinkParam11 : String | semmle.label | sinkParam11 : String | -| GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | semmle.label | access to parameter sinkParam11 | -| GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:325:9:325:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | -| GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:330:9:330:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | -| GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:336:9:336:36 | yield return ...; : IEnumerable | semmle.label | yield return ...; : IEnumerable | -| GlobalDataFlow.cs:336:22:336:35 | "taint source" : String | semmle.label | "taint source" : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:361:41:361:41 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:363:11:363:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:375:52:375:52 | x : String | semmle.label | x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:377:11:377:11 | access to parameter x : String | semmle.label | access to parameter x : String | -| GlobalDataFlow.cs:380:39:380:45 | tainted : String | semmle.label | tainted : String | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | semmle.label | access to local variable sink11 | -| GlobalDataFlow.cs:384:16:384:21 | access to local variable sink11 : String | semmle.label | access to local variable sink11 : String | -| GlobalDataFlow.cs:406:9:406:11 | value : String | semmle.label | value : String | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | -| GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | semmle.label | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | semmle.label | access to property SinkProperty0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:36:13:36:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:38:35:38:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:45:30:45:39 | sinkParam2 : String | semmle.label | sinkParam2 : String | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | semmle.label | access to parameter sinkParam2 | +| GlobalDataFlow.cs:46:13:46:30 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:53:20:53:37 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:54:15:54:15 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:54:24:54:24 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:54:28:54:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:55:44:55:61 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:56:28:56:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:57:37:57:37 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:57:46:57:46 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:58:35:58:52 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:65:22:65:39 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:71:21:71:46 | call to method Return : String | semmle.label | call to method Return : String | +| GlobalDataFlow.cs:71:28:71:45 | access to property SinkProperty0 : String | semmle.label | access to property SinkProperty0 : String | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | semmle.label | access to local variable sink0 | +| GlobalDataFlow.cs:73:21:73:101 | (...) ... : String | semmle.label | (...) ... : String | +| GlobalDataFlow.cs:73:29:73:101 | call to method Invoke : String | semmle.label | call to method Invoke : String | +| GlobalDataFlow.cs:73:94:73:98 | access to local variable sink0 : String | semmle.label | access to local variable sink0 : String | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | +| GlobalDataFlow.cs:76:19:76:23 | access to local variable sink1 : String | semmle.label | access to local variable sink1 : String | +| GlobalDataFlow.cs:76:30:76:34 | SSA def(sink2) : String | semmle.label | SSA def(sink2) : String | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | semmle.label | access to local variable sink2 | +| GlobalDataFlow.cs:79:19:79:23 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | +| GlobalDataFlow.cs:79:30:79:34 | SSA def(sink3) : String | semmle.label | SSA def(sink3) : String | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | semmle.label | access to local variable sink3 | +| GlobalDataFlow.cs:81:22:81:85 | call to method SelectEven [[]] : String | semmle.label | call to method SelectEven [[]] : String | +| GlobalDataFlow.cs:81:22:81:93 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:81:23:81:65 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:81:57:81:65 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:81:59:81:63 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | semmle.label | access to local variable sink13 | +| GlobalDataFlow.cs:83:22:83:87 | call to method Select [[]] : String | semmle.label | call to method Select [[]] : String | +| GlobalDataFlow.cs:83:22:83:95 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:83:23:83:66 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:83:57:83:66 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:83:59:83:64 | access to local variable sink13 : String | semmle.label | access to local variable sink13 : String | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | semmle.label | access to local variable sink14 | +| GlobalDataFlow.cs:85:22:85:128 | call to method Zip [[]] : String | semmle.label | call to method Zip [[]] : String | +| GlobalDataFlow.cs:85:22:85:136 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:85:23:85:66 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:85:57:85:66 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:85:59:85:64 | access to local variable sink14 : String | semmle.label | access to local variable sink14 : String | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | semmle.label | access to local variable sink15 | +| GlobalDataFlow.cs:87:22:87:128 | call to method Zip [[]] : String | semmle.label | call to method Zip [[]] : String | +| GlobalDataFlow.cs:87:22:87:136 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:87:70:87:113 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:87:104:87:113 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:87:106:87:111 | access to local variable sink15 : String | semmle.label | access to local variable sink15 : String | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | semmle.label | access to local variable sink16 | +| GlobalDataFlow.cs:89:22:89:110 | call to method Aggregate : String | semmle.label | call to method Aggregate : String | +| GlobalDataFlow.cs:89:23:89:66 | (...) ... [[]] : String | semmle.label | (...) ... [[]] : String | +| GlobalDataFlow.cs:89:57:89:66 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:89:59:89:64 | access to local variable sink14 : String | semmle.label | access to local variable sink14 : String | +| GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | semmle.label | access to local variable sink17 | +| GlobalDataFlow.cs:91:22:91:110 | call to method Aggregate : String | semmle.label | call to method Aggregate : String | +| GlobalDataFlow.cs:91:75:91:80 | access to local variable sink14 : String | semmle.label | access to local variable sink14 : String | +| GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | semmle.label | access to local variable sink18 | +| GlobalDataFlow.cs:94:24:94:29 | access to local variable sink18 : String | semmle.label | access to local variable sink18 : String | +| GlobalDataFlow.cs:94:36:94:41 | SSA def(sink21) : Int32 | semmle.label | SSA def(sink21) : Int32 | +| GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | semmle.label | access to local variable sink21 | +| GlobalDataFlow.cs:97:23:97:28 | access to local variable sink18 : String | semmle.label | access to local variable sink18 : String | +| GlobalDataFlow.cs:97:35:97:40 | SSA def(sink22) : Boolean | semmle.label | SSA def(sink22) : Boolean | +| GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | semmle.label | access to local variable sink22 | +| GlobalDataFlow.cs:136:21:136:34 | delegate call : String | semmle.label | delegate call : String | +| GlobalDataFlow.cs:136:29:136:33 | access to local variable sink3 : String | semmle.label | access to local variable sink3 : String | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | semmle.label | access to local variable sink4 | +| GlobalDataFlow.cs:144:21:144:44 | call to method ApplyFunc : String | semmle.label | call to method ApplyFunc : String | +| GlobalDataFlow.cs:144:39:144:43 | access to local variable sink4 : String | semmle.label | access to local variable sink4 : String | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | semmle.label | access to local variable sink5 | +| GlobalDataFlow.cs:154:21:154:25 | call to method Out : String | semmle.label | call to method Out : String | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | semmle.label | access to local variable sink6 | +| GlobalDataFlow.cs:157:20:157:24 | SSA def(sink7) : String | semmle.label | SSA def(sink7) : String | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | semmle.label | access to local variable sink7 | +| GlobalDataFlow.cs:160:20:160:24 | SSA def(sink8) : String | semmle.label | SSA def(sink8) : String | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | semmle.label | access to local variable sink8 | +| GlobalDataFlow.cs:162:22:162:31 | call to method OutYield [[]] : String | semmle.label | call to method OutYield [[]] : String | +| GlobalDataFlow.cs:162:22:162:39 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | semmle.label | access to local variable sink12 | +| GlobalDataFlow.cs:164:22:164:43 | call to method TaintedParam : String | semmle.label | call to method TaintedParam : String | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | semmle.label | access to local variable sink23 | +| GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:181:21:181:26 | delegate call : String | semmle.label | delegate call : String | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | semmle.label | access to local variable sink9 | +| GlobalDataFlow.cs:190:22:190:42 | object creation of type Lazy [Value] : String | semmle.label | object creation of type Lazy [Value] : String | +| GlobalDataFlow.cs:190:22:190:48 | access to property Value : String | semmle.label | access to property Value : String | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | semmle.label | access to local variable sink10 | +| GlobalDataFlow.cs:198:22:198:32 | access to property OutProperty : String | semmle.label | access to property OutProperty : String | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | semmle.label | access to local variable sink19 | +| GlobalDataFlow.cs:208:38:208:61 | array creation of type String[] [[]] : String | semmle.label | array creation of type String[] [[]] : String | +| GlobalDataFlow.cs:208:38:208:75 | call to method AsQueryable [[]] : String | semmle.label | call to method AsQueryable [[]] : String | +| GlobalDataFlow.cs:208:44:208:61 | { ..., ... } [[]] : String | semmle.label | { ..., ... } [[]] : String | +| GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:211:35:211:45 | sinkParam10 : String | semmle.label | sinkParam10 : String | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | semmle.label | access to parameter sinkParam10 | +| GlobalDataFlow.cs:212:71:212:71 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:212:89:212:89 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:213:22:213:28 | access to local variable tainted [[]] : String | semmle.label | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:213:22:213:39 | call to method Select [[]] : String | semmle.label | call to method Select [[]] : String | +| GlobalDataFlow.cs:213:22:213:47 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | semmle.label | access to local variable sink24 | +| GlobalDataFlow.cs:215:22:215:28 | access to local variable tainted [[]] : String | semmle.label | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:215:22:215:39 | call to method Select [[]] : String | semmle.label | call to method Select [[]] : String | +| GlobalDataFlow.cs:215:22:215:47 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | semmle.label | access to local variable sink25 | +| GlobalDataFlow.cs:217:22:217:28 | access to local variable tainted [[]] : String | semmle.label | access to local variable tainted [[]] : String | +| GlobalDataFlow.cs:217:22:217:49 | call to method Select [[]] : String | semmle.label | call to method Select [[]] : String | +| GlobalDataFlow.cs:217:22:217:57 | call to method First : String | semmle.label | call to method First : String | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | semmle.label | access to local variable sink26 | +| GlobalDataFlow.cs:238:20:238:49 | call to method Run [Result] : String | semmle.label | call to method Run [Result] : String | +| GlobalDataFlow.cs:238:35:238:48 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:239:22:239:25 | access to local variable task [Result] : String | semmle.label | access to local variable task [Result] : String | +| GlobalDataFlow.cs:239:22:239:32 | access to property Result : String | semmle.label | access to property Result : String | +| GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | semmle.label | access to local variable sink41 | +| GlobalDataFlow.cs:241:22:241:31 | await ... : String | semmle.label | await ... : String | +| GlobalDataFlow.cs:241:28:241:31 | access to local variable task [Result] : String | semmle.label | access to local variable task [Result] : String | +| GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | semmle.label | access to local variable sink42 | +| GlobalDataFlow.cs:254:26:254:35 | sinkParam0 : String | semmle.label | sinkParam0 : String | +| GlobalDataFlow.cs:256:16:256:25 | access to parameter sinkParam0 : String | semmle.label | access to parameter sinkParam0 : String | +| GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | semmle.label | access to parameter sinkParam0 | +| GlobalDataFlow.cs:260:26:260:35 | sinkParam1 : String | semmle.label | sinkParam1 : String | +| GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | semmle.label | access to parameter sinkParam1 | +| GlobalDataFlow.cs:265:26:265:35 | sinkParam3 : String | semmle.label | sinkParam3 : String | +| GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | semmle.label | access to parameter sinkParam3 | +| GlobalDataFlow.cs:270:26:270:35 | sinkParam4 : String | semmle.label | sinkParam4 : String | +| GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | semmle.label | access to parameter sinkParam4 | +| GlobalDataFlow.cs:275:26:275:35 | sinkParam5 : String | semmle.label | sinkParam5 : String | +| GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | semmle.label | access to parameter sinkParam5 | +| GlobalDataFlow.cs:280:26:280:35 | sinkParam6 : String | semmle.label | sinkParam6 : String | +| GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | semmle.label | access to parameter sinkParam6 | +| GlobalDataFlow.cs:285:26:285:35 | sinkParam7 : String | semmle.label | sinkParam7 : String | +| GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | semmle.label | access to parameter sinkParam7 | +| GlobalDataFlow.cs:312:31:312:40 | sinkParam8 : String | semmle.label | sinkParam8 : String | +| GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | semmle.label | access to parameter sinkParam8 | +| GlobalDataFlow.cs:318:32:318:41 | sinkParam9 : String | semmle.label | sinkParam9 : String | +| GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | semmle.label | access to parameter sinkParam9 | +| GlobalDataFlow.cs:324:32:324:42 | sinkParam11 : String | semmle.label | sinkParam11 : String | +| GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | semmle.label | access to parameter sinkParam11 | +| GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:343:9:343:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | +| GlobalDataFlow.cs:343:13:343:26 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:348:9:348:26 | SSA def(x) : String | semmle.label | SSA def(x) : String | +| GlobalDataFlow.cs:348:13:348:26 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:354:22:354:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:379:41:379:41 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:379:41:379:41 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:381:11:381:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:393:52:393:52 | x : String | semmle.label | x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:395:11:395:11 | access to parameter x : String | semmle.label | access to parameter x : String | +| GlobalDataFlow.cs:398:39:398:45 | tainted : String | semmle.label | tainted : String | +| GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | semmle.label | access to local variable sink11 | +| GlobalDataFlow.cs:402:16:402:21 | access to local variable sink11 : String | semmle.label | access to local variable sink11 : String | +| GlobalDataFlow.cs:424:9:424:11 | value : String | semmle.label | value : String | +| GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | semmle.label | access to local variable sink20 | +| GlobalDataFlow.cs:435:22:435:35 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:451:31:451:32 | [post] access to local variable sb [[]] : String | semmle.label | [post] access to local variable sb [[]] : String | +| GlobalDataFlow.cs:451:35:451:48 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:452:22:452:23 | access to local variable sb [[]] : String | semmle.label | access to local variable sb [[]] : String | +| GlobalDataFlow.cs:452:22:452:34 | call to method ToString : String | semmle.label | call to method ToString : String | +| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | semmle.label | access to local variable sink43 | +| GlobalDataFlow.cs:462:22:462:65 | call to method Join : String | semmle.label | call to method Join : String | +| GlobalDataFlow.cs:462:51:462:64 | "taint source" : String | semmle.label | "taint source" : String | +| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | semmle.label | access to local variable sink44 | | Splitting.cs:3:28:3:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:8:17:8:31 | [b (line 3): false] call to method Return : String | semmle.label | [b (line 3): false] call to method Return : String | | Splitting.cs:8:17:8:31 | [b (line 3): true] call to method Return : String | semmle.label | [b (line 3): true] call to method Return : String | @@ -460,7 +479,8 @@ nodes | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | semmle.label | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | semmle.label | access to local variable x | | Splitting.cs:21:9:21:11 | value : String | semmle.label | value : String | -| Splitting.cs:21:28:21:32 | access to parameter value | semmle.label | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | semmle.label | call to method Return | +| Splitting.cs:21:28:21:32 | access to parameter value : String | semmle.label | access to parameter value : String | | Splitting.cs:24:28:24:34 | tainted : String | semmle.label | tainted : String | | Splitting.cs:30:17:30:23 | [b (line 24): false] access to parameter tainted : String | semmle.label | [b (line 24): false] access to parameter tainted : String | | Splitting.cs:30:17:30:23 | [b (line 24): true] access to parameter tainted : String | semmle.label | [b (line 24): true] access to parameter tainted : String | @@ -471,6 +491,12 @@ nodes | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | semmle.label | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | semmle.label | [b (line 24): true] access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | semmle.label | access to local variable x | +| Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | semmle.label | [b (line 37): true] "taint source" : String | +| Splitting.cs:41:19:41:19 | access to local variable s | semmle.label | access to local variable s | +| Splitting.cs:43:19:43:19 | access to local variable s | semmle.label | access to local variable s | +| Splitting.cs:48:36:48:49 | "taint source" : String | semmle.label | "taint source" : String | +| Splitting.cs:50:19:50:19 | access to local variable s | semmle.label | access to local variable s | +| Splitting.cs:52:19:52:19 | access to local variable s | semmle.label | access to local variable s | #select | Capture.cs:12:19:12:24 | access to local variable sink27 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:12:19:12:24 | access to local variable sink27 | access to local variable sink27 | | Capture.cs:21:23:21:28 | access to local variable sink28 | Capture.cs:7:20:7:26 | tainted : String | Capture.cs:21:23:21:28 | access to local variable sink28 | access to local variable sink28 | @@ -486,51 +512,59 @@ nodes | Capture.cs:161:15:161:20 | access to local variable sink36 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:161:15:161:20 | access to local variable sink36 | access to local variable sink36 | | Capture.cs:169:15:169:20 | access to local variable sink37 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:169:15:169:20 | access to local variable sink37 | access to local variable sink37 | | Capture.cs:195:15:195:20 | access to local variable sink38 | Capture.cs:125:25:125:31 | tainted : String | Capture.cs:195:15:195:20 | access to local variable sink38 | access to local variable sink38 | -| GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:18:15:18:29 | access to field SinkField0 | access to field SinkField0 | -| GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:26:15:26:32 | access to property SinkProperty0 | access to property SinkProperty0 | -| GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:44:50:44:59 | access to parameter sinkParam2 | access to parameter sinkParam2 | -| GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:71:15:71:19 | access to local variable sink0 | access to local variable sink0 | -| GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:73:15:73:19 | access to local variable sink1 | access to local variable sink1 | -| GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:76:15:76:19 | access to local variable sink2 | access to local variable sink2 | -| GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:79:15:79:19 | access to local variable sink3 | access to local variable sink3 | -| GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:81:15:81:20 | access to local variable sink13 | access to local variable sink13 | -| GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:83:15:83:20 | access to local variable sink14 | access to local variable sink14 | -| GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:85:15:85:20 | access to local variable sink15 | access to local variable sink15 | -| GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:87:15:87:20 | access to local variable sink16 | access to local variable sink16 | -| GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:89:15:89:20 | access to local variable sink17 | access to local variable sink17 | -| GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:91:15:91:20 | access to local variable sink18 | access to local variable sink18 | -| GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:94:15:94:20 | access to local variable sink21 | access to local variable sink21 | -| GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:97:15:97:20 | access to local variable sink22 | access to local variable sink22 | -| GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:136:15:136:19 | access to local variable sink4 | access to local variable sink4 | -| GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:144:15:144:19 | access to local variable sink5 | access to local variable sink5 | -| GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:154:15:154:19 | access to local variable sink6 | access to local variable sink6 | -| GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | GlobalDataFlow.cs:325:13:325:26 | "taint source" : String | GlobalDataFlow.cs:157:15:157:19 | access to local variable sink7 | access to local variable sink7 | -| GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | GlobalDataFlow.cs:330:13:330:26 | "taint source" : String | GlobalDataFlow.cs:160:15:160:19 | access to local variable sink8 | access to local variable sink8 | -| GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | GlobalDataFlow.cs:336:22:336:35 | "taint source" : String | GlobalDataFlow.cs:162:15:162:20 | access to local variable sink12 | access to local variable sink12 | -| GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:164:15:164:20 | access to local variable sink23 | access to local variable sink23 | -| GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | GlobalDataFlow.cs:179:35:179:48 | "taint source" : String | GlobalDataFlow.cs:181:15:181:19 | access to local variable sink9 | access to local variable sink9 | -| GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | GlobalDataFlow.cs:320:16:320:29 | "taint source" : String | GlobalDataFlow.cs:190:15:190:20 | access to local variable sink10 | access to local variable sink10 | -| GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | GlobalDataFlow.cs:417:22:417:35 | "taint source" : String | GlobalDataFlow.cs:198:15:198:20 | access to local variable sink19 | access to local variable sink19 | -| GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:210:58:210:68 | access to parameter sinkParam10 | access to parameter sinkParam10 | -| GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:213:15:213:20 | access to local variable sink24 | access to local variable sink24 | -| GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:215:15:215:20 | access to local variable sink25 | access to local variable sink25 | -| GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:217:15:217:20 | access to local variable sink26 | access to local variable sink26 | -| GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:239:15:239:24 | access to parameter sinkParam0 | access to parameter sinkParam0 | -| GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:244:15:244:24 | access to parameter sinkParam1 | access to parameter sinkParam1 | -| GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:249:15:249:24 | access to parameter sinkParam3 | access to parameter sinkParam3 | -| GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:254:15:254:24 | access to parameter sinkParam4 | access to parameter sinkParam4 | -| GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:259:15:259:24 | access to parameter sinkParam5 | access to parameter sinkParam5 | -| GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:264:15:264:24 | access to parameter sinkParam6 | access to parameter sinkParam6 | -| GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:269:15:269:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | -| GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:296:15:296:24 | access to parameter sinkParam8 | access to parameter sinkParam8 | -| GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:302:15:302:24 | access to parameter sinkParam9 | access to parameter sinkParam9 | -| GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | GlobalDataFlow.cs:207:46:207:59 | "taint source" : String | GlobalDataFlow.cs:308:15:308:25 | access to parameter sinkParam11 | access to parameter sinkParam11 | -| GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | GlobalDataFlow.cs:380:39:380:45 | tainted : String | GlobalDataFlow.cs:383:15:383:20 | access to local variable sink11 | access to local variable sink11 | -| GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | GlobalDataFlow.cs:17:27:17:40 | "taint source" : String | GlobalDataFlow.cs:406:41:406:46 | access to local variable sink20 | access to local variable sink20 | +| GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:19:15:19:29 | access to field SinkField0 | access to field SinkField0 | +| GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:27:15:27:32 | access to property SinkProperty0 | access to property SinkProperty0 | +| GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:45:50:45:59 | access to parameter sinkParam2 | access to parameter sinkParam2 | +| GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:72:15:72:19 | access to local variable sink0 | access to local variable sink0 | +| GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:74:15:74:19 | access to local variable sink1 | access to local variable sink1 | +| GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:77:15:77:19 | access to local variable sink2 | access to local variable sink2 | +| GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:80:15:80:19 | access to local variable sink3 | access to local variable sink3 | +| GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:82:15:82:20 | access to local variable sink13 | access to local variable sink13 | +| GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:84:15:84:20 | access to local variable sink14 | access to local variable sink14 | +| GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:86:15:86:20 | access to local variable sink15 | access to local variable sink15 | +| GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:88:15:88:20 | access to local variable sink16 | access to local variable sink16 | +| GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:90:15:90:20 | access to local variable sink17 | access to local variable sink17 | +| GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:92:15:92:20 | access to local variable sink18 | access to local variable sink18 | +| GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:95:15:95:20 | access to local variable sink21 | access to local variable sink21 | +| GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:98:15:98:20 | access to local variable sink22 | access to local variable sink22 | +| GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:137:15:137:19 | access to local variable sink4 | access to local variable sink4 | +| GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:145:15:145:19 | access to local variable sink5 | access to local variable sink5 | +| GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | GlobalDataFlow.cs:155:15:155:19 | access to local variable sink6 | access to local variable sink6 | +| GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | GlobalDataFlow.cs:343:13:343:26 | "taint source" : String | GlobalDataFlow.cs:158:15:158:19 | access to local variable sink7 | access to local variable sink7 | +| GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | GlobalDataFlow.cs:348:13:348:26 | "taint source" : String | GlobalDataFlow.cs:161:15:161:19 | access to local variable sink8 | access to local variable sink8 | +| GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | GlobalDataFlow.cs:354:22:354:35 | "taint source" : String | GlobalDataFlow.cs:163:15:163:20 | access to local variable sink12 | access to local variable sink12 | +| GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:165:15:165:20 | access to local variable sink23 | access to local variable sink23 | +| GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | GlobalDataFlow.cs:180:35:180:48 | "taint source" : String | GlobalDataFlow.cs:182:15:182:19 | access to local variable sink9 | access to local variable sink9 | +| GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | GlobalDataFlow.cs:338:16:338:29 | "taint source" : String | GlobalDataFlow.cs:191:15:191:20 | access to local variable sink10 | access to local variable sink10 | +| GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | GlobalDataFlow.cs:435:22:435:35 | "taint source" : String | GlobalDataFlow.cs:199:15:199:20 | access to local variable sink19 | access to local variable sink19 | +| GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:211:58:211:68 | access to parameter sinkParam10 | access to parameter sinkParam10 | +| GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:214:15:214:20 | access to local variable sink24 | access to local variable sink24 | +| GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:216:15:216:20 | access to local variable sink25 | access to local variable sink25 | +| GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:218:15:218:20 | access to local variable sink26 | access to local variable sink26 | +| GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | GlobalDataFlow.cs:238:35:238:48 | "taint source" : String | GlobalDataFlow.cs:240:15:240:20 | access to local variable sink41 | access to local variable sink41 | +| GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | GlobalDataFlow.cs:238:35:238:48 | "taint source" : String | GlobalDataFlow.cs:242:15:242:20 | access to local variable sink42 | access to local variable sink42 | +| GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:257:15:257:24 | access to parameter sinkParam0 | access to parameter sinkParam0 | +| GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:262:15:262:24 | access to parameter sinkParam1 | access to parameter sinkParam1 | +| GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:267:15:267:24 | access to parameter sinkParam3 | access to parameter sinkParam3 | +| GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:272:15:272:24 | access to parameter sinkParam4 | access to parameter sinkParam4 | +| GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:277:15:277:24 | access to parameter sinkParam5 | access to parameter sinkParam5 | +| GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:282:15:282:24 | access to parameter sinkParam6 | access to parameter sinkParam6 | +| GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:287:15:287:24 | access to parameter sinkParam7 | access to parameter sinkParam7 | +| GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:314:15:314:24 | access to parameter sinkParam8 | access to parameter sinkParam8 | +| GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:320:15:320:24 | access to parameter sinkParam9 | access to parameter sinkParam9 | +| GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | GlobalDataFlow.cs:208:46:208:59 | "taint source" : String | GlobalDataFlow.cs:326:15:326:25 | access to parameter sinkParam11 | access to parameter sinkParam11 | +| GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | GlobalDataFlow.cs:398:39:398:45 | tainted : String | GlobalDataFlow.cs:401:15:401:20 | access to local variable sink11 | access to local variable sink11 | +| GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | GlobalDataFlow.cs:18:27:18:40 | "taint source" : String | GlobalDataFlow.cs:424:41:424:46 | access to local variable sink20 | access to local variable sink20 | +| GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | GlobalDataFlow.cs:451:35:451:48 | "taint source" : String | GlobalDataFlow.cs:453:15:453:20 | access to local variable sink43 | access to local variable sink43 | +| GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | GlobalDataFlow.cs:462:51:462:64 | "taint source" : String | GlobalDataFlow.cs:463:15:463:20 | access to local variable sink44 | access to local variable sink44 | | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x | [b (line 3): false] access to local variable x | | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x | [b (line 3): true] access to local variable x | | Splitting.cs:11:19:11:19 | access to local variable x | Splitting.cs:3:28:3:34 | tainted : String | Splitting.cs:11:19:11:19 | access to local variable x | access to local variable x | -| Splitting.cs:21:28:21:32 | access to parameter value | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:28:21:32 | access to parameter value | access to parameter value | +| Splitting.cs:21:21:21:33 | call to method Return | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:21:21:21:33 | call to method Return | call to method Return | | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): false] access to local variable x | [b (line 24): false] access to local variable x | | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:32:15:32:15 | [b (line 24): true] access to local variable x | [b (line 24): true] access to local variable x | | Splitting.cs:34:19:34:19 | access to local variable x | Splitting.cs:24:28:24:34 | tainted : String | Splitting.cs:34:19:34:19 | access to local variable x | access to local variable x | +| Splitting.cs:41:19:41:19 | access to local variable s | Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | Splitting.cs:41:19:41:19 | access to local variable s | access to local variable s | +| Splitting.cs:43:19:43:19 | access to local variable s | Splitting.cs:39:21:39:34 | [b (line 37): true] "taint source" : String | Splitting.cs:43:19:43:19 | access to local variable s | access to local variable s | +| Splitting.cs:50:19:50:19 | access to local variable s | Splitting.cs:48:36:48:49 | "taint source" : String | Splitting.cs:50:19:50:19 | access to local variable s | access to local variable s | +| Splitting.cs:52:19:52:19 | access to local variable s | Splitting.cs:48:36:48:49 | "taint source" : String | Splitting.cs:52:19:52:19 | access to local variable s | access to local variable s | diff --git a/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.cs b/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.cs index f6bf395eef2b..431cb6843e9c 100644 --- a/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.cs @@ -45,6 +45,7 @@ void M() ieint.Select(x => x); List list = null; list.Find(x => x > 0); + list.Insert(0, 0); Stack stack = null; stack.Peek(); ArrayList al = null; @@ -83,6 +84,8 @@ void M() Path.GetPathRoot(""); HttpContextBase context = null; string name = context.Request.QueryString["name"]; + + var dict = new Dictionary() { { "abc", 0 } }; } [DataContract] diff --git a/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.expected b/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.expected index 6769e2a14118..7a8df6dd4706 100644 --- a/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.expected @@ -1,223 +1,7 @@ callableFlow -| LibraryTypeDataFlow.DataContract.get_AString() | qualifier -> return | false | -| System.Array.Add(object) | argument 0 -> qualifier | false | -| System.Array.AsReadOnly(T[]) | qualifier -> return | false | -| System.Array.Clone() | qualifier -> return | false | -| System.Array.Find(T[], Predicate) | argument 0 -> parameter 0 of argument 1 | false | -| System.Array.Find(T[], Predicate) | argument 0 -> return | false | -| System.Array.FindAll(T[], Predicate) | argument 0 -> parameter 0 of argument 1 | false | -| System.Array.FindAll(T[], Predicate) | argument 0 -> return | false | -| System.Array.FindLast(T[], Predicate) | argument 0 -> parameter 0 of argument 1 | false | -| System.Array.FindLast(T[], Predicate) | argument 0 -> return | false | -| System.Array.GetEnumerator() | qualifier -> return | false | -| System.Array.Insert(int, object) | argument 1 -> qualifier | false | -| System.Array.Reverse(Array) | qualifier -> return | false | -| System.Array.Reverse(Array, int, int) | qualifier -> return | false | -| System.Array.Reverse(T[]) | qualifier -> return | false | -| System.Array.Reverse(T[], int, int) | qualifier -> return | false | | System.Boolean.Parse(string) | argument 0 -> return | false | | System.Boolean.TryParse(string, out bool) | argument 0 -> argument 1 | false | | System.Boolean.TryParse(string, out bool) | argument 0 -> return | false | -| System.Collections.ArrayList.Add(object) | argument 0 -> qualifier | false | -| System.Collections.ArrayList.AddRange(ICollection) | argument 0 -> qualifier | false | -| System.Collections.ArrayList.Clone() | qualifier -> return | false | -| System.Collections.ArrayList.FixedSize(ArrayList) | argument 0 -> return | false | -| System.Collections.ArrayList.FixedSize(IList) | argument 0 -> return | false | -| System.Collections.ArrayList.GetEnumerator() | qualifier -> return | false | -| System.Collections.ArrayList.GetEnumerator(int, int) | qualifier -> return | false | -| System.Collections.ArrayList.GetRange(int, int) | qualifier -> return | false | -| System.Collections.ArrayList.Insert(int, object) | argument 1 -> qualifier | false | -| System.Collections.ArrayList.InsertRange(int, ICollection) | argument 1 -> qualifier | false | -| System.Collections.ArrayList.Reverse() | qualifier -> return | false | -| System.Collections.ArrayList.Reverse(int, int) | qualifier -> return | false | -| System.Collections.BitArray.Clone() | qualifier -> return | false | -| System.Collections.BitArray.GetEnumerator() | qualifier -> return | false | -| System.Collections.CollectionBase.Add(object) | argument 0 -> qualifier | false | -| System.Collections.CollectionBase.GetEnumerator() | qualifier -> return | false | -| System.Collections.CollectionBase.Insert(int, object) | argument 1 -> qualifier | false | -| System.Collections.Concurrent.BlockingCollection<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.Concurrent.BlockingCollection<>.Add(T, CancellationToken) | argument 1 -> qualifier | false | -| System.Collections.Concurrent.BlockingCollection<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Concurrent.ConcurrentBag<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.Concurrent.ConcurrentBag<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(KeyValuePair) | argument 0 -> qualifier | false | -| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(TKey, TValue) | argument 1 -> qualifier | false | -| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.Concurrent.ConcurrentDictionary<,>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Concurrent.ConcurrentDictionary<,>.get_Values() | qualifier -> return | false | -| System.Collections.Concurrent.ConcurrentQueue<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Concurrent.ConcurrentStack<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.DictionaryBase.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.DictionaryBase.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.Dictionary<,>.Add(KeyValuePair) | argument 0 -> qualifier | false | -| System.Collections.Generic.Dictionary<,>.Add(TKey, TValue) | argument 1 -> qualifier | false | -| System.Collections.Generic.Dictionary<,>.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.Generic.Dictionary<,>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.Dictionary<,>.KeyCollection.Add(TKey) | argument 0 -> qualifier | false | -| System.Collections.Generic.Dictionary<,>.KeyCollection.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.Dictionary<,>.ValueCollection.Add(TValue) | argument 0 -> qualifier | false | -| System.Collections.Generic.Dictionary<,>.ValueCollection.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.Dictionary<,>.get_Values() | qualifier -> return | false | -| System.Collections.Generic.HashSet<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.Generic.HashSet<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.ICollection<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.Generic.IDictionary<,>.Add(TKey, TValue) | argument 1 -> qualifier | false | -| System.Collections.Generic.IDictionary<,>.get_Values() | qualifier -> return | false | -| System.Collections.Generic.IEnumerable<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.IEnumerator<>.get_Current() | qualifier -> return | true | -| System.Collections.Generic.IList<>.Insert(int, T) | argument 1 -> qualifier | false | -| System.Collections.Generic.IReadOnlyDictionary<,>.get_Values() | qualifier -> return | false | -| System.Collections.Generic.ISet<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.Generic.KeyValuePair<,>.KeyValuePair(TKey, TValue) | argument 1 -> return | true | -| System.Collections.Generic.KeyValuePair<,>.get_Value() | qualifier -> return | true | -| System.Collections.Generic.LinkedList<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.Generic.LinkedList<>.Find(T) | qualifier -> return | false | -| System.Collections.Generic.LinkedList<>.FindLast(T) | qualifier -> return | false | -| System.Collections.Generic.LinkedList<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.List<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.Generic.List<>.Add(object) | argument 0 -> qualifier | false | -| System.Collections.Generic.List<>.AddRange(IEnumerable) | argument 0 -> qualifier | false | -| System.Collections.Generic.List<>.AsReadOnly() | qualifier -> return | false | -| System.Collections.Generic.List<>.Find(Predicate) | qualifier -> parameter 0 of argument 0 | false | -| System.Collections.Generic.List<>.Find(Predicate) | qualifier -> return | false | -| System.Collections.Generic.List<>.FindAll(Predicate) | qualifier -> parameter 0 of argument 0 | false | -| System.Collections.Generic.List<>.FindAll(Predicate) | qualifier -> return | false | -| System.Collections.Generic.List<>.FindLast(Predicate) | qualifier -> parameter 0 of argument 0 | false | -| System.Collections.Generic.List<>.FindLast(Predicate) | qualifier -> return | false | -| System.Collections.Generic.List<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.List<>.GetRange(int, int) | qualifier -> return | false | -| System.Collections.Generic.List<>.Insert(int, T) | argument 1 -> qualifier | false | -| System.Collections.Generic.List<>.Insert(int, object) | argument 1 -> qualifier | false | -| System.Collections.Generic.List<>.InsertRange(int, IEnumerable) | argument 1 -> qualifier | false | -| System.Collections.Generic.List<>.Reverse() | qualifier -> return | false | -| System.Collections.Generic.List<>.Reverse(int, int) | qualifier -> return | false | -| System.Collections.Generic.Queue<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.Queue<>.Peek() | qualifier -> return | false | -| System.Collections.Generic.SortedDictionary<,>.Add(KeyValuePair) | argument 0 -> qualifier | false | -| System.Collections.Generic.SortedDictionary<,>.Add(TKey, TValue) | argument 1 -> qualifier | false | -| System.Collections.Generic.SortedDictionary<,>.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.Generic.SortedDictionary<,>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.SortedDictionary<,>.KeyCollection.Add(TKey) | argument 0 -> qualifier | false | -| System.Collections.Generic.SortedDictionary<,>.KeyCollection.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.SortedDictionary<,>.ValueCollection.Add(TValue) | argument 0 -> qualifier | false | -| System.Collections.Generic.SortedDictionary<,>.ValueCollection.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.SortedDictionary<,>.get_Values() | qualifier -> return | false | -| System.Collections.Generic.SortedList<,>.Add(KeyValuePair) | argument 0 -> qualifier | false | -| System.Collections.Generic.SortedList<,>.Add(TKey, TValue) | argument 1 -> qualifier | false | -| System.Collections.Generic.SortedList<,>.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.Generic.SortedList<,>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.SortedList<,>.KeyList.Add(TKey) | argument 0 -> qualifier | false | -| System.Collections.Generic.SortedList<,>.KeyList.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.SortedList<,>.KeyList.Insert(int, TKey) | argument 1 -> qualifier | false | -| System.Collections.Generic.SortedList<,>.ValueList.Add(TValue) | argument 0 -> qualifier | false | -| System.Collections.Generic.SortedList<,>.ValueList.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.SortedList<,>.ValueList.Insert(int, TValue) | argument 1 -> qualifier | false | -| System.Collections.Generic.SortedList<,>.get_Values() | qualifier -> return | false | -| System.Collections.Generic.SortedSet<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.Generic.SortedSet<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.SortedSet<>.Reverse() | qualifier -> return | false | -| System.Collections.Generic.Stack<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.Generic.Stack<>.Peek() | qualifier -> return | false | -| System.Collections.Generic.Stack<>.Pop() | qualifier -> return | false | -| System.Collections.Hashtable.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.Hashtable.Clone() | qualifier -> return | false | -| System.Collections.Hashtable.GetEnumerator() | qualifier -> return | false | -| System.Collections.Hashtable.get_Values() | qualifier -> return | false | -| System.Collections.IDictionary.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.IDictionary.GetEnumerator() | qualifier -> return | false | -| System.Collections.IDictionary.get_Values() | qualifier -> return | false | -| System.Collections.IEnumerable.GetEnumerator() | qualifier -> return | false | -| System.Collections.IEnumerator.get_Current() | qualifier -> return | true | -| System.Collections.IList.Add(object) | argument 0 -> qualifier | false | -| System.Collections.IList.Insert(int, object) | argument 1 -> qualifier | false | -| System.Collections.ListDictionaryInternal.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.ListDictionaryInternal.GetEnumerator() | qualifier -> return | false | -| System.Collections.ListDictionaryInternal.get_Values() | qualifier -> return | false | -| System.Collections.ObjectModel.Collection<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.ObjectModel.Collection<>.Add(object) | argument 0 -> qualifier | false | -| System.Collections.ObjectModel.Collection<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.ObjectModel.Collection<>.Insert(int, T) | argument 1 -> qualifier | false | -| System.Collections.ObjectModel.Collection<>.Insert(int, object) | argument 1 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyCollection<>.Add(T) | argument 0 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyCollection<>.Add(object) | argument 0 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyCollection<>.GetEnumerator() | qualifier -> return | false | -| System.Collections.ObjectModel.ReadOnlyCollection<>.Insert(int, T) | argument 1 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyCollection<>.Insert(int, object) | argument 1 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(KeyValuePair) | argument 0 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(TKey, TValue) | argument 1 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.GetEnumerator() | qualifier -> return | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.KeyCollection.Add(TKey) | argument 0 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.KeyCollection.GetEnumerator() | qualifier -> return | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ValueCollection.Add(TValue) | argument 0 -> qualifier | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ValueCollection.GetEnumerator() | qualifier -> return | false | -| System.Collections.ObjectModel.ReadOnlyDictionary<,>.get_Values() | qualifier -> return | false | -| System.Collections.Queue.Clone() | qualifier -> return | false | -| System.Collections.Queue.GetEnumerator() | qualifier -> return | false | -| System.Collections.Queue.Peek() | qualifier -> return | false | -| System.Collections.ReadOnlyCollectionBase.GetEnumerator() | qualifier -> return | false | -| System.Collections.SortedList.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.SortedList.Clone() | qualifier -> return | false | -| System.Collections.SortedList.GetByIndex(int) | qualifier -> return | false | -| System.Collections.SortedList.GetEnumerator() | qualifier -> return | false | -| System.Collections.SortedList.GetValueList() | qualifier -> return | false | -| System.Collections.SortedList.get_Values() | qualifier -> return | false | -| System.Collections.Specialized.HybridDictionary.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.Specialized.HybridDictionary.GetEnumerator() | qualifier -> return | false | -| System.Collections.Specialized.HybridDictionary.get_Values() | qualifier -> return | false | -| System.Collections.Specialized.IOrderedDictionary.GetEnumerator() | qualifier -> return | false | -| System.Collections.Specialized.ListDictionary.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.Specialized.ListDictionary.GetEnumerator() | qualifier -> return | false | -| System.Collections.Specialized.ListDictionary.get_Values() | qualifier -> return | false | -| System.Collections.Specialized.NameObjectCollectionBase.GetEnumerator() | qualifier -> return | false | -| System.Collections.Specialized.NameObjectCollectionBase.KeysCollection.GetEnumerator() | qualifier -> return | false | -| System.Collections.Specialized.NameValueCollection.Add(NameValueCollection) | argument 0 -> qualifier | false | -| System.Collections.Specialized.NameValueCollection.Add(string, string) | argument 1 -> qualifier | false | -| System.Collections.Specialized.OrderedDictionary.Add(object, object) | argument 1 -> qualifier | false | -| System.Collections.Specialized.OrderedDictionary.AsReadOnly() | qualifier -> return | false | -| System.Collections.Specialized.OrderedDictionary.GetEnumerator() | qualifier -> return | false | -| System.Collections.Specialized.OrderedDictionary.get_Values() | qualifier -> return | false | -| System.Collections.Specialized.StringCollection.Add(object) | argument 0 -> qualifier | false | -| System.Collections.Specialized.StringCollection.Add(string) | argument 0 -> qualifier | false | -| System.Collections.Specialized.StringCollection.AddRange(String[]) | argument 0 -> qualifier | false | -| System.Collections.Specialized.StringCollection.GetEnumerator() | qualifier -> return | false | -| System.Collections.Specialized.StringCollection.Insert(int, object) | argument 1 -> qualifier | false | -| System.Collections.Specialized.StringCollection.Insert(int, string) | argument 1 -> qualifier | false | -| System.Collections.Specialized.StringDictionary.Add(string, string) | argument 1 -> qualifier | false | -| System.Collections.Specialized.StringDictionary.GetEnumerator() | qualifier -> return | false | -| System.Collections.Specialized.StringDictionary.get_Values() | qualifier -> return | false | -| System.Collections.Stack.Clone() | qualifier -> return | false | -| System.Collections.Stack.GetEnumerator() | qualifier -> return | false | -| System.Collections.Stack.Peek() | qualifier -> return | false | -| System.Collections.Stack.Pop() | qualifier -> return | false | -| System.ComponentModel.AttributeCollection.GetEnumerator() | qualifier -> return | false | -| System.ComponentModel.BindingList<>.Find(PropertyDescriptor, object) | qualifier -> return | false | -| System.ComponentModel.Design.DesignerCollection.GetEnumerator() | qualifier -> return | false | -| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.Add(object) | argument 0 -> qualifier | false | -| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.GetEnumerator() | qualifier -> return | false | -| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.Insert(int, object) | argument 1 -> qualifier | false | -| System.ComponentModel.Design.DesignerVerbCollection.Add(DesignerVerb) | argument 0 -> qualifier | false | -| System.ComponentModel.Design.DesignerVerbCollection.AddRange(DesignerVerbCollection) | argument 0 -> qualifier | false | -| System.ComponentModel.Design.DesignerVerbCollection.AddRange(DesignerVerb[]) | argument 0 -> qualifier | false | -| System.ComponentModel.Design.DesignerVerbCollection.Insert(int, DesignerVerb) | argument 1 -> qualifier | false | -| System.ComponentModel.EventDescriptorCollection.Add(EventDescriptor) | argument 0 -> qualifier | false | -| System.ComponentModel.EventDescriptorCollection.Add(object) | argument 0 -> qualifier | false | -| System.ComponentModel.EventDescriptorCollection.Find(string, bool) | qualifier -> return | false | -| System.ComponentModel.EventDescriptorCollection.GetEnumerator() | qualifier -> return | false | -| System.ComponentModel.EventDescriptorCollection.Insert(int, EventDescriptor) | argument 1 -> qualifier | false | -| System.ComponentModel.EventDescriptorCollection.Insert(int, object) | argument 1 -> qualifier | false | -| System.ComponentModel.IBindingList.Find(PropertyDescriptor, object) | qualifier -> return | false | -| System.ComponentModel.ListSortDescriptionCollection.Add(object) | argument 0 -> qualifier | false | -| System.ComponentModel.ListSortDescriptionCollection.GetEnumerator() | qualifier -> return | false | -| System.ComponentModel.ListSortDescriptionCollection.Insert(int, object) | argument 1 -> qualifier | false | -| System.ComponentModel.PropertyDescriptorCollection.Add(PropertyDescriptor) | argument 0 -> qualifier | false | -| System.ComponentModel.PropertyDescriptorCollection.Add(object) | argument 0 -> qualifier | false | -| System.ComponentModel.PropertyDescriptorCollection.Add(object, object) | argument 1 -> qualifier | false | -| System.ComponentModel.PropertyDescriptorCollection.Find(string, bool) | qualifier -> return | false | -| System.ComponentModel.PropertyDescriptorCollection.GetEnumerator() | qualifier -> return | false | -| System.ComponentModel.PropertyDescriptorCollection.Insert(int, PropertyDescriptor) | argument 1 -> qualifier | false | -| System.ComponentModel.PropertyDescriptorCollection.Insert(int, object) | argument 1 -> qualifier | false | -| System.ComponentModel.TypeConverter.StandardValuesCollection.GetEnumerator() | qualifier -> return | false | | System.Convert.ChangeType(object, Type) | argument 0 -> return | false | | System.Convert.ChangeType(object, Type, IFormatProvider) | argument 0 -> return | false | | System.Convert.ChangeType(object, TypeCode) | argument 0 -> return | false | @@ -532,10 +316,6 @@ callableFlow | System.Convert.TryFromBase64Chars(ReadOnlySpan, Span, out int) | argument 0 -> return | false | | System.Convert.TryFromBase64String(string, Span, out int) | argument 0 -> return | false | | System.Convert.TryToBase64Chars(ReadOnlySpan, Span, out int, Base64FormattingOptions) | argument 0 -> return | false | -| System.Dynamic.ExpandoObject.Add(KeyValuePair) | argument 0 -> qualifier | false | -| System.Dynamic.ExpandoObject.Add(string, object) | argument 1 -> qualifier | false | -| System.Dynamic.ExpandoObject.GetEnumerator() | qualifier -> return | false | -| System.ICloneable.Clone() | qualifier -> return | false | | System.IO.BufferedStream.BeginRead(Byte[], int, int, AsyncCallback, object) | qualifier -> argument 0 | false | | System.IO.BufferedStream.BeginWrite(Byte[], int, int, AsyncCallback, object) | argument 0 -> qualifier | false | | System.IO.BufferedStream.CopyTo(Stream, int) | qualifier -> argument 0 | false | @@ -662,644 +442,45 @@ callableFlow | System.Int32.TryParse(string, NumberStyles, IFormatProvider, out int) | argument 0 -> return | false | | System.Int32.TryParse(string, out int) | argument 0 -> argument 1 | false | | System.Int32.TryParse(string, out int) | argument 0 -> return | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func, Func) | argument 0 -> parameter 1 of argument 2 | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func, Func) | argument 1 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func, Func) | output from argument 2 -> parameter 0 of argument 3 | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func, Func) | output from argument 3 -> return | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func) | argument 0 -> parameter 1 of argument 2 | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func) | argument 1 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func) | output from argument 2 -> return | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, Func) | argument 0 -> parameter 1 of argument 1 | false | -| System.Linq.Enumerable.Aggregate(IEnumerable, Func) | output from argument 1 -> return | false | -| System.Linq.Enumerable.All(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Any(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.AsEnumerable(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Cast(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Concat(IEnumerable, IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Concat(IEnumerable, IEnumerable) | argument 1 -> return | false | -| System.Linq.Enumerable.Count(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.DefaultIfEmpty(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.DefaultIfEmpty(IEnumerable, TSource) | argument 0 -> return | false | -| System.Linq.Enumerable.DefaultIfEmpty(IEnumerable, TSource) | argument 1 -> return | false | -| System.Linq.Enumerable.Distinct(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Distinct(IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.ElementAt(IEnumerable, int) | argument 0 -> return | false | -| System.Linq.Enumerable.ElementAtOrDefault(IEnumerable, int) | argument 0 -> return | false | -| System.Linq.Enumerable.Except(IEnumerable, IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Except(IEnumerable, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.First(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.First(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.First(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.FirstOrDefault(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.FirstOrDefault(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.FirstOrDefault(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | output from argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | output from argument 2 -> parameter 1 of argument 3 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | output from argument 3 -> return | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 2 -> parameter 1 of argument 3 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 3 -> return | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func) | output from argument 2 -> return | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>) | argument 0 -> parameter 1 of argument 2 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>) | output from argument 1 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>) | output from argument 2 -> return | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.GroupBy(IEnumerable, Func, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | output from argument 4 -> return | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 4 -> return | false | -| System.Linq.Enumerable.Intersect(IEnumerable, IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Intersect(IEnumerable, IEnumerable) | argument 1 -> return | false | -| System.Linq.Enumerable.Intersect(IEnumerable, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.Intersect(IEnumerable, IEnumerable, IEqualityComparer) | argument 1 -> return | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | output from argument 4 -> return | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | output from argument 4 -> return | false | -| System.Linq.Enumerable.Last(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Last(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Last(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.LastOrDefault(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.LastOrDefault(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.LastOrDefault(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.LongCount(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.OfType(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.OrderBy(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.OrderBy(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.OrderBy(IEnumerable, Func, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.OrderBy(IEnumerable, Func, IComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.OrderByDescending(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.OrderByDescending(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.OrderByDescending(IEnumerable, Func, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.OrderByDescending(IEnumerable, Func, IComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.Reverse(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Select(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Select(IEnumerable, Func) | output from argument 1 -> return | false | -| System.Linq.Enumerable.Select(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Select(IEnumerable, Func) | output from argument 1 -> return | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | output from argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | output from argument 2 -> return | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | output from argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | output from argument 2 -> return | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>) | output from argument 1 -> return | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.SelectMany(IEnumerable, Func>) | output from argument 1 -> return | false | -| System.Linq.Enumerable.Single(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Single(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Single(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.SingleOrDefault(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.SingleOrDefault(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.SingleOrDefault(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.Skip(IEnumerable, int) | argument 0 -> return | false | -| System.Linq.Enumerable.SkipWhile(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.SkipWhile(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.SkipWhile(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.SkipWhile(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Take(IEnumerable, int) | argument 0 -> return | false | -| System.Linq.Enumerable.TakeWhile(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.TakeWhile(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.TakeWhile(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.TakeWhile(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.ThenBy(IOrderedEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ThenBy(IOrderedEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.ThenBy(IOrderedEnumerable, Func, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ThenBy(IOrderedEnumerable, Func, IComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.ThenByDescending(IOrderedEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ThenByDescending(IOrderedEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.ThenByDescending(IOrderedEnumerable, Func, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ThenByDescending(IOrderedEnumerable, Func, IComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.ToArray(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func) | output from argument 2 -> return | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func, IEqualityComparer) | output from argument 2 -> return | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.ToList(IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func) | output from argument 2 -> return | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func, IEqualityComparer) | output from argument 2 -> return | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.ToLookup(IEnumerable, Func, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.Union(IEnumerable, IEnumerable) | argument 0 -> return | false | -| System.Linq.Enumerable.Union(IEnumerable, IEnumerable) | argument 1 -> return | false | -| System.Linq.Enumerable.Union(IEnumerable, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Enumerable.Union(IEnumerable, IEnumerable, IEqualityComparer) | argument 1 -> return | false | -| System.Linq.Enumerable.Where(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Where(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.Where(IEnumerable, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Enumerable.Where(IEnumerable, Func) | argument 0 -> return | false | -| System.Linq.Enumerable.Zip(IEnumerable, IEnumerable, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Enumerable.Zip(IEnumerable, IEnumerable, Func) | argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.Enumerable.Zip(IEnumerable, IEnumerable, Func) | output from argument 2 -> return | false | -| System.Linq.EnumerableQuery<>.GetEnumerator() | qualifier -> return | false | -| System.Linq.Grouping<,>.Add(TElement) | argument 0 -> qualifier | false | -| System.Linq.Grouping<,>.GetEnumerator() | qualifier -> return | false | -| System.Linq.Grouping<,>.Insert(int, TElement) | argument 1 -> qualifier | false | -| System.Linq.Lookup<,>.GetEnumerator() | qualifier -> return | false | -| System.Linq.OrderedParallelQuery<>.GetEnumerator() | qualifier -> return | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, Func) | argument 0 -> parameter 1 of argument 2 | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, Func) | argument 1 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, Func) | output from argument 2 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, Func) | output from argument 3 -> return | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | argument 0 -> parameter 1 of argument 2 | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | argument 1 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, Func) | argument 0 -> parameter 1 of argument 1 | false | -| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, Func) | output from argument 1 -> return | false | -| System.Linq.ParallelEnumerable.All(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Any(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.AsEnumerable(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Cast(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Concat(ParallelQuery, IEnumerable) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Concat(ParallelQuery, IEnumerable) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Concat(ParallelQuery, ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Concat(ParallelQuery, ParallelQuery) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Count(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.DefaultIfEmpty(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.DefaultIfEmpty(ParallelQuery, TSource) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.DefaultIfEmpty(ParallelQuery, TSource) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Distinct(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Distinct(ParallelQuery, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ElementAt(ParallelQuery, int) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ElementAtOrDefault(ParallelQuery, int) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Except(ParallelQuery, IEnumerable) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Except(ParallelQuery, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Except(ParallelQuery, ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Except(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.First(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.First(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.First(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.FirstOrDefault(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.FirstOrDefault(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.FirstOrDefault(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | output from argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | output from argument 2 -> parameter 1 of argument 3 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | output from argument 3 -> return | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 2 -> parameter 1 of argument 3 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 3 -> return | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>) | argument 0 -> parameter 1 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>) | output from argument 1 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | output from argument 4 -> return | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 4 -> return | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | output from argument 4 -> return | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 4 -> return | false | -| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, IEnumerable) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, IEnumerable) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, IEnumerable, IEqualityComparer) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, ParallelQuery) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | output from argument 4 -> return | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | output from argument 4 -> return | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | output from argument 4 -> return | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | output from argument 4 -> return | false | -| System.Linq.ParallelEnumerable.Last(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Last(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Last(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.LastOrDefault(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.LastOrDefault(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.LastOrDefault(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.LongCount(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.OfType(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.OrderBy(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.OrderBy(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.OrderBy(ParallelQuery, Func, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.OrderBy(ParallelQuery, Func, IComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.OrderByDescending(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.OrderByDescending(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.OrderByDescending(ParallelQuery, Func, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.OrderByDescending(ParallelQuery, Func, IComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Reverse(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Select(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Select(ParallelQuery, Func) | output from argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Select(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Select(ParallelQuery, Func) | output from argument 1 -> return | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | output from argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | output from argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>) | output from argument 1 -> return | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>) | output from argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Single(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Single(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Single(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.SingleOrDefault(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.SingleOrDefault(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.SingleOrDefault(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Skip(ParallelQuery, int) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.SkipWhile(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.SkipWhile(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.SkipWhile(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.SkipWhile(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Take(ParallelQuery, int) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.TakeWhile(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.TakeWhile(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.TakeWhile(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.TakeWhile(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ThenBy(OrderedParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ThenBy(OrderedParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ThenBy(OrderedParallelQuery, Func, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ThenBy(OrderedParallelQuery, Func, IComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ThenByDescending(OrderedParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ThenByDescending(OrderedParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ThenByDescending(OrderedParallelQuery, Func, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ThenByDescending(OrderedParallelQuery, Func, IComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ToArray(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func, IEqualityComparer) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ToList(ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func, IEqualityComparer) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Union(ParallelQuery, IEnumerable) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Union(ParallelQuery, IEnumerable) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Union(ParallelQuery, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Union(ParallelQuery, IEnumerable, IEqualityComparer) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Union(ParallelQuery, ParallelQuery) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Union(ParallelQuery, ParallelQuery) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Union(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Union(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 1 -> return | false | -| System.Linq.ParallelEnumerable.Where(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Where(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Where(ParallelQuery, Func) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.ParallelEnumerable.Where(ParallelQuery, Func) | argument 0 -> return | false | -| System.Linq.ParallelEnumerable.Zip(ParallelQuery, IEnumerable, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.Zip(ParallelQuery, IEnumerable, Func) | argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.ParallelEnumerable.Zip(ParallelQuery, IEnumerable, Func) | output from argument 2 -> return | false | -| System.Linq.ParallelEnumerable.Zip(ParallelQuery, ParallelQuery, Func) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.ParallelEnumerable.Zip(ParallelQuery, ParallelQuery, Func) | argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.ParallelEnumerable.Zip(ParallelQuery, ParallelQuery, Func) | output from argument 2 -> return | false | -| System.Linq.ParallelQuery.GetEnumerator() | qualifier -> return | false | -| System.Linq.ParallelQuery<>.GetEnumerator() | qualifier -> return | false | -| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>, Expression>) | argument 0 -> parameter 1 of argument 2 | false | -| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>, Expression>) | argument 1 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>, Expression>) | output from argument 2 -> parameter 0 of argument 3 | false | -| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>, Expression>) | output from argument 3 -> return | false | -| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>) | argument 0 -> parameter 1 of argument 2 | false | -| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>) | argument 1 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>) | output from argument 2 -> return | false | -| System.Linq.Queryable.Aggregate(IQueryable, Expression>) | argument 0 -> parameter 1 of argument 1 | false | -| System.Linq.Queryable.Aggregate(IQueryable, Expression>) | output from argument 1 -> return | false | -| System.Linq.Queryable.All(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Any(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.AsQueryable(IEnumerable) | argument 0 -> return | false | -| System.Linq.Queryable.AsQueryable(IEnumerable) | argument 0 -> return | false | -| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Cast(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.Concat(IQueryable, IEnumerable) | argument 0 -> return | false | -| System.Linq.Queryable.Concat(IQueryable, IEnumerable) | argument 1 -> return | false | -| System.Linq.Queryable.Count(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.DefaultIfEmpty(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.DefaultIfEmpty(IQueryable, TSource) | argument 0 -> return | false | -| System.Linq.Queryable.DefaultIfEmpty(IQueryable, TSource) | argument 1 -> return | false | -| System.Linq.Queryable.Distinct(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.Distinct(IQueryable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Queryable.ElementAt(IQueryable, int) | argument 0 -> return | false | -| System.Linq.Queryable.ElementAtOrDefault(IQueryable, int) | argument 0 -> return | false | -| System.Linq.Queryable.Except(IQueryable, IEnumerable) | argument 0 -> return | false | -| System.Linq.Queryable.Except(IQueryable, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Queryable.First(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.First(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.First(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.FirstOrDefault(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.FirstOrDefault(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.FirstOrDefault(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | output from argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | output from argument 2 -> parameter 1 of argument 3 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | output from argument 3 -> return | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 2 -> parameter 1 of argument 3 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 3 -> return | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>) | output from argument 2 -> return | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>) | output from argument 2 -> return | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, IEqualityComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.GroupBy(IQueryable, Expression>, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | output from argument 4 -> return | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 4 -> return | false | -| System.Linq.Queryable.Intersect(IQueryable, IEnumerable) | argument 0 -> return | false | -| System.Linq.Queryable.Intersect(IQueryable, IEnumerable) | argument 1 -> return | false | -| System.Linq.Queryable.Intersect(IQueryable, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Queryable.Intersect(IQueryable, IEnumerable, IEqualityComparer) | argument 1 -> return | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | output from argument 4 -> return | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | argument 0 -> parameter 0 of argument 4 | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | argument 1 -> parameter 0 of argument 3 | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | argument 1 -> parameter 1 of argument 4 | false | -| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | output from argument 4 -> return | false | -| System.Linq.Queryable.Last(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.Last(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Last(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.LastOrDefault(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.LastOrDefault(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.LastOrDefault(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.LongCount(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Max(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Min(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.OfType(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.OrderBy(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.OrderBy(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.OrderBy(IQueryable, Expression>, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.OrderBy(IQueryable, Expression>, IComparer) | argument 0 -> return | false | -| System.Linq.Queryable.OrderByDescending(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.OrderByDescending(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.OrderByDescending(IQueryable, Expression>, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.OrderByDescending(IQueryable, Expression>, IComparer) | argument 0 -> return | false | -| System.Linq.Queryable.Reverse(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.Select(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Select(IQueryable, Expression>) | output from argument 1 -> return | false | -| System.Linq.Queryable.Select(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Select(IQueryable, Expression>) | output from argument 1 -> return | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | output from argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | output from argument 2 -> return | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | output from argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | output from argument 2 -> return | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>) | output from argument 1 -> return | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.SelectMany(IQueryable, Expression>>) | output from argument 1 -> return | false | -| System.Linq.Queryable.Single(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.Single(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Single(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.SingleOrDefault(IQueryable) | argument 0 -> return | false | -| System.Linq.Queryable.SingleOrDefault(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.SingleOrDefault(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.Skip(IQueryable, int) | argument 0 -> return | false | -| System.Linq.Queryable.SkipWhile(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.SkipWhile(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.SkipWhile(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.SkipWhile(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Take(IQueryable, int) | argument 0 -> return | false | -| System.Linq.Queryable.TakeWhile(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.TakeWhile(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.TakeWhile(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.TakeWhile(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.ThenBy(IOrderedQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.ThenBy(IOrderedQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.ThenBy(IOrderedQueryable, Expression>, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.ThenBy(IOrderedQueryable, Expression>, IComparer) | argument 0 -> return | false | -| System.Linq.Queryable.ThenByDescending(IOrderedQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.ThenByDescending(IOrderedQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.ThenByDescending(IOrderedQueryable, Expression>, IComparer) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.ThenByDescending(IOrderedQueryable, Expression>, IComparer) | argument 0 -> return | false | -| System.Linq.Queryable.Union(IQueryable, IEnumerable) | argument 0 -> return | false | -| System.Linq.Queryable.Union(IQueryable, IEnumerable) | argument 1 -> return | false | -| System.Linq.Queryable.Union(IQueryable, IEnumerable, IEqualityComparer) | argument 0 -> return | false | -| System.Linq.Queryable.Union(IQueryable, IEnumerable, IEqualityComparer) | argument 1 -> return | false | -| System.Linq.Queryable.Where(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Where(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.Where(IQueryable, Expression>) | argument 0 -> parameter 0 of argument 1 | false | -| System.Linq.Queryable.Where(IQueryable, Expression>) | argument 0 -> return | false | -| System.Linq.Queryable.Zip(IQueryable, IEnumerable, Expression>) | argument 0 -> parameter 0 of argument 2 | false | -| System.Linq.Queryable.Zip(IQueryable, IEnumerable, Expression>) | argument 1 -> parameter 1 of argument 2 | false | -| System.Linq.Queryable.Zip(IQueryable, IEnumerable, Expression>) | output from argument 2 -> return | false | +| System.Lazy<>.get_Value() | qualifier -> return | false | +| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func, Func) | argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func, Func) | output from argument 2 -> parameter 0 of argument 3 | true | +| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func, Func) | output from argument 3 -> return | true | +| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func) | argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func) | output from argument 2 -> return | true | +| System.Linq.Enumerable.Aggregate(IEnumerable, Func) | output from argument 1 -> return | true | +| System.Linq.Enumerable.DefaultIfEmpty(IEnumerable, TSource) | argument 1 -> return | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>) | argument 0 -> parameter 1 of argument 2 | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, Func) | argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, Func) | output from argument 2 -> parameter 0 of argument 3 | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, Func) | output from argument 3 -> return | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | output from argument 2 -> return | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, Func) | output from argument 1 -> return | true | +| System.Linq.ParallelEnumerable.DefaultIfEmpty(ParallelQuery, TSource) | argument 1 -> return | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>) | argument 0 -> parameter 1 of argument 2 | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>, Expression>) | argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>, Expression>) | output from argument 2 -> parameter 0 of argument 3 | true | +| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>, Expression>) | output from argument 3 -> return | true | +| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>) | argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>) | output from argument 2 -> return | true | +| System.Linq.Queryable.Aggregate(IQueryable, Expression>) | output from argument 1 -> return | true | +| System.Linq.Queryable.DefaultIfEmpty(IQueryable, TSource) | argument 1 -> return | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 1 -> parameter 0 of argument 2 | true | | System.Net.Cookie.get_Value() | qualifier -> return | false | -| System.Net.CookieCollection.Add(Cookie) | argument 0 -> qualifier | false | -| System.Net.CookieCollection.Add(CookieCollection) | argument 0 -> qualifier | false | -| System.Net.CookieCollection.GetEnumerator() | qualifier -> return | false | -| System.Net.CredentialCache.GetEnumerator() | qualifier -> return | false | -| System.Net.HttpListenerPrefixCollection.Add(string) | argument 0 -> qualifier | false | -| System.Net.HttpListenerPrefixCollection.GetEnumerator() | qualifier -> return | false | -| System.Net.NetworkInformation.IPAddressCollection.Add(IPAddress) | argument 0 -> qualifier | false | -| System.Net.NetworkInformation.IPAddressCollection.GetEnumerator() | qualifier -> return | false | | System.Net.Security.NegotiateStream.BeginRead(Byte[], int, int, AsyncCallback, object) | qualifier -> argument 0 | false | | System.Net.Security.NegotiateStream.BeginWrite(Byte[], int, int, AsyncCallback, object) | argument 0 -> qualifier | false | | System.Net.Security.NegotiateStream.Read(Byte[], int, int) | qualifier -> argument 0 | false | @@ -1313,29 +494,15 @@ callableFlow | System.Net.WebUtility.HtmlEncode(string) | argument 0 -> return | false | | System.Net.WebUtility.HtmlEncode(string, TextWriter) | argument 0 -> return | false | | System.Net.WebUtility.UrlEncode(string) | argument 0 -> return | false | -| System.Object.ToString() | qualifier -> return | false | -| System.Resources.IResourceReader.GetEnumerator() | qualifier -> return | false | -| System.Resources.ResourceReader.GetEnumerator() | qualifier -> return | false | -| System.Resources.ResourceSet.GetEnumerator() | qualifier -> return | false | -| System.Runtime.CompilerServices.ConditionalWeakTable<,>.Add(TKey, TValue) | argument 1 -> qualifier | false | -| System.Runtime.CompilerServices.ConditionalWeakTable<,>.GetEnumerator() | qualifier -> return | false | -| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Add(T) | argument 0 -> qualifier | false | -| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Add(object) | argument 0 -> qualifier | false | -| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.GetEnumerator() | qualifier -> return | false | -| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Insert(int, T) | argument 1 -> qualifier | false | -| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Insert(int, object) | argument 1 -> qualifier | false | -| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Reverse() | qualifier -> return | false | -| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Reverse(int, int) | qualifier -> return | false | +| System.Nullable<>.GetValueOrDefault(T) | argument 0 -> return | true | +| System.Nullable<>.get_Value() | qualifier -> return | false | | System.Security.Cryptography.CryptoStream.BeginRead(Byte[], int, int, AsyncCallback, object) | qualifier -> argument 0 | false | | System.Security.Cryptography.CryptoStream.BeginWrite(Byte[], int, int, AsyncCallback, object) | argument 0 -> qualifier | false | | System.Security.Cryptography.CryptoStream.Read(Byte[], int, int) | qualifier -> argument 0 | false | | System.Security.Cryptography.CryptoStream.ReadAsync(Byte[], int, int, CancellationToken) | qualifier -> argument 0 | false | | System.Security.Cryptography.CryptoStream.Write(Byte[], int, int) | argument 0 -> qualifier | false | | System.Security.Cryptography.CryptoStream.WriteAsync(Byte[], int, int, CancellationToken) | argument 0 -> qualifier | false | -| System.Security.PermissionSet.GetEnumerator() | qualifier -> return | false | -| System.String.Clone() | qualifier -> return | false | | System.String.Clone() | qualifier -> return | true | -| System.String.Concat(IEnumerable) | argument 0 -> return | false | | System.String.Concat(ReadOnlySpan, ReadOnlySpan) | argument 0 -> return | false | | System.String.Concat(ReadOnlySpan, ReadOnlySpan) | argument 1 -> return | false | | System.String.Concat(ReadOnlySpan, ReadOnlySpan, ReadOnlySpan) | argument 0 -> return | false | @@ -1360,7 +527,6 @@ callableFlow | System.String.Concat(string, string, string, string) | argument 1 -> return | false | | System.String.Concat(string, string, string, string) | argument 2 -> return | false | | System.String.Concat(string, string, string, string) | argument 3 -> return | false | -| System.String.Concat(IEnumerable) | argument 0 -> return | false | | System.String.Copy(string) | argument 0 -> return | true | | System.String.Format(IFormatProvider, string, object) | argument 1 -> return | false | | System.String.Format(IFormatProvider, string, object) | argument 2 -> return | false | @@ -1371,6 +537,7 @@ callableFlow | System.String.Format(IFormatProvider, string, object, object, object) | argument 2 -> return | false | | System.String.Format(IFormatProvider, string, object, object, object) | argument 3 -> return | false | | System.String.Format(IFormatProvider, string, object, object, object) | argument 4 -> return | false | +| System.String.Format(IFormatProvider, string, params Object[]) | argument 1 -> return | false | | System.String.Format(string, object) | argument 0 -> return | false | | System.String.Format(string, object) | argument 1 -> return | false | | System.String.Format(string, object, object) | argument 0 -> return | false | @@ -1380,21 +547,18 @@ callableFlow | System.String.Format(string, object, object, object) | argument 1 -> return | false | | System.String.Format(string, object, object, object) | argument 2 -> return | false | | System.String.Format(string, object, object, object) | argument 3 -> return | false | -| System.String.GetEnumerator() | qualifier -> return | false | +| System.String.Format(string, params Object[]) | argument 0 -> return | false | | System.String.Insert(int, string) | argument 1 -> return | false | | System.String.Insert(int, string) | qualifier -> return | false | +| System.String.Join(char, String[], int, int) | argument 0 -> return | false | +| System.String.Join(char, params Object[]) | argument 0 -> return | false | +| System.String.Join(char, params String[]) | argument 0 -> return | false | | System.String.Join(string, IEnumerable) | argument 0 -> return | false | -| System.String.Join(string, IEnumerable) | argument 1 -> return | false | | System.String.Join(string, String[], int, int) | argument 0 -> return | false | -| System.String.Join(string, String[], int, int) | argument 1 -> return | false | -| System.String.Join(string, String[], int, int) | argument 2 -> return | false | -| System.String.Join(string, String[], int, int) | argument 3 -> return | false | +| System.String.Join(string, params Object[]) | argument 0 -> return | false | | System.String.Join(string, params String[]) | argument 0 -> return | false | -| System.String.Join(string, params String[]) | argument 1 -> return | false | -| System.String.Join(string, params String[]) | argument 2 -> return | false | -| System.String.Join(string, params String[]) | argument 3 -> return | false | +| System.String.Join(char, IEnumerable) | argument 0 -> return | false | | System.String.Join(string, IEnumerable) | argument 0 -> return | false | -| System.String.Join(string, IEnumerable) | argument 1 -> return | false | | System.String.Normalize() | qualifier -> return | false | | System.String.Normalize(NormalizationForm) | qualifier -> return | false | | System.String.PadLeft(int) | qualifier -> return | false | @@ -1407,24 +571,13 @@ callableFlow | System.String.Replace(char, char) | qualifier -> return | false | | System.String.Replace(string, string) | argument 1 -> return | false | | System.String.Replace(string, string) | qualifier -> return | false | -| System.String.Split(Char[], StringSplitOptions) | qualifier -> return | false | -| System.String.Split(Char[], int) | qualifier -> return | false | -| System.String.Split(Char[], int, StringSplitOptions) | qualifier -> return | false | -| System.String.Split(String[], StringSplitOptions) | qualifier -> return | false | -| System.String.Split(String[], int, StringSplitOptions) | qualifier -> return | false | -| System.String.Split(char, StringSplitOptions) | qualifier -> return | false | -| System.String.Split(char, int, StringSplitOptions) | qualifier -> return | false | -| System.String.Split(params Char[]) | qualifier -> return | false | -| System.String.Split(string, StringSplitOptions) | qualifier -> return | false | -| System.String.Split(string, int, StringSplitOptions) | qualifier -> return | false | -| System.String.String(Char[]) | argument 0 -> return | false | -| System.String.String(Char[], int, int) | argument 0 -> return | false | | System.String.Substring(int) | qualifier -> return | false | | System.String.Substring(int, int) | qualifier -> return | false | | System.String.ToLower() | qualifier -> return | false | | System.String.ToLower(CultureInfo) | qualifier -> return | false | | System.String.ToLowerInvariant() | qualifier -> return | false | | System.String.ToString() | qualifier -> return | true | +| System.String.ToString(IFormatProvider) | qualifier -> return | true | | System.String.ToUpper() | qualifier -> return | false | | System.String.ToUpper(CultureInfo) | qualifier -> return | false | | System.String.ToUpperInvariant() | qualifier -> return | false | @@ -1437,100 +590,25 @@ callableFlow | System.String.TrimStart() | qualifier -> return | false | | System.String.TrimStart(char) | qualifier -> return | false | | System.String.TrimStart(params Char[]) | qualifier -> return | false | -| System.Text.Encoding.GetBytes(Char[]) | argument 0 -> return | false | -| System.Text.Encoding.GetBytes(Char[], int, int) | argument 0 -> return | false | -| System.Text.Encoding.GetBytes(Char[], int, int, Byte[], int) | argument 0 -> return | false | | System.Text.Encoding.GetBytes(ReadOnlySpan, Span) | argument 0 -> return | false | | System.Text.Encoding.GetBytes(char*, int, byte*, int) | argument 0 -> return | false | | System.Text.Encoding.GetBytes(string) | argument 0 -> return | false | | System.Text.Encoding.GetBytes(string, int, int) | argument 0 -> return | false | | System.Text.Encoding.GetBytes(string, int, int, Byte[], int) | argument 0 -> return | false | -| System.Text.Encoding.GetChars(Byte[]) | argument 0 -> return | false | -| System.Text.Encoding.GetChars(Byte[], int, int) | argument 0 -> return | false | -| System.Text.Encoding.GetChars(Byte[], int, int, Char[], int) | argument 0 -> return | false | -| System.Text.Encoding.GetChars(ReadOnlySpan, Span) | argument 0 -> return | false | -| System.Text.Encoding.GetChars(byte*, int, char*, int) | argument 0 -> return | false | -| System.Text.Encoding.GetString(Byte[]) | argument 0 -> return | false | -| System.Text.Encoding.GetString(Byte[], int, int) | argument 0 -> return | false | -| System.Text.Encoding.GetString(ReadOnlySpan) | argument 0 -> return | false | -| System.Text.Encoding.GetString(byte*, int) | argument 0 -> return | false | -| System.Text.RegularExpressions.CaptureCollection.Add(Capture) | argument 0 -> qualifier | false | -| System.Text.RegularExpressions.CaptureCollection.Add(object) | argument 0 -> qualifier | false | -| System.Text.RegularExpressions.CaptureCollection.GetEnumerator() | qualifier -> return | false | -| System.Text.RegularExpressions.CaptureCollection.Insert(int, Capture) | argument 1 -> qualifier | false | -| System.Text.RegularExpressions.CaptureCollection.Insert(int, object) | argument 1 -> qualifier | false | -| System.Text.RegularExpressions.GroupCollection.Add(Group) | argument 0 -> qualifier | false | -| System.Text.RegularExpressions.GroupCollection.Add(object) | argument 0 -> qualifier | false | -| System.Text.RegularExpressions.GroupCollection.GetEnumerator() | qualifier -> return | false | -| System.Text.RegularExpressions.GroupCollection.Insert(int, Group) | argument 1 -> qualifier | false | -| System.Text.RegularExpressions.GroupCollection.Insert(int, object) | argument 1 -> qualifier | false | -| System.Text.RegularExpressions.GroupCollection.get_Values() | qualifier -> return | false | -| System.Text.RegularExpressions.MatchCollection.Add(Match) | argument 0 -> qualifier | false | -| System.Text.RegularExpressions.MatchCollection.Add(object) | argument 0 -> qualifier | false | -| System.Text.RegularExpressions.MatchCollection.GetEnumerator() | qualifier -> return | false | -| System.Text.RegularExpressions.MatchCollection.Insert(int, Match) | argument 1 -> qualifier | false | -| System.Text.RegularExpressions.MatchCollection.Insert(int, object) | argument 1 -> qualifier | false | -| System.Text.StringBuilder.Append(object) | argument 0 -> qualifier | false | -| System.Text.StringBuilder.Append(string) | argument 0 -> qualifier | false | -| System.Text.StringBuilder.Append(string, int, int) | argument 0 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 1 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 2 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 1 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 2 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 3 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 1 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 2 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 3 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 4 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object) | argument 0 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object) | argument 1 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 0 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 1 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 2 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 0 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 1 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 2 -> qualifier | false | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 3 -> qualifier | false | -| System.Text.StringBuilder.AppendLine(string) | argument 0 -> qualifier | false | -| System.Text.StringBuilder.StringBuilder(string) | argument 0 -> return | false | -| System.Text.StringBuilder.StringBuilder(string, int) | argument 0 -> return | false | -| System.Text.StringBuilder.StringBuilder(string, int, int, int) | argument 0 -> return | false | -| System.Text.StringBuilder.ToString() | qualifier -> return | false | | System.Threading.Tasks.Task.ContinueWith(Action, object) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Action, object, CancellationToken) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Action, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Action, object, TaskContinuationOptions) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Action, object, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Func, object) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task.ContinueWith(Func, object) | output from argument 0 -> return | true | | System.Threading.Tasks.Task.ContinueWith(Func, object, CancellationToken) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task.ContinueWith(Func, object, CancellationToken) | output from argument 0 -> return | true | | System.Threading.Tasks.Task.ContinueWith(Func, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task.ContinueWith(Func, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 0 -> return | true | | System.Threading.Tasks.Task.ContinueWith(Func, object, TaskContinuationOptions) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task.ContinueWith(Func, object, TaskContinuationOptions) | output from argument 0 -> return | true | | System.Threading.Tasks.Task.ContinueWith(Func, object, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task.ContinueWith(Func, object, TaskScheduler) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.ContinueWith(Func) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.ContinueWith(Func, CancellationToken) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.ContinueWith(Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.ContinueWith(Func, TaskContinuationOptions) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.ContinueWith(Func, TaskScheduler) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.FromResult(TResult) | argument 0 -> return | true | -| System.Threading.Tasks.Task.Run(Func) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.Run(Func, CancellationToken) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.Run(Func>) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task.Run(Func>, CancellationToken) | output from argument 0 -> return | true | | System.Threading.Tasks.Task.Task(Action, object) | argument 1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task.Task(Action, object, CancellationToken) | argument 1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task.Task(Action, object, CancellationToken, TaskCreationOptions) | argument 1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task.Task(Action, object, TaskCreationOptions) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task.WhenAll(IEnumerable>) | argument 0 -> return | true | -| System.Threading.Tasks.Task.WhenAll(params Task[]) | argument 0 -> return | true | -| System.Threading.Tasks.Task.WhenAll(params Task[]) | argument 1 -> return | true | -| System.Threading.Tasks.Task.WhenAny(IEnumerable>) | argument 0 -> return | true | -| System.Threading.Tasks.Task.WhenAny(params Task[]) | argument 0 -> return | true | -| System.Threading.Tasks.Task.WhenAny(params Task[]) | argument 1 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Action, Object>, object) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action, Object>, object) | qualifier -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action, Object>, object, CancellationToken) | argument 1 -> parameter 1 of argument 0 | true | @@ -1547,127 +625,61 @@ callableFlow | System.Threading.Tasks.Task<>.ContinueWith(Action>, TaskContinuationOptions) | qualifier -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action>, TaskScheduler) | qualifier -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object) | qualifier -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, CancellationToken) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, CancellationToken) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, CancellationToken) | qualifier -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | qualifier -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskContinuationOptions) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskContinuationOptions) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskContinuationOptions) | qualifier -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler) | qualifier -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>) | qualifier -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, CancellationToken) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, CancellationToken) | qualifier -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | qualifier -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskContinuationOptions) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskContinuationOptions) | qualifier -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskScheduler) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskScheduler) | qualifier -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.Task(Func, object) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.Task(Func, object) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.Task(Func, object, CancellationToken) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.Task(Func, object, CancellationToken) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.Task(Func, object, CancellationToken, TaskCreationOptions) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.Task(Func, object, CancellationToken, TaskCreationOptions) | output from argument 0 -> return | true | | System.Threading.Tasks.Task<>.Task(Func, object, TaskCreationOptions) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.Task<>.Task(Func, object, TaskCreationOptions) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task<>.Task(Func) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task<>.Task(Func, CancellationToken) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task<>.Task(Func, CancellationToken, TaskCreationOptions) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task<>.Task(Func, TaskCreationOptions) | output from argument 0 -> return | true | -| System.Threading.Tasks.Task<>.get_Result() | qualifier -> return | true | +| System.Threading.Tasks.Task<>.get_Result() | qualifier -> return | false | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Action) | argument 0 -> parameter 0 of argument 1 | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Action, CancellationToken) | argument 0 -> parameter 0 of argument 1 | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Action, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 0 -> parameter 0 of argument 1 | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Action, TaskContinuationOptions) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, CancellationToken) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, CancellationToken) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, TaskContinuationOptions) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, TaskContinuationOptions) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Action>) | argument 0 -> parameter 0 of argument 1 | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Action>, CancellationToken) | argument 0 -> parameter 0 of argument 1 | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Action>, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 0 -> parameter 0 of argument 1 | true | | System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Action>, TaskContinuationOptions) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, CancellationToken) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TaskContinuationOptions) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory.StartNew(Action, object) | argument 1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.TaskFactory.StartNew(Action, object, CancellationToken) | argument 1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.TaskFactory.StartNew(Action, object, CancellationToken, TaskCreationOptions, TaskScheduler) | argument 1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.TaskFactory.StartNew(Action, object, TaskCreationOptions) | argument 1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.TaskFactory.StartNew(Func, object) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.TaskFactory.StartNew(Func, object) | output from argument 0 -> return | true | | System.Threading.Tasks.TaskFactory.StartNew(Func, object, CancellationToken) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.TaskFactory.StartNew(Func, object, CancellationToken) | output from argument 0 -> return | true | | System.Threading.Tasks.TaskFactory.StartNew(Func, object, CancellationToken, TaskCreationOptions, TaskScheduler) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.TaskFactory.StartNew(Func, object, CancellationToken, TaskCreationOptions, TaskScheduler) | output from argument 0 -> return | true | | System.Threading.Tasks.TaskFactory.StartNew(Func, object, TaskCreationOptions) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.TaskFactory.StartNew(Func, object, TaskCreationOptions) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory.StartNew(Func) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory.StartNew(Func, CancellationToken) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory.StartNew(Func, CancellationToken, TaskCreationOptions, TaskScheduler) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory.StartNew(Func, TaskCreationOptions) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, CancellationToken) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 -> return | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TaskContinuationOptions) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, CancellationToken) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, CancellationToken) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, TaskContinuationOptions) | argument 0 -> parameter 0 of argument 1 | true | -| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, TaskContinuationOptions) | output from argument 1 -> return | true | | System.Threading.Tasks.TaskFactory<>.StartNew(Func, object) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.TaskFactory<>.StartNew(Func, object) | output from argument 0 -> return | true | | System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, CancellationToken) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, CancellationToken) | output from argument 0 -> return | true | | System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, CancellationToken, TaskCreationOptions, TaskScheduler) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, CancellationToken, TaskCreationOptions, TaskScheduler) | output from argument 0 -> return | true | | System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, TaskCreationOptions) | argument 1 -> parameter 0 of argument 0 | true | -| System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, TaskCreationOptions) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory<>.StartNew(Func) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory<>.StartNew(Func, CancellationToken) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory<>.StartNew(Func, CancellationToken, TaskCreationOptions, TaskScheduler) | output from argument 0 -> return | true | -| System.Threading.Tasks.TaskFactory<>.StartNew(Func, TaskCreationOptions) | output from argument 0 -> return | true | | System.Uri.ToString() | qualifier -> return | false | | System.Uri.Uri(string) | argument 0 -> return | false | | System.Uri.Uri(string, UriKind) | argument 0 -> return | false | @@ -1684,6 +696,1383 @@ callableFlow | System.Web.HttpUtility.UrlEncode(string) | argument 0 -> return | false | | System.Web.UI.WebControls.TextBox.get_Text() | qualifier -> return | false | callableFlowAccessPath -| System.Lazy<>.Lazy(Func) | output from argument 0 [] -> return [Value] | -| System.Lazy<>.Lazy(Func, LazyThreadSafetyMode) | output from argument 0 [] -> return [Value] | -| System.Lazy<>.Lazy(Func, bool) | output from argument 0 [] -> return [Value] | +| System.Array.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Array.AsReadOnly(T[]) | argument 0 [[]] -> return [[]] | true | +| System.Array.Clone() | argument 0 [[]] -> return [[]] | true | +| System.Array.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Array.CopyTo(Array, long) | qualifier [[]] -> argument 0 [[]] | true | +| System.Array.Find(T[], Predicate) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Array.Find(T[], Predicate) | argument 0 [[]] -> return [] | true | +| System.Array.FindAll(T[], Predicate) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Array.FindAll(T[], Predicate) | argument 0 [[]] -> return [] | true | +| System.Array.FindLast(T[], Predicate) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Array.FindLast(T[], Predicate) | argument 0 [[]] -> return [] | true | +| System.Array.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Array.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Array.Reverse(Array) | argument 0 [[]] -> return [[]] | true | +| System.Array.Reverse(Array, int, int) | argument 0 [[]] -> return [[]] | true | +| System.Array.Reverse(T[]) | argument 0 [[]] -> return [[]] | true | +| System.Array.Reverse(T[], int, int) | argument 0 [[]] -> return [[]] | true | +| System.Collections.ArrayList.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.ArrayList.AddRange(ICollection) | argument 0 [[]] -> qualifier [[]] | true | +| System.Collections.ArrayList.Clone() | argument 0 [[]] -> return [[]] | true | +| System.Collections.ArrayList.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ArrayList.FixedSize(ArrayList) | argument 0 [[]] -> return [[]] | true | +| System.Collections.ArrayList.FixedSize(IList) | argument 0 [[]] -> return [[]] | true | +| System.Collections.ArrayList.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.ArrayList.GetEnumerator(int, int) | qualifier [[]] -> return [Current] | true | +| System.Collections.ArrayList.GetRange(int, int) | argument 0 [[]] -> return [[]] | true | +| System.Collections.ArrayList.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.ArrayList.InsertRange(int, ICollection) | argument 1 [[]] -> qualifier [[]] | true | +| System.Collections.ArrayList.Repeat(object, int) | argument 0 [] -> return [[]] | true | +| System.Collections.ArrayList.Reverse() | argument 0 [[]] -> return [[]] | true | +| System.Collections.ArrayList.Reverse(int, int) | argument 0 [[]] -> return [[]] | true | +| System.Collections.ArrayList.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.ArrayList.set_Item(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.BitArray.Clone() | argument 0 [[]] -> return [[]] | true | +| System.Collections.BitArray.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.BitArray.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.CollectionBase.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.CollectionBase.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.CollectionBase.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.CollectionBase.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Concurrent.BlockingCollection<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Concurrent.BlockingCollection<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.BlockingCollection<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.BlockingCollection<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Concurrent.ConcurrentBag<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Concurrent.ConcurrentBag<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.ConcurrentBag<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.ConcurrentBag<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(KeyValuePair) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(KeyValuePair) | argument 0 [Key] -> qualifier [[], Key] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(KeyValuePair) | argument 0 [Value] -> qualifier [[], Value] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.ConcurrentDictionary(IEnumerable>) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.ConcurrentDictionary(IEnumerable>) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.ConcurrentDictionary(IEnumerable>, IEqualityComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.ConcurrentDictionary(IEnumerable>, IEqualityComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.ConcurrentDictionary(int, IEnumerable>, IEqualityComparer) | argument 1 [[], Key] -> return [[], Key] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.ConcurrentDictionary(int, IEnumerable>, IEqualityComparer) | argument 1 [[], Value] -> return [[], Value] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.CopyTo(KeyValuePair[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.get_Item(TKey) | qualifier [[], Value] -> return [] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.set_Item(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Concurrent.ConcurrentDictionary<,>.set_Item(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Concurrent.ConcurrentQueue<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.ConcurrentQueue<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.ConcurrentQueue<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Concurrent.ConcurrentStack<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.ConcurrentStack<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Concurrent.ConcurrentStack<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Concurrent.IProducerConsumerCollection<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.DictionaryBase.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.DictionaryBase.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.DictionaryBase.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.DictionaryBase.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.Dictionary<,>.Add(KeyValuePair) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.Dictionary<,>.Add(KeyValuePair) | argument 0 [Key] -> qualifier [[], Key] | true | +| System.Collections.Generic.Dictionary<,>.Add(KeyValuePair) | argument 0 [Value] -> qualifier [[], Value] | true | +| System.Collections.Generic.Dictionary<,>.Add(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.Dictionary<,>.Add(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.Dictionary<,>.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.Dictionary<,>.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.Dictionary<,>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Dictionary<,>.CopyTo(KeyValuePair[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Dictionary<,>.Dictionary(IDictionary) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Generic.Dictionary<,>.Dictionary(IDictionary) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Generic.Dictionary<,>.Dictionary(IDictionary, IEqualityComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Generic.Dictionary<,>.Dictionary(IDictionary, IEqualityComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Generic.Dictionary<,>.Dictionary(IEnumerable>) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Generic.Dictionary<,>.Dictionary(IEnumerable>) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Generic.Dictionary<,>.Dictionary(IEnumerable>, IEqualityComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Generic.Dictionary<,>.Dictionary(IEnumerable>, IEqualityComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Generic.Dictionary<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.Dictionary<,>.KeyCollection.Add(TKey) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.Dictionary<,>.KeyCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Dictionary<,>.KeyCollection.CopyTo(TKey[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Dictionary<,>.KeyCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.Dictionary<,>.ValueCollection.Add(TValue) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.Dictionary<,>.ValueCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Dictionary<,>.ValueCollection.CopyTo(TValue[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Dictionary<,>.ValueCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.Dictionary<,>.get_Item(TKey) | qualifier [[], Value] -> return [] | true | +| System.Collections.Generic.Dictionary<,>.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Generic.Dictionary<,>.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Generic.Dictionary<,>.set_Item(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.Dictionary<,>.set_Item(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.HashSet<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.HashSet<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.HashSet<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.ICollection<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.ICollection<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.IDictionary<,>.Add(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.IDictionary<,>.Add(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.IDictionary<,>.get_Item(TKey) | qualifier [[], Value] -> return [] | true | +| System.Collections.Generic.IDictionary<,>.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Generic.IDictionary<,>.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Generic.IDictionary<,>.set_Item(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.IDictionary<,>.set_Item(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.IList<>.Insert(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.IList<>.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.IList<>.set_Item(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.ISet<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.KeyValuePair<,>.KeyValuePair() | argument 0 [] -> return [Key] | true | +| System.Collections.Generic.KeyValuePair<,>.KeyValuePair() | argument 1 [] -> return [Value] | true | +| System.Collections.Generic.KeyValuePair<,>.KeyValuePair(TKey, TValue) | argument 0 [] -> return [Key] | true | +| System.Collections.Generic.KeyValuePair<,>.KeyValuePair(TKey, TValue) | argument 1 [] -> return [Value] | true | +| System.Collections.Generic.LinkedList<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.LinkedList<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.LinkedList<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.LinkedList<>.Find(T) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.LinkedList<>.FindLast(T) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.LinkedList<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.List<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.List<>.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.List<>.AddRange(IEnumerable) | argument 0 [[]] -> qualifier [[]] | true | +| System.Collections.Generic.List<>.AsReadOnly() | argument 0 [[]] -> return [[]] | true | +| System.Collections.Generic.List<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.List<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.List<>.Find(Predicate) | qualifier [[]] -> parameter 0 of argument 0 [] | true | +| System.Collections.Generic.List<>.Find(Predicate) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.List<>.FindAll(Predicate) | qualifier [[]] -> parameter 0 of argument 0 [] | true | +| System.Collections.Generic.List<>.FindAll(Predicate) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.List<>.FindLast(Predicate) | qualifier [[]] -> parameter 0 of argument 0 [] | true | +| System.Collections.Generic.List<>.FindLast(Predicate) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.List<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.List<>.GetRange(int, int) | argument 0 [[]] -> return [[]] | true | +| System.Collections.Generic.List<>.Insert(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.List<>.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.List<>.InsertRange(int, IEnumerable) | argument 1 [[]] -> qualifier [[]] | true | +| System.Collections.Generic.List<>.Reverse() | argument 0 [[]] -> return [[]] | true | +| System.Collections.Generic.List<>.Reverse(int, int) | argument 0 [[]] -> return [[]] | true | +| System.Collections.Generic.List<>.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.List<>.set_Item(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.Queue<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Queue<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Queue<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.Queue<>.Peek() | qualifier [[]] -> return [] | true | +| System.Collections.Generic.SortedDictionary<,>.Add(KeyValuePair) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.Add(KeyValuePair) | argument 0 [Key] -> qualifier [[], Key] | true | +| System.Collections.Generic.SortedDictionary<,>.Add(KeyValuePair) | argument 0 [Value] -> qualifier [[], Value] | true | +| System.Collections.Generic.SortedDictionary<,>.Add(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.SortedDictionary<,>.Add(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.SortedDictionary<,>.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.SortedDictionary<,>.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.SortedDictionary<,>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.CopyTo(KeyValuePair[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.SortedDictionary<,>.KeyCollection.Add(TKey) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.KeyCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.KeyCollection.CopyTo(TKey[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.KeyCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.SortedDictionary<,>.SortedDictionary(IDictionary) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Generic.SortedDictionary<,>.SortedDictionary(IDictionary) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Generic.SortedDictionary<,>.SortedDictionary(IDictionary, IComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Generic.SortedDictionary<,>.SortedDictionary(IDictionary, IComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Generic.SortedDictionary<,>.ValueCollection.Add(TValue) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.ValueCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.ValueCollection.CopyTo(TValue[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.ValueCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.SortedDictionary<,>.get_Item(TKey) | qualifier [[], Value] -> return [] | true | +| System.Collections.Generic.SortedDictionary<,>.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Generic.SortedDictionary<,>.set_Item(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.SortedDictionary<,>.set_Item(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.SortedList<,>.Add(KeyValuePair) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedList<,>.Add(KeyValuePair) | argument 0 [Key] -> qualifier [[], Key] | true | +| System.Collections.Generic.SortedList<,>.Add(KeyValuePair) | argument 0 [Value] -> qualifier [[], Value] | true | +| System.Collections.Generic.SortedList<,>.Add(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.SortedList<,>.Add(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.SortedList<,>.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.SortedList<,>.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.SortedList<,>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedList<,>.CopyTo(KeyValuePair[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedList<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.SortedList<,>.KeyList.Add(TKey) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedList<,>.KeyList.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedList<,>.KeyList.CopyTo(TKey[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedList<,>.KeyList.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.SortedList<,>.KeyList.Insert(int, TKey) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedList<,>.KeyList.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.SortedList<,>.KeyList.set_Item(int, TKey) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedList<,>.SortedList(IDictionary) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Generic.SortedList<,>.SortedList(IDictionary) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Generic.SortedList<,>.SortedList(IDictionary, IComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Generic.SortedList<,>.SortedList(IDictionary, IComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Generic.SortedList<,>.ValueList.Add(TValue) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedList<,>.ValueList.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedList<,>.ValueList.CopyTo(TValue[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedList<,>.ValueList.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.SortedList<,>.ValueList.Insert(int, TValue) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedList<,>.ValueList.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.Generic.SortedList<,>.ValueList.set_Item(int, TValue) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedList<,>.get_Item(TKey) | qualifier [[], Value] -> return [] | true | +| System.Collections.Generic.SortedList<,>.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Generic.SortedList<,>.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Generic.SortedList<,>.set_Item(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Generic.SortedList<,>.set_Item(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Generic.SortedSet<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Generic.SortedSet<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedSet<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.SortedSet<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.SortedSet<>.Reverse() | argument 0 [[]] -> return [[]] | true | +| System.Collections.Generic.Stack<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Stack<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Generic.Stack<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Generic.Stack<>.Peek() | qualifier [[]] -> return [] | true | +| System.Collections.Generic.Stack<>.Pop() | qualifier [[]] -> return [] | true | +| System.Collections.Hashtable.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Hashtable.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Hashtable.Clone() | argument 0 [[]] -> return [[]] | true | +| System.Collections.Hashtable.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Hashtable.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Hashtable.Hashtable(IDictionary) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Hashtable.Hashtable(IDictionary) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, IEqualityComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, IEqualityComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, IHashCodeProvider, IComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, IHashCodeProvider, IComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, float) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, float) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, float, IEqualityComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, float, IEqualityComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, float, IHashCodeProvider, IComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.Hashtable.Hashtable(IDictionary, float, IHashCodeProvider, IComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.Hashtable.get_Item(object) | qualifier [[], Value] -> return [] | true | +| System.Collections.Hashtable.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Hashtable.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Hashtable.set_Item(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Hashtable.set_Item(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.ICollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.IDictionary.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.IDictionary.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.IDictionary.get_Item(object) | qualifier [[], Value] -> return [] | true | +| System.Collections.IDictionary.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.IDictionary.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.IDictionary.set_Item(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.IDictionary.set_Item(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.IEnumerable.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.IList.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.IList.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.IList.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.IList.set_Item(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.ListDictionaryInternal.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.ListDictionaryInternal.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.ListDictionaryInternal.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ListDictionaryInternal.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.ListDictionaryInternal.get_Item(object) | qualifier [[], Value] -> return [] | true | +| System.Collections.ListDictionaryInternal.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.ListDictionaryInternal.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.ListDictionaryInternal.set_Item(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.ListDictionaryInternal.set_Item(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.ObjectModel.Collection<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.Collection<>.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.Collection<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.Collection<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.Collection<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.ObjectModel.Collection<>.Insert(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.Collection<>.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.Collection<>.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.ObjectModel.Collection<>.set_Item(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.KeyedCollection<,>.get_Item(TKey) | qualifier [[]] -> return [] | true | +| System.Collections.ObjectModel.ReadOnlyCollection<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.ReadOnlyCollection<>.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.ReadOnlyCollection<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.ReadOnlyCollection<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.ReadOnlyCollection<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.ObjectModel.ReadOnlyCollection<>.Insert(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.ReadOnlyCollection<>.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.ReadOnlyCollection<>.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(KeyValuePair) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(KeyValuePair) | argument 0 [Key] -> qualifier [[], Key] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(KeyValuePair) | argument 0 [Value] -> qualifier [[], Value] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(TKey, TValue) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(TKey, TValue) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.CopyTo(KeyValuePair[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.KeyCollection.Add(TKey) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.KeyCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.KeyCollection.CopyTo(TKey[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.KeyCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ReadOnlyDictionary(IDictionary) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ReadOnlyDictionary(IDictionary) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ValueCollection.Add(TValue) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ValueCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ValueCollection.CopyTo(TValue[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ValueCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.get_Item(TKey) | qualifier [[], Value] -> return [] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Queue.Clone() | argument 0 [[]] -> return [[]] | true | +| System.Collections.Queue.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Queue.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Queue.Peek() | qualifier [[]] -> return [] | true | +| System.Collections.ReadOnlyCollectionBase.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.ReadOnlyCollectionBase.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.SortedList.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.SortedList.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.SortedList.Clone() | argument 0 [[]] -> return [[]] | true | +| System.Collections.SortedList.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.SortedList.GetByIndex(int) | qualifier [[], Value] -> return [] | true | +| System.Collections.SortedList.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.SortedList.GetValueList() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.SortedList.SortedList(IDictionary) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.SortedList.SortedList(IDictionary) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.SortedList.SortedList(IDictionary, IComparer) | argument 0 [[], Key] -> return [[], Key] | true | +| System.Collections.SortedList.SortedList(IDictionary, IComparer) | argument 0 [[], Value] -> return [[], Value] | true | +| System.Collections.SortedList.get_Item(object) | qualifier [[], Value] -> return [] | true | +| System.Collections.SortedList.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.SortedList.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.SortedList.set_Item(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.SortedList.set_Item(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.HybridDictionary.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Specialized.HybridDictionary.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.HybridDictionary.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Specialized.HybridDictionary.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Specialized.HybridDictionary.get_Item(object) | qualifier [[], Value] -> return [] | true | +| System.Collections.Specialized.HybridDictionary.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Specialized.HybridDictionary.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Specialized.HybridDictionary.set_Item(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Specialized.HybridDictionary.set_Item(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.IOrderedDictionary.get_Item(int) | qualifier [[], Value] -> return [] | true | +| System.Collections.Specialized.IOrderedDictionary.set_Item(int, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Specialized.IOrderedDictionary.set_Item(int, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.ListDictionary.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Specialized.ListDictionary.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.ListDictionary.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Specialized.ListDictionary.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Specialized.ListDictionary.get_Item(object) | qualifier [[], Value] -> return [] | true | +| System.Collections.Specialized.ListDictionary.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Specialized.ListDictionary.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Specialized.ListDictionary.set_Item(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Specialized.ListDictionary.set_Item(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.NameObjectCollectionBase.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Specialized.NameObjectCollectionBase.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Specialized.NameObjectCollectionBase.KeysCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Specialized.NameObjectCollectionBase.KeysCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Specialized.NameValueCollection.Add(NameValueCollection) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Specialized.NameValueCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Specialized.OrderedDictionary.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Specialized.OrderedDictionary.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.OrderedDictionary.AsReadOnly() | argument 0 [[]] -> return [[]] | true | +| System.Collections.Specialized.OrderedDictionary.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Specialized.OrderedDictionary.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Specialized.OrderedDictionary.get_Item(int) | qualifier [[], Value] -> return [] | true | +| System.Collections.Specialized.OrderedDictionary.get_Item(object) | qualifier [[], Value] -> return [] | true | +| System.Collections.Specialized.OrderedDictionary.get_Keys() | qualifier [[], Key] -> return [[]] | true | +| System.Collections.Specialized.OrderedDictionary.get_Values() | qualifier [[], Value] -> return [[]] | true | +| System.Collections.Specialized.OrderedDictionary.set_Item(int, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Specialized.OrderedDictionary.set_Item(int, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.OrderedDictionary.set_Item(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Collections.Specialized.OrderedDictionary.set_Item(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Collections.Specialized.StringCollection.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Specialized.StringCollection.Add(string) | argument 0 [] -> qualifier [[]] | true | +| System.Collections.Specialized.StringCollection.AddRange(String[]) | argument 0 [[]] -> qualifier [[]] | true | +| System.Collections.Specialized.StringCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Specialized.StringCollection.CopyTo(String[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Specialized.StringCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Specialized.StringCollection.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Specialized.StringCollection.Insert(int, string) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Specialized.StringCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Collections.Specialized.StringCollection.set_Item(int, string) | argument 1 [] -> qualifier [[]] | true | +| System.Collections.Specialized.StringDictionary.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Stack.Clone() | argument 0 [[]] -> return [[]] | true | +| System.Collections.Stack.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Collections.Stack.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Collections.Stack.Peek() | qualifier [[]] -> return [] | true | +| System.Collections.Stack.Pop() | qualifier [[]] -> return [] | true | +| System.ComponentModel.AttributeCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.ComponentModel.AttributeCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.ComponentModel.BindingList<>.Find(PropertyDescriptor, object) | qualifier [[]] -> return [] | true | +| System.ComponentModel.Design.DesignerCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.ComponentModel.Design.DesignerCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.get_Item(string) | qualifier [[]] -> return [] | true | +| System.ComponentModel.Design.DesignerVerbCollection.Add(DesignerVerb) | argument 0 [] -> qualifier [[]] | true | +| System.ComponentModel.Design.DesignerVerbCollection.AddRange(DesignerVerbCollection) | argument 0 [[]] -> qualifier [[]] | true | +| System.ComponentModel.Design.DesignerVerbCollection.AddRange(DesignerVerb[]) | argument 0 [[]] -> qualifier [[]] | true | +| System.ComponentModel.Design.DesignerVerbCollection.CopyTo(DesignerVerb[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.ComponentModel.Design.DesignerVerbCollection.Insert(int, DesignerVerb) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.Design.DesignerVerbCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.ComponentModel.Design.DesignerVerbCollection.set_Item(int, DesignerVerb) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.EventDescriptorCollection.Add(EventDescriptor) | argument 0 [] -> qualifier [[]] | true | +| System.ComponentModel.EventDescriptorCollection.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.ComponentModel.EventDescriptorCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.ComponentModel.EventDescriptorCollection.Find(string, bool) | qualifier [[]] -> return [] | true | +| System.ComponentModel.EventDescriptorCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.ComponentModel.EventDescriptorCollection.Insert(int, EventDescriptor) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.EventDescriptorCollection.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.EventDescriptorCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.ComponentModel.EventDescriptorCollection.get_Item(string) | qualifier [[]] -> return [] | true | +| System.ComponentModel.IBindingList.Find(PropertyDescriptor, object) | qualifier [[]] -> return [] | true | +| System.ComponentModel.ListSortDescriptionCollection.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.ComponentModel.ListSortDescriptionCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.ComponentModel.ListSortDescriptionCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.ComponentModel.ListSortDescriptionCollection.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.ListSortDescriptionCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.ComponentModel.ListSortDescriptionCollection.set_Item(int, ListSortDescription) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.PropertyDescriptorCollection.Add(PropertyDescriptor) | argument 0 [] -> qualifier [[]] | true | +| System.ComponentModel.PropertyDescriptorCollection.Add(PropertyDescriptor) | argument 0 [Key] -> qualifier [[], Key] | true | +| System.ComponentModel.PropertyDescriptorCollection.Add(PropertyDescriptor) | argument 0 [Value] -> qualifier [[], Value] | true | +| System.ComponentModel.PropertyDescriptorCollection.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.ComponentModel.PropertyDescriptorCollection.Add(object) | argument 0 [Key] -> qualifier [[], Key] | true | +| System.ComponentModel.PropertyDescriptorCollection.Add(object) | argument 0 [Value] -> qualifier [[], Value] | true | +| System.ComponentModel.PropertyDescriptorCollection.Add(object, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.ComponentModel.PropertyDescriptorCollection.Add(object, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.ComponentModel.PropertyDescriptorCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.ComponentModel.PropertyDescriptorCollection.Find(string, bool) | qualifier [[]] -> return [] | true | +| System.ComponentModel.PropertyDescriptorCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.ComponentModel.PropertyDescriptorCollection.Insert(int, PropertyDescriptor) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.PropertyDescriptorCollection.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[]) | argument 0 [[], Key] -> return [[], Key] | true | +| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[]) | argument 0 [[], Value] -> return [[], Value] | true | +| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], bool) | argument 0 [[], Key] -> return [[], Key] | true | +| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], bool) | argument 0 [[], Value] -> return [[], Value] | true | +| System.ComponentModel.PropertyDescriptorCollection.get_Item(int) | qualifier [[], Value] -> return [] | true | +| System.ComponentModel.PropertyDescriptorCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.ComponentModel.PropertyDescriptorCollection.get_Item(string) | qualifier [[], Value] -> return [] | true | +| System.ComponentModel.PropertyDescriptorCollection.get_Item(string) | qualifier [[]] -> return [] | true | +| System.ComponentModel.TypeConverter.StandardValuesCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.ComponentModel.TypeConverter.StandardValuesCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Dynamic.ExpandoObject.Add(KeyValuePair) | argument 0 [] -> qualifier [[]] | true | +| System.Dynamic.ExpandoObject.Add(KeyValuePair) | argument 0 [Key] -> qualifier [[], Key] | true | +| System.Dynamic.ExpandoObject.Add(KeyValuePair) | argument 0 [Value] -> qualifier [[], Value] | true | +| System.Dynamic.ExpandoObject.Add(string, object) | argument 0 [] -> qualifier [[], Key] | true | +| System.Dynamic.ExpandoObject.Add(string, object) | argument 1 [] -> qualifier [[], Value] | true | +| System.Dynamic.ExpandoObject.CopyTo(KeyValuePair[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Dynamic.ExpandoObject.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.IO.Path.Combine(params String[]) | argument 0 [[]] -> return [] | false | +| System.Lazy<>.Lazy(Func) | output from argument 0 [] -> return [Value] | true | +| System.Lazy<>.Lazy(Func, LazyThreadSafetyMode) | output from argument 0 [] -> return [Value] | true | +| System.Lazy<>.Lazy(Func, bool) | output from argument 0 [] -> return [Value] | true | +| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func, Func) | argument 0 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Enumerable.Aggregate(IEnumerable, TAccumulate, Func) | argument 0 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Enumerable.Aggregate(IEnumerable, Func) | argument 0 [[]] -> parameter 1 of argument 1 [] | true | +| System.Linq.Enumerable.All(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Any(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.AsEnumerable(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Average(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Cast(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Concat(IEnumerable, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Concat(IEnumerable, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Count(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.DefaultIfEmpty(IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.DefaultIfEmpty(IEnumerable, TSource) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.Distinct(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Distinct(IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ElementAt(IEnumerable, int) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.ElementAtOrDefault(IEnumerable, int) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.Except(IEnumerable, IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.Except(IEnumerable, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.First(IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.First(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.First(IEnumerable, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.FirstOrDefault(IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.FirstOrDefault(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.FirstOrDefault(IEnumerable, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | output from argument 2 [] -> parameter 1 of argument 3 [[]] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>) | output from argument 3 [] -> return [[]] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 2 [] -> parameter 1 of argument 3 [[]] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 3 [] -> return [[]] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.GroupBy(IEnumerable, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>) | output from argument 4 [] -> return [[]] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.Enumerable.GroupJoin(IEnumerable, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 4 [] -> return [[]] | true | +| System.Linq.Enumerable.Intersect(IEnumerable, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Intersect(IEnumerable, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Intersect(IEnumerable, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Intersect(IEnumerable, IEnumerable, IEqualityComparer) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func) | output from argument 4 [] -> return [[]] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.Enumerable.Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) | output from argument 4 [] -> return [[]] | true | +| System.Linq.Enumerable.Last(IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.Last(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Last(IEnumerable, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.LastOrDefault(IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.LastOrDefault(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.LastOrDefault(IEnumerable, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.LongCount(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Max(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Min(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.OfType(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.OrderBy(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.OrderBy(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.OrderBy(IEnumerable, Func, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.OrderBy(IEnumerable, Func, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.OrderByDescending(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.OrderByDescending(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.OrderByDescending(IEnumerable, Func, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.OrderByDescending(IEnumerable, Func, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Reverse(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Select(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Select(IEnumerable, Func) | output from argument 1 [] -> return [[]] | true | +| System.Linq.Enumerable.Select(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Select(IEnumerable, Func) | output from argument 1 [] -> return [[]] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | output from argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | output from argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>) | output from argument 1 [] -> return [[]] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.SelectMany(IEnumerable, Func>) | output from argument 1 [] -> return [[]] | true | +| System.Linq.Enumerable.Single(IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.Single(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Single(IEnumerable, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.SingleOrDefault(IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.SingleOrDefault(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.SingleOrDefault(IEnumerable, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.Enumerable.Skip(IEnumerable, int) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.SkipWhile(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.SkipWhile(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.SkipWhile(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.SkipWhile(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Sum(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Take(IEnumerable, int) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.TakeWhile(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.TakeWhile(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.TakeWhile(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.TakeWhile(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ThenBy(IOrderedEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ThenBy(IOrderedEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ThenBy(IOrderedEnumerable, Func, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ThenBy(IOrderedEnumerable, Func, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ThenByDescending(IOrderedEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ThenByDescending(IOrderedEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ThenByDescending(IOrderedEnumerable, Func, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ThenByDescending(IOrderedEnumerable, Func, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ToArray(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, Func, IEqualityComparer) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ToDictionary(IEnumerable, Func, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ToList(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func, Func, IEqualityComparer) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.ToLookup(IEnumerable, Func, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Union(IEnumerable, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Union(IEnumerable, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Union(IEnumerable, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Union(IEnumerable, IEnumerable, IEqualityComparer) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Where(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Where(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Where(IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Enumerable.Where(IEnumerable, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Enumerable.Zip(IEnumerable, IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Enumerable.Zip(IEnumerable, IEnumerable, Func) | argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Enumerable.Zip(IEnumerable, IEnumerable, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.EnumerableQuery<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Linq.Grouping<,>.Add(TElement) | argument 0 [] -> qualifier [[]] | true | +| System.Linq.Grouping<,>.CopyTo(TElement[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Linq.Grouping<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Linq.Grouping<,>.Insert(int, TElement) | argument 1 [] -> qualifier [[]] | true | +| System.Linq.Lookup<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, Func) | argument 0 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | argument 0 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, Func) | argument 0 [[]] -> parameter 1 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.All(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Any(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.AsEnumerable(ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Average(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Cast(ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Concat(ParallelQuery, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Concat(ParallelQuery, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Concat(ParallelQuery, ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Concat(ParallelQuery, ParallelQuery) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Count(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.DefaultIfEmpty(ParallelQuery) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.DefaultIfEmpty(ParallelQuery, TSource) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.Distinct(ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Distinct(ParallelQuery, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ElementAt(ParallelQuery, int) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.ElementAtOrDefault(ParallelQuery, int) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.Except(ParallelQuery, IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.Except(ParallelQuery, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.Except(ParallelQuery, ParallelQuery) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.Except(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.First(ParallelQuery) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.First(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.First(ParallelQuery, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.FirstOrDefault(ParallelQuery) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.FirstOrDefault(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.FirstOrDefault(ParallelQuery, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | output from argument 2 [] -> parameter 1 of argument 3 [[]] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>) | output from argument 3 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 2 [] -> parameter 1 of argument 3 [[]] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 3 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.GroupBy(ParallelQuery, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>) | output from argument 4 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, IEnumerable, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 4 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>) | output from argument 4 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.GroupJoin(ParallelQuery, ParallelQuery, Func, Func, Func, TResult>, IEqualityComparer) | output from argument 4 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, IEnumerable, IEqualityComparer) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, ParallelQuery) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Intersect(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func) | output from argument 4 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, IEnumerable, Func, Func, Func, IEqualityComparer) | output from argument 4 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func) | output from argument 4 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.ParallelEnumerable.Join(ParallelQuery, ParallelQuery, Func, Func, Func, IEqualityComparer) | output from argument 4 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Last(ParallelQuery) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.Last(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Last(ParallelQuery, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.LastOrDefault(ParallelQuery) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.LastOrDefault(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.LastOrDefault(ParallelQuery, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.LongCount(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Max(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Min(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.OfType(ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.OrderBy(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.OrderBy(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.OrderBy(ParallelQuery, Func, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.OrderBy(ParallelQuery, Func, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.OrderByDescending(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.OrderByDescending(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.OrderByDescending(ParallelQuery, Func, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.OrderByDescending(ParallelQuery, Func, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Reverse(ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Select(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Select(ParallelQuery, Func) | output from argument 1 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Select(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Select(ParallelQuery, Func) | output from argument 1 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | output from argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | output from argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>) | output from argument 1 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.SelectMany(ParallelQuery, Func>) | output from argument 1 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Single(ParallelQuery) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.Single(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Single(ParallelQuery, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.SingleOrDefault(ParallelQuery) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.SingleOrDefault(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.SingleOrDefault(ParallelQuery, Func) | argument 0 [[]] -> return [] | true | +| System.Linq.ParallelEnumerable.Skip(ParallelQuery, int) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.SkipWhile(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.SkipWhile(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.SkipWhile(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.SkipWhile(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Sum(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Take(ParallelQuery, int) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.TakeWhile(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.TakeWhile(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.TakeWhile(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.TakeWhile(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ThenBy(OrderedParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ThenBy(OrderedParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ThenBy(OrderedParallelQuery, Func, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ThenBy(OrderedParallelQuery, Func, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ThenByDescending(OrderedParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ThenByDescending(OrderedParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ThenByDescending(OrderedParallelQuery, Func, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ThenByDescending(OrderedParallelQuery, Func, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToArray(ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, Func, IEqualityComparer) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ToDictionary(ParallelQuery, Func, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToList(ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, Func, IEqualityComparer) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.ToLookup(ParallelQuery, Func, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Union(ParallelQuery, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Union(ParallelQuery, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Union(ParallelQuery, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Union(ParallelQuery, IEnumerable, IEqualityComparer) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Union(ParallelQuery, ParallelQuery) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Union(ParallelQuery, ParallelQuery) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Union(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Union(ParallelQuery, ParallelQuery, IEqualityComparer) | argument 1 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Where(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Where(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Where(ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.ParallelEnumerable.Where(ParallelQuery, Func) | argument 0 [[]] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Zip(ParallelQuery, IEnumerable, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Zip(ParallelQuery, IEnumerable, Func) | argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Zip(ParallelQuery, IEnumerable, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelEnumerable.Zip(ParallelQuery, ParallelQuery, Func) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Zip(ParallelQuery, ParallelQuery, Func) | argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.ParallelEnumerable.Zip(ParallelQuery, ParallelQuery, Func) | output from argument 2 [] -> return [[]] | true | +| System.Linq.ParallelQuery.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>, Expression>) | argument 0 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Queryable.Aggregate(IQueryable, TAccumulate, Expression>) | argument 0 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Queryable.Aggregate(IQueryable, Expression>) | argument 0 [[]] -> parameter 1 of argument 1 [] | true | +| System.Linq.Queryable.All(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Any(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.AsQueryable(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.AsQueryable(IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Average(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Cast(IQueryable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Concat(IQueryable, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Concat(IQueryable, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Queryable.Count(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.DefaultIfEmpty(IQueryable) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.DefaultIfEmpty(IQueryable, TSource) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.Distinct(IQueryable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Distinct(IQueryable, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.ElementAt(IQueryable, int) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.ElementAtOrDefault(IQueryable, int) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.Except(IQueryable, IEnumerable) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.Except(IQueryable, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.First(IQueryable) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.First(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.First(IQueryable, Expression>) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.FirstOrDefault(IQueryable) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.FirstOrDefault(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.FirstOrDefault(IQueryable, Expression>) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | output from argument 2 [] -> parameter 1 of argument 3 [[]] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>) | output from argument 3 [] -> return [[]] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 2 [] -> parameter 1 of argument 3 [[]] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 3 [] -> return [[]] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.GroupBy(IQueryable, Expression>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>) | output from argument 4 [] -> return [[]] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.Queryable.GroupJoin(IQueryable, IEnumerable, Expression>, Expression>, Expression,TResult>>, IEqualityComparer) | output from argument 4 [] -> return [[]] | true | +| System.Linq.Queryable.Intersect(IQueryable, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Intersect(IQueryable, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Queryable.Intersect(IQueryable, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Intersect(IQueryable, IEnumerable, IEqualityComparer) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>) | output from argument 4 [] -> return [[]] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | argument 0 [[]] -> parameter 0 of argument 4 [] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | argument 1 [[]] -> parameter 0 of argument 3 [] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | argument 1 [[]] -> parameter 1 of argument 4 [] | true | +| System.Linq.Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>, IEqualityComparer) | output from argument 4 [] -> return [[]] | true | +| System.Linq.Queryable.Last(IQueryable) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.Last(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Last(IQueryable, Expression>) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.LastOrDefault(IQueryable) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.LastOrDefault(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.LastOrDefault(IQueryable, Expression>) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.LongCount(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Max(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Min(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.OfType(IQueryable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.OrderBy(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.OrderBy(IQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.OrderBy(IQueryable, Expression>, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.OrderBy(IQueryable, Expression>, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.OrderByDescending(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.OrderByDescending(IQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.OrderByDescending(IQueryable, Expression>, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.OrderByDescending(IQueryable, Expression>, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Reverse(IQueryable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Select(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Select(IQueryable, Expression>) | output from argument 1 [] -> return [[]] | true | +| System.Linq.Queryable.Select(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Select(IQueryable, Expression>) | output from argument 1 [] -> return [[]] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | output from argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | output from argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>, Expression>) | output from argument 2 [] -> return [[]] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>) | output from argument 1 [] -> return [[]] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.SelectMany(IQueryable, Expression>>) | output from argument 1 [] -> return [[]] | true | +| System.Linq.Queryable.Single(IQueryable) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.Single(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Single(IQueryable, Expression>) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.SingleOrDefault(IQueryable) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.SingleOrDefault(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.SingleOrDefault(IQueryable, Expression>) | argument 0 [[]] -> return [] | true | +| System.Linq.Queryable.Skip(IQueryable, int) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.SkipWhile(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.SkipWhile(IQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.SkipWhile(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.SkipWhile(IQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Sum(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Take(IQueryable, int) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.TakeWhile(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.TakeWhile(IQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.TakeWhile(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.TakeWhile(IQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.ThenBy(IOrderedQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.ThenBy(IOrderedQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.ThenBy(IOrderedQueryable, Expression>, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.ThenBy(IOrderedQueryable, Expression>, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.ThenByDescending(IOrderedQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.ThenByDescending(IOrderedQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.ThenByDescending(IOrderedQueryable, Expression>, IComparer) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.ThenByDescending(IOrderedQueryable, Expression>, IComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Union(IQueryable, IEnumerable) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Union(IQueryable, IEnumerable) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Queryable.Union(IQueryable, IEnumerable, IEqualityComparer) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Union(IQueryable, IEnumerable, IEqualityComparer) | argument 1 [[]] -> return [[]] | true | +| System.Linq.Queryable.Where(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Where(IQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Where(IQueryable, Expression>) | argument 0 [[]] -> parameter 0 of argument 1 [] | true | +| System.Linq.Queryable.Where(IQueryable, Expression>) | argument 0 [[]] -> return [[]] | true | +| System.Linq.Queryable.Zip(IQueryable, IEnumerable, Expression>) | argument 0 [[]] -> parameter 0 of argument 2 [] | true | +| System.Linq.Queryable.Zip(IQueryable, IEnumerable, Expression>) | argument 1 [[]] -> parameter 1 of argument 2 [] | true | +| System.Linq.Queryable.Zip(IQueryable, IEnumerable, Expression>) | output from argument 2 [] -> return [[]] | true | +| System.Net.CookieCollection.Add(Cookie) | argument 0 [] -> qualifier [[]] | true | +| System.Net.CookieCollection.Add(CookieCollection) | argument 0 [] -> qualifier [[]] | true | +| System.Net.CookieCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Net.CookieCollection.CopyTo(Cookie[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Net.CookieCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Net.CredentialCache.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Net.HttpListenerPrefixCollection.Add(string) | argument 0 [] -> qualifier [[]] | true | +| System.Net.HttpListenerPrefixCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Net.HttpListenerPrefixCollection.CopyTo(String[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Net.HttpListenerPrefixCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Net.NetworkInformation.IPAddressCollection.Add(IPAddress) | argument 0 [] -> qualifier [[]] | true | +| System.Net.NetworkInformation.IPAddressCollection.CopyTo(IPAddress[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Net.NetworkInformation.IPAddressCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Nullable<>.GetValueOrDefault() | qualifier [Value] -> return [] | true | +| System.Nullable<>.GetValueOrDefault(T) | qualifier [Value] -> return [] | true | +| System.Nullable<>.Nullable(T) | argument 0 [] -> return [Value] | true | +| System.Nullable<>.get_HasValue() | qualifier [Value] -> return [] | false | +| System.Resources.ResourceReader.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Resources.ResourceSet.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Runtime.CompilerServices.ConditionalWeakTable<,>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Add(T) | argument 0 [] -> qualifier [[]] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.CopyTo(T[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Insert(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Reverse() | argument 0 [[]] -> return [[]] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Reverse(int, int) | argument 0 [[]] -> return [[]] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.set_Item(int, T) | argument 1 [] -> qualifier [[]] | true | +| System.Runtime.CompilerServices.TaskAwaiter<>.GetResult() | qualifier [m_task, Result] -> return [] | true | +| System.Security.PermissionSet.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Security.PermissionSet.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.String.Concat(IEnumerable) | argument 0 [[]] -> return [] | false | +| System.String.Concat(params Object[]) | argument 0 [[]] -> return [] | false | +| System.String.Concat(params String[]) | argument 0 [[]] -> return [] | false | +| System.String.Concat(IEnumerable) | argument 0 [[]] -> return [] | false | +| System.String.Format(IFormatProvider, string, params Object[]) | argument 2 [[]] -> return [] | false | +| System.String.Format(string, params Object[]) | argument 1 [[]] -> return [] | false | +| System.String.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.String.Join(char, String[], int, int) | argument 1 [[]] -> return [] | false | +| System.String.Join(char, params Object[]) | argument 1 [[]] -> return [] | false | +| System.String.Join(char, params String[]) | argument 1 [[]] -> return [] | false | +| System.String.Join(string, IEnumerable) | argument 1 [[]] -> return [] | false | +| System.String.Join(string, String[], int, int) | argument 1 [[]] -> return [] | false | +| System.String.Join(string, params Object[]) | argument 1 [[]] -> return [] | false | +| System.String.Join(string, params String[]) | argument 1 [[]] -> return [] | false | +| System.String.Join(char, IEnumerable) | argument 1 [[]] -> return [] | false | +| System.String.Join(string, IEnumerable) | argument 1 [[]] -> return [] | false | +| System.String.Split(Char[], StringSplitOptions) | qualifier [] -> return [[]] | false | +| System.String.Split(Char[], int) | qualifier [] -> return [[]] | false | +| System.String.Split(Char[], int, StringSplitOptions) | qualifier [] -> return [[]] | false | +| System.String.Split(String[], StringSplitOptions) | qualifier [] -> return [[]] | false | +| System.String.Split(String[], int, StringSplitOptions) | qualifier [] -> return [[]] | false | +| System.String.Split(char, StringSplitOptions) | qualifier [] -> return [[]] | false | +| System.String.Split(char, int, StringSplitOptions) | qualifier [] -> return [[]] | false | +| System.String.Split(params Char[]) | qualifier [] -> return [[]] | false | +| System.String.Split(string, StringSplitOptions) | qualifier [] -> return [[]] | false | +| System.String.Split(string, int, StringSplitOptions) | qualifier [] -> return [[]] | false | +| System.String.String(Char[]) | argument 0 [[]] -> return [] | false | +| System.String.String(Char[], int, int) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetBytes(Char[]) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetBytes(Char[], int, int) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetBytes(Char[], int, int, Byte[], int) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetChars(Byte[]) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetChars(Byte[], int, int) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetChars(Byte[], int, int, Char[], int) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetChars(ReadOnlySpan, Span) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetChars(byte*, int, char*, int) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetString(Byte[]) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetString(Byte[], int, int) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetString(ReadOnlySpan) | argument 0 [[]] -> return [] | false | +| System.Text.Encoding.GetString(byte*, int) | argument 0 [[]] -> return [] | false | +| System.Text.RegularExpressions.CaptureCollection.Add(Capture) | argument 0 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.CaptureCollection.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.CaptureCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Text.RegularExpressions.CaptureCollection.CopyTo(Capture[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Text.RegularExpressions.CaptureCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Text.RegularExpressions.CaptureCollection.Insert(int, Capture) | argument 1 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.CaptureCollection.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.CaptureCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Text.RegularExpressions.GroupCollection.Add(Group) | argument 0 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.GroupCollection.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.GroupCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Text.RegularExpressions.GroupCollection.CopyTo(Group[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Text.RegularExpressions.GroupCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Text.RegularExpressions.GroupCollection.Insert(int, Group) | argument 1 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.GroupCollection.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.GroupCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Text.RegularExpressions.GroupCollection.get_Item(string) | qualifier [[]] -> return [] | true | +| System.Text.RegularExpressions.MatchCollection.Add(Match) | argument 0 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.MatchCollection.Add(object) | argument 0 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.MatchCollection.CopyTo(Array, int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Text.RegularExpressions.MatchCollection.CopyTo(Match[], int) | qualifier [[]] -> argument 0 [[]] | true | +| System.Text.RegularExpressions.MatchCollection.GetEnumerator() | qualifier [[]] -> return [Current] | true | +| System.Text.RegularExpressions.MatchCollection.Insert(int, Match) | argument 1 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.MatchCollection.Insert(int, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.RegularExpressions.MatchCollection.get_Item(int) | qualifier [[]] -> return [] | true | +| System.Text.StringBuilder.Append(object) | argument 0 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.Append(object) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.Append(string) | argument 0 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.Append(string) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.Append(string, int, int) | argument 0 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.Append(string, int, int) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 1 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 2 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 2 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 1 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 2 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 2 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 3 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 3 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 1 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 2 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 2 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 3 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 3 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 4 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 4 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | argument 1 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | argument 1 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object) | argument 0 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object) | argument 1 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 0 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 1 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 2 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 2 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 0 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 1 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 1 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 2 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 2 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 3 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 3 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, params Object[]) | argument 0 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendFormat(string, params Object[]) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.AppendLine(string) | argument 0 [] -> qualifier [[]] | true | +| System.Text.StringBuilder.AppendLine(string) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.StringBuilder(string) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.StringBuilder(string, int) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.StringBuilder(string, int, int, int) | argument 0 [] -> return [[]] | true | +| System.Text.StringBuilder.ToString() | qualifier [[]] -> return [] | false | +| System.Text.StringBuilder.ToString(int, int) | qualifier [[]] -> return [] | false | +| System.Threading.Tasks.Task.ContinueWith(Func, object) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func, object, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func, object, TaskContinuationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func, object, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func, TaskContinuationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.ContinueWith(Func, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.FromResult(TResult) | argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.Run(Func) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.Run(Func, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.Run(Func>) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.Run(Func>, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task.WhenAll(IEnumerable>) | argument 0 [[], Result] -> return [Result, []] | true | +| System.Threading.Tasks.Task.WhenAll(params Task[]) | argument 0 [[], Result] -> return [Result, []] | true | +| System.Threading.Tasks.Task.WhenAny(IEnumerable>) | argument 0 [[], Result] -> return [Result, []] | true | +| System.Threading.Tasks.Task.WhenAny(params Task[]) | argument 0 [[], Result] -> return [Result, []] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskContinuationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskContinuationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.GetAwaiter() | qualifier [] -> return [m_task] | true | +| System.Threading.Tasks.Task<>.Task(Func, object) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.Task(Func, object, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.Task(Func, object, CancellationToken, TaskCreationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.Task(Func, object, TaskCreationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.Task(Func) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.Task(Func, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.Task(Func, CancellationToken, TaskCreationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.Task<>.Task(Func, TaskCreationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, CancellationToken) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TResult>, TaskContinuationOptions) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, CancellationToken) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.ContinueWhenAny(Task[], Func, TaskContinuationOptions) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.StartNew(Func, object) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.StartNew(Func, object, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.StartNew(Func, object, CancellationToken, TaskCreationOptions, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.StartNew(Func, object, TaskCreationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.StartNew(Func) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.StartNew(Func, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.StartNew(Func, CancellationToken, TaskCreationOptions, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory.StartNew(Func, TaskCreationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAll(Task[], Func, TaskContinuationOptions) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, CancellationToken) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TaskContinuationOptions) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, CancellationToken) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, CancellationToken, TaskContinuationOptions, TaskScheduler) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.ContinueWhenAny(Task[], Func, TResult>, TaskContinuationOptions) | output from argument 1 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.StartNew(Func, object) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, CancellationToken, TaskCreationOptions, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.StartNew(Func, object, TaskCreationOptions) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.StartNew(Func) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.StartNew(Func, CancellationToken) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.StartNew(Func, CancellationToken, TaskCreationOptions, TaskScheduler) | output from argument 0 [] -> return [Result] | true | +| System.Threading.Tasks.TaskFactory<>.StartNew(Func, TaskCreationOptions) | output from argument 0 [] -> return [Result] | true | +clearsContent +| System.Array.Clear() | qualifier | [] | +| System.Array.Clear(Array, int, int) | qualifier | [] | +| System.Collections.ArrayList.Clear() | qualifier | [] | +| System.Collections.ArrayList.FixedSizeArrayList.Clear() | qualifier | [] | +| System.Collections.ArrayList.FixedSizeList.Clear() | qualifier | [] | +| System.Collections.ArrayList.IListWrapper.Clear() | qualifier | [] | +| System.Collections.ArrayList.Range.Clear() | qualifier | [] | +| System.Collections.ArrayList.ReadOnlyArrayList.Clear() | qualifier | [] | +| System.Collections.ArrayList.ReadOnlyList.Clear() | qualifier | [] | +| System.Collections.ArrayList.SyncArrayList.Clear() | qualifier | [] | +| System.Collections.ArrayList.SyncIList.Clear() | qualifier | [] | +| System.Collections.CollectionBase.Clear() | qualifier | [] | +| System.Collections.Concurrent.ConcurrentBag<>.Clear() | qualifier | [] | +| System.Collections.Concurrent.ConcurrentDictionary<,>.Clear() | qualifier | [] | +| System.Collections.Concurrent.ConcurrentQueue<>.Clear() | qualifier | [] | +| System.Collections.Concurrent.ConcurrentStack<>.Clear() | qualifier | [] | +| System.Collections.DictionaryBase.Clear() | qualifier | [] | +| System.Collections.EmptyReadOnlyDictionaryInternal.Clear() | qualifier | [] | +| System.Collections.Generic.Dictionary<,>.Clear() | qualifier | [] | +| System.Collections.Generic.Dictionary<,>.KeyCollection.Clear() | qualifier | [] | +| System.Collections.Generic.Dictionary<,>.ValueCollection.Clear() | qualifier | [] | +| System.Collections.Generic.HashSet<>.Clear() | qualifier | [] | +| System.Collections.Generic.ICollection<>.Clear() | qualifier | [] | +| System.Collections.Generic.LinkedList<>.Clear() | qualifier | [] | +| System.Collections.Generic.List<>.Clear() | qualifier | [] | +| System.Collections.Generic.Queue<>.Clear() | qualifier | [] | +| System.Collections.Generic.SortedDictionary<,>.Clear() | qualifier | [] | +| System.Collections.Generic.SortedDictionary<,>.KeyCollection.Clear() | qualifier | [] | +| System.Collections.Generic.SortedDictionary<,>.ValueCollection.Clear() | qualifier | [] | +| System.Collections.Generic.SortedList<,>.Clear() | qualifier | [] | +| System.Collections.Generic.SortedList<,>.KeyList.Clear() | qualifier | [] | +| System.Collections.Generic.SortedList<,>.ValueList.Clear() | qualifier | [] | +| System.Collections.Generic.SortedSet<>.Clear() | qualifier | [] | +| System.Collections.Generic.SortedSet<>.TreeSubSet.Clear() | qualifier | [] | +| System.Collections.Generic.Stack<>.Clear() | qualifier | [] | +| System.Collections.Hashtable.Clear() | qualifier | [] | +| System.Collections.Hashtable.SyncHashtable.Clear() | qualifier | [] | +| System.Collections.IDictionary.Clear() | qualifier | [] | +| System.Collections.IList.Clear() | qualifier | [] | +| System.Collections.ListDictionaryInternal.Clear() | qualifier | [] | +| System.Collections.ObjectModel.Collection<>.Clear() | qualifier | [] | +| System.Collections.ObjectModel.ReadOnlyCollection<>.Clear() | qualifier | [] | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.Clear() | qualifier | [] | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.KeyCollection.Clear() | qualifier | [] | +| System.Collections.ObjectModel.ReadOnlyDictionary<,>.ValueCollection.Clear() | qualifier | [] | +| System.Collections.Queue.Clear() | qualifier | [] | +| System.Collections.Queue.SynchronizedQueue.Clear() | qualifier | [] | +| System.Collections.SortedList.Clear() | qualifier | [] | +| System.Collections.SortedList.KeyList.Clear() | qualifier | [] | +| System.Collections.SortedList.SyncSortedList.Clear() | qualifier | [] | +| System.Collections.SortedList.ValueList.Clear() | qualifier | [] | +| System.Collections.Specialized.HybridDictionary.Clear() | qualifier | [] | +| System.Collections.Specialized.ListDictionary.Clear() | qualifier | [] | +| System.Collections.Specialized.NameValueCollection.Clear() | qualifier | [] | +| System.Collections.Specialized.OrderedDictionary.Clear() | qualifier | [] | +| System.Collections.Specialized.ReadOnlyList.Clear() | qualifier | [] | +| System.Collections.Specialized.StringCollection.Clear() | qualifier | [] | +| System.Collections.Specialized.StringDictionary.Clear() | qualifier | [] | +| System.Collections.Stack.Clear() | qualifier | [] | +| System.Collections.Stack.SyncStack.Clear() | qualifier | [] | +| System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection.Clear() | qualifier | [] | +| System.ComponentModel.EventDescriptorCollection.Clear() | qualifier | [] | +| System.ComponentModel.ListSortDescriptionCollection.Clear() | qualifier | [] | +| System.ComponentModel.PropertyDescriptorCollection.Clear() | qualifier | [] | +| System.Diagnostics.Tracing.EventPayload.Clear() | qualifier | [] | +| System.Dynamic.ExpandoObject.Clear() | qualifier | [] | +| System.Dynamic.ExpandoObject.KeyCollection.Clear() | qualifier | [] | +| System.Dynamic.ExpandoObject.ValueCollection.Clear() | qualifier | [] | +| System.Dynamic.Utils.ListProvider<>.Clear() | qualifier | [] | +| System.Linq.Expressions.BlockExpressionList.Clear() | qualifier | [] | +| System.Linq.Grouping<,>.Clear() | qualifier | [] | +| System.Linq.Parallel.QueryResults<>.Clear() | qualifier | [] | +| System.Net.CookieCollection.Clear() | qualifier | [] | +| System.Net.HttpListenerPrefixCollection.Clear() | qualifier | [] | +| System.Net.NetworkInformation.IPAddressCollection.Clear() | qualifier | [] | +| System.Runtime.CompilerServices.ConditionalWeakTable<,>.Clear() | qualifier | [] | +| System.Runtime.CompilerServices.ReadOnlyCollectionBuilder<>.Clear() | qualifier | [] | +| System.Text.RegularExpressions.CaptureCollection.Clear() | qualifier | [] | +| System.Text.RegularExpressions.GroupCollection.Clear() | qualifier | [] | +| System.Text.RegularExpressions.MatchCollection.Clear() | qualifier | [] | +| System.Text.StringBuilder.Clear() | qualifier | [] | diff --git a/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.ql b/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.ql index a5d65c711a37..fa11e138d1ff 100644 --- a/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/library/LibraryTypeDataFlow.ql @@ -1,27 +1,43 @@ +import csharp import semmle.code.csharp.dataflow.LibraryTypeDataFlow query predicate callableFlow(string callable, string flow, boolean preservesValue) { exists(LibraryTypeDataFlow x, CallableFlowSource source, CallableFlowSink sink, Callable c | c.(Modifiable).isPublic() and c.getDeclaringType().isPublic() and - x.callableFlow(source, sink, c, preservesValue) and callable = c.getQualifiedNameWithTypes() and flow = source + " -> " + sink and // Remove certain results to make the test output consistent // between different versions of .NET Core. not callable = "System.IO.FileStream.CopyToAsync(Stream, int, CancellationToken)" + | + x.callableFlow(source, sink, c, preservesValue) + or + x.callableFlow(source, AccessPath::empty(), sink, AccessPath::empty(), c, preservesValue) ) } -query predicate callableFlowAccessPath(string callable, string flow) { +query predicate callableFlowAccessPath(string callable, string flow, boolean preservesValue) { exists( LibraryTypeDataFlow x, CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, Callable c | c.(Modifiable).isPublic() and c.getDeclaringType().isPublic() and - x.callableFlow(source, sourceAp, sink, sinkAp, c) and + x.callableFlow(source, sourceAp, sink, sinkAp, c, preservesValue) and callable = c.getQualifiedNameWithTypes() and flow = source + " [" + sourceAp + "] -> " + sink + " [" + sinkAp + "]" + | + sourceAp.length() > 0 + or + sinkAp.length() > 0 + ) +} + +query predicate clearsContent(string callable, CallableFlowSource source, string content) { + exists(LibraryTypeDataFlow x, Callable callable0, DataFlow::Content content0 | + x.clearsContent(source, content0, callable0) and + callable = callable0.getQualifiedNameWithTypes() and + content = content0.toString() ) } diff --git a/csharp/ql/test/library-tests/dataflow/library/TaintedMember.expected b/csharp/ql/test/library-tests/dataflow/library/TaintedMember.expected new file mode 100644 index 000000000000..6e924a05cc21 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/library/TaintedMember.expected @@ -0,0 +1 @@ +| LibraryTypeDataFlow.cs:95:23:95:29 | AString | diff --git a/csharp/ql/test/library-tests/dataflow/library/TaintedMember.ql b/csharp/ql/test/library-tests/dataflow/library/TaintedMember.ql new file mode 100644 index 000000000000..a2460ac9b199 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/library/TaintedMember.ql @@ -0,0 +1,5 @@ +import csharp + +from TaintTracking::TaintedMember m +where m.fromSource() +select m diff --git a/csharp/ql/test/library-tests/dataflow/local/Common.qll b/csharp/ql/test/library-tests/dataflow/local/Common.qll index 5c65005407db..4a5be2f007ee 100644 --- a/csharp/ql/test/library-tests/dataflow/local/Common.qll +++ b/csharp/ql/test/library-tests/dataflow/local/Common.qll @@ -13,9 +13,9 @@ class MyFlowSource extends DataFlow::Node { or this.asParameter().hasName("tainted") or - exists(Expr e | this = TImplicitDelegateOutNode(e.getAControlFlowNode(), _) | - e.(DelegateCreation).getArgument().(MethodAccess).getTarget().hasName("TaintedMethod") or - e.(LambdaExpr).getExpressionBody().(StringLiteral).getValue() = "taint source" + exists(MyFlowSource mid, DataFlow::ExprNode e | + TaintTracking::localTaintStep+(mid, e) and + e.getExpr() = this.asExpr().(ArrayCreation).getInitializer().getAnElement() ) } } diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected b/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected index e9a03e58b0a1..005d84b1c97f 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected @@ -1,12 +1,10 @@ -| LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | -| LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | -| LocalDataFlow.cs:277:15:277:20 | access to local variable sink68 | -| LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | -| LocalDataFlow.cs:303:19:303:24 | access to local variable sink71 | -| LocalDataFlow.cs:313:23:313:28 | access to local variable sink72 | -| LocalDataFlow.cs:328:15:328:20 | access to local variable sink73 | -| LocalDataFlow.cs:329:15:329:20 | access to local variable sink74 | -| LocalDataFlow.cs:355:15:355:21 | access to parameter tainted | +| LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | +| LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | +| LocalDataFlow.cs:290:19:290:24 | access to local variable sink71 | +| LocalDataFlow.cs:300:23:300:28 | access to local variable sink72 | +| LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 | +| LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 | +| LocalDataFlow.cs:342:15:342:21 | access to parameter tainted | | SSA.cs:9:15:9:22 | access to local variable ssaSink0 | | SSA.cs:25:15:25:22 | access to local variable ssaSink1 | | SSA.cs:43:15:43:22 | access to local variable ssaSink2 | diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected index f641a355f833..26fc8fffd3a0 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -1,7 +1,4 @@ | Capture.cs:5:17:5:17 | this | Capture.cs:13:9:13:14 | this access | -| Capture.cs:7:13:7:17 | SSA def(i) | Capture.cs:13:9:13:16 | [implicit argument] i | -| Capture.cs:7:13:7:17 | SSA def(i) | Capture.cs:23:9:23:16 | [implicit argument] i | -| Capture.cs:7:13:7:17 | SSA def(i) | Capture.cs:34:9:34:16 | [implicit argument] i | | Capture.cs:7:17:7:17 | 0 | Capture.cs:7:13:7:17 | SSA def(i) | | Capture.cs:9:9:12:9 | SSA capture def(i) | Capture.cs:11:17:11:17 | access to local variable i | | Capture.cs:13:9:13:14 | this access | Capture.cs:23:9:23:14 | this access | @@ -10,7 +7,6 @@ | Capture.cs:23:9:23:14 | this access | Capture.cs:34:9:34:14 | this access | | Capture.cs:25:9:33:9 | this | Capture.cs:32:13:32:18 | this access | | Capture.cs:27:13:30:13 | SSA capture def(i) | Capture.cs:29:21:29:21 | access to local variable i | -| Capture.cs:31:13:31:17 | SSA def(i) | Capture.cs:32:13:32:20 | [implicit argument] i | | Capture.cs:31:17:31:17 | 1 | Capture.cs:31:13:31:17 | SSA def(i) | | Capture.cs:34:9:34:14 | this access | Capture.cs:40:9:40:15 | this access | | Capture.cs:38:17:38:17 | 0 | Capture.cs:38:13:38:17 | SSA def(i) | @@ -24,415 +20,395 @@ | Capture.cs:58:21:58:21 | 1 | Capture.cs:58:17:58:21 | SSA def(i) | | Capture.cs:61:17:61:17 | 1 | Capture.cs:61:13:61:17 | SSA def(i) | | Capture.cs:63:9:63:17 | SSA call def(i) | Capture.cs:64:13:64:13 | access to local variable i | -| LocalDataFlow.cs:49:30:49:30 | b | LocalDataFlow.cs:85:21:85:21 | access to parameter b | -| LocalDataFlow.cs:52:13:52:34 | SSA def(sink0) | LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | -| LocalDataFlow.cs:52:21:52:34 | "taint source" | LocalDataFlow.cs:52:13:52:34 | SSA def(sink0) | -| LocalDataFlow.cs:53:15:53:19 | [post] access to local variable sink0 | LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | -| LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | -| LocalDataFlow.cs:56:13:56:25 | SSA def(nonSink0) | LocalDataFlow.cs:57:15:57:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:56:24:56:25 | "" | LocalDataFlow.cs:56:13:56:25 | SSA def(nonSink0) | -| LocalDataFlow.cs:57:15:57:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | -| LocalDataFlow.cs:57:15:57:22 | access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | -| LocalDataFlow.cs:60:13:60:25 | SSA def(sink1) | LocalDataFlow.cs:61:9:61:13 | access to local variable sink1 | -| LocalDataFlow.cs:60:21:60:25 | "abc" | LocalDataFlow.cs:60:13:60:25 | SSA def(sink1) | -| LocalDataFlow.cs:61:9:61:22 | ... + ... | LocalDataFlow.cs:61:9:61:22 | SSA def(sink1) | -| LocalDataFlow.cs:61:9:61:22 | SSA def(sink1) | LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | -| LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | LocalDataFlow.cs:169:20:169:24 | access to local variable sink0 | -| LocalDataFlow.cs:62:15:62:19 | [post] access to local variable sink1 | LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | -| LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | -| LocalDataFlow.cs:65:9:65:25 | ... + ... | LocalDataFlow.cs:65:9:65:25 | SSA def(nonSink0) | -| LocalDataFlow.cs:65:9:65:25 | SSA def(nonSink0) | LocalDataFlow.cs:66:15:66:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:66:15:66:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:66:15:66:22 | access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:69:13:69:32 | SSA def(sink5) | LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | -| LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | LocalDataFlow.cs:169:33:169:37 | access to local variable sink1 | -| LocalDataFlow.cs:69:21:69:32 | ... + ... | LocalDataFlow.cs:69:13:69:32 | SSA def(sink5) | -| LocalDataFlow.cs:70:15:70:19 | [post] access to local variable sink5 | LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | -| LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | -| LocalDataFlow.cs:73:9:73:36 | SSA def(nonSink0) | LocalDataFlow.cs:74:15:74:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:73:20:73:36 | ... + ... | LocalDataFlow.cs:73:9:73:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:74:15:74:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | -| LocalDataFlow.cs:74:15:74:22 | access to local variable nonSink0 | LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | -| LocalDataFlow.cs:77:13:77:27 | SSA def(sink6) | LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | -| LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | LocalDataFlow.cs:77:13:77:27 | SSA def(sink6) | -| LocalDataFlow.cs:78:15:78:19 | [post] access to local variable sink6 | LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | -| LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | -| LocalDataFlow.cs:81:9:81:29 | SSA def(nonSink0) | LocalDataFlow.cs:82:15:82:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | LocalDataFlow.cs:81:9:81:29 | SSA def(nonSink0) | -| LocalDataFlow.cs:85:13:85:35 | [b (line 49): false] SSA def(sink7) | LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | -| LocalDataFlow.cs:85:13:85:35 | [b (line 49): true] SSA def(sink7) | LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | -| LocalDataFlow.cs:85:21:85:21 | access to parameter b | LocalDataFlow.cs:89:20:89:20 | [b (line 49): false] access to parameter b | -| LocalDataFlow.cs:85:21:85:21 | access to parameter b | LocalDataFlow.cs:89:20:89:20 | [b (line 49): true] access to parameter b | -| LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | LocalDataFlow.cs:85:13:85:35 | [b (line 49): false] SSA def(sink7) | -| LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | LocalDataFlow.cs:85:13:85:35 | [b (line 49): true] SSA def(sink7) | -| LocalDataFlow.cs:85:25:85:27 | [b (line 49): true] "a" | LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | -| LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | -| LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | LocalDataFlow.cs:90:15:90:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | LocalDataFlow.cs:93:29:93:33 | access to local variable sink7 | -| LocalDataFlow.cs:89:20:89:36 | [b (line 49): false] ... ? ... : ... | LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:89:20:89:36 | [b (line 49): true] ... ? ... : ... | LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:89:24:89:28 | "abc" | LocalDataFlow.cs:89:20:89:36 | [b (line 49): true] ... ? ... : ... | -| LocalDataFlow.cs:89:32:89:36 | "def" | LocalDataFlow.cs:89:20:89:36 | [b (line 49): false] ... ? ... : ... | -| LocalDataFlow.cs:90:15:90:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:90:15:90:22 | access to local variable nonSink0 | LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:93:13:93:33 | SSA def(sink8) | LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | -| LocalDataFlow.cs:93:21:93:33 | (...) ... | LocalDataFlow.cs:93:13:93:33 | SSA def(sink8) | -| LocalDataFlow.cs:93:29:93:33 | access to local variable sink7 | LocalDataFlow.cs:93:21:93:33 | (...) ... | -| LocalDataFlow.cs:94:15:94:19 | [post] access to local variable sink8 | LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | -| LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | -| LocalDataFlow.cs:97:13:97:39 | SSA def(nonSink3) | LocalDataFlow.cs:98:15:98:22 | access to local variable nonSink3 | -| LocalDataFlow.cs:97:24:97:39 | (...) ... | LocalDataFlow.cs:97:13:97:39 | SSA def(nonSink3) | -| LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | LocalDataFlow.cs:97:24:97:39 | (...) ... | -| LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:101:13:101:35 | SSA def(sink9) | LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | -| LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | LocalDataFlow.cs:101:21:101:35 | ... as ... | -| LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | -| LocalDataFlow.cs:101:21:101:35 | ... as ... | LocalDataFlow.cs:101:13:101:35 | SSA def(sink9) | -| LocalDataFlow.cs:102:15:102:19 | [post] access to local variable sink9 | LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | -| LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | -| LocalDataFlow.cs:105:9:105:37 | SSA def(nonSink3) | LocalDataFlow.cs:106:15:106:22 | access to local variable nonSink3 | -| LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | LocalDataFlow.cs:105:20:105:37 | ... as ... | -| LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | -| LocalDataFlow.cs:105:20:105:37 | ... as ... | LocalDataFlow.cs:105:9:105:37 | SSA def(nonSink3) | -| LocalDataFlow.cs:106:15:106:22 | [post] access to local variable nonSink3 | LocalDataFlow.cs:171:33:171:40 | access to local variable nonSink3 | -| LocalDataFlow.cs:106:15:106:22 | access to local variable nonSink3 | LocalDataFlow.cs:171:33:171:40 | access to local variable nonSink3 | -| LocalDataFlow.cs:109:13:109:39 | SSA def(sink15) | LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | -| LocalDataFlow.cs:109:22:109:39 | call to method Parse | LocalDataFlow.cs:109:13:109:39 | SSA def(sink15) | -| LocalDataFlow.cs:109:34:109:38 | [post] access to local variable sink9 | LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | -| LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | -| LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | LocalDataFlow.cs:161:22:161:27 | access to local variable sink15 | -| LocalDataFlow.cs:112:13:112:56 | SSA def(sink16) | LocalDataFlow.cs:113:15:113:20 | access to local variable sink16 | -| LocalDataFlow.cs:112:22:112:56 | call to method TryParse | LocalDataFlow.cs:112:13:112:56 | SSA def(sink16) | -| LocalDataFlow.cs:112:37:112:41 | [post] access to local variable sink9 | LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | -| LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | -| LocalDataFlow.cs:114:13:114:49 | SSA def(sink17) | LocalDataFlow.cs:115:15:115:20 | access to local variable sink17 | -| LocalDataFlow.cs:114:22:114:29 | [post] access to local variable nonSink0 | LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | -| LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | -| LocalDataFlow.cs:114:22:114:49 | call to method Replace | LocalDataFlow.cs:114:13:114:49 | SSA def(sink17) | -| LocalDataFlow.cs:114:44:114:48 | [post] access to local variable sink9 | LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | -| LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | -| LocalDataFlow.cs:116:13:116:51 | SSA def(sink18) | LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | -| LocalDataFlow.cs:116:22:116:51 | call to method Format | LocalDataFlow.cs:116:13:116:51 | SSA def(sink18) | -| LocalDataFlow.cs:116:36:116:43 | [post] access to local variable nonSink0 | LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:116:46:116:50 | [post] access to local variable sink9 | LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | -| LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | -| LocalDataFlow.cs:117:15:117:20 | [post] access to local variable sink18 | LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | -| LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | -| LocalDataFlow.cs:118:13:118:52 | SSA def(sink19) | LocalDataFlow.cs:119:15:119:20 | access to local variable sink19 | -| LocalDataFlow.cs:118:22:118:52 | call to method Format | LocalDataFlow.cs:118:13:118:52 | SSA def(sink19) | -| LocalDataFlow.cs:118:44:118:51 | [post] access to local variable nonSink0 | LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:120:13:120:38 | SSA def(sink45) | LocalDataFlow.cs:121:15:121:20 | access to local variable sink45 | -| LocalDataFlow.cs:120:22:120:38 | call to method Parse | LocalDataFlow.cs:120:13:120:38 | SSA def(sink45) | -| LocalDataFlow.cs:120:33:120:37 | [post] access to local variable sink9 | LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | -| LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | -| LocalDataFlow.cs:123:13:123:56 | SSA def(sink46) | LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | -| LocalDataFlow.cs:123:22:123:56 | call to method TryParse | LocalDataFlow.cs:123:13:123:56 | SSA def(sink46) | -| LocalDataFlow.cs:123:36:123:40 | [post] access to local variable sink9 | LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | -| LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | -| LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | LocalDataFlow.cs:125:37:125:42 | access to local variable sink46 | -| LocalDataFlow.cs:125:13:125:43 | SSA def(sink47) | LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | -| LocalDataFlow.cs:125:22:125:43 | call to method ToByte | LocalDataFlow.cs:125:13:125:43 | SSA def(sink47) | -| LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | LocalDataFlow.cs:127:40:127:45 | access to local variable sink47 | -| LocalDataFlow.cs:127:13:127:46 | SSA def(sink49) | LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | -| LocalDataFlow.cs:127:22:127:46 | call to method Concat | LocalDataFlow.cs:127:13:127:46 | SSA def(sink49) | -| LocalDataFlow.cs:127:40:127:45 | access to local variable sink47 | LocalDataFlow.cs:127:40:127:45 | (...) ... | -| LocalDataFlow.cs:128:15:128:20 | [post] access to local variable sink49 | LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | -| LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | -| LocalDataFlow.cs:129:13:129:40 | SSA def(sink50) | LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | -| LocalDataFlow.cs:129:22:129:40 | [library code] call to method Copy | LocalDataFlow.cs:129:22:129:40 | call to method Copy | -| LocalDataFlow.cs:129:22:129:40 | call to method Copy | LocalDataFlow.cs:129:13:129:40 | SSA def(sink50) | -| LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | LocalDataFlow.cs:129:22:129:40 | [library code] call to method Copy | -| LocalDataFlow.cs:130:15:130:20 | [post] access to local variable sink50 | LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | -| LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | -| LocalDataFlow.cs:131:13:131:54 | SSA def(sink51) | LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | -| LocalDataFlow.cs:131:22:131:54 | call to method Join | LocalDataFlow.cs:131:13:131:54 | SSA def(sink51) | -| LocalDataFlow.cs:132:15:132:20 | [post] access to local variable sink51 | LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | -| LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | -| LocalDataFlow.cs:133:13:133:41 | SSA def(sink52) | LocalDataFlow.cs:134:15:134:20 | access to local variable sink52 | -| LocalDataFlow.cs:133:22:133:41 | call to method Insert | LocalDataFlow.cs:133:13:133:41 | SSA def(sink52) | -| LocalDataFlow.cs:137:9:137:40 | SSA def(nonSink2) | LocalDataFlow.cs:138:15:138:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:137:20:137:40 | call to method Parse | LocalDataFlow.cs:137:9:137:40 | SSA def(nonSink2) | -| LocalDataFlow.cs:137:32:137:39 | [post] access to local variable nonSink0 | LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:139:13:139:61 | SSA def(nonSink7) | LocalDataFlow.cs:140:15:140:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:139:24:139:61 | call to method TryParse | LocalDataFlow.cs:139:13:139:61 | SSA def(nonSink7) | -| LocalDataFlow.cs:139:39:139:46 | [post] access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:9:141:50 | SSA def(nonSink0) | LocalDataFlow.cs:142:15:142:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:27 | [post] access to local variable nonSink0 | LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:50 | call to method Replace | LocalDataFlow.cs:141:9:141:50 | SSA def(nonSink0) | -| LocalDataFlow.cs:142:15:142:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:142:15:142:22 | access to local variable nonSink0 | LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:9:143:52 | SSA def(nonSink0) | LocalDataFlow.cs:144:15:144:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:20:143:52 | call to method Format | LocalDataFlow.cs:143:9:143:52 | SSA def(nonSink0) | -| LocalDataFlow.cs:143:34:143:41 | [post] access to local variable nonSink0 | LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:144:15:144:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:144:15:144:22 | access to local variable nonSink0 | LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:145:9:145:39 | SSA def(nonSink7) | LocalDataFlow.cs:146:15:146:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:145:20:145:39 | call to method Parse | LocalDataFlow.cs:145:9:145:39 | SSA def(nonSink7) | -| LocalDataFlow.cs:145:31:145:38 | [post] access to local variable nonSink0 | LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:147:9:147:57 | SSA def(nonSink7) | LocalDataFlow.cs:148:15:148:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:147:20:147:57 | call to method TryParse | LocalDataFlow.cs:147:9:147:57 | SSA def(nonSink7) | -| LocalDataFlow.cs:148:15:148:22 | access to local variable nonSink7 | LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | -| LocalDataFlow.cs:149:13:149:48 | SSA def(nonSink14) | LocalDataFlow.cs:150:15:150:23 | access to local variable nonSink14 | -| LocalDataFlow.cs:149:25:149:48 | call to method ToByte | LocalDataFlow.cs:149:13:149:48 | SSA def(nonSink14) | -| LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | LocalDataFlow.cs:151:38:151:45 | access to local variable nonSink7 | -| LocalDataFlow.cs:151:9:151:46 | SSA def(nonSink0) | LocalDataFlow.cs:152:15:152:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:151:20:151:46 | call to method Concat | LocalDataFlow.cs:151:9:151:46 | SSA def(nonSink0) | -| LocalDataFlow.cs:151:38:151:45 | access to local variable nonSink7 | LocalDataFlow.cs:151:38:151:45 | (...) ... | -| LocalDataFlow.cs:152:15:152:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:152:15:152:22 | access to local variable nonSink0 | LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:153:9:153:40 | SSA def(nonSink0) | LocalDataFlow.cs:154:15:154:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:153:20:153:40 | [library code] call to method Copy | LocalDataFlow.cs:153:20:153:40 | call to method Copy | -| LocalDataFlow.cs:153:20:153:40 | call to method Copy | LocalDataFlow.cs:153:9:153:40 | SSA def(nonSink0) | -| LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | LocalDataFlow.cs:153:20:153:40 | [library code] call to method Copy | -| LocalDataFlow.cs:154:15:154:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:154:15:154:22 | access to local variable nonSink0 | LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:155:9:155:54 | SSA def(nonSink0) | LocalDataFlow.cs:156:15:156:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:155:20:155:54 | call to method Join | LocalDataFlow.cs:155:9:155:54 | SSA def(nonSink0) | -| LocalDataFlow.cs:156:15:156:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | -| LocalDataFlow.cs:156:15:156:22 | access to local variable nonSink0 | LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | -| LocalDataFlow.cs:157:9:157:41 | SSA def(nonSink0) | LocalDataFlow.cs:158:15:158:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:157:20:157:41 | call to method Insert | LocalDataFlow.cs:157:9:157:41 | SSA def(nonSink0) | -| LocalDataFlow.cs:158:15:158:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:158:15:158:22 | access to local variable nonSink0 | LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:161:13:161:32 | SSA def(sink20) | LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | -| LocalDataFlow.cs:161:22:161:32 | ... > ... | LocalDataFlow.cs:161:13:161:32 | SSA def(sink20) | -| LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | LocalDataFlow.cs:175:22:175:27 | access to local variable sink20 | -| LocalDataFlow.cs:163:13:163:40 | SSA def(sink21) | LocalDataFlow.cs:164:15:164:20 | access to local variable sink21 | -| LocalDataFlow.cs:163:22:163:26 | [post] access to local variable sink9 | LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | -| LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | -| LocalDataFlow.cs:163:22:163:40 | call to method Equals | LocalDataFlow.cs:163:13:163:40 | SSA def(sink21) | -| LocalDataFlow.cs:165:13:165:45 | SSA def(sink22) | LocalDataFlow.cs:166:15:166:20 | access to local variable sink22 | -| LocalDataFlow.cs:165:22:165:26 | [post] access to local variable sink8 | LocalDataFlow.cs:171:20:171:24 | access to local variable sink8 | -| LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | LocalDataFlow.cs:171:20:171:24 | access to local variable sink8 | -| LocalDataFlow.cs:165:22:165:45 | call to method Equals | LocalDataFlow.cs:165:13:165:45 | SSA def(sink22) | -| LocalDataFlow.cs:165:43:165:44 | 41 | LocalDataFlow.cs:165:35:165:44 | (...) ... | -| LocalDataFlow.cs:169:9:169:38 | SSA def(nonSink7) | LocalDataFlow.cs:170:15:170:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:169:20:169:24 | [post] access to local variable sink0 | LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | -| LocalDataFlow.cs:169:20:169:24 | access to local variable sink0 | LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | -| LocalDataFlow.cs:169:20:169:38 | call to method Equals | LocalDataFlow.cs:169:9:169:38 | SSA def(nonSink7) | -| LocalDataFlow.cs:169:33:169:37 | [post] access to local variable sink1 | LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | -| LocalDataFlow.cs:169:33:169:37 | access to local variable sink1 | LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | -| LocalDataFlow.cs:171:9:171:41 | SSA def(nonSink7) | LocalDataFlow.cs:172:15:172:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:171:20:171:41 | call to method Equals | LocalDataFlow.cs:171:9:171:41 | SSA def(nonSink7) | -| LocalDataFlow.cs:172:15:172:22 | access to local variable nonSink7 | LocalDataFlow.cs:179:20:179:27 | access to local variable nonSink7 | -| LocalDataFlow.cs:175:13:175:36 | SSA def(sink25) | LocalDataFlow.cs:176:15:176:20 | access to local variable sink25 | -| LocalDataFlow.cs:175:22:175:36 | ... \|\| ... | LocalDataFlow.cs:175:13:175:36 | SSA def(sink25) | -| LocalDataFlow.cs:179:9:179:36 | SSA def(nonSink7) | LocalDataFlow.cs:180:15:180:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:179:20:179:36 | ... \|\| ... | LocalDataFlow.cs:179:9:179:36 | SSA def(nonSink7) | -| LocalDataFlow.cs:183:13:183:42 | SSA def(sink26) | LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | -| LocalDataFlow.cs:183:22:183:42 | object creation of type Uri | LocalDataFlow.cs:183:13:183:42 | SSA def(sink26) | -| LocalDataFlow.cs:184:15:184:20 | [post] access to local variable sink26 | LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | -| LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:13:185:38 | SSA def(sink27) | LocalDataFlow.cs:186:15:186:20 | access to local variable sink27 | -| LocalDataFlow.cs:185:22:185:27 | [post] access to local variable sink26 | LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:22:185:38 | call to method ToString | LocalDataFlow.cs:185:13:185:38 | SSA def(sink27) | -| LocalDataFlow.cs:187:13:187:40 | SSA def(sink28) | LocalDataFlow.cs:188:15:188:20 | access to local variable sink28 | -| LocalDataFlow.cs:187:22:187:27 | [post] access to local variable sink26 | LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | -| LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | -| LocalDataFlow.cs:187:22:187:40 | access to property PathAndQuery | LocalDataFlow.cs:187:13:187:40 | SSA def(sink28) | -| LocalDataFlow.cs:189:13:189:33 | SSA def(sink29) | LocalDataFlow.cs:190:15:190:20 | access to local variable sink29 | -| LocalDataFlow.cs:189:22:189:27 | [post] access to local variable sink26 | LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | -| LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | -| LocalDataFlow.cs:189:22:189:33 | access to property Query | LocalDataFlow.cs:189:13:189:33 | SSA def(sink29) | -| LocalDataFlow.cs:191:13:191:42 | SSA def(sink30) | LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | -| LocalDataFlow.cs:191:22:191:42 | access to property OriginalString | LocalDataFlow.cs:191:13:191:42 | SSA def(sink30) | -| LocalDataFlow.cs:192:15:192:20 | [post] access to local variable sink30 | LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | -| LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | -| LocalDataFlow.cs:195:13:195:47 | SSA def(nonSink8) | LocalDataFlow.cs:196:15:196:22 | access to local variable nonSink8 | -| LocalDataFlow.cs:195:24:195:47 | object creation of type Uri | LocalDataFlow.cs:195:13:195:47 | SSA def(nonSink8) | -| LocalDataFlow.cs:196:15:196:22 | [post] access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:196:15:196:22 | access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:9:197:38 | SSA def(nonSink0) | LocalDataFlow.cs:198:15:198:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:197:20:197:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:20:197:38 | call to method ToString | LocalDataFlow.cs:197:9:197:38 | SSA def(nonSink0) | -| LocalDataFlow.cs:199:9:199:40 | SSA def(nonSink0) | LocalDataFlow.cs:200:15:200:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:199:20:199:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:199:20:199:40 | access to property PathAndQuery | LocalDataFlow.cs:199:9:199:40 | SSA def(nonSink0) | -| LocalDataFlow.cs:201:9:201:33 | SSA def(nonSink0) | LocalDataFlow.cs:202:15:202:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:201:20:201:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:201:20:201:33 | access to property Query | LocalDataFlow.cs:201:9:201:33 | SSA def(nonSink0) | -| LocalDataFlow.cs:203:9:203:42 | SSA def(nonSink0) | LocalDataFlow.cs:204:15:204:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:203:20:203:42 | access to property OriginalString | LocalDataFlow.cs:203:9:203:42 | SSA def(nonSink0) | -| LocalDataFlow.cs:204:15:204:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | -| LocalDataFlow.cs:204:15:204:22 | access to local variable nonSink0 | LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | -| LocalDataFlow.cs:207:13:207:55 | SSA def(sink31) | LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | -| LocalDataFlow.cs:207:22:207:55 | object creation of type StringReader | LocalDataFlow.cs:207:13:207:55 | SSA def(sink31) | -| LocalDataFlow.cs:208:15:208:20 | [post] access to local variable sink31 | LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | -| LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | -| LocalDataFlow.cs:209:13:209:39 | SSA def(sink32) | LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | -| LocalDataFlow.cs:209:22:209:39 | call to method ReadToEnd | LocalDataFlow.cs:209:13:209:39 | SSA def(sink32) | -| LocalDataFlow.cs:210:15:210:20 | [post] access to local variable sink32 | LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | -| LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | -| LocalDataFlow.cs:213:13:213:59 | SSA def(nonSink9) | LocalDataFlow.cs:214:15:214:22 | access to local variable nonSink9 | -| LocalDataFlow.cs:213:24:213:59 | object creation of type StringReader | LocalDataFlow.cs:213:13:213:59 | SSA def(nonSink9) | -| LocalDataFlow.cs:214:15:214:22 | [post] access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | -| LocalDataFlow.cs:214:15:214:22 | access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | -| LocalDataFlow.cs:215:9:215:39 | SSA def(nonSink0) | LocalDataFlow.cs:216:15:216:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:215:20:215:39 | call to method ReadToEnd | LocalDataFlow.cs:215:9:215:39 | SSA def(nonSink0) | -| LocalDataFlow.cs:216:15:216:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:216:15:216:22 | access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:219:13:219:127 | SSA def(sink33) | LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | -| LocalDataFlow.cs:219:22:219:127 | (...) ... | LocalDataFlow.cs:219:13:219:127 | SSA def(sink33) | -| LocalDataFlow.cs:219:30:219:119 | call to method Insert | LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | LocalDataFlow.cs:219:30:219:127 | call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | call to method Clone | LocalDataFlow.cs:219:22:219:127 | (...) ... | -| LocalDataFlow.cs:220:15:220:20 | [post] access to local variable sink33 | LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | -| LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | -| LocalDataFlow.cs:221:13:221:63 | SSA def(sink48) | LocalDataFlow.cs:222:15:222:20 | access to local variable sink48 | -| LocalDataFlow.cs:221:22:221:27 | [post] access to local variable sink33 | LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | -| LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | -| LocalDataFlow.cs:221:22:221:63 | call to method Split | LocalDataFlow.cs:221:13:221:63 | SSA def(sink48) | -| LocalDataFlow.cs:225:9:225:127 | SSA def(nonSink0) | LocalDataFlow.cs:226:15:226:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:225:20:225:127 | (...) ... | LocalDataFlow.cs:225:9:225:127 | SSA def(nonSink0) | -| LocalDataFlow.cs:225:28:225:119 | call to method Insert | LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | LocalDataFlow.cs:225:28:225:127 | call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | call to method Clone | LocalDataFlow.cs:225:20:225:127 | (...) ... | -| LocalDataFlow.cs:226:15:226:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | -| LocalDataFlow.cs:226:15:226:22 | access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:13:227:68 | SSA def(nonSink15) | LocalDataFlow.cs:228:15:228:23 | access to local variable nonSink15 | -| LocalDataFlow.cs:227:25:227:32 | [post] access to local variable nonSink0 | LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:25:227:68 | call to method Split | LocalDataFlow.cs:227:13:227:68 | SSA def(nonSink15) | -| LocalDataFlow.cs:231:13:231:46 | SSA def(sink34) | LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | -| LocalDataFlow.cs:231:22:231:46 | object creation of type StringBuilder | LocalDataFlow.cs:231:13:231:46 | SSA def(sink34) | -| LocalDataFlow.cs:232:15:232:20 | [post] access to local variable sink34 | LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | -| LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | -| LocalDataFlow.cs:233:13:233:38 | SSA def(sink35) | LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | -| LocalDataFlow.cs:233:22:233:38 | call to method ToString | LocalDataFlow.cs:233:13:233:38 | SSA def(sink35) | -| LocalDataFlow.cs:234:15:234:20 | [post] access to local variable sink35 | LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | -| LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | -| LocalDataFlow.cs:235:13:235:42 | SSA def(sink36) | LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | -| LocalDataFlow.cs:235:22:235:42 | object creation of type StringBuilder | LocalDataFlow.cs:235:13:235:42 | SSA def(sink36) | -| LocalDataFlow.cs:236:9:236:14 | [post] access to local variable sink36 | LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:240:13:240:51 | SSA def(nonSink10) | LocalDataFlow.cs:241:15:241:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:240:25:240:51 | object creation of type StringBuilder | LocalDataFlow.cs:240:13:240:51 | SSA def(nonSink10) | -| LocalDataFlow.cs:241:15:241:23 | [post] access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | -| LocalDataFlow.cs:241:15:241:23 | access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:9:242:39 | SSA def(nonSink0) | LocalDataFlow.cs:243:15:243:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:242:20:242:28 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:20:242:39 | call to method ToString | LocalDataFlow.cs:242:9:242:39 | SSA def(nonSink0) | -| LocalDataFlow.cs:243:15:243:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | -| LocalDataFlow.cs:243:15:243:22 | access to local variable nonSink0 | LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | -| LocalDataFlow.cs:244:9:244:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:245:15:245:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | LocalDataFlow.cs:245:15:245:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:248:13:248:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:248:13:248:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:251:22:251:46 | access to property AList | -| LocalDataFlow.cs:248:35:248:52 | object creation of type DataContract | LocalDataFlow.cs:248:13:248:52 | SSA def(taintedDataContract) | -| LocalDataFlow.cs:249:13:249:48 | SSA def(sink53) | LocalDataFlow.cs:250:15:250:20 | access to local variable sink53 | -| LocalDataFlow.cs:249:22:249:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:249:22:249:48 | access to property AString | LocalDataFlow.cs:249:13:249:48 | SSA def(sink53) | -| LocalDataFlow.cs:251:13:251:57 | SSA def(sink54) | LocalDataFlow.cs:252:15:252:20 | access to local variable sink54 | -| LocalDataFlow.cs:251:22:251:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:251:22:251:46 | [post] access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:251:22:251:46 | access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:251:22:251:57 | access to property AString | LocalDataFlow.cs:251:13:251:57 | SSA def(sink54) | -| LocalDataFlow.cs:255:13:255:55 | SSA def(nonTaintedDataContract) | LocalDataFlow.cs:256:20:256:41 | access to local variable nonTaintedDataContract | -| LocalDataFlow.cs:255:38:255:55 | object creation of type DataContract | LocalDataFlow.cs:255:13:255:55 | SSA def(nonTaintedDataContract) | -| LocalDataFlow.cs:256:9:256:49 | SSA def(nonSink0) | LocalDataFlow.cs:257:15:257:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:256:20:256:49 | access to property AString | LocalDataFlow.cs:256:9:256:49 | SSA def(nonSink0) | -| LocalDataFlow.cs:258:9:258:44 | SSA def(nonSink2) | LocalDataFlow.cs:259:15:259:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:258:20:258:38 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:258:20:258:44 | access to property AnInt | LocalDataFlow.cs:258:9:258:44 | SSA def(nonSink2) | -| LocalDataFlow.cs:260:9:260:53 | SSA def(nonSink2) | LocalDataFlow.cs:261:15:261:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:260:20:260:53 | access to property AnInt | LocalDataFlow.cs:260:9:260:53 | SSA def(nonSink2) | -| LocalDataFlow.cs:264:17:264:37 | SSA def(taintedTextBox) | LocalDataFlow.cs:265:22:265:35 | access to local variable taintedTextBox | -| LocalDataFlow.cs:264:34:264:37 | null | LocalDataFlow.cs:264:17:264:37 | SSA def(taintedTextBox) | -| LocalDataFlow.cs:265:13:265:40 | SSA def(sink60) | LocalDataFlow.cs:266:15:266:20 | access to local variable sink60 | -| LocalDataFlow.cs:265:22:265:40 | access to property Text | LocalDataFlow.cs:265:13:265:40 | SSA def(sink60) | -| LocalDataFlow.cs:269:17:269:40 | SSA def(nonTaintedTextBox) | LocalDataFlow.cs:270:20:270:36 | access to local variable nonTaintedTextBox | -| LocalDataFlow.cs:269:37:269:40 | null | LocalDataFlow.cs:269:17:269:40 | SSA def(nonTaintedTextBox) | -| LocalDataFlow.cs:270:9:270:41 | SSA def(nonSink0) | LocalDataFlow.cs:271:15:271:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:270:20:270:41 | access to property Text | LocalDataFlow.cs:270:9:270:41 | SSA def(nonSink0) | -| LocalDataFlow.cs:274:13:274:51 | SSA def(sink67) | LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | -| LocalDataFlow.cs:274:22:274:51 | [library code] call to method Run | LocalDataFlow.cs:274:22:274:51 | call to method Run | -| LocalDataFlow.cs:274:22:274:51 | call to method Run | LocalDataFlow.cs:274:13:274:51 | SSA def(sink67) | -| LocalDataFlow.cs:274:31:274:50 | [output] (...) => ... | LocalDataFlow.cs:274:22:274:51 | [library code] call to method Run | -| LocalDataFlow.cs:275:15:275:20 | [post] access to local variable sink67 | LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | -| LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | -| LocalDataFlow.cs:276:13:276:33 | SSA def(sink68) | LocalDataFlow.cs:277:15:277:20 | access to local variable sink68 | -| LocalDataFlow.cs:276:22:276:33 | await ... | LocalDataFlow.cs:276:13:276:33 | SSA def(sink68) | -| LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | LocalDataFlow.cs:276:22:276:33 | await ... | -| LocalDataFlow.cs:280:13:280:42 | SSA def(nonSink21) | LocalDataFlow.cs:281:15:281:23 | access to local variable nonSink21 | -| LocalDataFlow.cs:280:25:280:42 | [library code] call to method Run | LocalDataFlow.cs:280:25:280:42 | call to method Run | -| LocalDataFlow.cs:280:25:280:42 | call to method Run | LocalDataFlow.cs:280:13:280:42 | SSA def(nonSink21) | -| LocalDataFlow.cs:280:34:280:41 | [output] (...) => ... | LocalDataFlow.cs:280:25:280:42 | [library code] call to method Run | -| LocalDataFlow.cs:281:15:281:23 | [post] access to local variable nonSink21 | LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | -| LocalDataFlow.cs:281:15:281:23 | access to local variable nonSink21 | LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | -| LocalDataFlow.cs:282:9:282:34 | SSA def(nonSink0) | LocalDataFlow.cs:283:15:283:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:282:20:282:34 | await ... | LocalDataFlow.cs:282:9:282:34 | SSA def(nonSink0) | -| LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | LocalDataFlow.cs:282:20:282:34 | await ... | -| LocalDataFlow.cs:283:15:283:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:283:15:283:22 | access to local variable nonSink0 | LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:286:13:286:36 | SSA def(sink69) | LocalDataFlow.cs:287:15:287:20 | access to local variable sink69 | -| LocalDataFlow.cs:286:22:286:36 | $"..." | LocalDataFlow.cs:286:13:286:36 | SSA def(sink69) | -| LocalDataFlow.cs:290:9:290:37 | SSA def(nonSink0) | LocalDataFlow.cs:291:15:291:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:290:20:290:37 | $"..." | LocalDataFlow.cs:290:9:290:37 | SSA def(nonSink0) | -| LocalDataFlow.cs:291:15:291:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:291:15:291:22 | access to local variable nonSink0 | LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:294:13:294:34 | SSA def(sink70) | LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | -| LocalDataFlow.cs:294:22:294:34 | ... = ... | LocalDataFlow.cs:294:13:294:34 | SSA def(sink70) | -| LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | -| LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | -| LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | LocalDataFlow.cs:294:22:294:34 | ... = ... | -| LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | -| LocalDataFlow.cs:295:15:295:20 | [post] access to local variable sink70 | LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | -| LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | -| LocalDataFlow.cs:298:9:298:38 | SSA def(nonSink0) | LocalDataFlow.cs:299:15:299:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:298:20:298:38 | ... = ... | LocalDataFlow.cs:298:9:298:38 | SSA def(nonSink0) | -| LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | LocalDataFlow.cs:298:20:298:38 | ... = ... | -| LocalDataFlow.cs:299:15:299:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | -| LocalDataFlow.cs:299:15:299:22 | access to local variable nonSink0 | LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | -| LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | LocalDataFlow.cs:302:23:302:35 | SSA def(sink71) | -| LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | LocalDataFlow.cs:310:17:310:22 | access to local variable sink70 | -| LocalDataFlow.cs:302:23:302:35 | SSA def(sink71) | LocalDataFlow.cs:303:19:303:24 | access to local variable sink71 | -| LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | LocalDataFlow.cs:306:25:306:40 | SSA def(nonSink16) | -| LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | -| LocalDataFlow.cs:306:25:306:40 | SSA def(nonSink16) | LocalDataFlow.cs:307:19:307:27 | access to local variable nonSink16 | -| LocalDataFlow.cs:310:17:310:22 | access to local variable sink70 | LocalDataFlow.cs:312:18:312:30 | SSA def(sink72) | -| LocalDataFlow.cs:312:18:312:30 | SSA def(sink72) | LocalDataFlow.cs:313:23:313:28 | access to local variable sink72 | -| LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | LocalDataFlow.cs:320:18:320:33 | SSA def(nonSink17) | -| LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | -| LocalDataFlow.cs:320:18:320:33 | SSA def(nonSink17) | LocalDataFlow.cs:321:23:321:31 | access to local variable nonSink17 | -| LocalDataFlow.cs:326:13:326:38 | SSA def(sink73) | LocalDataFlow.cs:328:15:328:20 | access to local variable sink73 | -| LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | LocalDataFlow.cs:326:22:326:38 | ... ?? ... | -| LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | LocalDataFlow.cs:327:31:327:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:326:22:326:38 | ... ?? ... | LocalDataFlow.cs:326:13:326:38 | SSA def(sink73) | -| LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | LocalDataFlow.cs:326:22:326:38 | ... ?? ... | -| LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | -| LocalDataFlow.cs:327:13:327:38 | SSA def(sink74) | LocalDataFlow.cs:329:15:329:20 | access to local variable sink74 | -| LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | LocalDataFlow.cs:327:22:327:38 | ... ?? ... | -| LocalDataFlow.cs:327:22:327:38 | ... ?? ... | LocalDataFlow.cs:327:13:327:38 | SSA def(sink74) | -| LocalDataFlow.cs:327:31:327:38 | access to local variable nonSink0 | LocalDataFlow.cs:327:22:327:38 | ... ?? ... | -| LocalDataFlow.cs:347:28:347:30 | this | LocalDataFlow.cs:347:41:347:45 | this access | -| LocalDataFlow.cs:347:50:347:52 | this | LocalDataFlow.cs:347:56:347:60 | this access | -| LocalDataFlow.cs:347:50:347:52 | value | LocalDataFlow.cs:347:64:347:68 | access to parameter value | -| LocalDataFlow.cs:353:41:353:47 | tainted | LocalDataFlow.cs:355:15:355:21 | access to parameter tainted | -| LocalDataFlow.cs:358:44:358:53 | nonTainted | LocalDataFlow.cs:360:15:360:24 | access to parameter nonTainted | -| LocalDataFlow.cs:363:44:363:44 | x | LocalDataFlow.cs:366:21:366:21 | access to parameter x | -| LocalDataFlow.cs:363:67:363:68 | os | LocalDataFlow.cs:369:32:369:33 | access to parameter os | -| LocalDataFlow.cs:366:21:366:21 | access to parameter x | LocalDataFlow.cs:366:16:366:21 | ... = ... | -| LocalDataFlow.cs:369:32:369:33 | access to parameter os | LocalDataFlow.cs:369:26:369:33 | ... = ... | -| LocalDataFlow.cs:374:41:374:44 | args | LocalDataFlow.cs:376:29:376:32 | access to parameter args | -| LocalDataFlow.cs:376:29:376:32 | [post] access to parameter args | LocalDataFlow.cs:377:27:377:30 | access to parameter args | -| LocalDataFlow.cs:376:29:376:32 | access to parameter args | LocalDataFlow.cs:377:27:377:30 | access to parameter args | +| LocalDataFlow.cs:48:24:48:24 | b | LocalDataFlow.cs:84:21:84:21 | access to parameter b | +| LocalDataFlow.cs:51:13:51:34 | SSA def(sink0) | LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | +| LocalDataFlow.cs:51:21:51:34 | "taint source" | LocalDataFlow.cs:51:13:51:34 | SSA def(sink0) | +| LocalDataFlow.cs:52:15:52:19 | [post] access to local variable sink0 | LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | +| LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | +| LocalDataFlow.cs:55:13:55:25 | SSA def(nonSink0) | LocalDataFlow.cs:56:15:56:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:55:24:55:25 | "" | LocalDataFlow.cs:55:13:55:25 | SSA def(nonSink0) | +| LocalDataFlow.cs:56:15:56:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | +| LocalDataFlow.cs:56:15:56:22 | access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | +| LocalDataFlow.cs:59:13:59:25 | SSA def(sink1) | LocalDataFlow.cs:60:9:60:13 | access to local variable sink1 | +| LocalDataFlow.cs:59:21:59:25 | "abc" | LocalDataFlow.cs:59:13:59:25 | SSA def(sink1) | +| LocalDataFlow.cs:60:9:60:22 | ... + ... | LocalDataFlow.cs:60:9:60:22 | SSA def(sink1) | +| LocalDataFlow.cs:60:9:60:22 | SSA def(sink1) | LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | +| LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | LocalDataFlow.cs:168:20:168:24 | access to local variable sink0 | +| LocalDataFlow.cs:61:15:61:19 | [post] access to local variable sink1 | LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | +| LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | +| LocalDataFlow.cs:64:9:64:25 | ... + ... | LocalDataFlow.cs:64:9:64:25 | SSA def(nonSink0) | +| LocalDataFlow.cs:64:9:64:25 | SSA def(nonSink0) | LocalDataFlow.cs:65:15:65:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:65:15:65:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:65:15:65:22 | access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:68:13:68:32 | SSA def(sink5) | LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | +| LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | LocalDataFlow.cs:168:33:168:37 | access to local variable sink1 | +| LocalDataFlow.cs:68:21:68:32 | ... + ... | LocalDataFlow.cs:68:13:68:32 | SSA def(sink5) | +| LocalDataFlow.cs:69:15:69:19 | [post] access to local variable sink5 | LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | +| LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | +| LocalDataFlow.cs:72:9:72:36 | SSA def(nonSink0) | LocalDataFlow.cs:73:15:73:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:72:20:72:36 | ... + ... | LocalDataFlow.cs:72:9:72:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:73:15:73:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | +| LocalDataFlow.cs:73:15:73:22 | access to local variable nonSink0 | LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | +| LocalDataFlow.cs:76:13:76:27 | SSA def(sink6) | LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | +| LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | LocalDataFlow.cs:76:13:76:27 | SSA def(sink6) | +| LocalDataFlow.cs:77:15:77:19 | [post] access to local variable sink6 | LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | +| LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | +| LocalDataFlow.cs:80:9:80:29 | SSA def(nonSink0) | LocalDataFlow.cs:81:15:81:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | LocalDataFlow.cs:80:9:80:29 | SSA def(nonSink0) | +| LocalDataFlow.cs:84:13:84:35 | [b (line 48): false] SSA def(sink7) | LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | +| LocalDataFlow.cs:84:13:84:35 | [b (line 48): true] SSA def(sink7) | LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | +| LocalDataFlow.cs:84:21:84:21 | access to parameter b | LocalDataFlow.cs:88:20:88:20 | [b (line 48): false] access to parameter b | +| LocalDataFlow.cs:84:21:84:21 | access to parameter b | LocalDataFlow.cs:88:20:88:20 | [b (line 48): true] access to parameter b | +| LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | LocalDataFlow.cs:84:13:84:35 | [b (line 48): false] SSA def(sink7) | +| LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | LocalDataFlow.cs:84:13:84:35 | [b (line 48): true] SSA def(sink7) | +| LocalDataFlow.cs:84:25:84:27 | [b (line 48): true] "a" | LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | +| LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | +| LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | LocalDataFlow.cs:89:15:89:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | LocalDataFlow.cs:92:29:92:33 | access to local variable sink7 | +| LocalDataFlow.cs:88:20:88:36 | [b (line 48): false] ... ? ... : ... | LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:88:20:88:36 | [b (line 48): true] ... ? ... : ... | LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:88:24:88:28 | "abc" | LocalDataFlow.cs:88:20:88:36 | [b (line 48): true] ... ? ... : ... | +| LocalDataFlow.cs:88:32:88:36 | "def" | LocalDataFlow.cs:88:20:88:36 | [b (line 48): false] ... ? ... : ... | +| LocalDataFlow.cs:89:15:89:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:89:15:89:22 | access to local variable nonSink0 | LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:92:13:92:33 | SSA def(sink8) | LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | +| LocalDataFlow.cs:92:21:92:33 | (...) ... | LocalDataFlow.cs:92:13:92:33 | SSA def(sink8) | +| LocalDataFlow.cs:92:29:92:33 | access to local variable sink7 | LocalDataFlow.cs:92:21:92:33 | (...) ... | +| LocalDataFlow.cs:93:15:93:19 | [post] access to local variable sink8 | LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | +| LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | +| LocalDataFlow.cs:96:13:96:39 | SSA def(nonSink3) | LocalDataFlow.cs:97:15:97:22 | access to local variable nonSink3 | +| LocalDataFlow.cs:96:24:96:39 | (...) ... | LocalDataFlow.cs:96:13:96:39 | SSA def(nonSink3) | +| LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | LocalDataFlow.cs:96:24:96:39 | (...) ... | +| LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:100:13:100:35 | SSA def(sink9) | LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | +| LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | LocalDataFlow.cs:100:21:100:35 | ... as ... | +| LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | +| LocalDataFlow.cs:100:21:100:35 | ... as ... | LocalDataFlow.cs:100:13:100:35 | SSA def(sink9) | +| LocalDataFlow.cs:101:15:101:19 | [post] access to local variable sink9 | LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | +| LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | +| LocalDataFlow.cs:104:9:104:37 | SSA def(nonSink3) | LocalDataFlow.cs:105:15:105:22 | access to local variable nonSink3 | +| LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | LocalDataFlow.cs:104:20:104:37 | ... as ... | +| LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | +| LocalDataFlow.cs:104:20:104:37 | ... as ... | LocalDataFlow.cs:104:9:104:37 | SSA def(nonSink3) | +| LocalDataFlow.cs:105:15:105:22 | [post] access to local variable nonSink3 | LocalDataFlow.cs:170:33:170:40 | access to local variable nonSink3 | +| LocalDataFlow.cs:105:15:105:22 | access to local variable nonSink3 | LocalDataFlow.cs:170:33:170:40 | access to local variable nonSink3 | +| LocalDataFlow.cs:108:13:108:39 | SSA def(sink15) | LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | +| LocalDataFlow.cs:108:22:108:39 | call to method Parse | LocalDataFlow.cs:108:13:108:39 | SSA def(sink15) | +| LocalDataFlow.cs:108:34:108:38 | [post] access to local variable sink9 | LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | +| LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | +| LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | LocalDataFlow.cs:160:22:160:27 | access to local variable sink15 | +| LocalDataFlow.cs:111:13:111:56 | SSA def(sink16) | LocalDataFlow.cs:112:15:112:20 | access to local variable sink16 | +| LocalDataFlow.cs:111:22:111:56 | call to method TryParse | LocalDataFlow.cs:111:13:111:56 | SSA def(sink16) | +| LocalDataFlow.cs:111:37:111:41 | [post] access to local variable sink9 | LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | +| LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | +| LocalDataFlow.cs:113:13:113:49 | SSA def(sink17) | LocalDataFlow.cs:114:15:114:20 | access to local variable sink17 | +| LocalDataFlow.cs:113:22:113:29 | [post] access to local variable nonSink0 | LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | +| LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | +| LocalDataFlow.cs:113:22:113:49 | call to method Replace | LocalDataFlow.cs:113:13:113:49 | SSA def(sink17) | +| LocalDataFlow.cs:113:44:113:48 | [post] access to local variable sink9 | LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | +| LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | +| LocalDataFlow.cs:115:13:115:51 | SSA def(sink18) | LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | +| LocalDataFlow.cs:115:22:115:51 | call to method Format | LocalDataFlow.cs:115:13:115:51 | SSA def(sink18) | +| LocalDataFlow.cs:115:36:115:43 | [post] access to local variable nonSink0 | LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:115:46:115:50 | [post] access to local variable sink9 | LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | +| LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | +| LocalDataFlow.cs:116:15:116:20 | [post] access to local variable sink18 | LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | +| LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | +| LocalDataFlow.cs:117:13:117:52 | SSA def(sink19) | LocalDataFlow.cs:118:15:118:20 | access to local variable sink19 | +| LocalDataFlow.cs:117:22:117:52 | call to method Format | LocalDataFlow.cs:117:13:117:52 | SSA def(sink19) | +| LocalDataFlow.cs:117:44:117:51 | [post] access to local variable nonSink0 | LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:119:13:119:38 | SSA def(sink45) | LocalDataFlow.cs:120:15:120:20 | access to local variable sink45 | +| LocalDataFlow.cs:119:22:119:38 | call to method Parse | LocalDataFlow.cs:119:13:119:38 | SSA def(sink45) | +| LocalDataFlow.cs:119:33:119:37 | [post] access to local variable sink9 | LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | +| LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | +| LocalDataFlow.cs:122:13:122:56 | SSA def(sink46) | LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | +| LocalDataFlow.cs:122:22:122:56 | call to method TryParse | LocalDataFlow.cs:122:13:122:56 | SSA def(sink46) | +| LocalDataFlow.cs:122:36:122:40 | [post] access to local variable sink9 | LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | +| LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | +| LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | LocalDataFlow.cs:124:37:124:42 | access to local variable sink46 | +| LocalDataFlow.cs:124:13:124:43 | SSA def(sink47) | LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | +| LocalDataFlow.cs:124:22:124:43 | call to method ToByte | LocalDataFlow.cs:124:13:124:43 | SSA def(sink47) | +| LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | LocalDataFlow.cs:126:40:126:45 | access to local variable sink47 | +| LocalDataFlow.cs:126:13:126:46 | SSA def(sink49) | LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | +| LocalDataFlow.cs:126:22:126:46 | call to method Concat | LocalDataFlow.cs:126:13:126:46 | SSA def(sink49) | +| LocalDataFlow.cs:126:40:126:45 | access to local variable sink47 | LocalDataFlow.cs:126:40:126:45 | (...) ... | +| LocalDataFlow.cs:127:15:127:20 | [post] access to local variable sink49 | LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | +| LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | +| LocalDataFlow.cs:128:13:128:40 | SSA def(sink50) | LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | +| LocalDataFlow.cs:128:22:128:40 | call to method Copy | LocalDataFlow.cs:128:13:128:40 | SSA def(sink50) | +| LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | LocalDataFlow.cs:128:22:128:40 | call to method Copy | +| LocalDataFlow.cs:129:15:129:20 | [post] access to local variable sink50 | LocalDataFlow.cs:130:59:130:64 | access to local variable sink50 | +| LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | LocalDataFlow.cs:130:59:130:64 | access to local variable sink50 | +| LocalDataFlow.cs:130:13:130:71 | SSA def(sink51) | LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | +| LocalDataFlow.cs:130:22:130:71 | call to method Join | LocalDataFlow.cs:130:13:130:71 | SSA def(sink51) | +| LocalDataFlow.cs:130:53:130:70 | { ..., ... } | LocalDataFlow.cs:130:40:130:70 | array creation of type String[] | +| LocalDataFlow.cs:131:15:131:20 | [post] access to local variable sink51 | LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | +| LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | +| LocalDataFlow.cs:132:13:132:41 | SSA def(sink52) | LocalDataFlow.cs:133:15:133:20 | access to local variable sink52 | +| LocalDataFlow.cs:132:22:132:41 | call to method Insert | LocalDataFlow.cs:132:13:132:41 | SSA def(sink52) | +| LocalDataFlow.cs:136:9:136:40 | SSA def(nonSink2) | LocalDataFlow.cs:137:15:137:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:136:20:136:40 | call to method Parse | LocalDataFlow.cs:136:9:136:40 | SSA def(nonSink2) | +| LocalDataFlow.cs:136:32:136:39 | [post] access to local variable nonSink0 | LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:138:13:138:61 | SSA def(nonSink7) | LocalDataFlow.cs:139:15:139:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:138:24:138:61 | call to method TryParse | LocalDataFlow.cs:138:13:138:61 | SSA def(nonSink7) | +| LocalDataFlow.cs:138:39:138:46 | [post] access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:9:140:50 | SSA def(nonSink0) | LocalDataFlow.cs:141:15:141:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:27 | [post] access to local variable nonSink0 | LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:50 | call to method Replace | LocalDataFlow.cs:140:9:140:50 | SSA def(nonSink0) | +| LocalDataFlow.cs:141:15:141:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:141:15:141:22 | access to local variable nonSink0 | LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:9:142:52 | SSA def(nonSink0) | LocalDataFlow.cs:143:15:143:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:20:142:52 | call to method Format | LocalDataFlow.cs:142:9:142:52 | SSA def(nonSink0) | +| LocalDataFlow.cs:142:34:142:41 | [post] access to local variable nonSink0 | LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:143:15:143:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:143:15:143:22 | access to local variable nonSink0 | LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:144:9:144:39 | SSA def(nonSink7) | LocalDataFlow.cs:145:15:145:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:144:20:144:39 | call to method Parse | LocalDataFlow.cs:144:9:144:39 | SSA def(nonSink7) | +| LocalDataFlow.cs:144:31:144:38 | [post] access to local variable nonSink0 | LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:146:9:146:57 | SSA def(nonSink7) | LocalDataFlow.cs:147:15:147:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:146:20:146:57 | call to method TryParse | LocalDataFlow.cs:146:9:146:57 | SSA def(nonSink7) | +| LocalDataFlow.cs:147:15:147:22 | access to local variable nonSink7 | LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | +| LocalDataFlow.cs:148:13:148:48 | SSA def(nonSink14) | LocalDataFlow.cs:149:15:149:23 | access to local variable nonSink14 | +| LocalDataFlow.cs:148:25:148:48 | call to method ToByte | LocalDataFlow.cs:148:13:148:48 | SSA def(nonSink14) | +| LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | LocalDataFlow.cs:150:38:150:45 | access to local variable nonSink7 | +| LocalDataFlow.cs:150:9:150:46 | SSA def(nonSink0) | LocalDataFlow.cs:151:15:151:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:150:20:150:46 | call to method Concat | LocalDataFlow.cs:150:9:150:46 | SSA def(nonSink0) | +| LocalDataFlow.cs:150:38:150:45 | access to local variable nonSink7 | LocalDataFlow.cs:150:38:150:45 | (...) ... | +| LocalDataFlow.cs:151:15:151:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:151:15:151:22 | access to local variable nonSink0 | LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:152:9:152:40 | SSA def(nonSink0) | LocalDataFlow.cs:153:15:153:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:152:20:152:40 | call to method Copy | LocalDataFlow.cs:152:9:152:40 | SSA def(nonSink0) | +| LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | LocalDataFlow.cs:152:20:152:40 | call to method Copy | +| LocalDataFlow.cs:153:15:153:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:154:57:154:64 | access to local variable nonSink0 | +| LocalDataFlow.cs:153:15:153:22 | access to local variable nonSink0 | LocalDataFlow.cs:154:57:154:64 | access to local variable nonSink0 | +| LocalDataFlow.cs:154:9:154:71 | SSA def(nonSink0) | LocalDataFlow.cs:155:15:155:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:154:20:154:71 | call to method Join | LocalDataFlow.cs:154:9:154:71 | SSA def(nonSink0) | +| LocalDataFlow.cs:154:51:154:70 | { ..., ... } | LocalDataFlow.cs:154:38:154:70 | array creation of type String[] | +| LocalDataFlow.cs:155:15:155:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | +| LocalDataFlow.cs:155:15:155:22 | access to local variable nonSink0 | LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | +| LocalDataFlow.cs:156:9:156:41 | SSA def(nonSink0) | LocalDataFlow.cs:157:15:157:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:156:20:156:41 | call to method Insert | LocalDataFlow.cs:156:9:156:41 | SSA def(nonSink0) | +| LocalDataFlow.cs:157:15:157:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:157:15:157:22 | access to local variable nonSink0 | LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:160:13:160:32 | SSA def(sink20) | LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | +| LocalDataFlow.cs:160:22:160:32 | ... > ... | LocalDataFlow.cs:160:13:160:32 | SSA def(sink20) | +| LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | LocalDataFlow.cs:174:22:174:27 | access to local variable sink20 | +| LocalDataFlow.cs:162:13:162:40 | SSA def(sink21) | LocalDataFlow.cs:163:15:163:20 | access to local variable sink21 | +| LocalDataFlow.cs:162:22:162:26 | [post] access to local variable sink9 | LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | +| LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | +| LocalDataFlow.cs:162:22:162:40 | call to method Equals | LocalDataFlow.cs:162:13:162:40 | SSA def(sink21) | +| LocalDataFlow.cs:164:13:164:45 | SSA def(sink22) | LocalDataFlow.cs:165:15:165:20 | access to local variable sink22 | +| LocalDataFlow.cs:164:22:164:26 | [post] access to local variable sink8 | LocalDataFlow.cs:170:20:170:24 | access to local variable sink8 | +| LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | LocalDataFlow.cs:170:20:170:24 | access to local variable sink8 | +| LocalDataFlow.cs:164:22:164:45 | call to method Equals | LocalDataFlow.cs:164:13:164:45 | SSA def(sink22) | +| LocalDataFlow.cs:164:43:164:44 | 41 | LocalDataFlow.cs:164:35:164:44 | (...) ... | +| LocalDataFlow.cs:168:9:168:38 | SSA def(nonSink7) | LocalDataFlow.cs:169:15:169:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:168:20:168:24 | [post] access to local variable sink0 | LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | +| LocalDataFlow.cs:168:20:168:24 | access to local variable sink0 | LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | +| LocalDataFlow.cs:168:20:168:38 | call to method Equals | LocalDataFlow.cs:168:9:168:38 | SSA def(nonSink7) | +| LocalDataFlow.cs:168:33:168:37 | [post] access to local variable sink1 | LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | +| LocalDataFlow.cs:168:33:168:37 | access to local variable sink1 | LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | +| LocalDataFlow.cs:170:9:170:41 | SSA def(nonSink7) | LocalDataFlow.cs:171:15:171:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:170:20:170:41 | call to method Equals | LocalDataFlow.cs:170:9:170:41 | SSA def(nonSink7) | +| LocalDataFlow.cs:171:15:171:22 | access to local variable nonSink7 | LocalDataFlow.cs:178:20:178:27 | access to local variable nonSink7 | +| LocalDataFlow.cs:174:13:174:36 | SSA def(sink25) | LocalDataFlow.cs:175:15:175:20 | access to local variable sink25 | +| LocalDataFlow.cs:174:22:174:36 | ... \|\| ... | LocalDataFlow.cs:174:13:174:36 | SSA def(sink25) | +| LocalDataFlow.cs:178:9:178:36 | SSA def(nonSink7) | LocalDataFlow.cs:179:15:179:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:178:20:178:36 | ... \|\| ... | LocalDataFlow.cs:178:9:178:36 | SSA def(nonSink7) | +| LocalDataFlow.cs:182:13:182:42 | SSA def(sink26) | LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | +| LocalDataFlow.cs:182:22:182:42 | object creation of type Uri | LocalDataFlow.cs:182:13:182:42 | SSA def(sink26) | +| LocalDataFlow.cs:183:15:183:20 | [post] access to local variable sink26 | LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | +| LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:13:184:38 | SSA def(sink27) | LocalDataFlow.cs:185:15:185:20 | access to local variable sink27 | +| LocalDataFlow.cs:184:22:184:27 | [post] access to local variable sink26 | LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:22:184:38 | call to method ToString | LocalDataFlow.cs:184:13:184:38 | SSA def(sink27) | +| LocalDataFlow.cs:186:13:186:40 | SSA def(sink28) | LocalDataFlow.cs:187:15:187:20 | access to local variable sink28 | +| LocalDataFlow.cs:186:22:186:27 | [post] access to local variable sink26 | LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | +| LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | +| LocalDataFlow.cs:186:22:186:40 | access to property PathAndQuery | LocalDataFlow.cs:186:13:186:40 | SSA def(sink28) | +| LocalDataFlow.cs:188:13:188:33 | SSA def(sink29) | LocalDataFlow.cs:189:15:189:20 | access to local variable sink29 | +| LocalDataFlow.cs:188:22:188:27 | [post] access to local variable sink26 | LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | +| LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | +| LocalDataFlow.cs:188:22:188:33 | access to property Query | LocalDataFlow.cs:188:13:188:33 | SSA def(sink29) | +| LocalDataFlow.cs:190:13:190:42 | SSA def(sink30) | LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | +| LocalDataFlow.cs:190:22:190:42 | access to property OriginalString | LocalDataFlow.cs:190:13:190:42 | SSA def(sink30) | +| LocalDataFlow.cs:191:15:191:20 | [post] access to local variable sink30 | LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | +| LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | +| LocalDataFlow.cs:194:13:194:47 | SSA def(nonSink8) | LocalDataFlow.cs:195:15:195:22 | access to local variable nonSink8 | +| LocalDataFlow.cs:194:24:194:47 | object creation of type Uri | LocalDataFlow.cs:194:13:194:47 | SSA def(nonSink8) | +| LocalDataFlow.cs:195:15:195:22 | [post] access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:195:15:195:22 | access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:9:196:38 | SSA def(nonSink0) | LocalDataFlow.cs:197:15:197:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:196:20:196:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:20:196:38 | call to method ToString | LocalDataFlow.cs:196:9:196:38 | SSA def(nonSink0) | +| LocalDataFlow.cs:198:9:198:40 | SSA def(nonSink0) | LocalDataFlow.cs:199:15:199:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:198:20:198:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:198:20:198:40 | access to property PathAndQuery | LocalDataFlow.cs:198:9:198:40 | SSA def(nonSink0) | +| LocalDataFlow.cs:200:9:200:33 | SSA def(nonSink0) | LocalDataFlow.cs:201:15:201:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:200:20:200:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:200:20:200:33 | access to property Query | LocalDataFlow.cs:200:9:200:33 | SSA def(nonSink0) | +| LocalDataFlow.cs:202:9:202:42 | SSA def(nonSink0) | LocalDataFlow.cs:203:15:203:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:202:20:202:42 | access to property OriginalString | LocalDataFlow.cs:202:9:202:42 | SSA def(nonSink0) | +| LocalDataFlow.cs:203:15:203:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | +| LocalDataFlow.cs:203:15:203:22 | access to local variable nonSink0 | LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | +| LocalDataFlow.cs:206:13:206:55 | SSA def(sink31) | LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | +| LocalDataFlow.cs:206:22:206:55 | object creation of type StringReader | LocalDataFlow.cs:206:13:206:55 | SSA def(sink31) | +| LocalDataFlow.cs:207:15:207:20 | [post] access to local variable sink31 | LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | +| LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | +| LocalDataFlow.cs:208:13:208:39 | SSA def(sink32) | LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | +| LocalDataFlow.cs:208:22:208:39 | call to method ReadToEnd | LocalDataFlow.cs:208:13:208:39 | SSA def(sink32) | +| LocalDataFlow.cs:209:15:209:20 | [post] access to local variable sink32 | LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | +| LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | +| LocalDataFlow.cs:212:13:212:59 | SSA def(nonSink9) | LocalDataFlow.cs:213:15:213:22 | access to local variable nonSink9 | +| LocalDataFlow.cs:212:24:212:59 | object creation of type StringReader | LocalDataFlow.cs:212:13:212:59 | SSA def(nonSink9) | +| LocalDataFlow.cs:213:15:213:22 | [post] access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | +| LocalDataFlow.cs:213:15:213:22 | access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | +| LocalDataFlow.cs:214:9:214:39 | SSA def(nonSink0) | LocalDataFlow.cs:215:15:215:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:214:20:214:39 | call to method ReadToEnd | LocalDataFlow.cs:214:9:214:39 | SSA def(nonSink0) | +| LocalDataFlow.cs:215:15:215:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:215:15:215:22 | access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:218:13:218:127 | SSA def(sink33) | LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | +| LocalDataFlow.cs:218:22:218:127 | (...) ... | LocalDataFlow.cs:218:13:218:127 | SSA def(sink33) | +| LocalDataFlow.cs:218:30:218:119 | call to method Insert | LocalDataFlow.cs:218:30:218:127 | call to method Clone | +| LocalDataFlow.cs:218:30:218:127 | call to method Clone | LocalDataFlow.cs:218:22:218:127 | (...) ... | +| LocalDataFlow.cs:219:15:219:20 | [post] access to local variable sink33 | LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | +| LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | +| LocalDataFlow.cs:220:13:220:52 | SSA def(sink48) | LocalDataFlow.cs:221:15:221:20 | access to local variable sink48 | +| LocalDataFlow.cs:220:22:220:27 | [post] access to local variable sink33 | LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | +| LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | +| LocalDataFlow.cs:220:22:220:52 | call to method Remove | LocalDataFlow.cs:220:13:220:52 | SSA def(sink48) | +| LocalDataFlow.cs:224:9:224:127 | SSA def(nonSink0) | LocalDataFlow.cs:225:15:225:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:224:20:224:127 | (...) ... | LocalDataFlow.cs:224:9:224:127 | SSA def(nonSink0) | +| LocalDataFlow.cs:224:28:224:119 | call to method Insert | LocalDataFlow.cs:224:28:224:127 | call to method Clone | +| LocalDataFlow.cs:224:28:224:127 | call to method Clone | LocalDataFlow.cs:224:20:224:127 | (...) ... | +| LocalDataFlow.cs:225:15:225:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | +| LocalDataFlow.cs:225:15:225:22 | access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:13:226:57 | SSA def(nonSink15) | LocalDataFlow.cs:227:15:227:23 | access to local variable nonSink15 | +| LocalDataFlow.cs:226:25:226:32 | [post] access to local variable nonSink0 | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:25:226:57 | call to method Remove | LocalDataFlow.cs:226:13:226:57 | SSA def(nonSink15) | +| LocalDataFlow.cs:230:13:230:46 | SSA def(sink34) | LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | +| LocalDataFlow.cs:230:22:230:46 | object creation of type StringBuilder | LocalDataFlow.cs:230:13:230:46 | SSA def(sink34) | +| LocalDataFlow.cs:231:15:231:20 | [post] access to local variable sink34 | LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | +| LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | +| LocalDataFlow.cs:232:13:232:38 | SSA def(sink35) | LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | +| LocalDataFlow.cs:232:22:232:38 | call to method ToString | LocalDataFlow.cs:232:13:232:38 | SSA def(sink35) | +| LocalDataFlow.cs:233:15:233:20 | [post] access to local variable sink35 | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | +| LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | +| LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | +| LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | +| LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | +| LocalDataFlow.cs:240:15:240:23 | [post] access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | +| LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:9:241:39 | SSA def(nonSink0) | LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:241:20:241:28 | [post] access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:20:241:39 | call to method ToString | LocalDataFlow.cs:241:9:241:39 | SSA def(nonSink0) | +| LocalDataFlow.cs:242:15:242:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | +| LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | +| LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList | +| LocalDataFlow.cs:247:35:247:52 | object creation of type DataContract | LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | +| LocalDataFlow.cs:248:13:248:48 | SSA def(sink53) | LocalDataFlow.cs:249:15:249:20 | access to local variable sink53 | +| LocalDataFlow.cs:248:22:248:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:248:22:248:48 | access to property AString | LocalDataFlow.cs:248:13:248:48 | SSA def(sink53) | +| LocalDataFlow.cs:250:13:250:57 | SSA def(sink54) | LocalDataFlow.cs:251:15:251:20 | access to local variable sink54 | +| LocalDataFlow.cs:250:22:250:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:250:22:250:46 | [post] access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:250:22:250:46 | access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:250:22:250:57 | access to property AString | LocalDataFlow.cs:250:13:250:57 | SSA def(sink54) | +| LocalDataFlow.cs:254:13:254:55 | SSA def(nonTaintedDataContract) | LocalDataFlow.cs:255:20:255:41 | access to local variable nonTaintedDataContract | +| LocalDataFlow.cs:254:38:254:55 | object creation of type DataContract | LocalDataFlow.cs:254:13:254:55 | SSA def(nonTaintedDataContract) | +| LocalDataFlow.cs:255:9:255:49 | SSA def(nonSink0) | LocalDataFlow.cs:256:15:256:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:255:20:255:49 | access to property AString | LocalDataFlow.cs:255:9:255:49 | SSA def(nonSink0) | +| LocalDataFlow.cs:257:9:257:44 | SSA def(nonSink2) | LocalDataFlow.cs:258:15:258:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:257:20:257:38 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:257:20:257:44 | access to property AnInt | LocalDataFlow.cs:257:9:257:44 | SSA def(nonSink2) | +| LocalDataFlow.cs:259:9:259:53 | SSA def(nonSink2) | LocalDataFlow.cs:260:15:260:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:259:20:259:53 | access to property AnInt | LocalDataFlow.cs:259:9:259:53 | SSA def(nonSink2) | +| LocalDataFlow.cs:263:17:263:37 | SSA def(taintedTextBox) | LocalDataFlow.cs:264:22:264:35 | access to local variable taintedTextBox | +| LocalDataFlow.cs:263:34:263:37 | null | LocalDataFlow.cs:263:17:263:37 | SSA def(taintedTextBox) | +| LocalDataFlow.cs:264:13:264:40 | SSA def(sink60) | LocalDataFlow.cs:265:15:265:20 | access to local variable sink60 | +| LocalDataFlow.cs:264:22:264:40 | access to property Text | LocalDataFlow.cs:264:13:264:40 | SSA def(sink60) | +| LocalDataFlow.cs:268:17:268:40 | SSA def(nonTaintedTextBox) | LocalDataFlow.cs:269:20:269:36 | access to local variable nonTaintedTextBox | +| LocalDataFlow.cs:268:37:268:40 | null | LocalDataFlow.cs:268:17:268:40 | SSA def(nonTaintedTextBox) | +| LocalDataFlow.cs:269:9:269:41 | SSA def(nonSink0) | LocalDataFlow.cs:270:15:270:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:269:20:269:41 | access to property Text | LocalDataFlow.cs:269:9:269:41 | SSA def(nonSink0) | +| LocalDataFlow.cs:270:15:270:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:270:15:270:22 | access to local variable nonSink0 | LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | LocalDataFlow.cs:274:15:274:20 | access to local variable sink69 | +| LocalDataFlow.cs:273:22:273:36 | $"..." | LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | +| LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:277:20:277:37 | $"..." | LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | +| LocalDataFlow.cs:278:15:278:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) | LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | +| LocalDataFlow.cs:281:22:281:34 | ... = ... | LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) | +| LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | +| LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | +| LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | LocalDataFlow.cs:281:22:281:34 | ... = ... | +| LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | +| LocalDataFlow.cs:282:15:282:20 | [post] access to local variable sink70 | LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | +| LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | +| LocalDataFlow.cs:285:9:285:38 | SSA def(nonSink0) | LocalDataFlow.cs:286:15:286:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:285:20:285:38 | ... = ... | LocalDataFlow.cs:285:9:285:38 | SSA def(nonSink0) | +| LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | LocalDataFlow.cs:285:20:285:38 | ... = ... | +| LocalDataFlow.cs:286:15:286:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | +| LocalDataFlow.cs:286:15:286:22 | access to local variable nonSink0 | LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | +| LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | LocalDataFlow.cs:289:23:289:35 | SSA def(sink71) | +| LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | LocalDataFlow.cs:297:17:297:22 | access to local variable sink70 | +| LocalDataFlow.cs:289:23:289:35 | SSA def(sink71) | LocalDataFlow.cs:290:19:290:24 | access to local variable sink71 | +| LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | LocalDataFlow.cs:293:25:293:40 | SSA def(nonSink16) | +| LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | +| LocalDataFlow.cs:293:25:293:40 | SSA def(nonSink16) | LocalDataFlow.cs:294:19:294:27 | access to local variable nonSink16 | +| LocalDataFlow.cs:297:17:297:22 | access to local variable sink70 | LocalDataFlow.cs:299:18:299:30 | SSA def(sink72) | +| LocalDataFlow.cs:299:18:299:30 | SSA def(sink72) | LocalDataFlow.cs:300:23:300:28 | access to local variable sink72 | +| LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | LocalDataFlow.cs:307:18:307:33 | SSA def(nonSink17) | +| LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | +| LocalDataFlow.cs:307:18:307:33 | SSA def(nonSink17) | LocalDataFlow.cs:308:23:308:31 | access to local variable nonSink17 | +| LocalDataFlow.cs:313:13:313:38 | SSA def(sink73) | LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 | +| LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | LocalDataFlow.cs:313:22:313:38 | ... ?? ... | +| LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | LocalDataFlow.cs:314:31:314:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:313:22:313:38 | ... ?? ... | LocalDataFlow.cs:313:13:313:38 | SSA def(sink73) | +| LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | LocalDataFlow.cs:313:22:313:38 | ... ?? ... | +| LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | +| LocalDataFlow.cs:314:13:314:38 | SSA def(sink74) | LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 | +| LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | LocalDataFlow.cs:314:22:314:38 | ... ?? ... | +| LocalDataFlow.cs:314:22:314:38 | ... ?? ... | LocalDataFlow.cs:314:13:314:38 | SSA def(sink74) | +| LocalDataFlow.cs:314:31:314:38 | access to local variable nonSink0 | LocalDataFlow.cs:314:22:314:38 | ... ?? ... | +| LocalDataFlow.cs:334:28:334:30 | this | LocalDataFlow.cs:334:41:334:45 | this access | +| LocalDataFlow.cs:334:50:334:52 | this | LocalDataFlow.cs:334:56:334:60 | this access | +| LocalDataFlow.cs:334:50:334:52 | value | LocalDataFlow.cs:334:64:334:68 | access to parameter value | +| LocalDataFlow.cs:340:41:340:47 | tainted | LocalDataFlow.cs:342:15:342:21 | access to parameter tainted | +| LocalDataFlow.cs:345:44:345:53 | nonTainted | LocalDataFlow.cs:347:15:347:24 | access to parameter nonTainted | +| LocalDataFlow.cs:350:44:350:44 | x | LocalDataFlow.cs:353:21:353:21 | access to parameter x | +| LocalDataFlow.cs:350:67:350:68 | os | LocalDataFlow.cs:356:33:356:34 | access to parameter os | +| LocalDataFlow.cs:353:21:353:21 | access to parameter x | LocalDataFlow.cs:353:16:353:21 | ... = ... | +| LocalDataFlow.cs:356:33:356:34 | access to parameter os | LocalDataFlow.cs:356:27:356:34 | ... = ... | +| LocalDataFlow.cs:361:41:361:44 | args | LocalDataFlow.cs:363:29:363:32 | access to parameter args | +| LocalDataFlow.cs:363:29:363:32 | [post] access to parameter args | LocalDataFlow.cs:364:27:364:30 | access to parameter args | +| LocalDataFlow.cs:363:29:363:32 | access to parameter args | LocalDataFlow.cs:364:27:364:30 | access to parameter args | | SSA.cs:5:17:5:17 | SSA entry def(this.S) | SSA.cs:67:9:67:14 | access to field S | | SSA.cs:5:17:5:17 | this | SSA.cs:67:9:67:12 | this access | | SSA.cs:5:26:5:32 | tainted | SSA.cs:8:24:8:30 | access to parameter tainted | @@ -785,8 +761,12 @@ | Splitting.cs:51:13:51:36 | [b (line 46): true] SSA def(y) | Splitting.cs:52:9:52:9 | [b (line 46): true] access to local variable y | | Splitting.cs:51:17:51:36 | [b (line 46): false] array creation of type String[] | Splitting.cs:51:13:51:36 | [b (line 46): false] SSA def(y) | | Splitting.cs:51:17:51:36 | [b (line 46): true] array creation of type String[] | Splitting.cs:51:13:51:36 | [b (line 46): true] SSA def(y) | +| Splitting.cs:51:30:51:36 | [b (line 46): false] { ..., ... } | Splitting.cs:51:17:51:36 | [b (line 46): false] array creation of type String[] | +| Splitting.cs:51:30:51:36 | [b (line 46): true] { ..., ... } | Splitting.cs:51:17:51:36 | [b (line 46): true] array creation of type String[] | | Splitting.cs:52:9:52:9 | [b (line 46): false] access to local variable y | Splitting.cs:53:17:53:17 | [b (line 46): false] access to local variable y | | Splitting.cs:52:9:52:9 | [b (line 46): true] access to local variable y | Splitting.cs:53:17:53:17 | [b (line 46): true] access to local variable y | +| Splitting.cs:52:9:52:9 | [post] [b (line 46): false] access to local variable y | Splitting.cs:53:17:53:17 | [b (line 46): false] access to local variable y | +| Splitting.cs:52:9:52:9 | [post] [b (line 46): true] access to local variable y | Splitting.cs:53:17:53:17 | [b (line 46): true] access to local variable y | | Splitting.cs:53:9:53:20 | [b (line 46): false] SSA def(x) | Splitting.cs:54:17:54:17 | [b (line 46): false] access to local variable x | | Splitting.cs:53:9:53:20 | [b (line 46): true] SSA def(x) | Splitting.cs:54:17:54:17 | [b (line 46): true] access to local variable x | | Splitting.cs:53:13:53:20 | [b (line 46): false] ... + ... | Splitting.cs:53:9:53:20 | [b (line 46): false] SSA def(x) | diff --git a/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs b/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs index eea365281044..a5a1e34178fe 100644 --- a/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs @@ -5,7 +5,6 @@ using System.Collections.Specialized; using System.Linq; using System.Runtime.Serialization; -using System.Threading.Tasks; using System.Web; using System.Web.UI.WebControls; @@ -46,7 +45,7 @@ public sealed class DataMemberAttribute : Attribute { } /// public class LocalDataFlow { - public async void M(bool b) + public void M(bool b) { // Assignment, tainted var sink0 = "taint source"; @@ -128,7 +127,7 @@ public async void M(bool b) Check(sink49); var sink50 = String.Copy(sink49); Check(sink50); - var sink51 = String.Join(", ", "", sink50, ""); + var sink51 = String.Join(", ", new string[] { "", sink50, "" }); Check(sink51); var sink52 = "".Insert(0, sink51); Check(sink52); @@ -152,7 +151,7 @@ public async void M(bool b) Check(nonSink0); nonSink0 = String.Copy(nonSink0); Check(nonSink0); - nonSink0 = String.Join(", ", "", nonSink0, ""); + nonSink0 = String.Join(", ", new string[] { "", nonSink0, "" }); Check(nonSink0); nonSink0 = "".Insert(0, nonSink0); Check(nonSink0); @@ -218,13 +217,13 @@ public async void M(bool b) // Ad hoc tracking (System.String), tainted var sink33 = (string)sink32.Substring(0).ToLowerInvariant().ToUpper().Trim(' ').Replace("a", "b").Insert(0, "").Clone(); Check(sink33); - var sink48 = sink33.Normalize().Remove(4, 5).Split(' '); + var sink48 = sink33.Normalize().Remove(4, 5); Check(sink48); // Ad hoc tracking (System.String), not tainted nonSink0 = (string)nonSink0.Substring(0).ToLowerInvariant().ToUpper().Trim(' ').Replace("a", "b").Insert(0, "").Clone(); Check(nonSink0); - var nonSink15 = nonSink0.Normalize().Remove(4, 5).Split(' '); + var nonSink15 = nonSink0.Normalize().Remove(4, 5); Check(nonSink15); // Ad hoc tracking (System.Text.StringBuilder), tainted @@ -270,18 +269,6 @@ public async void M(bool b) nonSink0 = nonTaintedTextBox.Text; Check(nonSink0); - // async await, tainted - var sink67 = Task.Run(() => "taint source"); - Check(sink67); - var sink68 = await sink67; - Check(sink68); - - // async await, not tainted - var nonSink21 = Task.Run(() => ""); - Check(nonSink21); - nonSink0 = await nonSink21; - Check(nonSink0); - // Interpolated string, tainted var sink69 = $"test {sink1}"; Check(sink69); @@ -366,7 +353,7 @@ public void AssignmentFlow(IDisposable x, IEnumerable os) using (x1 = x) { } IEnumerable os2; - foreach(var o in os2 = os) { } + foreach (var o in os2 = os) { } } public static implicit operator LocalDataFlow(string[] args) => null; diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected index e8a0153dc92b..30a274af50df 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected @@ -1,51 +1,49 @@ -| LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | -| LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | -| LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | -| LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | -| LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | -| LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | -| LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | -| LocalDataFlow.cs:113:15:113:20 | access to local variable sink16 | -| LocalDataFlow.cs:115:15:115:20 | access to local variable sink17 | -| LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | -| LocalDataFlow.cs:119:15:119:20 | access to local variable sink19 | -| LocalDataFlow.cs:121:15:121:20 | access to local variable sink45 | -| LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | -| LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | -| LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | -| LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | -| LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | -| LocalDataFlow.cs:134:15:134:20 | access to local variable sink52 | -| LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | -| LocalDataFlow.cs:164:15:164:20 | access to local variable sink21 | -| LocalDataFlow.cs:166:15:166:20 | access to local variable sink22 | -| LocalDataFlow.cs:176:15:176:20 | access to local variable sink25 | -| LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | -| LocalDataFlow.cs:186:15:186:20 | access to local variable sink27 | -| LocalDataFlow.cs:188:15:188:20 | access to local variable sink28 | -| LocalDataFlow.cs:190:15:190:20 | access to local variable sink29 | -| LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | -| LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | -| LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | -| LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | -| LocalDataFlow.cs:222:15:222:20 | access to local variable sink48 | -| LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | -| LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | -| LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:250:15:250:20 | access to local variable sink53 | -| LocalDataFlow.cs:252:15:252:20 | access to local variable sink54 | -| LocalDataFlow.cs:266:15:266:20 | access to local variable sink60 | -| LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | -| LocalDataFlow.cs:277:15:277:20 | access to local variable sink68 | -| LocalDataFlow.cs:287:15:287:20 | access to local variable sink69 | -| LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | -| LocalDataFlow.cs:303:19:303:24 | access to local variable sink71 | -| LocalDataFlow.cs:313:23:313:28 | access to local variable sink72 | -| LocalDataFlow.cs:328:15:328:20 | access to local variable sink73 | -| LocalDataFlow.cs:329:15:329:20 | access to local variable sink74 | -| LocalDataFlow.cs:355:15:355:21 | access to parameter tainted | +| LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | +| LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | +| LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | +| LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | +| LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | +| LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | +| LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | +| LocalDataFlow.cs:112:15:112:20 | access to local variable sink16 | +| LocalDataFlow.cs:114:15:114:20 | access to local variable sink17 | +| LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | +| LocalDataFlow.cs:118:15:118:20 | access to local variable sink19 | +| LocalDataFlow.cs:120:15:120:20 | access to local variable sink45 | +| LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | +| LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | +| LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | +| LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | +| LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | +| LocalDataFlow.cs:133:15:133:20 | access to local variable sink52 | +| LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | +| LocalDataFlow.cs:163:15:163:20 | access to local variable sink21 | +| LocalDataFlow.cs:165:15:165:20 | access to local variable sink22 | +| LocalDataFlow.cs:175:15:175:20 | access to local variable sink25 | +| LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | +| LocalDataFlow.cs:185:15:185:20 | access to local variable sink27 | +| LocalDataFlow.cs:187:15:187:20 | access to local variable sink28 | +| LocalDataFlow.cs:189:15:189:20 | access to local variable sink29 | +| LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | +| LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | +| LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | +| LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | +| LocalDataFlow.cs:221:15:221:20 | access to local variable sink48 | +| LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | +| LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | +| LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:249:15:249:20 | access to local variable sink53 | +| LocalDataFlow.cs:251:15:251:20 | access to local variable sink54 | +| LocalDataFlow.cs:265:15:265:20 | access to local variable sink60 | +| LocalDataFlow.cs:274:15:274:20 | access to local variable sink69 | +| LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | +| LocalDataFlow.cs:290:19:290:24 | access to local variable sink71 | +| LocalDataFlow.cs:300:23:300:28 | access to local variable sink72 | +| LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 | +| LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 | +| LocalDataFlow.cs:342:15:342:21 | access to parameter tainted | | SSA.cs:9:15:9:22 | access to local variable ssaSink0 | | SSA.cs:25:15:25:22 | access to local variable ssaSink1 | | SSA.cs:43:15:43:22 | access to local variable ssaSink2 | diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected index 77195b49f3de..fd781d984dd4 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected @@ -1,7 +1,4 @@ | Capture.cs:5:17:5:17 | this | Capture.cs:13:9:13:14 | this access | -| Capture.cs:7:13:7:17 | SSA def(i) | Capture.cs:13:9:13:16 | [implicit argument] i | -| Capture.cs:7:13:7:17 | SSA def(i) | Capture.cs:23:9:23:16 | [implicit argument] i | -| Capture.cs:7:13:7:17 | SSA def(i) | Capture.cs:34:9:34:16 | [implicit argument] i | | Capture.cs:7:17:7:17 | 0 | Capture.cs:7:13:7:17 | SSA def(i) | | Capture.cs:9:9:12:9 | SSA capture def(i) | Capture.cs:11:17:11:17 | access to local variable i | | Capture.cs:13:9:13:14 | this access | Capture.cs:23:9:23:14 | this access | @@ -10,7 +7,6 @@ | Capture.cs:23:9:23:14 | this access | Capture.cs:34:9:34:14 | this access | | Capture.cs:25:9:33:9 | this | Capture.cs:32:13:32:18 | this access | | Capture.cs:27:13:30:13 | SSA capture def(i) | Capture.cs:29:21:29:21 | access to local variable i | -| Capture.cs:31:13:31:17 | SSA def(i) | Capture.cs:32:13:32:20 | [implicit argument] i | | Capture.cs:31:17:31:17 | 1 | Capture.cs:31:13:31:17 | SSA def(i) | | Capture.cs:34:9:34:14 | this access | Capture.cs:40:9:40:15 | this access | | Capture.cs:38:17:38:17 | 0 | Capture.cs:38:13:38:17 | SSA def(i) | @@ -24,622 +20,505 @@ | Capture.cs:58:21:58:21 | 1 | Capture.cs:58:17:58:21 | SSA def(i) | | Capture.cs:61:17:61:17 | 1 | Capture.cs:61:13:61:17 | SSA def(i) | | Capture.cs:63:9:63:17 | SSA call def(i) | Capture.cs:64:13:64:13 | access to local variable i | -| LocalDataFlow.cs:49:30:49:30 | b | LocalDataFlow.cs:85:21:85:21 | access to parameter b | -| LocalDataFlow.cs:52:13:52:34 | SSA def(sink0) | LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | -| LocalDataFlow.cs:52:21:52:34 | "taint source" | LocalDataFlow.cs:52:13:52:34 | SSA def(sink0) | -| LocalDataFlow.cs:53:15:53:19 | [post] access to local variable sink0 | LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | -| LocalDataFlow.cs:53:15:53:19 | access to local variable sink0 | LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | -| LocalDataFlow.cs:56:13:56:25 | SSA def(nonSink0) | LocalDataFlow.cs:57:15:57:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:56:24:56:25 | "" | LocalDataFlow.cs:56:13:56:25 | SSA def(nonSink0) | -| LocalDataFlow.cs:57:15:57:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | -| LocalDataFlow.cs:57:15:57:22 | access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | -| LocalDataFlow.cs:60:13:60:25 | SSA def(sink1) | LocalDataFlow.cs:61:9:61:13 | access to local variable sink1 | -| LocalDataFlow.cs:60:21:60:25 | "abc" | LocalDataFlow.cs:60:13:60:25 | SSA def(sink1) | -| LocalDataFlow.cs:61:9:61:13 | access to local variable sink1 | LocalDataFlow.cs:61:9:61:22 | ... + ... | -| LocalDataFlow.cs:61:9:61:22 | ... + ... | LocalDataFlow.cs:61:9:61:22 | SSA def(sink1) | -| LocalDataFlow.cs:61:9:61:22 | SSA def(sink1) | LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | -| LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | LocalDataFlow.cs:61:9:61:22 | ... + ... | -| LocalDataFlow.cs:61:18:61:22 | access to local variable sink0 | LocalDataFlow.cs:169:20:169:24 | access to local variable sink0 | -| LocalDataFlow.cs:62:15:62:19 | [post] access to local variable sink1 | LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | -| LocalDataFlow.cs:62:15:62:19 | access to local variable sink1 | LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | -| LocalDataFlow.cs:65:9:65:16 | access to local variable nonSink0 | LocalDataFlow.cs:65:9:65:25 | ... + ... | -| LocalDataFlow.cs:65:9:65:25 | ... + ... | LocalDataFlow.cs:65:9:65:25 | SSA def(nonSink0) | -| LocalDataFlow.cs:65:9:65:25 | SSA def(nonSink0) | LocalDataFlow.cs:66:15:66:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:65:21:65:25 | "abc" | LocalDataFlow.cs:65:9:65:25 | ... + ... | -| LocalDataFlow.cs:66:15:66:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:66:15:66:22 | access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:69:13:69:32 | SSA def(sink5) | LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | -| LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | LocalDataFlow.cs:69:21:69:32 | ... + ... | -| LocalDataFlow.cs:69:21:69:25 | access to local variable sink1 | LocalDataFlow.cs:169:33:169:37 | access to local variable sink1 | -| LocalDataFlow.cs:69:21:69:32 | ... + ... | LocalDataFlow.cs:69:13:69:32 | SSA def(sink5) | -| LocalDataFlow.cs:69:29:69:32 | "ok" | LocalDataFlow.cs:69:21:69:32 | ... + ... | -| LocalDataFlow.cs:70:15:70:19 | [post] access to local variable sink5 | LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | -| LocalDataFlow.cs:70:15:70:19 | access to local variable sink5 | LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | -| LocalDataFlow.cs:73:9:73:36 | SSA def(nonSink0) | LocalDataFlow.cs:74:15:74:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:73:20:73:27 | access to local variable nonSink0 | LocalDataFlow.cs:73:20:73:36 | ... + ... | -| LocalDataFlow.cs:73:20:73:36 | ... + ... | LocalDataFlow.cs:73:9:73:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:73:31:73:36 | "test" | LocalDataFlow.cs:73:20:73:36 | ... + ... | -| LocalDataFlow.cs:74:15:74:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | -| LocalDataFlow.cs:74:15:74:22 | access to local variable nonSink0 | LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | -| LocalDataFlow.cs:77:13:77:27 | SSA def(sink6) | LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | -| LocalDataFlow.cs:77:22:77:26 | access to local variable sink5 | LocalDataFlow.cs:77:13:77:27 | SSA def(sink6) | -| LocalDataFlow.cs:78:15:78:19 | [post] access to local variable sink6 | LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | -| LocalDataFlow.cs:78:15:78:19 | access to local variable sink6 | LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | -| LocalDataFlow.cs:81:9:81:29 | SSA def(nonSink0) | LocalDataFlow.cs:82:15:82:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:81:21:81:28 | access to local variable nonSink0 | LocalDataFlow.cs:81:9:81:29 | SSA def(nonSink0) | -| LocalDataFlow.cs:85:13:85:35 | [b (line 49): false] SSA def(sink7) | LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | -| LocalDataFlow.cs:85:13:85:35 | [b (line 49): true] SSA def(sink7) | LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | -| LocalDataFlow.cs:85:21:85:21 | access to parameter b | LocalDataFlow.cs:89:20:89:20 | [b (line 49): false] access to parameter b | -| LocalDataFlow.cs:85:21:85:21 | access to parameter b | LocalDataFlow.cs:89:20:89:20 | [b (line 49): true] access to parameter b | -| LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | LocalDataFlow.cs:85:13:85:35 | [b (line 49): false] SSA def(sink7) | -| LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | LocalDataFlow.cs:85:13:85:35 | [b (line 49): true] SSA def(sink7) | -| LocalDataFlow.cs:85:25:85:27 | [b (line 49): true] "a" | LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | -| LocalDataFlow.cs:85:31:85:35 | [b (line 49): false] access to local variable sink6 | LocalDataFlow.cs:85:21:85:35 | ... ? ... : ... | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): false] access to local variable sink7 | LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | -| LocalDataFlow.cs:86:15:86:19 | [b (line 49): true] access to local variable sink7 | LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | -| LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | LocalDataFlow.cs:90:15:90:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:89:9:89:36 | SSA phi(sink7) | LocalDataFlow.cs:93:29:93:33 | access to local variable sink7 | -| LocalDataFlow.cs:89:20:89:36 | [b (line 49): false] ... ? ... : ... | LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:89:20:89:36 | [b (line 49): true] ... ? ... : ... | LocalDataFlow.cs:89:9:89:36 | SSA def(nonSink0) | -| LocalDataFlow.cs:89:24:89:28 | "abc" | LocalDataFlow.cs:89:20:89:36 | [b (line 49): true] ... ? ... : ... | -| LocalDataFlow.cs:89:32:89:36 | "def" | LocalDataFlow.cs:89:20:89:36 | [b (line 49): false] ... ? ... : ... | -| LocalDataFlow.cs:90:15:90:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:90:15:90:22 | access to local variable nonSink0 | LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:93:13:93:33 | SSA def(sink8) | LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | -| LocalDataFlow.cs:93:21:93:33 | (...) ... | LocalDataFlow.cs:93:13:93:33 | SSA def(sink8) | -| LocalDataFlow.cs:93:29:93:33 | access to local variable sink7 | LocalDataFlow.cs:93:21:93:33 | (...) ... | -| LocalDataFlow.cs:94:15:94:19 | [post] access to local variable sink8 | LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | -| LocalDataFlow.cs:94:15:94:19 | access to local variable sink8 | LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | -| LocalDataFlow.cs:97:13:97:39 | SSA def(nonSink3) | LocalDataFlow.cs:98:15:98:22 | access to local variable nonSink3 | -| LocalDataFlow.cs:97:24:97:39 | (...) ... | LocalDataFlow.cs:97:13:97:39 | SSA def(nonSink3) | -| LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | LocalDataFlow.cs:97:24:97:39 | (...) ... | -| LocalDataFlow.cs:97:32:97:39 | access to local variable nonSink0 | LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:101:13:101:35 | SSA def(sink9) | LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | -| LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | LocalDataFlow.cs:101:21:101:35 | ... as ... | -| LocalDataFlow.cs:101:21:101:25 | access to local variable sink8 | LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | -| LocalDataFlow.cs:101:21:101:35 | ... as ... | LocalDataFlow.cs:101:13:101:35 | SSA def(sink9) | -| LocalDataFlow.cs:102:15:102:19 | [post] access to local variable sink9 | LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | -| LocalDataFlow.cs:102:15:102:19 | access to local variable sink9 | LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | -| LocalDataFlow.cs:105:9:105:37 | SSA def(nonSink3) | LocalDataFlow.cs:106:15:106:22 | access to local variable nonSink3 | -| LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | LocalDataFlow.cs:105:20:105:37 | ... as ... | -| LocalDataFlow.cs:105:20:105:27 | access to local variable nonSink0 | LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | -| LocalDataFlow.cs:105:20:105:37 | ... as ... | LocalDataFlow.cs:105:9:105:37 | SSA def(nonSink3) | -| LocalDataFlow.cs:106:15:106:22 | [post] access to local variable nonSink3 | LocalDataFlow.cs:171:33:171:40 | access to local variable nonSink3 | -| LocalDataFlow.cs:106:15:106:22 | access to local variable nonSink3 | LocalDataFlow.cs:171:33:171:40 | access to local variable nonSink3 | -| LocalDataFlow.cs:109:13:109:39 | SSA def(sink15) | LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | -| LocalDataFlow.cs:109:22:109:39 | [library code] call to method Parse | LocalDataFlow.cs:109:22:109:39 | call to method Parse | -| LocalDataFlow.cs:109:22:109:39 | call to method Parse | LocalDataFlow.cs:109:13:109:39 | SSA def(sink15) | -| LocalDataFlow.cs:109:34:109:38 | [post] access to local variable sink9 | LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | -| LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | LocalDataFlow.cs:109:22:109:39 | [library code] call to method Parse | -| LocalDataFlow.cs:109:34:109:38 | access to local variable sink9 | LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | -| LocalDataFlow.cs:110:15:110:20 | access to local variable sink15 | LocalDataFlow.cs:161:22:161:27 | access to local variable sink15 | -| LocalDataFlow.cs:112:13:112:56 | SSA def(sink16) | LocalDataFlow.cs:113:15:113:20 | access to local variable sink16 | -| LocalDataFlow.cs:112:22:112:56 | [library code] call to method TryParse | LocalDataFlow.cs:112:22:112:56 | call to method TryParse | -| LocalDataFlow.cs:112:22:112:56 | call to method TryParse | LocalDataFlow.cs:112:13:112:56 | SSA def(sink16) | -| LocalDataFlow.cs:112:37:112:41 | [post] access to local variable sink9 | LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | -| LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | LocalDataFlow.cs:112:22:112:56 | [library code] call to method TryParse | -| LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | LocalDataFlow.cs:112:22:112:56 | [library code] call to method TryParse | -| LocalDataFlow.cs:112:37:112:41 | access to local variable sink9 | LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | -| LocalDataFlow.cs:114:13:114:49 | SSA def(sink17) | LocalDataFlow.cs:115:15:115:20 | access to local variable sink17 | -| LocalDataFlow.cs:114:22:114:29 | [post] access to local variable nonSink0 | LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | -| LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | LocalDataFlow.cs:114:22:114:49 | [library code] call to method Replace | -| LocalDataFlow.cs:114:22:114:29 | access to local variable nonSink0 | LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | -| LocalDataFlow.cs:114:22:114:49 | [library code] call to method Replace | LocalDataFlow.cs:114:22:114:49 | call to method Replace | -| LocalDataFlow.cs:114:22:114:49 | [library code] call to method Replace | LocalDataFlow.cs:114:22:114:49 | call to method Replace | -| LocalDataFlow.cs:114:22:114:49 | call to method Replace | LocalDataFlow.cs:114:13:114:49 | SSA def(sink17) | -| LocalDataFlow.cs:114:44:114:48 | [post] access to local variable sink9 | LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | -| LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | LocalDataFlow.cs:114:22:114:49 | [library code] call to method Replace | -| LocalDataFlow.cs:114:44:114:48 | access to local variable sink9 | LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | -| LocalDataFlow.cs:116:13:116:51 | SSA def(sink18) | LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | -| LocalDataFlow.cs:116:22:116:51 | [library code] call to method Format | LocalDataFlow.cs:116:22:116:51 | call to method Format | -| LocalDataFlow.cs:116:22:116:51 | [library code] call to method Format | LocalDataFlow.cs:116:22:116:51 | call to method Format | -| LocalDataFlow.cs:116:22:116:51 | call to method Format | LocalDataFlow.cs:116:13:116:51 | SSA def(sink18) | -| LocalDataFlow.cs:116:36:116:43 | [post] access to local variable nonSink0 | LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | LocalDataFlow.cs:116:22:116:51 | [library code] call to method Format | -| LocalDataFlow.cs:116:36:116:43 | access to local variable nonSink0 | LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:116:46:116:50 | [post] access to local variable sink9 | LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | -| LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | LocalDataFlow.cs:116:22:116:51 | [library code] call to method Format | -| LocalDataFlow.cs:116:46:116:50 | access to local variable sink9 | LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | -| LocalDataFlow.cs:117:15:117:20 | [post] access to local variable sink18 | LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | -| LocalDataFlow.cs:117:15:117:20 | access to local variable sink18 | LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | -| LocalDataFlow.cs:118:13:118:52 | SSA def(sink19) | LocalDataFlow.cs:119:15:119:20 | access to local variable sink19 | -| LocalDataFlow.cs:118:22:118:52 | [library code] call to method Format | LocalDataFlow.cs:118:22:118:52 | call to method Format | -| LocalDataFlow.cs:118:22:118:52 | [library code] call to method Format | LocalDataFlow.cs:118:22:118:52 | call to method Format | -| LocalDataFlow.cs:118:22:118:52 | call to method Format | LocalDataFlow.cs:118:13:118:52 | SSA def(sink19) | -| LocalDataFlow.cs:118:36:118:41 | access to local variable sink18 | LocalDataFlow.cs:118:22:118:52 | [library code] call to method Format | -| LocalDataFlow.cs:118:44:118:51 | [post] access to local variable nonSink0 | LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | LocalDataFlow.cs:118:22:118:52 | [library code] call to method Format | -| LocalDataFlow.cs:118:44:118:51 | access to local variable nonSink0 | LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:120:13:120:38 | SSA def(sink45) | LocalDataFlow.cs:121:15:121:20 | access to local variable sink45 | -| LocalDataFlow.cs:120:22:120:38 | [library code] call to method Parse | LocalDataFlow.cs:120:22:120:38 | call to method Parse | -| LocalDataFlow.cs:120:22:120:38 | call to method Parse | LocalDataFlow.cs:120:13:120:38 | SSA def(sink45) | -| LocalDataFlow.cs:120:33:120:37 | [post] access to local variable sink9 | LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | -| LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | LocalDataFlow.cs:120:22:120:38 | [library code] call to method Parse | -| LocalDataFlow.cs:120:33:120:37 | access to local variable sink9 | LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | -| LocalDataFlow.cs:123:13:123:56 | SSA def(sink46) | LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | -| LocalDataFlow.cs:123:22:123:56 | [library code] call to method TryParse | LocalDataFlow.cs:123:22:123:56 | call to method TryParse | -| LocalDataFlow.cs:123:22:123:56 | call to method TryParse | LocalDataFlow.cs:123:13:123:56 | SSA def(sink46) | -| LocalDataFlow.cs:123:36:123:40 | [post] access to local variable sink9 | LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | -| LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | LocalDataFlow.cs:123:22:123:56 | [library code] call to method TryParse | -| LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | LocalDataFlow.cs:123:22:123:56 | [library code] call to method TryParse | -| LocalDataFlow.cs:123:36:123:40 | access to local variable sink9 | LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | -| LocalDataFlow.cs:124:15:124:20 | access to local variable sink46 | LocalDataFlow.cs:125:37:125:42 | access to local variable sink46 | -| LocalDataFlow.cs:125:13:125:43 | SSA def(sink47) | LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | -| LocalDataFlow.cs:125:22:125:43 | [library code] call to method ToByte | LocalDataFlow.cs:125:22:125:43 | call to method ToByte | -| LocalDataFlow.cs:125:22:125:43 | call to method ToByte | LocalDataFlow.cs:125:13:125:43 | SSA def(sink47) | -| LocalDataFlow.cs:125:37:125:42 | access to local variable sink46 | LocalDataFlow.cs:125:22:125:43 | [library code] call to method ToByte | -| LocalDataFlow.cs:126:15:126:20 | access to local variable sink47 | LocalDataFlow.cs:127:40:127:45 | access to local variable sink47 | -| LocalDataFlow.cs:127:13:127:46 | SSA def(sink49) | LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | -| LocalDataFlow.cs:127:22:127:46 | [library code] call to method Concat | LocalDataFlow.cs:127:22:127:46 | call to method Concat | -| LocalDataFlow.cs:127:22:127:46 | [library code] call to method Concat | LocalDataFlow.cs:127:22:127:46 | call to method Concat | -| LocalDataFlow.cs:127:22:127:46 | call to method Concat | LocalDataFlow.cs:127:13:127:46 | SSA def(sink49) | -| LocalDataFlow.cs:127:36:127:37 | "" | LocalDataFlow.cs:127:22:127:46 | [library code] call to method Concat | -| LocalDataFlow.cs:127:40:127:45 | (...) ... | LocalDataFlow.cs:127:22:127:46 | [library code] call to method Concat | -| LocalDataFlow.cs:127:40:127:45 | access to local variable sink47 | LocalDataFlow.cs:127:40:127:45 | (...) ... | -| LocalDataFlow.cs:128:15:128:20 | [post] access to local variable sink49 | LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | -| LocalDataFlow.cs:128:15:128:20 | access to local variable sink49 | LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | -| LocalDataFlow.cs:129:13:129:40 | SSA def(sink50) | LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | -| LocalDataFlow.cs:129:22:129:40 | [library code] call to method Copy | LocalDataFlow.cs:129:22:129:40 | call to method Copy | -| LocalDataFlow.cs:129:22:129:40 | call to method Copy | LocalDataFlow.cs:129:13:129:40 | SSA def(sink50) | -| LocalDataFlow.cs:129:34:129:39 | access to local variable sink49 | LocalDataFlow.cs:129:22:129:40 | [library code] call to method Copy | -| LocalDataFlow.cs:130:15:130:20 | [post] access to local variable sink50 | LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | -| LocalDataFlow.cs:130:15:130:20 | access to local variable sink50 | LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | -| LocalDataFlow.cs:131:13:131:54 | SSA def(sink51) | LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | -| LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | LocalDataFlow.cs:131:22:131:54 | call to method Join | -| LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | LocalDataFlow.cs:131:22:131:54 | call to method Join | -| LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | LocalDataFlow.cs:131:22:131:54 | call to method Join | -| LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | LocalDataFlow.cs:131:22:131:54 | call to method Join | -| LocalDataFlow.cs:131:22:131:54 | call to method Join | LocalDataFlow.cs:131:13:131:54 | SSA def(sink51) | -| LocalDataFlow.cs:131:34:131:37 | ", " | LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | -| LocalDataFlow.cs:131:40:131:41 | "" | LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | -| LocalDataFlow.cs:131:44:131:49 | access to local variable sink50 | LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | -| LocalDataFlow.cs:131:52:131:53 | "" | LocalDataFlow.cs:131:22:131:54 | [library code] call to method Join | -| LocalDataFlow.cs:132:15:132:20 | [post] access to local variable sink51 | LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | -| LocalDataFlow.cs:132:15:132:20 | access to local variable sink51 | LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | -| LocalDataFlow.cs:133:13:133:41 | SSA def(sink52) | LocalDataFlow.cs:134:15:134:20 | access to local variable sink52 | -| LocalDataFlow.cs:133:22:133:23 | "" | LocalDataFlow.cs:133:22:133:41 | [library code] call to method Insert | -| LocalDataFlow.cs:133:22:133:41 | [library code] call to method Insert | LocalDataFlow.cs:133:22:133:41 | call to method Insert | -| LocalDataFlow.cs:133:22:133:41 | [library code] call to method Insert | LocalDataFlow.cs:133:22:133:41 | call to method Insert | -| LocalDataFlow.cs:133:22:133:41 | call to method Insert | LocalDataFlow.cs:133:13:133:41 | SSA def(sink52) | -| LocalDataFlow.cs:133:35:133:40 | access to local variable sink51 | LocalDataFlow.cs:133:22:133:41 | [library code] call to method Insert | -| LocalDataFlow.cs:137:9:137:40 | SSA def(nonSink2) | LocalDataFlow.cs:138:15:138:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:137:20:137:40 | [library code] call to method Parse | LocalDataFlow.cs:137:20:137:40 | call to method Parse | -| LocalDataFlow.cs:137:20:137:40 | call to method Parse | LocalDataFlow.cs:137:9:137:40 | SSA def(nonSink2) | -| LocalDataFlow.cs:137:32:137:39 | [post] access to local variable nonSink0 | LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | LocalDataFlow.cs:137:20:137:40 | [library code] call to method Parse | -| LocalDataFlow.cs:137:32:137:39 | access to local variable nonSink0 | LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:139:13:139:61 | SSA def(nonSink7) | LocalDataFlow.cs:140:15:140:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:139:24:139:61 | [library code] call to method TryParse | LocalDataFlow.cs:139:24:139:61 | call to method TryParse | -| LocalDataFlow.cs:139:24:139:61 | call to method TryParse | LocalDataFlow.cs:139:13:139:61 | SSA def(nonSink7) | -| LocalDataFlow.cs:139:39:139:46 | [post] access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | LocalDataFlow.cs:139:24:139:61 | [library code] call to method TryParse | -| LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | LocalDataFlow.cs:139:24:139:61 | [library code] call to method TryParse | -| LocalDataFlow.cs:139:39:139:46 | access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:9:141:50 | SSA def(nonSink0) | LocalDataFlow.cs:142:15:142:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:27 | [post] access to local variable nonSink0 | LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:50 | [library code] call to method Replace | -| LocalDataFlow.cs:141:20:141:27 | access to local variable nonSink0 | LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:141:20:141:50 | [library code] call to method Replace | LocalDataFlow.cs:141:20:141:50 | call to method Replace | -| LocalDataFlow.cs:141:20:141:50 | [library code] call to method Replace | LocalDataFlow.cs:141:20:141:50 | call to method Replace | -| LocalDataFlow.cs:141:20:141:50 | call to method Replace | LocalDataFlow.cs:141:9:141:50 | SSA def(nonSink0) | -| LocalDataFlow.cs:141:42:141:49 | access to local variable nonSink0 | LocalDataFlow.cs:141:20:141:50 | [library code] call to method Replace | -| LocalDataFlow.cs:142:15:142:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:142:15:142:22 | access to local variable nonSink0 | LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:9:143:52 | SSA def(nonSink0) | LocalDataFlow.cs:144:15:144:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:20:143:52 | [library code] call to method Format | LocalDataFlow.cs:143:20:143:52 | call to method Format | -| LocalDataFlow.cs:143:20:143:52 | [library code] call to method Format | LocalDataFlow.cs:143:20:143:52 | call to method Format | -| LocalDataFlow.cs:143:20:143:52 | call to method Format | LocalDataFlow.cs:143:9:143:52 | SSA def(nonSink0) | -| LocalDataFlow.cs:143:34:143:41 | [post] access to local variable nonSink0 | LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | LocalDataFlow.cs:143:20:143:52 | [library code] call to method Format | -| LocalDataFlow.cs:143:34:143:41 | access to local variable nonSink0 | LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | -| LocalDataFlow.cs:143:44:143:51 | access to local variable nonSink0 | LocalDataFlow.cs:143:20:143:52 | [library code] call to method Format | -| LocalDataFlow.cs:144:15:144:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:144:15:144:22 | access to local variable nonSink0 | LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:145:9:145:39 | SSA def(nonSink7) | LocalDataFlow.cs:146:15:146:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:145:20:145:39 | [library code] call to method Parse | LocalDataFlow.cs:145:20:145:39 | call to method Parse | -| LocalDataFlow.cs:145:20:145:39 | call to method Parse | LocalDataFlow.cs:145:9:145:39 | SSA def(nonSink7) | -| LocalDataFlow.cs:145:31:145:38 | [post] access to local variable nonSink0 | LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | LocalDataFlow.cs:145:20:145:39 | [library code] call to method Parse | -| LocalDataFlow.cs:145:31:145:38 | access to local variable nonSink0 | LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | -| LocalDataFlow.cs:147:9:147:57 | SSA def(nonSink7) | LocalDataFlow.cs:148:15:148:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:147:20:147:57 | [library code] call to method TryParse | LocalDataFlow.cs:147:20:147:57 | call to method TryParse | -| LocalDataFlow.cs:147:20:147:57 | call to method TryParse | LocalDataFlow.cs:147:9:147:57 | SSA def(nonSink7) | -| LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | LocalDataFlow.cs:147:20:147:57 | [library code] call to method TryParse | -| LocalDataFlow.cs:147:34:147:41 | access to local variable nonSink0 | LocalDataFlow.cs:147:20:147:57 | [library code] call to method TryParse | -| LocalDataFlow.cs:148:15:148:22 | access to local variable nonSink7 | LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | -| LocalDataFlow.cs:149:13:149:48 | SSA def(nonSink14) | LocalDataFlow.cs:150:15:150:23 | access to local variable nonSink14 | -| LocalDataFlow.cs:149:25:149:48 | [library code] call to method ToByte | LocalDataFlow.cs:149:25:149:48 | call to method ToByte | -| LocalDataFlow.cs:149:25:149:48 | call to method ToByte | LocalDataFlow.cs:149:13:149:48 | SSA def(nonSink14) | -| LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | LocalDataFlow.cs:149:25:149:48 | [library code] call to method ToByte | -| LocalDataFlow.cs:149:40:149:47 | access to local variable nonSink7 | LocalDataFlow.cs:151:38:151:45 | access to local variable nonSink7 | -| LocalDataFlow.cs:151:9:151:46 | SSA def(nonSink0) | LocalDataFlow.cs:152:15:152:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:151:20:151:46 | [library code] call to method Concat | LocalDataFlow.cs:151:20:151:46 | call to method Concat | -| LocalDataFlow.cs:151:20:151:46 | [library code] call to method Concat | LocalDataFlow.cs:151:20:151:46 | call to method Concat | -| LocalDataFlow.cs:151:20:151:46 | call to method Concat | LocalDataFlow.cs:151:9:151:46 | SSA def(nonSink0) | -| LocalDataFlow.cs:151:34:151:35 | "" | LocalDataFlow.cs:151:20:151:46 | [library code] call to method Concat | -| LocalDataFlow.cs:151:38:151:45 | (...) ... | LocalDataFlow.cs:151:20:151:46 | [library code] call to method Concat | -| LocalDataFlow.cs:151:38:151:45 | access to local variable nonSink7 | LocalDataFlow.cs:151:38:151:45 | (...) ... | -| LocalDataFlow.cs:152:15:152:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:152:15:152:22 | access to local variable nonSink0 | LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | -| LocalDataFlow.cs:153:9:153:40 | SSA def(nonSink0) | LocalDataFlow.cs:154:15:154:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:153:20:153:40 | [library code] call to method Copy | LocalDataFlow.cs:153:20:153:40 | call to method Copy | -| LocalDataFlow.cs:153:20:153:40 | call to method Copy | LocalDataFlow.cs:153:9:153:40 | SSA def(nonSink0) | -| LocalDataFlow.cs:153:32:153:39 | access to local variable nonSink0 | LocalDataFlow.cs:153:20:153:40 | [library code] call to method Copy | -| LocalDataFlow.cs:154:15:154:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:154:15:154:22 | access to local variable nonSink0 | LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | -| LocalDataFlow.cs:155:9:155:54 | SSA def(nonSink0) | LocalDataFlow.cs:156:15:156:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | LocalDataFlow.cs:155:20:155:54 | call to method Join | -| LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | LocalDataFlow.cs:155:20:155:54 | call to method Join | -| LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | LocalDataFlow.cs:155:20:155:54 | call to method Join | -| LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | LocalDataFlow.cs:155:20:155:54 | call to method Join | -| LocalDataFlow.cs:155:20:155:54 | call to method Join | LocalDataFlow.cs:155:9:155:54 | SSA def(nonSink0) | -| LocalDataFlow.cs:155:32:155:35 | ", " | LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | -| LocalDataFlow.cs:155:38:155:39 | "" | LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | -| LocalDataFlow.cs:155:42:155:49 | access to local variable nonSink0 | LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | -| LocalDataFlow.cs:155:52:155:53 | "" | LocalDataFlow.cs:155:20:155:54 | [library code] call to method Join | -| LocalDataFlow.cs:156:15:156:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | -| LocalDataFlow.cs:156:15:156:22 | access to local variable nonSink0 | LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | -| LocalDataFlow.cs:157:9:157:41 | SSA def(nonSink0) | LocalDataFlow.cs:158:15:158:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:157:20:157:21 | "" | LocalDataFlow.cs:157:20:157:41 | [library code] call to method Insert | -| LocalDataFlow.cs:157:20:157:41 | [library code] call to method Insert | LocalDataFlow.cs:157:20:157:41 | call to method Insert | -| LocalDataFlow.cs:157:20:157:41 | [library code] call to method Insert | LocalDataFlow.cs:157:20:157:41 | call to method Insert | -| LocalDataFlow.cs:157:20:157:41 | call to method Insert | LocalDataFlow.cs:157:9:157:41 | SSA def(nonSink0) | -| LocalDataFlow.cs:157:33:157:40 | access to local variable nonSink0 | LocalDataFlow.cs:157:20:157:41 | [library code] call to method Insert | -| LocalDataFlow.cs:158:15:158:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:158:15:158:22 | access to local variable nonSink0 | LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | -| LocalDataFlow.cs:161:13:161:32 | SSA def(sink20) | LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | -| LocalDataFlow.cs:161:22:161:27 | access to local variable sink15 | LocalDataFlow.cs:161:22:161:32 | ... > ... | -| LocalDataFlow.cs:161:22:161:32 | ... > ... | LocalDataFlow.cs:161:13:161:32 | SSA def(sink20) | -| LocalDataFlow.cs:162:15:162:20 | access to local variable sink20 | LocalDataFlow.cs:175:22:175:27 | access to local variable sink20 | -| LocalDataFlow.cs:163:13:163:40 | SSA def(sink21) | LocalDataFlow.cs:164:15:164:20 | access to local variable sink21 | -| LocalDataFlow.cs:163:22:163:26 | [post] access to local variable sink9 | LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | -| LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | LocalDataFlow.cs:163:22:163:40 | call to method Equals | -| LocalDataFlow.cs:163:22:163:26 | access to local variable sink9 | LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | -| LocalDataFlow.cs:163:22:163:40 | call to method Equals | LocalDataFlow.cs:163:13:163:40 | SSA def(sink21) | -| LocalDataFlow.cs:165:13:165:45 | SSA def(sink22) | LocalDataFlow.cs:166:15:166:20 | access to local variable sink22 | -| LocalDataFlow.cs:165:22:165:26 | [post] access to local variable sink8 | LocalDataFlow.cs:171:20:171:24 | access to local variable sink8 | -| LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | LocalDataFlow.cs:165:22:165:45 | call to method Equals | -| LocalDataFlow.cs:165:22:165:26 | access to local variable sink8 | LocalDataFlow.cs:171:20:171:24 | access to local variable sink8 | -| LocalDataFlow.cs:165:22:165:45 | call to method Equals | LocalDataFlow.cs:165:13:165:45 | SSA def(sink22) | -| LocalDataFlow.cs:165:43:165:44 | 41 | LocalDataFlow.cs:165:35:165:44 | (...) ... | -| LocalDataFlow.cs:169:9:169:38 | SSA def(nonSink7) | LocalDataFlow.cs:170:15:170:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:169:20:169:24 | [post] access to local variable sink0 | LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | -| LocalDataFlow.cs:169:20:169:24 | access to local variable sink0 | LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | -| LocalDataFlow.cs:169:20:169:38 | call to method Equals | LocalDataFlow.cs:169:9:169:38 | SSA def(nonSink7) | -| LocalDataFlow.cs:169:33:169:37 | [post] access to local variable sink1 | LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | -| LocalDataFlow.cs:169:33:169:37 | access to local variable sink1 | LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | -| LocalDataFlow.cs:171:9:171:41 | SSA def(nonSink7) | LocalDataFlow.cs:172:15:172:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:171:20:171:41 | call to method Equals | LocalDataFlow.cs:171:9:171:41 | SSA def(nonSink7) | -| LocalDataFlow.cs:172:15:172:22 | access to local variable nonSink7 | LocalDataFlow.cs:179:20:179:27 | access to local variable nonSink7 | -| LocalDataFlow.cs:175:13:175:36 | SSA def(sink25) | LocalDataFlow.cs:176:15:176:20 | access to local variable sink25 | -| LocalDataFlow.cs:175:22:175:27 | access to local variable sink20 | LocalDataFlow.cs:175:22:175:36 | ... \|\| ... | -| LocalDataFlow.cs:175:22:175:36 | ... \|\| ... | LocalDataFlow.cs:175:13:175:36 | SSA def(sink25) | -| LocalDataFlow.cs:175:32:175:36 | false | LocalDataFlow.cs:175:22:175:36 | ... \|\| ... | -| LocalDataFlow.cs:179:9:179:36 | SSA def(nonSink7) | LocalDataFlow.cs:180:15:180:22 | access to local variable nonSink7 | -| LocalDataFlow.cs:179:20:179:27 | access to local variable nonSink7 | LocalDataFlow.cs:179:20:179:36 | ... \|\| ... | -| LocalDataFlow.cs:179:20:179:36 | ... \|\| ... | LocalDataFlow.cs:179:9:179:36 | SSA def(nonSink7) | -| LocalDataFlow.cs:179:32:179:36 | false | LocalDataFlow.cs:179:20:179:36 | ... \|\| ... | -| LocalDataFlow.cs:183:13:183:42 | SSA def(sink26) | LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | -| LocalDataFlow.cs:183:22:183:42 | [library code] object creation of type Uri | LocalDataFlow.cs:183:22:183:42 | object creation of type Uri | -| LocalDataFlow.cs:183:22:183:42 | object creation of type Uri | LocalDataFlow.cs:183:13:183:42 | SSA def(sink26) | -| LocalDataFlow.cs:183:37:183:41 | access to local variable sink9 | LocalDataFlow.cs:183:22:183:42 | [library code] object creation of type Uri | -| LocalDataFlow.cs:184:15:184:20 | [post] access to local variable sink26 | LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | -| LocalDataFlow.cs:184:15:184:20 | access to local variable sink26 | LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:13:185:38 | SSA def(sink27) | LocalDataFlow.cs:186:15:186:20 | access to local variable sink27 | -| LocalDataFlow.cs:185:22:185:27 | [post] access to local variable sink26 | LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | LocalDataFlow.cs:185:22:185:38 | [library code] call to method ToString | -| LocalDataFlow.cs:185:22:185:27 | access to local variable sink26 | LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | -| LocalDataFlow.cs:185:22:185:38 | [library code] call to method ToString | LocalDataFlow.cs:185:22:185:38 | call to method ToString | -| LocalDataFlow.cs:185:22:185:38 | call to method ToString | LocalDataFlow.cs:185:13:185:38 | SSA def(sink27) | -| LocalDataFlow.cs:187:13:187:40 | SSA def(sink28) | LocalDataFlow.cs:188:15:188:20 | access to local variable sink28 | -| LocalDataFlow.cs:187:22:187:27 | [post] access to local variable sink26 | LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | -| LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | LocalDataFlow.cs:187:22:187:40 | [library code] access to property PathAndQuery | -| LocalDataFlow.cs:187:22:187:27 | access to local variable sink26 | LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | -| LocalDataFlow.cs:187:22:187:40 | [library code] access to property PathAndQuery | LocalDataFlow.cs:187:22:187:40 | access to property PathAndQuery | -| LocalDataFlow.cs:187:22:187:40 | access to property PathAndQuery | LocalDataFlow.cs:187:13:187:40 | SSA def(sink28) | -| LocalDataFlow.cs:189:13:189:33 | SSA def(sink29) | LocalDataFlow.cs:190:15:190:20 | access to local variable sink29 | -| LocalDataFlow.cs:189:22:189:27 | [post] access to local variable sink26 | LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | -| LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | LocalDataFlow.cs:189:22:189:33 | [library code] access to property Query | -| LocalDataFlow.cs:189:22:189:27 | access to local variable sink26 | LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | -| LocalDataFlow.cs:189:22:189:33 | [library code] access to property Query | LocalDataFlow.cs:189:22:189:33 | access to property Query | -| LocalDataFlow.cs:189:22:189:33 | access to property Query | LocalDataFlow.cs:189:13:189:33 | SSA def(sink29) | -| LocalDataFlow.cs:191:13:191:42 | SSA def(sink30) | LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | -| LocalDataFlow.cs:191:22:191:27 | access to local variable sink26 | LocalDataFlow.cs:191:22:191:42 | [library code] access to property OriginalString | -| LocalDataFlow.cs:191:22:191:42 | [library code] access to property OriginalString | LocalDataFlow.cs:191:22:191:42 | access to property OriginalString | -| LocalDataFlow.cs:191:22:191:42 | access to property OriginalString | LocalDataFlow.cs:191:13:191:42 | SSA def(sink30) | -| LocalDataFlow.cs:192:15:192:20 | [post] access to local variable sink30 | LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | -| LocalDataFlow.cs:192:15:192:20 | access to local variable sink30 | LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | -| LocalDataFlow.cs:195:13:195:47 | SSA def(nonSink8) | LocalDataFlow.cs:196:15:196:22 | access to local variable nonSink8 | -| LocalDataFlow.cs:195:24:195:47 | [library code] object creation of type Uri | LocalDataFlow.cs:195:24:195:47 | object creation of type Uri | -| LocalDataFlow.cs:195:24:195:47 | object creation of type Uri | LocalDataFlow.cs:195:13:195:47 | SSA def(nonSink8) | -| LocalDataFlow.cs:195:39:195:46 | access to local variable nonSink0 | LocalDataFlow.cs:195:24:195:47 | [library code] object creation of type Uri | -| LocalDataFlow.cs:196:15:196:22 | [post] access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:196:15:196:22 | access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:9:197:38 | SSA def(nonSink0) | LocalDataFlow.cs:198:15:198:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:197:20:197:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | LocalDataFlow.cs:197:20:197:38 | [library code] call to method ToString | -| LocalDataFlow.cs:197:20:197:27 | access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:197:20:197:38 | [library code] call to method ToString | LocalDataFlow.cs:197:20:197:38 | call to method ToString | -| LocalDataFlow.cs:197:20:197:38 | call to method ToString | LocalDataFlow.cs:197:9:197:38 | SSA def(nonSink0) | -| LocalDataFlow.cs:199:9:199:40 | SSA def(nonSink0) | LocalDataFlow.cs:200:15:200:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:199:20:199:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | LocalDataFlow.cs:199:20:199:40 | [library code] access to property PathAndQuery | -| LocalDataFlow.cs:199:20:199:27 | access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:199:20:199:40 | [library code] access to property PathAndQuery | LocalDataFlow.cs:199:20:199:40 | access to property PathAndQuery | -| LocalDataFlow.cs:199:20:199:40 | access to property PathAndQuery | LocalDataFlow.cs:199:9:199:40 | SSA def(nonSink0) | -| LocalDataFlow.cs:201:9:201:33 | SSA def(nonSink0) | LocalDataFlow.cs:202:15:202:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:201:20:201:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | LocalDataFlow.cs:201:20:201:33 | [library code] access to property Query | -| LocalDataFlow.cs:201:20:201:27 | access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | -| LocalDataFlow.cs:201:20:201:33 | [library code] access to property Query | LocalDataFlow.cs:201:20:201:33 | access to property Query | -| LocalDataFlow.cs:201:20:201:33 | access to property Query | LocalDataFlow.cs:201:9:201:33 | SSA def(nonSink0) | -| LocalDataFlow.cs:203:9:203:42 | SSA def(nonSink0) | LocalDataFlow.cs:204:15:204:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:203:20:203:27 | access to local variable nonSink8 | LocalDataFlow.cs:203:20:203:42 | [library code] access to property OriginalString | -| LocalDataFlow.cs:203:20:203:42 | [library code] access to property OriginalString | LocalDataFlow.cs:203:20:203:42 | access to property OriginalString | -| LocalDataFlow.cs:203:20:203:42 | access to property OriginalString | LocalDataFlow.cs:203:9:203:42 | SSA def(nonSink0) | -| LocalDataFlow.cs:204:15:204:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | -| LocalDataFlow.cs:204:15:204:22 | access to local variable nonSink0 | LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | -| LocalDataFlow.cs:207:13:207:55 | SSA def(sink31) | LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | -| LocalDataFlow.cs:207:22:207:55 | [library code] object creation of type StringReader | LocalDataFlow.cs:207:22:207:55 | object creation of type StringReader | -| LocalDataFlow.cs:207:22:207:55 | object creation of type StringReader | LocalDataFlow.cs:207:13:207:55 | SSA def(sink31) | -| LocalDataFlow.cs:207:49:207:54 | access to local variable sink30 | LocalDataFlow.cs:207:22:207:55 | [library code] object creation of type StringReader | -| LocalDataFlow.cs:208:15:208:20 | [post] access to local variable sink31 | LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | -| LocalDataFlow.cs:208:15:208:20 | access to local variable sink31 | LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | -| LocalDataFlow.cs:209:13:209:39 | SSA def(sink32) | LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | -| LocalDataFlow.cs:209:22:209:27 | access to local variable sink31 | LocalDataFlow.cs:209:22:209:39 | [library code] call to method ReadToEnd | -| LocalDataFlow.cs:209:22:209:39 | [library code] call to method ReadToEnd | LocalDataFlow.cs:209:22:209:39 | call to method ReadToEnd | -| LocalDataFlow.cs:209:22:209:39 | call to method ReadToEnd | LocalDataFlow.cs:209:13:209:39 | SSA def(sink32) | -| LocalDataFlow.cs:210:15:210:20 | [post] access to local variable sink32 | LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | -| LocalDataFlow.cs:210:15:210:20 | access to local variable sink32 | LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | -| LocalDataFlow.cs:213:13:213:59 | SSA def(nonSink9) | LocalDataFlow.cs:214:15:214:22 | access to local variable nonSink9 | -| LocalDataFlow.cs:213:24:213:59 | [library code] object creation of type StringReader | LocalDataFlow.cs:213:24:213:59 | object creation of type StringReader | -| LocalDataFlow.cs:213:24:213:59 | object creation of type StringReader | LocalDataFlow.cs:213:13:213:59 | SSA def(nonSink9) | -| LocalDataFlow.cs:213:51:213:58 | access to local variable nonSink0 | LocalDataFlow.cs:213:24:213:59 | [library code] object creation of type StringReader | -| LocalDataFlow.cs:214:15:214:22 | [post] access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | -| LocalDataFlow.cs:214:15:214:22 | access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | -| LocalDataFlow.cs:215:9:215:39 | SSA def(nonSink0) | LocalDataFlow.cs:216:15:216:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:215:20:215:27 | access to local variable nonSink9 | LocalDataFlow.cs:215:20:215:39 | [library code] call to method ReadToEnd | -| LocalDataFlow.cs:215:20:215:39 | [library code] call to method ReadToEnd | LocalDataFlow.cs:215:20:215:39 | call to method ReadToEnd | -| LocalDataFlow.cs:215:20:215:39 | call to method ReadToEnd | LocalDataFlow.cs:215:9:215:39 | SSA def(nonSink0) | -| LocalDataFlow.cs:216:15:216:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:216:15:216:22 | access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:219:13:219:127 | SSA def(sink33) | LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | -| LocalDataFlow.cs:219:22:219:127 | (...) ... | LocalDataFlow.cs:219:13:219:127 | SSA def(sink33) | -| LocalDataFlow.cs:219:30:219:35 | access to local variable sink32 | LocalDataFlow.cs:219:30:219:48 | [library code] call to method Substring | -| LocalDataFlow.cs:219:30:219:48 | [library code] call to method Substring | LocalDataFlow.cs:219:30:219:48 | call to method Substring | -| LocalDataFlow.cs:219:30:219:48 | call to method Substring | LocalDataFlow.cs:219:30:219:67 | [library code] call to method ToLowerInvariant | -| LocalDataFlow.cs:219:30:219:67 | [library code] call to method ToLowerInvariant | LocalDataFlow.cs:219:30:219:67 | call to method ToLowerInvariant | -| LocalDataFlow.cs:219:30:219:67 | call to method ToLowerInvariant | LocalDataFlow.cs:219:30:219:77 | [library code] call to method ToUpper | -| LocalDataFlow.cs:219:30:219:77 | [library code] call to method ToUpper | LocalDataFlow.cs:219:30:219:77 | call to method ToUpper | -| LocalDataFlow.cs:219:30:219:77 | call to method ToUpper | LocalDataFlow.cs:219:30:219:87 | [library code] call to method Trim | -| LocalDataFlow.cs:219:30:219:87 | [library code] call to method Trim | LocalDataFlow.cs:219:30:219:87 | call to method Trim | -| LocalDataFlow.cs:219:30:219:87 | call to method Trim | LocalDataFlow.cs:219:30:219:105 | [library code] call to method Replace | -| LocalDataFlow.cs:219:30:219:105 | [library code] call to method Replace | LocalDataFlow.cs:219:30:219:105 | call to method Replace | -| LocalDataFlow.cs:219:30:219:105 | [library code] call to method Replace | LocalDataFlow.cs:219:30:219:105 | call to method Replace | -| LocalDataFlow.cs:219:30:219:105 | call to method Replace | LocalDataFlow.cs:219:30:219:119 | [library code] call to method Insert | -| LocalDataFlow.cs:219:30:219:119 | [library code] call to method Insert | LocalDataFlow.cs:219:30:219:119 | call to method Insert | -| LocalDataFlow.cs:219:30:219:119 | [library code] call to method Insert | LocalDataFlow.cs:219:30:219:119 | call to method Insert | -| LocalDataFlow.cs:219:30:219:119 | call to method Insert | LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | -| LocalDataFlow.cs:219:30:219:119 | call to method Insert | LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | LocalDataFlow.cs:219:30:219:127 | call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | [library code] call to method Clone | LocalDataFlow.cs:219:30:219:127 | call to method Clone | -| LocalDataFlow.cs:219:30:219:127 | call to method Clone | LocalDataFlow.cs:219:22:219:127 | (...) ... | -| LocalDataFlow.cs:219:102:219:104 | "b" | LocalDataFlow.cs:219:30:219:105 | [library code] call to method Replace | -| LocalDataFlow.cs:219:117:219:118 | "" | LocalDataFlow.cs:219:30:219:119 | [library code] call to method Insert | -| LocalDataFlow.cs:220:15:220:20 | [post] access to local variable sink33 | LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | -| LocalDataFlow.cs:220:15:220:20 | access to local variable sink33 | LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | -| LocalDataFlow.cs:221:13:221:63 | SSA def(sink48) | LocalDataFlow.cs:222:15:222:20 | access to local variable sink48 | -| LocalDataFlow.cs:221:22:221:27 | [post] access to local variable sink33 | LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | -| LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | LocalDataFlow.cs:221:22:221:39 | [library code] call to method Normalize | -| LocalDataFlow.cs:221:22:221:27 | access to local variable sink33 | LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | -| LocalDataFlow.cs:221:22:221:39 | [library code] call to method Normalize | LocalDataFlow.cs:221:22:221:39 | call to method Normalize | -| LocalDataFlow.cs:221:22:221:39 | call to method Normalize | LocalDataFlow.cs:221:22:221:52 | [library code] call to method Remove | -| LocalDataFlow.cs:221:22:221:52 | [library code] call to method Remove | LocalDataFlow.cs:221:22:221:52 | call to method Remove | -| LocalDataFlow.cs:221:22:221:52 | call to method Remove | LocalDataFlow.cs:221:22:221:63 | [library code] call to method Split | -| LocalDataFlow.cs:221:22:221:63 | [library code] call to method Split | LocalDataFlow.cs:221:22:221:63 | call to method Split | -| LocalDataFlow.cs:221:22:221:63 | call to method Split | LocalDataFlow.cs:221:13:221:63 | SSA def(sink48) | -| LocalDataFlow.cs:225:9:225:127 | SSA def(nonSink0) | LocalDataFlow.cs:226:15:226:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:225:20:225:127 | (...) ... | LocalDataFlow.cs:225:9:225:127 | SSA def(nonSink0) | -| LocalDataFlow.cs:225:28:225:35 | access to local variable nonSink0 | LocalDataFlow.cs:225:28:225:48 | [library code] call to method Substring | -| LocalDataFlow.cs:225:28:225:48 | [library code] call to method Substring | LocalDataFlow.cs:225:28:225:48 | call to method Substring | -| LocalDataFlow.cs:225:28:225:48 | call to method Substring | LocalDataFlow.cs:225:28:225:67 | [library code] call to method ToLowerInvariant | -| LocalDataFlow.cs:225:28:225:67 | [library code] call to method ToLowerInvariant | LocalDataFlow.cs:225:28:225:67 | call to method ToLowerInvariant | -| LocalDataFlow.cs:225:28:225:67 | call to method ToLowerInvariant | LocalDataFlow.cs:225:28:225:77 | [library code] call to method ToUpper | -| LocalDataFlow.cs:225:28:225:77 | [library code] call to method ToUpper | LocalDataFlow.cs:225:28:225:77 | call to method ToUpper | -| LocalDataFlow.cs:225:28:225:77 | call to method ToUpper | LocalDataFlow.cs:225:28:225:87 | [library code] call to method Trim | -| LocalDataFlow.cs:225:28:225:87 | [library code] call to method Trim | LocalDataFlow.cs:225:28:225:87 | call to method Trim | -| LocalDataFlow.cs:225:28:225:87 | call to method Trim | LocalDataFlow.cs:225:28:225:105 | [library code] call to method Replace | -| LocalDataFlow.cs:225:28:225:105 | [library code] call to method Replace | LocalDataFlow.cs:225:28:225:105 | call to method Replace | -| LocalDataFlow.cs:225:28:225:105 | [library code] call to method Replace | LocalDataFlow.cs:225:28:225:105 | call to method Replace | -| LocalDataFlow.cs:225:28:225:105 | call to method Replace | LocalDataFlow.cs:225:28:225:119 | [library code] call to method Insert | -| LocalDataFlow.cs:225:28:225:119 | [library code] call to method Insert | LocalDataFlow.cs:225:28:225:119 | call to method Insert | -| LocalDataFlow.cs:225:28:225:119 | [library code] call to method Insert | LocalDataFlow.cs:225:28:225:119 | call to method Insert | -| LocalDataFlow.cs:225:28:225:119 | call to method Insert | LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | -| LocalDataFlow.cs:225:28:225:119 | call to method Insert | LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | LocalDataFlow.cs:225:28:225:127 | call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | [library code] call to method Clone | LocalDataFlow.cs:225:28:225:127 | call to method Clone | -| LocalDataFlow.cs:225:28:225:127 | call to method Clone | LocalDataFlow.cs:225:20:225:127 | (...) ... | -| LocalDataFlow.cs:225:102:225:104 | "b" | LocalDataFlow.cs:225:28:225:105 | [library code] call to method Replace | -| LocalDataFlow.cs:225:117:225:118 | "" | LocalDataFlow.cs:225:28:225:119 | [library code] call to method Insert | -| LocalDataFlow.cs:226:15:226:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | -| LocalDataFlow.cs:226:15:226:22 | access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:13:227:68 | SSA def(nonSink15) | LocalDataFlow.cs:228:15:228:23 | access to local variable nonSink15 | -| LocalDataFlow.cs:227:25:227:32 | [post] access to local variable nonSink0 | LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | LocalDataFlow.cs:227:25:227:44 | [library code] call to method Normalize | -| LocalDataFlow.cs:227:25:227:32 | access to local variable nonSink0 | LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | -| LocalDataFlow.cs:227:25:227:44 | [library code] call to method Normalize | LocalDataFlow.cs:227:25:227:44 | call to method Normalize | -| LocalDataFlow.cs:227:25:227:44 | call to method Normalize | LocalDataFlow.cs:227:25:227:57 | [library code] call to method Remove | -| LocalDataFlow.cs:227:25:227:57 | [library code] call to method Remove | LocalDataFlow.cs:227:25:227:57 | call to method Remove | -| LocalDataFlow.cs:227:25:227:57 | call to method Remove | LocalDataFlow.cs:227:25:227:68 | [library code] call to method Split | -| LocalDataFlow.cs:227:25:227:68 | [library code] call to method Split | LocalDataFlow.cs:227:25:227:68 | call to method Split | -| LocalDataFlow.cs:227:25:227:68 | call to method Split | LocalDataFlow.cs:227:13:227:68 | SSA def(nonSink15) | -| LocalDataFlow.cs:231:13:231:46 | SSA def(sink34) | LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | -| LocalDataFlow.cs:231:22:231:46 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:231:22:231:46 | object creation of type StringBuilder | -| LocalDataFlow.cs:231:22:231:46 | object creation of type StringBuilder | LocalDataFlow.cs:231:13:231:46 | SSA def(sink34) | -| LocalDataFlow.cs:231:40:231:45 | access to local variable sink33 | LocalDataFlow.cs:231:22:231:46 | [library code] object creation of type StringBuilder | -| LocalDataFlow.cs:232:15:232:20 | [post] access to local variable sink34 | LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | -| LocalDataFlow.cs:232:15:232:20 | access to local variable sink34 | LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | -| LocalDataFlow.cs:233:13:233:38 | SSA def(sink35) | LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | -| LocalDataFlow.cs:233:22:233:27 | access to local variable sink34 | LocalDataFlow.cs:233:22:233:38 | [library code] call to method ToString | -| LocalDataFlow.cs:233:22:233:38 | [library code] call to method ToString | LocalDataFlow.cs:233:22:233:38 | call to method ToString | -| LocalDataFlow.cs:233:22:233:38 | call to method ToString | LocalDataFlow.cs:233:13:233:38 | SSA def(sink35) | -| LocalDataFlow.cs:234:15:234:20 | [post] access to local variable sink35 | LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | -| LocalDataFlow.cs:234:15:234:20 | access to local variable sink35 | LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | -| LocalDataFlow.cs:235:13:235:42 | SSA def(sink36) | LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | -| LocalDataFlow.cs:235:22:235:42 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:235:22:235:42 | object creation of type StringBuilder | -| LocalDataFlow.cs:235:22:235:42 | object creation of type StringBuilder | LocalDataFlow.cs:235:13:235:42 | SSA def(sink36) | -| LocalDataFlow.cs:235:40:235:41 | "" | LocalDataFlow.cs:235:22:235:42 | [library code] object creation of type StringBuilder | -| LocalDataFlow.cs:236:9:236:14 | [post] access to local variable sink36 | LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | LocalDataFlow.cs:237:15:237:20 | access to local variable sink36 | -| LocalDataFlow.cs:236:9:236:33 | [library code] call to method AppendLine | LocalDataFlow.cs:236:9:236:14 | access to local variable sink36 | -| LocalDataFlow.cs:236:27:236:32 | access to local variable sink35 | LocalDataFlow.cs:236:9:236:33 | [library code] call to method AppendLine | -| LocalDataFlow.cs:240:13:240:51 | SSA def(nonSink10) | LocalDataFlow.cs:241:15:241:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:240:25:240:51 | [library code] object creation of type StringBuilder | LocalDataFlow.cs:240:25:240:51 | object creation of type StringBuilder | -| LocalDataFlow.cs:240:25:240:51 | object creation of type StringBuilder | LocalDataFlow.cs:240:13:240:51 | SSA def(nonSink10) | -| LocalDataFlow.cs:240:43:240:50 | access to local variable nonSink0 | LocalDataFlow.cs:240:25:240:51 | [library code] object creation of type StringBuilder | -| LocalDataFlow.cs:241:15:241:23 | [post] access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | -| LocalDataFlow.cs:241:15:241:23 | access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:9:242:39 | SSA def(nonSink0) | LocalDataFlow.cs:243:15:243:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:242:20:242:28 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | LocalDataFlow.cs:242:20:242:39 | [library code] call to method ToString | -| LocalDataFlow.cs:242:20:242:28 | access to local variable nonSink10 | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:242:20:242:39 | [library code] call to method ToString | LocalDataFlow.cs:242:20:242:39 | call to method ToString | -| LocalDataFlow.cs:242:20:242:39 | call to method ToString | LocalDataFlow.cs:242:9:242:39 | SSA def(nonSink0) | -| LocalDataFlow.cs:243:15:243:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | -| LocalDataFlow.cs:243:15:243:22 | access to local variable nonSink0 | LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | -| LocalDataFlow.cs:244:9:244:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:245:15:245:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | LocalDataFlow.cs:245:15:245:23 | access to local variable nonSink10 | -| LocalDataFlow.cs:244:9:244:38 | [library code] call to method AppendLine | LocalDataFlow.cs:244:9:244:17 | access to local variable nonSink10 | -| LocalDataFlow.cs:244:30:244:37 | access to local variable nonSink0 | LocalDataFlow.cs:244:9:244:38 | [library code] call to method AppendLine | -| LocalDataFlow.cs:248:13:248:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:248:13:248:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:251:22:251:46 | access to property AList | -| LocalDataFlow.cs:248:35:248:52 | object creation of type DataContract | LocalDataFlow.cs:248:13:248:52 | SSA def(taintedDataContract) | -| LocalDataFlow.cs:249:13:249:48 | SSA def(sink53) | LocalDataFlow.cs:250:15:250:20 | access to local variable sink53 | -| LocalDataFlow.cs:249:22:249:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | LocalDataFlow.cs:249:22:249:48 | [library code] access to property AString | -| LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | LocalDataFlow.cs:249:22:249:48 | access to property AString | -| LocalDataFlow.cs:249:22:249:40 | access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | -| LocalDataFlow.cs:249:22:249:48 | [library code] access to property AString | LocalDataFlow.cs:249:22:249:48 | access to property AString | -| LocalDataFlow.cs:249:22:249:48 | access to property AString | LocalDataFlow.cs:249:13:249:48 | SSA def(sink53) | -| LocalDataFlow.cs:251:13:251:57 | SSA def(sink54) | LocalDataFlow.cs:252:15:252:20 | access to local variable sink54 | -| LocalDataFlow.cs:251:22:251:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:46 | [library code] access to property AList | -| LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | LocalDataFlow.cs:251:22:251:46 | access to property AList | -| LocalDataFlow.cs:251:22:251:40 | access to local variable taintedDataContract | LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:251:22:251:46 | [library code] access to property AList | LocalDataFlow.cs:251:22:251:46 | access to property AList | -| LocalDataFlow.cs:251:22:251:46 | [post] access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:251:22:251:46 | access to property AList | LocalDataFlow.cs:251:22:251:49 | access to indexer | -| LocalDataFlow.cs:251:22:251:46 | access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:251:22:251:49 | access to indexer | LocalDataFlow.cs:251:22:251:57 | [library code] access to property AString | -| LocalDataFlow.cs:251:22:251:49 | access to indexer | LocalDataFlow.cs:251:22:251:57 | access to property AString | -| LocalDataFlow.cs:251:22:251:57 | [library code] access to property AString | LocalDataFlow.cs:251:22:251:57 | access to property AString | -| LocalDataFlow.cs:251:22:251:57 | access to property AString | LocalDataFlow.cs:251:13:251:57 | SSA def(sink54) | -| LocalDataFlow.cs:255:13:255:55 | SSA def(nonTaintedDataContract) | LocalDataFlow.cs:256:20:256:41 | access to local variable nonTaintedDataContract | -| LocalDataFlow.cs:255:38:255:55 | object creation of type DataContract | LocalDataFlow.cs:255:13:255:55 | SSA def(nonTaintedDataContract) | -| LocalDataFlow.cs:256:9:256:49 | SSA def(nonSink0) | LocalDataFlow.cs:257:15:257:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:256:20:256:41 | access to local variable nonTaintedDataContract | LocalDataFlow.cs:256:20:256:49 | [library code] access to property AString | -| LocalDataFlow.cs:256:20:256:41 | access to local variable nonTaintedDataContract | LocalDataFlow.cs:256:20:256:49 | access to property AString | -| LocalDataFlow.cs:256:20:256:49 | [library code] access to property AString | LocalDataFlow.cs:256:20:256:49 | access to property AString | -| LocalDataFlow.cs:256:20:256:49 | access to property AString | LocalDataFlow.cs:256:9:256:49 | SSA def(nonSink0) | -| LocalDataFlow.cs:258:9:258:44 | SSA def(nonSink2) | LocalDataFlow.cs:259:15:259:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:258:20:258:38 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:258:20:258:38 | access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | -| LocalDataFlow.cs:258:20:258:44 | access to property AnInt | LocalDataFlow.cs:258:9:258:44 | SSA def(nonSink2) | -| LocalDataFlow.cs:260:9:260:53 | SSA def(nonSink2) | LocalDataFlow.cs:261:15:261:22 | access to local variable nonSink2 | -| LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:44 | [library code] access to property AList | -| LocalDataFlow.cs:260:20:260:38 | access to local variable taintedDataContract | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:260:20:260:44 | [library code] access to property AList | LocalDataFlow.cs:260:20:260:44 | access to property AList | -| LocalDataFlow.cs:260:20:260:44 | access to property AList | LocalDataFlow.cs:260:20:260:47 | access to indexer | -| LocalDataFlow.cs:260:20:260:53 | access to property AnInt | LocalDataFlow.cs:260:9:260:53 | SSA def(nonSink2) | -| LocalDataFlow.cs:264:17:264:37 | SSA def(taintedTextBox) | LocalDataFlow.cs:265:22:265:35 | access to local variable taintedTextBox | -| LocalDataFlow.cs:264:34:264:37 | null | LocalDataFlow.cs:264:17:264:37 | SSA def(taintedTextBox) | -| LocalDataFlow.cs:265:13:265:40 | SSA def(sink60) | LocalDataFlow.cs:266:15:266:20 | access to local variable sink60 | -| LocalDataFlow.cs:265:22:265:35 | access to local variable taintedTextBox | LocalDataFlow.cs:265:22:265:40 | [library code] access to property Text | -| LocalDataFlow.cs:265:22:265:40 | [library code] access to property Text | LocalDataFlow.cs:265:22:265:40 | access to property Text | -| LocalDataFlow.cs:265:22:265:40 | access to property Text | LocalDataFlow.cs:265:13:265:40 | SSA def(sink60) | -| LocalDataFlow.cs:269:17:269:40 | SSA def(nonTaintedTextBox) | LocalDataFlow.cs:270:20:270:36 | access to local variable nonTaintedTextBox | -| LocalDataFlow.cs:269:37:269:40 | null | LocalDataFlow.cs:269:17:269:40 | SSA def(nonTaintedTextBox) | -| LocalDataFlow.cs:270:9:270:41 | SSA def(nonSink0) | LocalDataFlow.cs:271:15:271:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:270:20:270:36 | access to local variable nonTaintedTextBox | LocalDataFlow.cs:270:20:270:41 | [library code] access to property Text | -| LocalDataFlow.cs:270:20:270:41 | [library code] access to property Text | LocalDataFlow.cs:270:20:270:41 | access to property Text | -| LocalDataFlow.cs:270:20:270:41 | access to property Text | LocalDataFlow.cs:270:9:270:41 | SSA def(nonSink0) | -| LocalDataFlow.cs:274:13:274:51 | SSA def(sink67) | LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | -| LocalDataFlow.cs:274:22:274:51 | [library code] call to method Run | LocalDataFlow.cs:274:22:274:51 | call to method Run | -| LocalDataFlow.cs:274:22:274:51 | call to method Run | LocalDataFlow.cs:274:13:274:51 | SSA def(sink67) | -| LocalDataFlow.cs:274:31:274:50 | [output] (...) => ... | LocalDataFlow.cs:274:22:274:51 | [library code] call to method Run | -| LocalDataFlow.cs:275:15:275:20 | [post] access to local variable sink67 | LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | -| LocalDataFlow.cs:275:15:275:20 | access to local variable sink67 | LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | -| LocalDataFlow.cs:276:13:276:33 | SSA def(sink68) | LocalDataFlow.cs:277:15:277:20 | access to local variable sink68 | -| LocalDataFlow.cs:276:22:276:33 | await ... | LocalDataFlow.cs:276:13:276:33 | SSA def(sink68) | -| LocalDataFlow.cs:276:28:276:33 | access to local variable sink67 | LocalDataFlow.cs:276:22:276:33 | await ... | -| LocalDataFlow.cs:280:13:280:42 | SSA def(nonSink21) | LocalDataFlow.cs:281:15:281:23 | access to local variable nonSink21 | -| LocalDataFlow.cs:280:25:280:42 | [library code] call to method Run | LocalDataFlow.cs:280:25:280:42 | call to method Run | -| LocalDataFlow.cs:280:25:280:42 | call to method Run | LocalDataFlow.cs:280:13:280:42 | SSA def(nonSink21) | -| LocalDataFlow.cs:280:34:280:41 | [output] (...) => ... | LocalDataFlow.cs:280:25:280:42 | [library code] call to method Run | -| LocalDataFlow.cs:281:15:281:23 | [post] access to local variable nonSink21 | LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | -| LocalDataFlow.cs:281:15:281:23 | access to local variable nonSink21 | LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | -| LocalDataFlow.cs:282:9:282:34 | SSA def(nonSink0) | LocalDataFlow.cs:283:15:283:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:282:20:282:34 | await ... | LocalDataFlow.cs:282:9:282:34 | SSA def(nonSink0) | -| LocalDataFlow.cs:282:26:282:34 | access to local variable nonSink21 | LocalDataFlow.cs:282:20:282:34 | await ... | -| LocalDataFlow.cs:283:15:283:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:283:15:283:22 | access to local variable nonSink0 | LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | -| LocalDataFlow.cs:286:13:286:36 | SSA def(sink69) | LocalDataFlow.cs:287:15:287:20 | access to local variable sink69 | -| LocalDataFlow.cs:286:22:286:36 | $"..." | LocalDataFlow.cs:286:13:286:36 | SSA def(sink69) | -| LocalDataFlow.cs:286:24:286:28 | "test " | LocalDataFlow.cs:286:22:286:36 | $"..." | -| LocalDataFlow.cs:286:30:286:34 | access to local variable sink1 | LocalDataFlow.cs:286:22:286:36 | $"..." | -| LocalDataFlow.cs:290:9:290:37 | SSA def(nonSink0) | LocalDataFlow.cs:291:15:291:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:290:20:290:37 | $"..." | LocalDataFlow.cs:290:9:290:37 | SSA def(nonSink0) | -| LocalDataFlow.cs:290:22:290:26 | "test " | LocalDataFlow.cs:290:20:290:37 | $"..." | -| LocalDataFlow.cs:290:28:290:35 | access to local variable nonSink0 | LocalDataFlow.cs:290:20:290:37 | $"..." | -| LocalDataFlow.cs:291:15:291:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:291:15:291:22 | access to local variable nonSink0 | LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:294:13:294:34 | SSA def(sink70) | LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | -| LocalDataFlow.cs:294:22:294:34 | ... = ... | LocalDataFlow.cs:294:13:294:34 | SSA def(sink70) | -| LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | -| LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | -| LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | LocalDataFlow.cs:294:22:294:34 | ... = ... | -| LocalDataFlow.cs:294:30:294:34 | access to local variable sink0 | LocalDataFlow.cs:294:22:294:34 | SSA def(sink0) | -| LocalDataFlow.cs:295:15:295:20 | [post] access to local variable sink70 | LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | -| LocalDataFlow.cs:295:15:295:20 | access to local variable sink70 | LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | -| LocalDataFlow.cs:298:9:298:38 | SSA def(nonSink0) | LocalDataFlow.cs:299:15:299:22 | access to local variable nonSink0 | -| LocalDataFlow.cs:298:20:298:38 | ... = ... | LocalDataFlow.cs:298:9:298:38 | SSA def(nonSink0) | -| LocalDataFlow.cs:298:31:298:38 | access to local variable nonSink0 | LocalDataFlow.cs:298:20:298:38 | ... = ... | -| LocalDataFlow.cs:299:15:299:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | -| LocalDataFlow.cs:299:15:299:22 | access to local variable nonSink0 | LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | -| LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | LocalDataFlow.cs:302:23:302:35 | SSA def(sink71) | -| LocalDataFlow.cs:302:13:302:18 | access to local variable sink70 | LocalDataFlow.cs:310:17:310:22 | access to local variable sink70 | -| LocalDataFlow.cs:302:23:302:35 | SSA def(sink71) | LocalDataFlow.cs:303:19:303:24 | access to local variable sink71 | -| LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | LocalDataFlow.cs:306:25:306:40 | SSA def(nonSink16) | -| LocalDataFlow.cs:306:13:306:20 | access to local variable nonSink0 | LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | -| LocalDataFlow.cs:306:25:306:40 | SSA def(nonSink16) | LocalDataFlow.cs:307:19:307:27 | access to local variable nonSink16 | -| LocalDataFlow.cs:310:17:310:22 | access to local variable sink70 | LocalDataFlow.cs:312:18:312:30 | SSA def(sink72) | -| LocalDataFlow.cs:312:18:312:30 | SSA def(sink72) | LocalDataFlow.cs:313:23:313:28 | access to local variable sink72 | -| LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | LocalDataFlow.cs:320:18:320:33 | SSA def(nonSink17) | -| LocalDataFlow.cs:318:17:318:24 | access to local variable nonSink0 | LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | -| LocalDataFlow.cs:320:18:320:33 | SSA def(nonSink17) | LocalDataFlow.cs:321:23:321:31 | access to local variable nonSink17 | -| LocalDataFlow.cs:326:13:326:38 | SSA def(sink73) | LocalDataFlow.cs:328:15:328:20 | access to local variable sink73 | -| LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | LocalDataFlow.cs:326:22:326:38 | ... ?? ... | -| LocalDataFlow.cs:326:22:326:29 | access to local variable nonSink0 | LocalDataFlow.cs:327:31:327:38 | access to local variable nonSink0 | -| LocalDataFlow.cs:326:22:326:38 | ... ?? ... | LocalDataFlow.cs:326:13:326:38 | SSA def(sink73) | -| LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | LocalDataFlow.cs:326:22:326:38 | ... ?? ... | -| LocalDataFlow.cs:326:34:326:38 | access to local variable sink0 | LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | -| LocalDataFlow.cs:327:13:327:38 | SSA def(sink74) | LocalDataFlow.cs:329:15:329:20 | access to local variable sink74 | -| LocalDataFlow.cs:327:22:327:26 | access to local variable sink0 | LocalDataFlow.cs:327:22:327:38 | ... ?? ... | -| LocalDataFlow.cs:327:22:327:38 | ... ?? ... | LocalDataFlow.cs:327:13:327:38 | SSA def(sink74) | -| LocalDataFlow.cs:327:31:327:38 | access to local variable nonSink0 | LocalDataFlow.cs:327:22:327:38 | ... ?? ... | -| LocalDataFlow.cs:347:28:347:30 | this | LocalDataFlow.cs:347:41:347:45 | this access | -| LocalDataFlow.cs:347:50:347:52 | this | LocalDataFlow.cs:347:56:347:60 | this access | -| LocalDataFlow.cs:347:50:347:52 | value | LocalDataFlow.cs:347:64:347:68 | access to parameter value | -| LocalDataFlow.cs:353:41:353:47 | tainted | LocalDataFlow.cs:355:15:355:21 | access to parameter tainted | -| LocalDataFlow.cs:358:44:358:53 | nonTainted | LocalDataFlow.cs:360:15:360:24 | access to parameter nonTainted | -| LocalDataFlow.cs:363:44:363:44 | x | LocalDataFlow.cs:366:21:366:21 | access to parameter x | -| LocalDataFlow.cs:363:67:363:68 | os | LocalDataFlow.cs:369:32:369:33 | access to parameter os | -| LocalDataFlow.cs:366:21:366:21 | access to parameter x | LocalDataFlow.cs:366:16:366:21 | ... = ... | -| LocalDataFlow.cs:369:32:369:33 | access to parameter os | LocalDataFlow.cs:369:26:369:33 | ... = ... | -| LocalDataFlow.cs:374:41:374:44 | args | LocalDataFlow.cs:376:29:376:32 | access to parameter args | -| LocalDataFlow.cs:376:29:376:32 | [post] access to parameter args | LocalDataFlow.cs:377:27:377:30 | access to parameter args | -| LocalDataFlow.cs:376:29:376:32 | access to parameter args | LocalDataFlow.cs:376:29:376:32 | call to operator implicit conversion | -| LocalDataFlow.cs:376:29:376:32 | access to parameter args | LocalDataFlow.cs:377:27:377:30 | access to parameter args | +| LocalDataFlow.cs:48:24:48:24 | b | LocalDataFlow.cs:84:21:84:21 | access to parameter b | +| LocalDataFlow.cs:51:13:51:34 | SSA def(sink0) | LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | +| LocalDataFlow.cs:51:21:51:34 | "taint source" | LocalDataFlow.cs:51:13:51:34 | SSA def(sink0) | +| LocalDataFlow.cs:52:15:52:19 | [post] access to local variable sink0 | LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | +| LocalDataFlow.cs:52:15:52:19 | access to local variable sink0 | LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | +| LocalDataFlow.cs:55:13:55:25 | SSA def(nonSink0) | LocalDataFlow.cs:56:15:56:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:55:24:55:25 | "" | LocalDataFlow.cs:55:13:55:25 | SSA def(nonSink0) | +| LocalDataFlow.cs:56:15:56:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | +| LocalDataFlow.cs:56:15:56:22 | access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | +| LocalDataFlow.cs:59:13:59:25 | SSA def(sink1) | LocalDataFlow.cs:60:9:60:13 | access to local variable sink1 | +| LocalDataFlow.cs:59:21:59:25 | "abc" | LocalDataFlow.cs:59:13:59:25 | SSA def(sink1) | +| LocalDataFlow.cs:60:9:60:13 | access to local variable sink1 | LocalDataFlow.cs:60:9:60:22 | ... + ... | +| LocalDataFlow.cs:60:9:60:22 | ... + ... | LocalDataFlow.cs:60:9:60:22 | SSA def(sink1) | +| LocalDataFlow.cs:60:9:60:22 | SSA def(sink1) | LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | +| LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | LocalDataFlow.cs:60:9:60:22 | ... + ... | +| LocalDataFlow.cs:60:18:60:22 | access to local variable sink0 | LocalDataFlow.cs:168:20:168:24 | access to local variable sink0 | +| LocalDataFlow.cs:61:15:61:19 | [post] access to local variable sink1 | LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | +| LocalDataFlow.cs:61:15:61:19 | access to local variable sink1 | LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | +| LocalDataFlow.cs:64:9:64:16 | access to local variable nonSink0 | LocalDataFlow.cs:64:9:64:25 | ... + ... | +| LocalDataFlow.cs:64:9:64:25 | ... + ... | LocalDataFlow.cs:64:9:64:25 | SSA def(nonSink0) | +| LocalDataFlow.cs:64:9:64:25 | SSA def(nonSink0) | LocalDataFlow.cs:65:15:65:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:64:21:64:25 | "abc" | LocalDataFlow.cs:64:9:64:25 | ... + ... | +| LocalDataFlow.cs:65:15:65:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:65:15:65:22 | access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:68:13:68:32 | SSA def(sink5) | LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | +| LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | LocalDataFlow.cs:68:21:68:32 | ... + ... | +| LocalDataFlow.cs:68:21:68:25 | access to local variable sink1 | LocalDataFlow.cs:168:33:168:37 | access to local variable sink1 | +| LocalDataFlow.cs:68:21:68:32 | ... + ... | LocalDataFlow.cs:68:13:68:32 | SSA def(sink5) | +| LocalDataFlow.cs:68:29:68:32 | "ok" | LocalDataFlow.cs:68:21:68:32 | ... + ... | +| LocalDataFlow.cs:69:15:69:19 | [post] access to local variable sink5 | LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | +| LocalDataFlow.cs:69:15:69:19 | access to local variable sink5 | LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | +| LocalDataFlow.cs:72:9:72:36 | SSA def(nonSink0) | LocalDataFlow.cs:73:15:73:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:72:20:72:27 | access to local variable nonSink0 | LocalDataFlow.cs:72:20:72:36 | ... + ... | +| LocalDataFlow.cs:72:20:72:36 | ... + ... | LocalDataFlow.cs:72:9:72:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:72:31:72:36 | "test" | LocalDataFlow.cs:72:20:72:36 | ... + ... | +| LocalDataFlow.cs:73:15:73:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | +| LocalDataFlow.cs:73:15:73:22 | access to local variable nonSink0 | LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | +| LocalDataFlow.cs:76:13:76:27 | SSA def(sink6) | LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | +| LocalDataFlow.cs:76:22:76:26 | access to local variable sink5 | LocalDataFlow.cs:76:13:76:27 | SSA def(sink6) | +| LocalDataFlow.cs:77:15:77:19 | [post] access to local variable sink6 | LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | +| LocalDataFlow.cs:77:15:77:19 | access to local variable sink6 | LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | +| LocalDataFlow.cs:80:9:80:29 | SSA def(nonSink0) | LocalDataFlow.cs:81:15:81:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:80:21:80:28 | access to local variable nonSink0 | LocalDataFlow.cs:80:9:80:29 | SSA def(nonSink0) | +| LocalDataFlow.cs:84:13:84:35 | [b (line 48): false] SSA def(sink7) | LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | +| LocalDataFlow.cs:84:13:84:35 | [b (line 48): true] SSA def(sink7) | LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | +| LocalDataFlow.cs:84:21:84:21 | access to parameter b | LocalDataFlow.cs:88:20:88:20 | [b (line 48): false] access to parameter b | +| LocalDataFlow.cs:84:21:84:21 | access to parameter b | LocalDataFlow.cs:88:20:88:20 | [b (line 48): true] access to parameter b | +| LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | LocalDataFlow.cs:84:13:84:35 | [b (line 48): false] SSA def(sink7) | +| LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | LocalDataFlow.cs:84:13:84:35 | [b (line 48): true] SSA def(sink7) | +| LocalDataFlow.cs:84:25:84:27 | [b (line 48): true] "a" | LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | +| LocalDataFlow.cs:84:31:84:35 | [b (line 48): false] access to local variable sink6 | LocalDataFlow.cs:84:21:84:35 | ... ? ... : ... | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): false] access to local variable sink7 | LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | +| LocalDataFlow.cs:85:15:85:19 | [b (line 48): true] access to local variable sink7 | LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | +| LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | LocalDataFlow.cs:89:15:89:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:88:9:88:36 | SSA phi(sink7) | LocalDataFlow.cs:92:29:92:33 | access to local variable sink7 | +| LocalDataFlow.cs:88:20:88:36 | [b (line 48): false] ... ? ... : ... | LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:88:20:88:36 | [b (line 48): true] ... ? ... : ... | LocalDataFlow.cs:88:9:88:36 | SSA def(nonSink0) | +| LocalDataFlow.cs:88:24:88:28 | "abc" | LocalDataFlow.cs:88:20:88:36 | [b (line 48): true] ... ? ... : ... | +| LocalDataFlow.cs:88:32:88:36 | "def" | LocalDataFlow.cs:88:20:88:36 | [b (line 48): false] ... ? ... : ... | +| LocalDataFlow.cs:89:15:89:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:89:15:89:22 | access to local variable nonSink0 | LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:92:13:92:33 | SSA def(sink8) | LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | +| LocalDataFlow.cs:92:21:92:33 | (...) ... | LocalDataFlow.cs:92:13:92:33 | SSA def(sink8) | +| LocalDataFlow.cs:92:29:92:33 | access to local variable sink7 | LocalDataFlow.cs:92:21:92:33 | (...) ... | +| LocalDataFlow.cs:93:15:93:19 | [post] access to local variable sink8 | LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | +| LocalDataFlow.cs:93:15:93:19 | access to local variable sink8 | LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | +| LocalDataFlow.cs:96:13:96:39 | SSA def(nonSink3) | LocalDataFlow.cs:97:15:97:22 | access to local variable nonSink3 | +| LocalDataFlow.cs:96:24:96:39 | (...) ... | LocalDataFlow.cs:96:13:96:39 | SSA def(nonSink3) | +| LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | LocalDataFlow.cs:96:24:96:39 | (...) ... | +| LocalDataFlow.cs:96:32:96:39 | access to local variable nonSink0 | LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:100:13:100:35 | SSA def(sink9) | LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | +| LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | LocalDataFlow.cs:100:21:100:35 | ... as ... | +| LocalDataFlow.cs:100:21:100:25 | access to local variable sink8 | LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | +| LocalDataFlow.cs:100:21:100:35 | ... as ... | LocalDataFlow.cs:100:13:100:35 | SSA def(sink9) | +| LocalDataFlow.cs:101:15:101:19 | [post] access to local variable sink9 | LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | +| LocalDataFlow.cs:101:15:101:19 | access to local variable sink9 | LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | +| LocalDataFlow.cs:104:9:104:37 | SSA def(nonSink3) | LocalDataFlow.cs:105:15:105:22 | access to local variable nonSink3 | +| LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | LocalDataFlow.cs:104:20:104:37 | ... as ... | +| LocalDataFlow.cs:104:20:104:27 | access to local variable nonSink0 | LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | +| LocalDataFlow.cs:104:20:104:37 | ... as ... | LocalDataFlow.cs:104:9:104:37 | SSA def(nonSink3) | +| LocalDataFlow.cs:105:15:105:22 | [post] access to local variable nonSink3 | LocalDataFlow.cs:170:33:170:40 | access to local variable nonSink3 | +| LocalDataFlow.cs:105:15:105:22 | access to local variable nonSink3 | LocalDataFlow.cs:170:33:170:40 | access to local variable nonSink3 | +| LocalDataFlow.cs:108:13:108:39 | SSA def(sink15) | LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | +| LocalDataFlow.cs:108:22:108:39 | call to method Parse | LocalDataFlow.cs:108:13:108:39 | SSA def(sink15) | +| LocalDataFlow.cs:108:34:108:38 | [post] access to local variable sink9 | LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | +| LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | LocalDataFlow.cs:108:22:108:39 | call to method Parse | +| LocalDataFlow.cs:108:34:108:38 | access to local variable sink9 | LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | +| LocalDataFlow.cs:109:15:109:20 | access to local variable sink15 | LocalDataFlow.cs:160:22:160:27 | access to local variable sink15 | +| LocalDataFlow.cs:111:13:111:56 | SSA def(sink16) | LocalDataFlow.cs:112:15:112:20 | access to local variable sink16 | +| LocalDataFlow.cs:111:22:111:56 | call to method TryParse | LocalDataFlow.cs:111:13:111:56 | SSA def(sink16) | +| LocalDataFlow.cs:111:37:111:41 | [post] access to local variable sink9 | LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | +| LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | LocalDataFlow.cs:111:22:111:56 | call to method TryParse | +| LocalDataFlow.cs:111:37:111:41 | access to local variable sink9 | LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | +| LocalDataFlow.cs:113:13:113:49 | SSA def(sink17) | LocalDataFlow.cs:114:15:114:20 | access to local variable sink17 | +| LocalDataFlow.cs:113:22:113:29 | [post] access to local variable nonSink0 | LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | +| LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | LocalDataFlow.cs:113:22:113:49 | call to method Replace | +| LocalDataFlow.cs:113:22:113:29 | access to local variable nonSink0 | LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | +| LocalDataFlow.cs:113:22:113:49 | call to method Replace | LocalDataFlow.cs:113:13:113:49 | SSA def(sink17) | +| LocalDataFlow.cs:113:44:113:48 | [post] access to local variable sink9 | LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | +| LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | LocalDataFlow.cs:113:22:113:49 | call to method Replace | +| LocalDataFlow.cs:113:44:113:48 | access to local variable sink9 | LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | +| LocalDataFlow.cs:115:13:115:51 | SSA def(sink18) | LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | +| LocalDataFlow.cs:115:22:115:51 | call to method Format | LocalDataFlow.cs:115:13:115:51 | SSA def(sink18) | +| LocalDataFlow.cs:115:36:115:43 | [post] access to local variable nonSink0 | LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | LocalDataFlow.cs:115:22:115:51 | call to method Format | +| LocalDataFlow.cs:115:36:115:43 | access to local variable nonSink0 | LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:115:46:115:50 | [post] access to local variable sink9 | LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | +| LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | LocalDataFlow.cs:115:22:115:51 | call to method Format | +| LocalDataFlow.cs:115:46:115:50 | access to local variable sink9 | LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | +| LocalDataFlow.cs:116:15:116:20 | [post] access to local variable sink18 | LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | +| LocalDataFlow.cs:116:15:116:20 | access to local variable sink18 | LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | +| LocalDataFlow.cs:117:13:117:52 | SSA def(sink19) | LocalDataFlow.cs:118:15:118:20 | access to local variable sink19 | +| LocalDataFlow.cs:117:22:117:52 | call to method Format | LocalDataFlow.cs:117:13:117:52 | SSA def(sink19) | +| LocalDataFlow.cs:117:36:117:41 | access to local variable sink18 | LocalDataFlow.cs:117:22:117:52 | call to method Format | +| LocalDataFlow.cs:117:44:117:51 | [post] access to local variable nonSink0 | LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | LocalDataFlow.cs:117:22:117:52 | call to method Format | +| LocalDataFlow.cs:117:44:117:51 | access to local variable nonSink0 | LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:119:13:119:38 | SSA def(sink45) | LocalDataFlow.cs:120:15:120:20 | access to local variable sink45 | +| LocalDataFlow.cs:119:22:119:38 | call to method Parse | LocalDataFlow.cs:119:13:119:38 | SSA def(sink45) | +| LocalDataFlow.cs:119:33:119:37 | [post] access to local variable sink9 | LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | +| LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | LocalDataFlow.cs:119:22:119:38 | call to method Parse | +| LocalDataFlow.cs:119:33:119:37 | access to local variable sink9 | LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | +| LocalDataFlow.cs:122:13:122:56 | SSA def(sink46) | LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | +| LocalDataFlow.cs:122:22:122:56 | call to method TryParse | LocalDataFlow.cs:122:13:122:56 | SSA def(sink46) | +| LocalDataFlow.cs:122:36:122:40 | [post] access to local variable sink9 | LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | +| LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | LocalDataFlow.cs:122:22:122:56 | call to method TryParse | +| LocalDataFlow.cs:122:36:122:40 | access to local variable sink9 | LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | +| LocalDataFlow.cs:123:15:123:20 | access to local variable sink46 | LocalDataFlow.cs:124:37:124:42 | access to local variable sink46 | +| LocalDataFlow.cs:124:13:124:43 | SSA def(sink47) | LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | +| LocalDataFlow.cs:124:22:124:43 | call to method ToByte | LocalDataFlow.cs:124:13:124:43 | SSA def(sink47) | +| LocalDataFlow.cs:124:37:124:42 | access to local variable sink46 | LocalDataFlow.cs:124:22:124:43 | call to method ToByte | +| LocalDataFlow.cs:125:15:125:20 | access to local variable sink47 | LocalDataFlow.cs:126:40:126:45 | access to local variable sink47 | +| LocalDataFlow.cs:126:13:126:46 | SSA def(sink49) | LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | +| LocalDataFlow.cs:126:22:126:46 | call to method Concat | LocalDataFlow.cs:126:13:126:46 | SSA def(sink49) | +| LocalDataFlow.cs:126:36:126:37 | "" | LocalDataFlow.cs:126:22:126:46 | call to method Concat | +| LocalDataFlow.cs:126:40:126:45 | (...) ... | LocalDataFlow.cs:126:22:126:46 | call to method Concat | +| LocalDataFlow.cs:126:40:126:45 | access to local variable sink47 | LocalDataFlow.cs:126:40:126:45 | (...) ... | +| LocalDataFlow.cs:127:15:127:20 | [post] access to local variable sink49 | LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | +| LocalDataFlow.cs:127:15:127:20 | access to local variable sink49 | LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | +| LocalDataFlow.cs:128:13:128:40 | SSA def(sink50) | LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | +| LocalDataFlow.cs:128:22:128:40 | call to method Copy | LocalDataFlow.cs:128:13:128:40 | SSA def(sink50) | +| LocalDataFlow.cs:128:34:128:39 | access to local variable sink49 | LocalDataFlow.cs:128:22:128:40 | call to method Copy | +| LocalDataFlow.cs:129:15:129:20 | [post] access to local variable sink50 | LocalDataFlow.cs:130:59:130:64 | access to local variable sink50 | +| LocalDataFlow.cs:129:15:129:20 | access to local variable sink50 | LocalDataFlow.cs:130:59:130:64 | access to local variable sink50 | +| LocalDataFlow.cs:130:13:130:71 | SSA def(sink51) | LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | +| LocalDataFlow.cs:130:22:130:71 | call to method Join | LocalDataFlow.cs:130:13:130:71 | SSA def(sink51) | +| LocalDataFlow.cs:130:34:130:37 | ", " | LocalDataFlow.cs:130:22:130:71 | call to method Join | +| LocalDataFlow.cs:130:40:130:70 | array creation of type String[] | LocalDataFlow.cs:130:22:130:71 | call to method Join | +| LocalDataFlow.cs:130:53:130:70 | { ..., ... } | LocalDataFlow.cs:130:40:130:70 | array creation of type String[] | +| LocalDataFlow.cs:130:55:130:56 | "" | LocalDataFlow.cs:130:53:130:70 | { ..., ... } | +| LocalDataFlow.cs:130:59:130:64 | access to local variable sink50 | LocalDataFlow.cs:130:53:130:70 | { ..., ... } | +| LocalDataFlow.cs:130:67:130:68 | "" | LocalDataFlow.cs:130:53:130:70 | { ..., ... } | +| LocalDataFlow.cs:131:15:131:20 | [post] access to local variable sink51 | LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | +| LocalDataFlow.cs:131:15:131:20 | access to local variable sink51 | LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | +| LocalDataFlow.cs:132:13:132:41 | SSA def(sink52) | LocalDataFlow.cs:133:15:133:20 | access to local variable sink52 | +| LocalDataFlow.cs:132:22:132:23 | "" | LocalDataFlow.cs:132:22:132:41 | call to method Insert | +| LocalDataFlow.cs:132:22:132:41 | call to method Insert | LocalDataFlow.cs:132:13:132:41 | SSA def(sink52) | +| LocalDataFlow.cs:132:35:132:40 | access to local variable sink51 | LocalDataFlow.cs:132:22:132:41 | call to method Insert | +| LocalDataFlow.cs:136:9:136:40 | SSA def(nonSink2) | LocalDataFlow.cs:137:15:137:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:136:20:136:40 | call to method Parse | LocalDataFlow.cs:136:9:136:40 | SSA def(nonSink2) | +| LocalDataFlow.cs:136:32:136:39 | [post] access to local variable nonSink0 | LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | LocalDataFlow.cs:136:20:136:40 | call to method Parse | +| LocalDataFlow.cs:136:32:136:39 | access to local variable nonSink0 | LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:138:13:138:61 | SSA def(nonSink7) | LocalDataFlow.cs:139:15:139:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:138:24:138:61 | call to method TryParse | LocalDataFlow.cs:138:13:138:61 | SSA def(nonSink7) | +| LocalDataFlow.cs:138:39:138:46 | [post] access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | LocalDataFlow.cs:138:24:138:61 | call to method TryParse | +| LocalDataFlow.cs:138:39:138:46 | access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:9:140:50 | SSA def(nonSink0) | LocalDataFlow.cs:141:15:141:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:27 | [post] access to local variable nonSink0 | LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:50 | call to method Replace | +| LocalDataFlow.cs:140:20:140:27 | access to local variable nonSink0 | LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | +| LocalDataFlow.cs:140:20:140:50 | call to method Replace | LocalDataFlow.cs:140:9:140:50 | SSA def(nonSink0) | +| LocalDataFlow.cs:140:42:140:49 | access to local variable nonSink0 | LocalDataFlow.cs:140:20:140:50 | call to method Replace | +| LocalDataFlow.cs:141:15:141:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:141:15:141:22 | access to local variable nonSink0 | LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:9:142:52 | SSA def(nonSink0) | LocalDataFlow.cs:143:15:143:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:20:142:52 | call to method Format | LocalDataFlow.cs:142:9:142:52 | SSA def(nonSink0) | +| LocalDataFlow.cs:142:34:142:41 | [post] access to local variable nonSink0 | LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | LocalDataFlow.cs:142:20:142:52 | call to method Format | +| LocalDataFlow.cs:142:34:142:41 | access to local variable nonSink0 | LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | +| LocalDataFlow.cs:142:44:142:51 | access to local variable nonSink0 | LocalDataFlow.cs:142:20:142:52 | call to method Format | +| LocalDataFlow.cs:143:15:143:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:143:15:143:22 | access to local variable nonSink0 | LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:144:9:144:39 | SSA def(nonSink7) | LocalDataFlow.cs:145:15:145:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:144:20:144:39 | call to method Parse | LocalDataFlow.cs:144:9:144:39 | SSA def(nonSink7) | +| LocalDataFlow.cs:144:31:144:38 | [post] access to local variable nonSink0 | LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | LocalDataFlow.cs:144:20:144:39 | call to method Parse | +| LocalDataFlow.cs:144:31:144:38 | access to local variable nonSink0 | LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | +| LocalDataFlow.cs:146:9:146:57 | SSA def(nonSink7) | LocalDataFlow.cs:147:15:147:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:146:20:146:57 | call to method TryParse | LocalDataFlow.cs:146:9:146:57 | SSA def(nonSink7) | +| LocalDataFlow.cs:146:34:146:41 | access to local variable nonSink0 | LocalDataFlow.cs:146:20:146:57 | call to method TryParse | +| LocalDataFlow.cs:147:15:147:22 | access to local variable nonSink7 | LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | +| LocalDataFlow.cs:148:13:148:48 | SSA def(nonSink14) | LocalDataFlow.cs:149:15:149:23 | access to local variable nonSink14 | +| LocalDataFlow.cs:148:25:148:48 | call to method ToByte | LocalDataFlow.cs:148:13:148:48 | SSA def(nonSink14) | +| LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | LocalDataFlow.cs:148:25:148:48 | call to method ToByte | +| LocalDataFlow.cs:148:40:148:47 | access to local variable nonSink7 | LocalDataFlow.cs:150:38:150:45 | access to local variable nonSink7 | +| LocalDataFlow.cs:150:9:150:46 | SSA def(nonSink0) | LocalDataFlow.cs:151:15:151:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:150:20:150:46 | call to method Concat | LocalDataFlow.cs:150:9:150:46 | SSA def(nonSink0) | +| LocalDataFlow.cs:150:34:150:35 | "" | LocalDataFlow.cs:150:20:150:46 | call to method Concat | +| LocalDataFlow.cs:150:38:150:45 | (...) ... | LocalDataFlow.cs:150:20:150:46 | call to method Concat | +| LocalDataFlow.cs:150:38:150:45 | access to local variable nonSink7 | LocalDataFlow.cs:150:38:150:45 | (...) ... | +| LocalDataFlow.cs:151:15:151:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:151:15:151:22 | access to local variable nonSink0 | LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | +| LocalDataFlow.cs:152:9:152:40 | SSA def(nonSink0) | LocalDataFlow.cs:153:15:153:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:152:20:152:40 | call to method Copy | LocalDataFlow.cs:152:9:152:40 | SSA def(nonSink0) | +| LocalDataFlow.cs:152:32:152:39 | access to local variable nonSink0 | LocalDataFlow.cs:152:20:152:40 | call to method Copy | +| LocalDataFlow.cs:153:15:153:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:154:57:154:64 | access to local variable nonSink0 | +| LocalDataFlow.cs:153:15:153:22 | access to local variable nonSink0 | LocalDataFlow.cs:154:57:154:64 | access to local variable nonSink0 | +| LocalDataFlow.cs:154:9:154:71 | SSA def(nonSink0) | LocalDataFlow.cs:155:15:155:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:154:20:154:71 | call to method Join | LocalDataFlow.cs:154:9:154:71 | SSA def(nonSink0) | +| LocalDataFlow.cs:154:32:154:35 | ", " | LocalDataFlow.cs:154:20:154:71 | call to method Join | +| LocalDataFlow.cs:154:38:154:70 | array creation of type String[] | LocalDataFlow.cs:154:20:154:71 | call to method Join | +| LocalDataFlow.cs:154:51:154:70 | { ..., ... } | LocalDataFlow.cs:154:38:154:70 | array creation of type String[] | +| LocalDataFlow.cs:154:53:154:54 | "" | LocalDataFlow.cs:154:51:154:70 | { ..., ... } | +| LocalDataFlow.cs:154:57:154:64 | access to local variable nonSink0 | LocalDataFlow.cs:154:51:154:70 | { ..., ... } | +| LocalDataFlow.cs:154:67:154:68 | "" | LocalDataFlow.cs:154:51:154:70 | { ..., ... } | +| LocalDataFlow.cs:155:15:155:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | +| LocalDataFlow.cs:155:15:155:22 | access to local variable nonSink0 | LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | +| LocalDataFlow.cs:156:9:156:41 | SSA def(nonSink0) | LocalDataFlow.cs:157:15:157:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:156:20:156:21 | "" | LocalDataFlow.cs:156:20:156:41 | call to method Insert | +| LocalDataFlow.cs:156:20:156:41 | call to method Insert | LocalDataFlow.cs:156:9:156:41 | SSA def(nonSink0) | +| LocalDataFlow.cs:156:33:156:40 | access to local variable nonSink0 | LocalDataFlow.cs:156:20:156:41 | call to method Insert | +| LocalDataFlow.cs:157:15:157:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:157:15:157:22 | access to local variable nonSink0 | LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | +| LocalDataFlow.cs:160:13:160:32 | SSA def(sink20) | LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | +| LocalDataFlow.cs:160:22:160:27 | access to local variable sink15 | LocalDataFlow.cs:160:22:160:32 | ... > ... | +| LocalDataFlow.cs:160:22:160:32 | ... > ... | LocalDataFlow.cs:160:13:160:32 | SSA def(sink20) | +| LocalDataFlow.cs:161:15:161:20 | access to local variable sink20 | LocalDataFlow.cs:174:22:174:27 | access to local variable sink20 | +| LocalDataFlow.cs:162:13:162:40 | SSA def(sink21) | LocalDataFlow.cs:163:15:163:20 | access to local variable sink21 | +| LocalDataFlow.cs:162:22:162:26 | [post] access to local variable sink9 | LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | +| LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | LocalDataFlow.cs:162:22:162:40 | call to method Equals | +| LocalDataFlow.cs:162:22:162:26 | access to local variable sink9 | LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | +| LocalDataFlow.cs:162:22:162:40 | call to method Equals | LocalDataFlow.cs:162:13:162:40 | SSA def(sink21) | +| LocalDataFlow.cs:164:13:164:45 | SSA def(sink22) | LocalDataFlow.cs:165:15:165:20 | access to local variable sink22 | +| LocalDataFlow.cs:164:22:164:26 | [post] access to local variable sink8 | LocalDataFlow.cs:170:20:170:24 | access to local variable sink8 | +| LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | LocalDataFlow.cs:164:22:164:45 | call to method Equals | +| LocalDataFlow.cs:164:22:164:26 | access to local variable sink8 | LocalDataFlow.cs:170:20:170:24 | access to local variable sink8 | +| LocalDataFlow.cs:164:22:164:45 | call to method Equals | LocalDataFlow.cs:164:13:164:45 | SSA def(sink22) | +| LocalDataFlow.cs:164:43:164:44 | 41 | LocalDataFlow.cs:164:35:164:44 | (...) ... | +| LocalDataFlow.cs:168:9:168:38 | SSA def(nonSink7) | LocalDataFlow.cs:169:15:169:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:168:20:168:24 | [post] access to local variable sink0 | LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | +| LocalDataFlow.cs:168:20:168:24 | access to local variable sink0 | LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | +| LocalDataFlow.cs:168:20:168:38 | call to method Equals | LocalDataFlow.cs:168:9:168:38 | SSA def(nonSink7) | +| LocalDataFlow.cs:168:33:168:37 | [post] access to local variable sink1 | LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | +| LocalDataFlow.cs:168:33:168:37 | access to local variable sink1 | LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | +| LocalDataFlow.cs:170:9:170:41 | SSA def(nonSink7) | LocalDataFlow.cs:171:15:171:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:170:20:170:41 | call to method Equals | LocalDataFlow.cs:170:9:170:41 | SSA def(nonSink7) | +| LocalDataFlow.cs:171:15:171:22 | access to local variable nonSink7 | LocalDataFlow.cs:178:20:178:27 | access to local variable nonSink7 | +| LocalDataFlow.cs:174:13:174:36 | SSA def(sink25) | LocalDataFlow.cs:175:15:175:20 | access to local variable sink25 | +| LocalDataFlow.cs:174:22:174:27 | access to local variable sink20 | LocalDataFlow.cs:174:22:174:36 | ... \|\| ... | +| LocalDataFlow.cs:174:22:174:36 | ... \|\| ... | LocalDataFlow.cs:174:13:174:36 | SSA def(sink25) | +| LocalDataFlow.cs:174:32:174:36 | false | LocalDataFlow.cs:174:22:174:36 | ... \|\| ... | +| LocalDataFlow.cs:178:9:178:36 | SSA def(nonSink7) | LocalDataFlow.cs:179:15:179:22 | access to local variable nonSink7 | +| LocalDataFlow.cs:178:20:178:27 | access to local variable nonSink7 | LocalDataFlow.cs:178:20:178:36 | ... \|\| ... | +| LocalDataFlow.cs:178:20:178:36 | ... \|\| ... | LocalDataFlow.cs:178:9:178:36 | SSA def(nonSink7) | +| LocalDataFlow.cs:178:32:178:36 | false | LocalDataFlow.cs:178:20:178:36 | ... \|\| ... | +| LocalDataFlow.cs:182:13:182:42 | SSA def(sink26) | LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | +| LocalDataFlow.cs:182:22:182:42 | object creation of type Uri | LocalDataFlow.cs:182:13:182:42 | SSA def(sink26) | +| LocalDataFlow.cs:182:37:182:41 | access to local variable sink9 | LocalDataFlow.cs:182:22:182:42 | object creation of type Uri | +| LocalDataFlow.cs:183:15:183:20 | [post] access to local variable sink26 | LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | +| LocalDataFlow.cs:183:15:183:20 | access to local variable sink26 | LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:13:184:38 | SSA def(sink27) | LocalDataFlow.cs:185:15:185:20 | access to local variable sink27 | +| LocalDataFlow.cs:184:22:184:27 | [post] access to local variable sink26 | LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | LocalDataFlow.cs:184:22:184:38 | call to method ToString | +| LocalDataFlow.cs:184:22:184:27 | access to local variable sink26 | LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | +| LocalDataFlow.cs:184:22:184:38 | call to method ToString | LocalDataFlow.cs:184:13:184:38 | SSA def(sink27) | +| LocalDataFlow.cs:186:13:186:40 | SSA def(sink28) | LocalDataFlow.cs:187:15:187:20 | access to local variable sink28 | +| LocalDataFlow.cs:186:22:186:27 | [post] access to local variable sink26 | LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | +| LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | LocalDataFlow.cs:186:22:186:40 | access to property PathAndQuery | +| LocalDataFlow.cs:186:22:186:27 | access to local variable sink26 | LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | +| LocalDataFlow.cs:186:22:186:40 | access to property PathAndQuery | LocalDataFlow.cs:186:13:186:40 | SSA def(sink28) | +| LocalDataFlow.cs:188:13:188:33 | SSA def(sink29) | LocalDataFlow.cs:189:15:189:20 | access to local variable sink29 | +| LocalDataFlow.cs:188:22:188:27 | [post] access to local variable sink26 | LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | +| LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | LocalDataFlow.cs:188:22:188:33 | access to property Query | +| LocalDataFlow.cs:188:22:188:27 | access to local variable sink26 | LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | +| LocalDataFlow.cs:188:22:188:33 | access to property Query | LocalDataFlow.cs:188:13:188:33 | SSA def(sink29) | +| LocalDataFlow.cs:190:13:190:42 | SSA def(sink30) | LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | +| LocalDataFlow.cs:190:22:190:27 | access to local variable sink26 | LocalDataFlow.cs:190:22:190:42 | access to property OriginalString | +| LocalDataFlow.cs:190:22:190:42 | access to property OriginalString | LocalDataFlow.cs:190:13:190:42 | SSA def(sink30) | +| LocalDataFlow.cs:191:15:191:20 | [post] access to local variable sink30 | LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | +| LocalDataFlow.cs:191:15:191:20 | access to local variable sink30 | LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | +| LocalDataFlow.cs:194:13:194:47 | SSA def(nonSink8) | LocalDataFlow.cs:195:15:195:22 | access to local variable nonSink8 | +| LocalDataFlow.cs:194:24:194:47 | object creation of type Uri | LocalDataFlow.cs:194:13:194:47 | SSA def(nonSink8) | +| LocalDataFlow.cs:194:39:194:46 | access to local variable nonSink0 | LocalDataFlow.cs:194:24:194:47 | object creation of type Uri | +| LocalDataFlow.cs:195:15:195:22 | [post] access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:195:15:195:22 | access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:9:196:38 | SSA def(nonSink0) | LocalDataFlow.cs:197:15:197:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:196:20:196:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | LocalDataFlow.cs:196:20:196:38 | call to method ToString | +| LocalDataFlow.cs:196:20:196:27 | access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:196:20:196:38 | call to method ToString | LocalDataFlow.cs:196:9:196:38 | SSA def(nonSink0) | +| LocalDataFlow.cs:198:9:198:40 | SSA def(nonSink0) | LocalDataFlow.cs:199:15:199:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:198:20:198:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | LocalDataFlow.cs:198:20:198:40 | access to property PathAndQuery | +| LocalDataFlow.cs:198:20:198:27 | access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:198:20:198:40 | access to property PathAndQuery | LocalDataFlow.cs:198:9:198:40 | SSA def(nonSink0) | +| LocalDataFlow.cs:200:9:200:33 | SSA def(nonSink0) | LocalDataFlow.cs:201:15:201:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:200:20:200:27 | [post] access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | LocalDataFlow.cs:200:20:200:33 | access to property Query | +| LocalDataFlow.cs:200:20:200:27 | access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | +| LocalDataFlow.cs:200:20:200:33 | access to property Query | LocalDataFlow.cs:200:9:200:33 | SSA def(nonSink0) | +| LocalDataFlow.cs:202:9:202:42 | SSA def(nonSink0) | LocalDataFlow.cs:203:15:203:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:202:20:202:27 | access to local variable nonSink8 | LocalDataFlow.cs:202:20:202:42 | access to property OriginalString | +| LocalDataFlow.cs:202:20:202:42 | access to property OriginalString | LocalDataFlow.cs:202:9:202:42 | SSA def(nonSink0) | +| LocalDataFlow.cs:203:15:203:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | +| LocalDataFlow.cs:203:15:203:22 | access to local variable nonSink0 | LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | +| LocalDataFlow.cs:206:13:206:55 | SSA def(sink31) | LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | +| LocalDataFlow.cs:206:22:206:55 | object creation of type StringReader | LocalDataFlow.cs:206:13:206:55 | SSA def(sink31) | +| LocalDataFlow.cs:206:49:206:54 | access to local variable sink30 | LocalDataFlow.cs:206:22:206:55 | object creation of type StringReader | +| LocalDataFlow.cs:207:15:207:20 | [post] access to local variable sink31 | LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | +| LocalDataFlow.cs:207:15:207:20 | access to local variable sink31 | LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | +| LocalDataFlow.cs:208:13:208:39 | SSA def(sink32) | LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | +| LocalDataFlow.cs:208:22:208:27 | access to local variable sink31 | LocalDataFlow.cs:208:22:208:39 | call to method ReadToEnd | +| LocalDataFlow.cs:208:22:208:39 | call to method ReadToEnd | LocalDataFlow.cs:208:13:208:39 | SSA def(sink32) | +| LocalDataFlow.cs:209:15:209:20 | [post] access to local variable sink32 | LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | +| LocalDataFlow.cs:209:15:209:20 | access to local variable sink32 | LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | +| LocalDataFlow.cs:212:13:212:59 | SSA def(nonSink9) | LocalDataFlow.cs:213:15:213:22 | access to local variable nonSink9 | +| LocalDataFlow.cs:212:24:212:59 | object creation of type StringReader | LocalDataFlow.cs:212:13:212:59 | SSA def(nonSink9) | +| LocalDataFlow.cs:212:51:212:58 | access to local variable nonSink0 | LocalDataFlow.cs:212:24:212:59 | object creation of type StringReader | +| LocalDataFlow.cs:213:15:213:22 | [post] access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | +| LocalDataFlow.cs:213:15:213:22 | access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | +| LocalDataFlow.cs:214:9:214:39 | SSA def(nonSink0) | LocalDataFlow.cs:215:15:215:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:214:20:214:27 | access to local variable nonSink9 | LocalDataFlow.cs:214:20:214:39 | call to method ReadToEnd | +| LocalDataFlow.cs:214:20:214:39 | call to method ReadToEnd | LocalDataFlow.cs:214:9:214:39 | SSA def(nonSink0) | +| LocalDataFlow.cs:215:15:215:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:215:15:215:22 | access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:218:13:218:127 | SSA def(sink33) | LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | +| LocalDataFlow.cs:218:22:218:127 | (...) ... | LocalDataFlow.cs:218:13:218:127 | SSA def(sink33) | +| LocalDataFlow.cs:218:30:218:35 | access to local variable sink32 | LocalDataFlow.cs:218:30:218:48 | call to method Substring | +| LocalDataFlow.cs:218:30:218:48 | call to method Substring | LocalDataFlow.cs:218:30:218:67 | call to method ToLowerInvariant | +| LocalDataFlow.cs:218:30:218:67 | call to method ToLowerInvariant | LocalDataFlow.cs:218:30:218:77 | call to method ToUpper | +| LocalDataFlow.cs:218:30:218:77 | call to method ToUpper | LocalDataFlow.cs:218:30:218:87 | call to method Trim | +| LocalDataFlow.cs:218:30:218:87 | call to method Trim | LocalDataFlow.cs:218:30:218:105 | call to method Replace | +| LocalDataFlow.cs:218:30:218:105 | call to method Replace | LocalDataFlow.cs:218:30:218:119 | call to method Insert | +| LocalDataFlow.cs:218:30:218:119 | call to method Insert | LocalDataFlow.cs:218:30:218:127 | call to method Clone | +| LocalDataFlow.cs:218:30:218:127 | call to method Clone | LocalDataFlow.cs:218:22:218:127 | (...) ... | +| LocalDataFlow.cs:218:102:218:104 | "b" | LocalDataFlow.cs:218:30:218:105 | call to method Replace | +| LocalDataFlow.cs:218:117:218:118 | "" | LocalDataFlow.cs:218:30:218:119 | call to method Insert | +| LocalDataFlow.cs:219:15:219:20 | [post] access to local variable sink33 | LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | +| LocalDataFlow.cs:219:15:219:20 | access to local variable sink33 | LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | +| LocalDataFlow.cs:220:13:220:52 | SSA def(sink48) | LocalDataFlow.cs:221:15:221:20 | access to local variable sink48 | +| LocalDataFlow.cs:220:22:220:27 | [post] access to local variable sink33 | LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | +| LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | LocalDataFlow.cs:220:22:220:39 | call to method Normalize | +| LocalDataFlow.cs:220:22:220:27 | access to local variable sink33 | LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | +| LocalDataFlow.cs:220:22:220:39 | call to method Normalize | LocalDataFlow.cs:220:22:220:52 | call to method Remove | +| LocalDataFlow.cs:220:22:220:52 | call to method Remove | LocalDataFlow.cs:220:13:220:52 | SSA def(sink48) | +| LocalDataFlow.cs:224:9:224:127 | SSA def(nonSink0) | LocalDataFlow.cs:225:15:225:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:224:20:224:127 | (...) ... | LocalDataFlow.cs:224:9:224:127 | SSA def(nonSink0) | +| LocalDataFlow.cs:224:28:224:35 | access to local variable nonSink0 | LocalDataFlow.cs:224:28:224:48 | call to method Substring | +| LocalDataFlow.cs:224:28:224:48 | call to method Substring | LocalDataFlow.cs:224:28:224:67 | call to method ToLowerInvariant | +| LocalDataFlow.cs:224:28:224:67 | call to method ToLowerInvariant | LocalDataFlow.cs:224:28:224:77 | call to method ToUpper | +| LocalDataFlow.cs:224:28:224:77 | call to method ToUpper | LocalDataFlow.cs:224:28:224:87 | call to method Trim | +| LocalDataFlow.cs:224:28:224:87 | call to method Trim | LocalDataFlow.cs:224:28:224:105 | call to method Replace | +| LocalDataFlow.cs:224:28:224:105 | call to method Replace | LocalDataFlow.cs:224:28:224:119 | call to method Insert | +| LocalDataFlow.cs:224:28:224:119 | call to method Insert | LocalDataFlow.cs:224:28:224:127 | call to method Clone | +| LocalDataFlow.cs:224:28:224:127 | call to method Clone | LocalDataFlow.cs:224:20:224:127 | (...) ... | +| LocalDataFlow.cs:224:102:224:104 | "b" | LocalDataFlow.cs:224:28:224:105 | call to method Replace | +| LocalDataFlow.cs:224:117:224:118 | "" | LocalDataFlow.cs:224:28:224:119 | call to method Insert | +| LocalDataFlow.cs:225:15:225:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | +| LocalDataFlow.cs:225:15:225:22 | access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:13:226:57 | SSA def(nonSink15) | LocalDataFlow.cs:227:15:227:23 | access to local variable nonSink15 | +| LocalDataFlow.cs:226:25:226:32 | [post] access to local variable nonSink0 | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | LocalDataFlow.cs:226:25:226:44 | call to method Normalize | +| LocalDataFlow.cs:226:25:226:32 | access to local variable nonSink0 | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | +| LocalDataFlow.cs:226:25:226:44 | call to method Normalize | LocalDataFlow.cs:226:25:226:57 | call to method Remove | +| LocalDataFlow.cs:226:25:226:57 | call to method Remove | LocalDataFlow.cs:226:13:226:57 | SSA def(nonSink15) | +| LocalDataFlow.cs:230:13:230:46 | SSA def(sink34) | LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | +| LocalDataFlow.cs:230:22:230:46 | object creation of type StringBuilder | LocalDataFlow.cs:230:13:230:46 | SSA def(sink34) | +| LocalDataFlow.cs:230:40:230:45 | access to local variable sink33 | LocalDataFlow.cs:230:22:230:46 | object creation of type StringBuilder | +| LocalDataFlow.cs:231:15:231:20 | [post] access to local variable sink34 | LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | +| LocalDataFlow.cs:231:15:231:20 | access to local variable sink34 | LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | +| LocalDataFlow.cs:232:13:232:38 | SSA def(sink35) | LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | +| LocalDataFlow.cs:232:22:232:27 | access to local variable sink34 | LocalDataFlow.cs:232:22:232:38 | call to method ToString | +| LocalDataFlow.cs:232:22:232:38 | call to method ToString | LocalDataFlow.cs:232:13:232:38 | SSA def(sink35) | +| LocalDataFlow.cs:233:15:233:20 | [post] access to local variable sink35 | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | +| LocalDataFlow.cs:233:15:233:20 | access to local variable sink35 | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | +| LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | +| LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | +| LocalDataFlow.cs:234:40:234:41 | "" | LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | +| LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | +| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine | +| LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | +| LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | +| LocalDataFlow.cs:240:15:240:23 | [post] access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | +| LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:9:241:39 | SSA def(nonSink0) | LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:241:20:241:28 | [post] access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | LocalDataFlow.cs:241:20:241:39 | call to method ToString | +| LocalDataFlow.cs:241:20:241:28 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | +| LocalDataFlow.cs:241:20:241:39 | call to method ToString | LocalDataFlow.cs:241:9:241:39 | SSA def(nonSink0) | +| LocalDataFlow.cs:242:15:242:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | +| LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | +| LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | +| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine | +| LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList | +| LocalDataFlow.cs:247:35:247:52 | object creation of type DataContract | LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | +| LocalDataFlow.cs:248:13:248:48 | SSA def(sink53) | LocalDataFlow.cs:249:15:249:20 | access to local variable sink53 | +| LocalDataFlow.cs:248:22:248:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | LocalDataFlow.cs:248:22:248:48 | access to property AString | +| LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | +| LocalDataFlow.cs:248:22:248:48 | access to property AString | LocalDataFlow.cs:248:13:248:48 | SSA def(sink53) | +| LocalDataFlow.cs:250:13:250:57 | SSA def(sink54) | LocalDataFlow.cs:251:15:251:20 | access to local variable sink54 | +| LocalDataFlow.cs:250:22:250:40 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | LocalDataFlow.cs:250:22:250:46 | access to property AList | +| LocalDataFlow.cs:250:22:250:40 | access to local variable taintedDataContract | LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:250:22:250:46 | [post] access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:250:22:250:46 | access to property AList | LocalDataFlow.cs:250:22:250:49 | access to indexer | +| LocalDataFlow.cs:250:22:250:46 | access to property AList | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:250:22:250:49 | access to indexer | LocalDataFlow.cs:250:22:250:57 | access to property AString | +| LocalDataFlow.cs:250:22:250:57 | access to property AString | LocalDataFlow.cs:250:13:250:57 | SSA def(sink54) | +| LocalDataFlow.cs:254:13:254:55 | SSA def(nonTaintedDataContract) | LocalDataFlow.cs:255:20:255:41 | access to local variable nonTaintedDataContract | +| LocalDataFlow.cs:254:38:254:55 | object creation of type DataContract | LocalDataFlow.cs:254:13:254:55 | SSA def(nonTaintedDataContract) | +| LocalDataFlow.cs:255:9:255:49 | SSA def(nonSink0) | LocalDataFlow.cs:256:15:256:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:255:20:255:41 | access to local variable nonTaintedDataContract | LocalDataFlow.cs:255:20:255:49 | access to property AString | +| LocalDataFlow.cs:255:20:255:49 | access to property AString | LocalDataFlow.cs:255:9:255:49 | SSA def(nonSink0) | +| LocalDataFlow.cs:257:9:257:44 | SSA def(nonSink2) | LocalDataFlow.cs:258:15:258:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:257:20:257:38 | [post] access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:257:20:257:38 | access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | +| LocalDataFlow.cs:257:20:257:44 | access to property AnInt | LocalDataFlow.cs:257:9:257:44 | SSA def(nonSink2) | +| LocalDataFlow.cs:259:9:259:53 | SSA def(nonSink2) | LocalDataFlow.cs:260:15:260:22 | access to local variable nonSink2 | +| LocalDataFlow.cs:259:20:259:38 | access to local variable taintedDataContract | LocalDataFlow.cs:259:20:259:44 | access to property AList | +| LocalDataFlow.cs:259:20:259:44 | access to property AList | LocalDataFlow.cs:259:20:259:47 | access to indexer | +| LocalDataFlow.cs:259:20:259:53 | access to property AnInt | LocalDataFlow.cs:259:9:259:53 | SSA def(nonSink2) | +| LocalDataFlow.cs:263:17:263:37 | SSA def(taintedTextBox) | LocalDataFlow.cs:264:22:264:35 | access to local variable taintedTextBox | +| LocalDataFlow.cs:263:34:263:37 | null | LocalDataFlow.cs:263:17:263:37 | SSA def(taintedTextBox) | +| LocalDataFlow.cs:264:13:264:40 | SSA def(sink60) | LocalDataFlow.cs:265:15:265:20 | access to local variable sink60 | +| LocalDataFlow.cs:264:22:264:35 | access to local variable taintedTextBox | LocalDataFlow.cs:264:22:264:40 | access to property Text | +| LocalDataFlow.cs:264:22:264:40 | access to property Text | LocalDataFlow.cs:264:13:264:40 | SSA def(sink60) | +| LocalDataFlow.cs:268:17:268:40 | SSA def(nonTaintedTextBox) | LocalDataFlow.cs:269:20:269:36 | access to local variable nonTaintedTextBox | +| LocalDataFlow.cs:268:37:268:40 | null | LocalDataFlow.cs:268:17:268:40 | SSA def(nonTaintedTextBox) | +| LocalDataFlow.cs:269:9:269:41 | SSA def(nonSink0) | LocalDataFlow.cs:270:15:270:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:269:20:269:36 | access to local variable nonTaintedTextBox | LocalDataFlow.cs:269:20:269:41 | access to property Text | +| LocalDataFlow.cs:269:20:269:41 | access to property Text | LocalDataFlow.cs:269:9:269:41 | SSA def(nonSink0) | +| LocalDataFlow.cs:270:15:270:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:270:15:270:22 | access to local variable nonSink0 | LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | +| LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | LocalDataFlow.cs:274:15:274:20 | access to local variable sink69 | +| LocalDataFlow.cs:273:22:273:36 | $"..." | LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | +| LocalDataFlow.cs:273:24:273:28 | "test " | LocalDataFlow.cs:273:22:273:36 | $"..." | +| LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | LocalDataFlow.cs:273:22:273:36 | $"..." | +| LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:277:20:277:37 | $"..." | LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | +| LocalDataFlow.cs:277:22:277:26 | "test " | LocalDataFlow.cs:277:20:277:37 | $"..." | +| LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | LocalDataFlow.cs:277:20:277:37 | $"..." | +| LocalDataFlow.cs:278:15:278:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) | LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | +| LocalDataFlow.cs:281:22:281:34 | ... = ... | LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) | +| LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | +| LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | +| LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | LocalDataFlow.cs:281:22:281:34 | ... = ... | +| LocalDataFlow.cs:281:30:281:34 | access to local variable sink0 | LocalDataFlow.cs:281:22:281:34 | SSA def(sink0) | +| LocalDataFlow.cs:282:15:282:20 | [post] access to local variable sink70 | LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | +| LocalDataFlow.cs:282:15:282:20 | access to local variable sink70 | LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | +| LocalDataFlow.cs:285:9:285:38 | SSA def(nonSink0) | LocalDataFlow.cs:286:15:286:22 | access to local variable nonSink0 | +| LocalDataFlow.cs:285:20:285:38 | ... = ... | LocalDataFlow.cs:285:9:285:38 | SSA def(nonSink0) | +| LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 | LocalDataFlow.cs:285:20:285:38 | ... = ... | +| LocalDataFlow.cs:286:15:286:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | +| LocalDataFlow.cs:286:15:286:22 | access to local variable nonSink0 | LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | +| LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | LocalDataFlow.cs:289:23:289:35 | SSA def(sink71) | +| LocalDataFlow.cs:289:13:289:18 | access to local variable sink70 | LocalDataFlow.cs:297:17:297:22 | access to local variable sink70 | +| LocalDataFlow.cs:289:23:289:35 | SSA def(sink71) | LocalDataFlow.cs:290:19:290:24 | access to local variable sink71 | +| LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | LocalDataFlow.cs:293:25:293:40 | SSA def(nonSink16) | +| LocalDataFlow.cs:293:13:293:20 | access to local variable nonSink0 | LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | +| LocalDataFlow.cs:293:25:293:40 | SSA def(nonSink16) | LocalDataFlow.cs:294:19:294:27 | access to local variable nonSink16 | +| LocalDataFlow.cs:297:17:297:22 | access to local variable sink70 | LocalDataFlow.cs:299:18:299:30 | SSA def(sink72) | +| LocalDataFlow.cs:299:18:299:30 | SSA def(sink72) | LocalDataFlow.cs:300:23:300:28 | access to local variable sink72 | +| LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | LocalDataFlow.cs:307:18:307:33 | SSA def(nonSink17) | +| LocalDataFlow.cs:305:17:305:24 | access to local variable nonSink0 | LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | +| LocalDataFlow.cs:307:18:307:33 | SSA def(nonSink17) | LocalDataFlow.cs:308:23:308:31 | access to local variable nonSink17 | +| LocalDataFlow.cs:313:13:313:38 | SSA def(sink73) | LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 | +| LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | LocalDataFlow.cs:313:22:313:38 | ... ?? ... | +| LocalDataFlow.cs:313:22:313:29 | access to local variable nonSink0 | LocalDataFlow.cs:314:31:314:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:313:22:313:38 | ... ?? ... | LocalDataFlow.cs:313:13:313:38 | SSA def(sink73) | +| LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | LocalDataFlow.cs:313:22:313:38 | ... ?? ... | +| LocalDataFlow.cs:313:34:313:38 | access to local variable sink0 | LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | +| LocalDataFlow.cs:314:13:314:38 | SSA def(sink74) | LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 | +| LocalDataFlow.cs:314:22:314:26 | access to local variable sink0 | LocalDataFlow.cs:314:22:314:38 | ... ?? ... | +| LocalDataFlow.cs:314:22:314:38 | ... ?? ... | LocalDataFlow.cs:314:13:314:38 | SSA def(sink74) | +| LocalDataFlow.cs:314:31:314:38 | access to local variable nonSink0 | LocalDataFlow.cs:314:22:314:38 | ... ?? ... | +| LocalDataFlow.cs:334:28:334:30 | this | LocalDataFlow.cs:334:41:334:45 | this access | +| LocalDataFlow.cs:334:50:334:52 | this | LocalDataFlow.cs:334:56:334:60 | this access | +| LocalDataFlow.cs:334:50:334:52 | value | LocalDataFlow.cs:334:64:334:68 | access to parameter value | +| LocalDataFlow.cs:340:41:340:47 | tainted | LocalDataFlow.cs:342:15:342:21 | access to parameter tainted | +| LocalDataFlow.cs:345:44:345:53 | nonTainted | LocalDataFlow.cs:347:15:347:24 | access to parameter nonTainted | +| LocalDataFlow.cs:350:44:350:44 | x | LocalDataFlow.cs:353:21:353:21 | access to parameter x | +| LocalDataFlow.cs:350:67:350:68 | os | LocalDataFlow.cs:356:33:356:34 | access to parameter os | +| LocalDataFlow.cs:353:21:353:21 | access to parameter x | LocalDataFlow.cs:353:16:353:21 | ... = ... | +| LocalDataFlow.cs:356:33:356:34 | access to parameter os | LocalDataFlow.cs:356:27:356:34 | ... = ... | +| LocalDataFlow.cs:361:41:361:44 | args | LocalDataFlow.cs:363:29:363:32 | access to parameter args | +| LocalDataFlow.cs:363:29:363:32 | [post] access to parameter args | LocalDataFlow.cs:364:27:364:30 | access to parameter args | +| LocalDataFlow.cs:363:29:363:32 | access to parameter args | LocalDataFlow.cs:363:29:363:32 | call to operator implicit conversion | +| LocalDataFlow.cs:363:29:363:32 | access to parameter args | LocalDataFlow.cs:364:27:364:30 | access to parameter args | | SSA.cs:5:17:5:17 | SSA entry def(this.S) | SSA.cs:67:9:67:14 | access to field S | | SSA.cs:5:17:5:17 | this | SSA.cs:67:9:67:12 | this access | | SSA.cs:5:26:5:32 | tainted | SSA.cs:8:24:8:30 | access to parameter tainted | @@ -1011,12 +890,16 @@ | Splitting.cs:51:13:51:36 | [b (line 46): true] SSA def(y) | Splitting.cs:52:9:52:9 | [b (line 46): true] access to local variable y | | Splitting.cs:51:17:51:36 | [b (line 46): false] array creation of type String[] | Splitting.cs:51:13:51:36 | [b (line 46): false] SSA def(y) | | Splitting.cs:51:17:51:36 | [b (line 46): true] array creation of type String[] | Splitting.cs:51:13:51:36 | [b (line 46): true] SSA def(y) | -| Splitting.cs:51:32:51:34 | [b (line 46): false] "a" | Splitting.cs:51:17:51:36 | [b (line 46): false] array creation of type String[] | -| Splitting.cs:51:32:51:34 | [b (line 46): true] "a" | Splitting.cs:51:17:51:36 | [b (line 46): true] array creation of type String[] | +| Splitting.cs:51:30:51:36 | [b (line 46): false] { ..., ... } | Splitting.cs:51:17:51:36 | [b (line 46): false] array creation of type String[] | +| Splitting.cs:51:30:51:36 | [b (line 46): true] { ..., ... } | Splitting.cs:51:17:51:36 | [b (line 46): true] array creation of type String[] | +| Splitting.cs:51:32:51:34 | [b (line 46): false] "a" | Splitting.cs:51:30:51:36 | [b (line 46): false] { ..., ... } | +| Splitting.cs:51:32:51:34 | [b (line 46): true] "a" | Splitting.cs:51:30:51:36 | [b (line 46): true] { ..., ... } | | Splitting.cs:52:9:52:9 | [b (line 46): false] access to local variable y | Splitting.cs:53:17:53:17 | [b (line 46): false] access to local variable y | | Splitting.cs:52:9:52:9 | [b (line 46): true] access to local variable y | Splitting.cs:53:17:53:17 | [b (line 46): true] access to local variable y | -| Splitting.cs:52:16:52:18 | [b (line 46): false] "b" | Splitting.cs:52:9:52:9 | [b (line 46): false] access to local variable y | -| Splitting.cs:52:16:52:18 | [b (line 46): true] "b" | Splitting.cs:52:9:52:9 | [b (line 46): true] access to local variable y | +| Splitting.cs:52:9:52:9 | [post] [b (line 46): false] access to local variable y | Splitting.cs:53:17:53:17 | [b (line 46): false] access to local variable y | +| Splitting.cs:52:9:52:9 | [post] [b (line 46): true] access to local variable y | Splitting.cs:53:17:53:17 | [b (line 46): true] access to local variable y | +| Splitting.cs:52:16:52:18 | [b (line 46): false] "b" | Splitting.cs:52:9:52:9 | [post] [b (line 46): false] access to local variable y | +| Splitting.cs:52:16:52:18 | [b (line 46): true] "b" | Splitting.cs:52:9:52:9 | [post] [b (line 46): true] access to local variable y | | Splitting.cs:53:9:53:20 | [b (line 46): false] SSA def(x) | Splitting.cs:54:17:54:17 | [b (line 46): false] access to local variable x | | Splitting.cs:53:9:53:20 | [b (line 46): true] SSA def(x) | Splitting.cs:54:17:54:17 | [b (line 46): true] access to local variable x | | Splitting.cs:53:13:53:13 | [b (line 46): false] access to local variable x | Splitting.cs:53:13:53:20 | [b (line 46): false] ... + ... | diff --git a/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.cs b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.cs new file mode 100644 index 000000000000..8c55e74372be --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.cs @@ -0,0 +1,84 @@ +using System; +using System.Linq; + +class ModulusAnalysis +{ + const int c1 = 42; + const int c2 = 43; + + void M(int i, bool cond, int x, int y, int[] arr, int otherSeven) + { + var eq = i + 3; + + var mul = eq * c1 + 3; // congruent 3 mod 42 + + int seven = 7; + if (mul % c2 == seven) + { + System.Console.WriteLine(mul); // congruent 7 mod 43, 3 mod 42 + } + + if (otherSeven == 7) + { + if (mul % c2 == otherSeven) + { + System.Console.WriteLine(mul); // congruent 3 mod 42, 7 mod 43 missing + } + } + + var j = cond + ? i * 4 + 3 + : i * 8 + 7; + System.Console.WriteLine(j); // congruent 3 mod 4 + + if (x % c1 == 3 && y % c1 == 7) + { + System.Console.WriteLine(x + y); // congruent 10 mod 42 + } + + if (x % c1 == 3 && y % c1 == 7) + { + System.Console.WriteLine(x - y); // congruent 38 mod 42 + } + + var l = arr.Length * 4 - 11; // congruent 1 mod 4 + System.Console.WriteLine(l); + + l = GetArray().Length * 4 - 11; + System.Console.WriteLine(l); // congruent 1 mod 4 + + if (cond) + { + j = i * 4 + 3; + } + else + { + j = i * 8 + 7; + } + System.Console.WriteLine(j); // congruent 3 mod 4 or 7 mod 8 + + if (cond) + { + System.Console.WriteLine(j); // congruent 3 mod 4 + } + else + { + System.Console.WriteLine(j); // congruent 7 mod 8 + } + + var t = 64; + System.Console.WriteLine(t & 32); // congruent 0 mod 32 + System.Console.WriteLine(t & 16); // congruent 0 mod 16 + t = 1; + System.Console.WriteLine(t << 2); // congruent 0 mod 4 + + if ((x & 15) == 3) + { + System.Console.WriteLine(x); // congruent 3 mod 16 + } + } + + + + int[] GetArray(){ return new int[42]; } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.expected b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.expected new file mode 100644 index 000000000000..c7bacd769437 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.expected @@ -0,0 +1,138 @@ +| ModulusAnalysis.cs:6:15:6:21 | ... = ... | 0 | 42 | 0 | +| ModulusAnalysis.cs:6:20:6:21 | 42 | 0 | 42 | 0 | +| ModulusAnalysis.cs:7:15:7:21 | ... = ... | 0 | 43 | 0 | +| ModulusAnalysis.cs:7:20:7:21 | 43 | 0 | 43 | 0 | +| ModulusAnalysis.cs:11:18:11:18 | access to parameter i | SSA param(i) | 0 | 0 | +| ModulusAnalysis.cs:11:18:11:22 | ... + ... | SSA param(i) | 3 | 0 | +| ModulusAnalysis.cs:11:22:11:22 | 3 | 0 | 3 | 0 | +| ModulusAnalysis.cs:13:19:13:20 | access to local variable eq | SSA def(eq) | 0 | 0 | +| ModulusAnalysis.cs:13:19:13:20 | access to local variable eq | SSA param(i) | 3 | 0 | +| ModulusAnalysis.cs:13:19:13:25 | ... * ... | 0 | 0 | 42 | +| ModulusAnalysis.cs:13:19:13:29 | ... + ... | 0 | 3 | 42 | +| ModulusAnalysis.cs:13:24:13:25 | access to constant c1 | 0 | 42 | 0 | +| ModulusAnalysis.cs:13:24:13:25 | access to constant c1 | SSA entry def(ModulusAnalysis.c1) | 0 | 0 | +| ModulusAnalysis.cs:13:29:13:29 | 3 | 0 | 3 | 0 | +| ModulusAnalysis.cs:15:21:15:21 | 7 | 0 | 7 | 0 | +| ModulusAnalysis.cs:16:13:16:15 | access to local variable mul | 0 | 3 | 42 | +| ModulusAnalysis.cs:16:13:16:15 | access to local variable mul | SSA def(mul) | 0 | 0 | +| ModulusAnalysis.cs:16:19:16:20 | access to constant c2 | 0 | 43 | 0 | +| ModulusAnalysis.cs:16:19:16:20 | access to constant c2 | SSA entry def(ModulusAnalysis.c2) | 0 | 0 | +| ModulusAnalysis.cs:16:25:16:29 | access to local variable seven | 0 | 7 | 0 | +| ModulusAnalysis.cs:16:25:16:29 | access to local variable seven | SSA def(seven) | 0 | 0 | +| ModulusAnalysis.cs:18:38:18:40 | access to local variable mul | 0 | 3 | 42 | +| ModulusAnalysis.cs:18:38:18:40 | access to local variable mul | 0 | 7 | 43 | +| ModulusAnalysis.cs:18:38:18:40 | access to local variable mul | SSA def(mul) | 0 | 0 | +| ModulusAnalysis.cs:21:13:21:22 | access to parameter otherSeven | SSA param(otherSeven) | 0 | 0 | +| ModulusAnalysis.cs:21:27:21:27 | 7 | 0 | 7 | 0 | +| ModulusAnalysis.cs:23:17:23:19 | access to local variable mul | 0 | 3 | 42 | +| ModulusAnalysis.cs:23:17:23:19 | access to local variable mul | SSA def(mul) | 0 | 0 | +| ModulusAnalysis.cs:23:23:23:24 | access to constant c2 | 0 | 43 | 0 | +| ModulusAnalysis.cs:23:23:23:24 | access to constant c2 | SSA entry def(ModulusAnalysis.c2) | 0 | 0 | +| ModulusAnalysis.cs:23:29:23:38 | access to parameter otherSeven | 0 | 7 | 0 | +| ModulusAnalysis.cs:23:29:23:38 | access to parameter otherSeven | SSA param(otherSeven) | 0 | 0 | +| ModulusAnalysis.cs:25:42:25:44 | access to local variable mul | 0 | 3 | 42 | +| ModulusAnalysis.cs:25:42:25:44 | access to local variable mul | SSA def(mul) | 0 | 0 | +| ModulusAnalysis.cs:29:17:31:23 | ... ? ... : ... | 0 | 3 | 4 | +| ModulusAnalysis.cs:30:15:30:15 | access to parameter i | SSA param(i) | 0 | 0 | +| ModulusAnalysis.cs:30:15:30:19 | ... * ... | 0 | 0 | 4 | +| ModulusAnalysis.cs:30:15:30:23 | ... + ... | 0 | 3 | 4 | +| ModulusAnalysis.cs:30:19:30:19 | 4 | 0 | 4 | 0 | +| ModulusAnalysis.cs:30:23:30:23 | 3 | 0 | 3 | 0 | +| ModulusAnalysis.cs:31:15:31:15 | access to parameter i | SSA param(i) | 0 | 0 | +| ModulusAnalysis.cs:31:15:31:19 | ... * ... | 0 | 0 | 8 | +| ModulusAnalysis.cs:31:15:31:23 | ... + ... | 0 | 7 | 8 | +| ModulusAnalysis.cs:31:19:31:19 | 8 | 0 | 8 | 0 | +| ModulusAnalysis.cs:31:23:31:23 | 7 | 0 | 7 | 0 | +| ModulusAnalysis.cs:32:34:32:34 | access to local variable j | 0 | 3 | 4 | +| ModulusAnalysis.cs:32:34:32:34 | access to local variable j | [cond (line 9): false] SSA def(j) | 0 | 0 | +| ModulusAnalysis.cs:32:34:32:34 | access to local variable j | [cond (line 9): true] SSA def(j) | 0 | 0 | +| ModulusAnalysis.cs:34:13:34:13 | access to parameter x | SSA param(x) | 0 | 0 | +| ModulusAnalysis.cs:34:17:34:18 | access to constant c1 | 0 | 42 | 0 | +| ModulusAnalysis.cs:34:17:34:18 | access to constant c1 | SSA entry def(ModulusAnalysis.c1) | 0 | 0 | +| ModulusAnalysis.cs:34:23:34:23 | 3 | 0 | 3 | 0 | +| ModulusAnalysis.cs:34:28:34:28 | access to parameter y | SSA param(y) | 0 | 0 | +| ModulusAnalysis.cs:34:32:34:33 | access to constant c1 | 0 | 42 | 0 | +| ModulusAnalysis.cs:34:32:34:33 | access to constant c1 | SSA entry def(ModulusAnalysis.c1) | 0 | 0 | +| ModulusAnalysis.cs:34:38:34:38 | 7 | 0 | 7 | 0 | +| ModulusAnalysis.cs:36:38:36:38 | access to parameter x | 0 | 3 | 42 | +| ModulusAnalysis.cs:36:38:36:38 | access to parameter x | SSA param(x) | 0 | 0 | +| ModulusAnalysis.cs:36:38:36:42 | ... + ... | 0 | 10 | 42 | +| ModulusAnalysis.cs:36:38:36:42 | ... + ... | SSA param(x) | 7 | 42 | +| ModulusAnalysis.cs:36:38:36:42 | ... + ... | SSA param(y) | 3 | 42 | +| ModulusAnalysis.cs:36:42:36:42 | access to parameter y | 0 | 7 | 42 | +| ModulusAnalysis.cs:36:42:36:42 | access to parameter y | SSA param(y) | 0 | 0 | +| ModulusAnalysis.cs:39:13:39:13 | access to parameter x | SSA param(x) | 0 | 0 | +| ModulusAnalysis.cs:39:17:39:18 | access to constant c1 | 0 | 42 | 0 | +| ModulusAnalysis.cs:39:17:39:18 | access to constant c1 | SSA entry def(ModulusAnalysis.c1) | 0 | 0 | +| ModulusAnalysis.cs:39:23:39:23 | 3 | 0 | 3 | 0 | +| ModulusAnalysis.cs:39:28:39:28 | access to parameter y | SSA param(y) | 0 | 0 | +| ModulusAnalysis.cs:39:32:39:33 | access to constant c1 | 0 | 42 | 0 | +| ModulusAnalysis.cs:39:32:39:33 | access to constant c1 | SSA entry def(ModulusAnalysis.c1) | 0 | 0 | +| ModulusAnalysis.cs:39:38:39:38 | 7 | 0 | 7 | 0 | +| ModulusAnalysis.cs:41:38:41:38 | access to parameter x | 0 | 3 | 42 | +| ModulusAnalysis.cs:41:38:41:38 | access to parameter x | SSA param(x) | 0 | 0 | +| ModulusAnalysis.cs:41:38:41:42 | ... - ... | 0 | 38 | 42 | +| ModulusAnalysis.cs:41:38:41:42 | ... - ... | SSA param(x) | 35 | 42 | +| ModulusAnalysis.cs:41:42:41:42 | access to parameter y | 0 | 7 | 42 | +| ModulusAnalysis.cs:41:42:41:42 | access to parameter y | SSA param(y) | 0 | 0 | +| ModulusAnalysis.cs:44:17:44:26 | access to property Length | [cond (line 9): false] SSA untracked def(arr.Length) | 0 | 0 | +| ModulusAnalysis.cs:44:17:44:26 | access to property Length | [cond (line 9): true] SSA untracked def(arr.Length) | 0 | 0 | +| ModulusAnalysis.cs:44:17:44:30 | ... * ... | 0 | 0 | 4 | +| ModulusAnalysis.cs:44:17:44:35 | ... - ... | 0 | 1 | 4 | +| ModulusAnalysis.cs:44:30:44:30 | 4 | 0 | 4 | 0 | +| ModulusAnalysis.cs:44:34:44:35 | 11 | 0 | 11 | 0 | +| ModulusAnalysis.cs:45:34:45:34 | access to local variable l | 0 | 1 | 4 | +| ModulusAnalysis.cs:45:34:45:34 | access to local variable l | [cond (line 9): false] SSA def(l) | 0 | 0 | +| ModulusAnalysis.cs:45:34:45:34 | access to local variable l | [cond (line 9): true] SSA def(l) | 0 | 0 | +| ModulusAnalysis.cs:47:9:47:38 | ... = ... | 0 | 1 | 4 | +| ModulusAnalysis.cs:47:13:47:29 | access to property Length | access to property Length | 0 | 0 | +| ModulusAnalysis.cs:47:13:47:33 | ... * ... | 0 | 0 | 4 | +| ModulusAnalysis.cs:47:13:47:38 | ... - ... | 0 | 1 | 4 | +| ModulusAnalysis.cs:47:33:47:33 | 4 | 0 | 4 | 0 | +| ModulusAnalysis.cs:47:37:47:38 | 11 | 0 | 11 | 0 | +| ModulusAnalysis.cs:48:34:48:34 | access to local variable l | 0 | 1 | 4 | +| ModulusAnalysis.cs:48:34:48:34 | access to local variable l | [cond (line 9): false] SSA def(l) | 0 | 0 | +| ModulusAnalysis.cs:48:34:48:34 | access to local variable l | [cond (line 9): true] SSA def(l) | 0 | 0 | +| ModulusAnalysis.cs:52:13:52:25 | ... = ... | 0 | 3 | 4 | +| ModulusAnalysis.cs:52:17:52:17 | access to parameter i | SSA param(i) | 0 | 0 | +| ModulusAnalysis.cs:52:17:52:21 | ... * ... | 0 | 0 | 4 | +| ModulusAnalysis.cs:52:17:52:25 | ... + ... | 0 | 3 | 4 | +| ModulusAnalysis.cs:52:21:52:21 | 4 | 0 | 4 | 0 | +| ModulusAnalysis.cs:52:25:52:25 | 3 | 0 | 3 | 0 | +| ModulusAnalysis.cs:56:13:56:25 | ... = ... | 0 | 7 | 8 | +| ModulusAnalysis.cs:56:17:56:17 | access to parameter i | SSA param(i) | 0 | 0 | +| ModulusAnalysis.cs:56:17:56:21 | ... * ... | 0 | 0 | 8 | +| ModulusAnalysis.cs:56:17:56:25 | ... + ... | 0 | 7 | 8 | +| ModulusAnalysis.cs:56:21:56:21 | 8 | 0 | 8 | 0 | +| ModulusAnalysis.cs:56:25:56:25 | 7 | 0 | 7 | 0 | +| ModulusAnalysis.cs:58:34:58:34 | access to local variable j | 0 | 3 | 4 | +| ModulusAnalysis.cs:58:34:58:34 | access to local variable j | 0 | 7 | 8 | +| ModulusAnalysis.cs:58:34:58:34 | access to local variable j | [cond (line 9): false] SSA def(j) | 0 | 0 | +| ModulusAnalysis.cs:58:34:58:34 | access to local variable j | [cond (line 9): true] SSA def(j) | 0 | 0 | +| ModulusAnalysis.cs:62:38:62:38 | access to local variable j | 0 | 3 | 4 | +| ModulusAnalysis.cs:62:38:62:38 | access to local variable j | [cond (line 9): true] SSA def(j) | 0 | 0 | +| ModulusAnalysis.cs:66:38:66:38 | access to local variable j | 0 | 7 | 8 | +| ModulusAnalysis.cs:66:38:66:38 | access to local variable j | [cond (line 9): false] SSA def(j) | 0 | 0 | +| ModulusAnalysis.cs:69:17:69:18 | 64 | 0 | 64 | 0 | +| ModulusAnalysis.cs:70:34:70:34 | access to local variable t | 0 | 64 | 0 | +| ModulusAnalysis.cs:70:34:70:34 | access to local variable t | SSA def(t) | 0 | 0 | +| ModulusAnalysis.cs:70:34:70:39 | ... & ... | 0 | 0 | 32 | +| ModulusAnalysis.cs:70:34:70:39 | ... & ... | 0 | 0 | 64 | +| ModulusAnalysis.cs:70:38:70:39 | 32 | 0 | 32 | 0 | +| ModulusAnalysis.cs:71:34:71:34 | access to local variable t | 0 | 64 | 0 | +| ModulusAnalysis.cs:71:34:71:34 | access to local variable t | SSA def(t) | 0 | 0 | +| ModulusAnalysis.cs:71:34:71:39 | ... & ... | 0 | 0 | 16 | +| ModulusAnalysis.cs:71:34:71:39 | ... & ... | 0 | 0 | 64 | +| ModulusAnalysis.cs:71:38:71:39 | 16 | 0 | 16 | 0 | +| ModulusAnalysis.cs:72:9:72:13 | ... = ... | 0 | 1 | 0 | +| ModulusAnalysis.cs:72:13:72:13 | 1 | 0 | 1 | 0 | +| ModulusAnalysis.cs:73:34:73:34 | access to local variable t | 0 | 1 | 0 | +| ModulusAnalysis.cs:73:34:73:34 | access to local variable t | SSA def(t) | 0 | 0 | +| ModulusAnalysis.cs:73:34:73:39 | ... << ... | 0 | 0 | 4 | +| ModulusAnalysis.cs:73:39:73:39 | 2 | 0 | 2 | 0 | +| ModulusAnalysis.cs:75:14:75:14 | access to parameter x | SSA param(x) | 0 | 0 | +| ModulusAnalysis.cs:75:18:75:19 | 15 | 0 | 15 | 0 | +| ModulusAnalysis.cs:75:25:75:25 | 3 | 0 | 3 | 0 | +| ModulusAnalysis.cs:77:38:77:38 | access to parameter x | 0 | 3 | 16 | +| ModulusAnalysis.cs:77:38:77:38 | access to parameter x | SSA param(x) | 0 | 0 | +| ModulusAnalysis.cs:83:38:83:39 | 42 | 0 | 42 | 0 | diff --git a/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql new file mode 100644 index 000000000000..f92649fa5b25 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql @@ -0,0 +1,7 @@ +import csharp +import semmle.code.csharp.dataflow.ModulusAnalysis +import semmle.code.csharp.dataflow.Bound + +from Expr e, Bound b, int delta, int mod +where exprModulus(e, b, delta, mod) +select e, b.toString(), delta, mod diff --git a/docs/language/global-sphinx-files/_static/.gitkeep b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected similarity index 100% rename from docs/language/global-sphinx-files/_static/.gitkeep rename to csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql new file mode 100644 index 000000000000..836980829a33 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql @@ -0,0 +1,16 @@ +import csharp +import semmle.code.csharp.dataflow.SignAnalysis + +from Expr e +where + not exists(exprSign(e)) and + not e instanceof TypeAccess and + ( + e.getType() instanceof CharType or + e.getType() instanceof IntegralType or + e.getType() instanceof FloatingPointType or + e.getType() instanceof DecimalType or + e.getType() instanceof Enum or + e.getType() instanceof PointerType + ) +select e diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs new file mode 100644 index 000000000000..de8f52d77f3c --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs @@ -0,0 +1,469 @@ +using System; +using System.Linq; +using System.Diagnostics; + +class SignAnalysis +{ + static int GetRandomValue() { return (new System.Random()).Next(0, 1000) - 500; } + + int RandomValue { get => GetRandomValue(); } + + int random = GetRandomValue(); + + int SsaSources(int p, int[] values) + { + var v = GetRandomValue(); + if (v < 0) + { + return v; + } + + v = RandomValue; + if (v < 0) + { + return v; + } + + v = p; + if (v < 0) + { + return v; + } + + v = random; + if (v < 0) + { + return v; + } + + v = values[0]; + if (v < 0) + { + return v; + } + + int x = values[1]; + v = x; + if (v < 0) + { + return v; + } + + return 0; + } + + void Operations(int i, int j, bool b) + { + if (i < 0 && j < 0) + { + var x = i + j; + System.Console.WriteLine(x); // strictly neg + x = i * j; + System.Console.WriteLine(x); // strictly pos + x = i / j; + System.Console.WriteLine(x); // pos + x = i - j; + System.Console.WriteLine(x); // no clue + x = i % j; + System.Console.WriteLine(x); // neg + x = i++; + System.Console.WriteLine(x); // strictly neg + x = i--; + System.Console.WriteLine(x); // neg + x = -i; + System.Console.WriteLine(x); // strictly pos + x = +i; + System.Console.WriteLine(x); // strictly neg + var l = (long)i; + System.Console.WriteLine(l); // strictly neg + + x = i; + x += i; + System.Console.WriteLine(x); // strictly neg + } + + if (i < 0 && j > 0) + { + var x = i + j; + System.Console.WriteLine(x); + x = i * j; + System.Console.WriteLine(x); // strictly neg + x = i / j; + System.Console.WriteLine(x); // neg + x = i - j; + System.Console.WriteLine(x); // strictly neg + x = i % j; + System.Console.WriteLine(x); // neg + x = b ? i : j; + System.Console.WriteLine(x); // any (except 0) + } + } + + void NumericalTypes() + { + var f = 4.2f; + System.Console.WriteLine(f); + var d = 4.2; + System.Console.WriteLine(d); + var de = 4.2m; + System.Console.WriteLine(de); + var c = 'a'; + System.Console.WriteLine(c); + } + + int f0; + + int f1; + + void Field0() + { + f0++; + System.Console.WriteLine(f0); // strictly positive + f0 = 0; + } + + void Field1() + { + f1++; + System.Console.WriteLine(f1); // no clue + f1 = -10; + } + + void Field2() + { + System.Console.WriteLine(f1); // no clue + } + + void Ctor() + { + var i = new Int32(); // const 0 value + i++; + System.Console.WriteLine(i); // strictly pos + } + + int Guards(int x, int y) + { + if (x < 0) + { + return x; // strictly negative + } + + if (y == 1) + { + return y; // strictly positive + } + + if (y is -1) + { + return y; // strictly negative + } + + if (x < y) + { + return y; // strictly positive + } + + var b = y == 1; + if (b) + { + return y; // strictly positive + } + + return 0; + } + + void Inconsistent() + { + var i = 1; + if (i < 0) + { + System.Console.WriteLine(i); // reported as strictly pos, although unreachable + } + } + + void SpecialValues(int[] ints) + { + System.Console.WriteLine(ints.Length); // positive + ints = new int[] { 1, 2, 3 }; + System.Console.WriteLine(ints.Length); // 3, so strictly positive + System.Console.WriteLine(ints.Count()); // positive + System.Console.WriteLine(ints.Count(i => i > 1)); // positive + + var s = "abc"; + System.Console.WriteLine(s.Length); // positive, could be strictly positive + + var enumerable = Enumerable.Empty(); + System.Console.WriteLine(enumerable.Count()); // positive + + var i = new int[,] { { 1, 1 }, { 1, 2 }, { 1, 3 } }; + System.Console.WriteLine(i.Length); // 6, so strictly positive + } + + void Phi1(int i) + { + if (i > 0) + { + System.Console.WriteLine(i); // strictly positive + } + else + { + System.Console.WriteLine(i); // negative + } + System.Console.WriteLine(i); // any + } + + void Phi2(int i) + { + if (i > 0) + { + System.Console.WriteLine(i); // strictly positive + } + else + { + if (i < 0) // negative + { + System.Console.WriteLine(i); // strictly negative + return; + } + } + System.Console.WriteLine(i); // positive, not found + } + + void Phi3(int i) + { + if (i > 0) + { + System.Console.WriteLine(i); // strictly positive + } + else + { + if (i < 0) // negative + { + System.Console.WriteLine(i); // strictly negative + } + else + { + System.Console.WriteLine(i); // zero, nothing is reported + } + } + } + + void Loop(int i, int j, int k) + { + if (i > 0) + { + while (i >= 0) // any + { + i--; // positive + System.Console.WriteLine(i); // any + } + System.Console.WriteLine(i); // strictly neg + } + + if (j > 0) + { + while (j > 0) + { + j--; // strictly pos + System.Console.WriteLine(j); // positive + } + System.Console.WriteLine(j); // reported negative, can only be 0 + } + + if (k > 0) + { + while (k > 0) + { + k--; // strictly pos + System.Console.WriteLine(k); // positive + + if (k == 5) // positive + { + break; + } + } + System.Console.WriteLine(k); // any + } + } + + void Assert(int i, bool b) + { + Debug.Assert(i > 0); + System.Console.WriteLine(i); // strictly positive + + if (b) + System.Console.WriteLine(i); // strictly positive + } + + void CheckedUnchecked(int i) + { + var x = unchecked(-1 * i * i); + if (x < 0) + { + System.Console.WriteLine(x); // strictly negative + } + + x = checked(-1 * i * i); + if (x < 0) + { + System.Console.WriteLine(x); // strictly negative + } + } + + void CharMinMax() + { + var min = char.MinValue; + var max = char.MaxValue; + var c = min + 1; + System.Console.WriteLine(c); // strictly positive + c = min - 1; + System.Console.WriteLine(c); // strictly negative + c = max + 1; + System.Console.WriteLine(c); // strictly positive + } + + void NullCoalesce(int? v) + { + if (v > 0) + { + var x = v ?? 1; + System.Console.WriteLine(x); // strictly positive + } + + if (v == null) + { + var x = v ?? 1; + System.Console.WriteLine(x); // strictly positive + } + + if (v < 0) + { + var x = v ?? 0; + System.Console.WriteLine(x); // negative + } + } + + async System.Threading.Tasks.Task Await() + { + var i = await System.Threading.Tasks.Task.FromResult(5); + if (i < 0) + { + System.Console.WriteLine(i); // strictly negative + } + } + + void Unsigned(uint i) + { + if (i != 0) // positive + { + System.Console.WriteLine(i); // strictly positive + } + } + + public int MyField = 0; + + void FieldAccess() + { + var x = new SignAnalysis(); + var y = x.MyField; + if (y < 0) + { + System.Console.WriteLine(y); // strictly negative + } + } + + private static unsafe void Pointer(float d) + { + float* dp = &d; + var x = *dp; + if (x < 0) + { + System.Console.WriteLine(x); // strictly negative + } + } + + [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit, Size = 15)] + struct MyStruct { } + + unsafe void Sizeof() + { + var x = sizeof(MyStruct); + System.Console.WriteLine(x); // strictly positive + } + + void SwitchCase(string s) + { + var x = s switch + { + "x" => 0, + _ => 2 + }; + System.Console.WriteLine(x); // positive + } + + void Capture() + { + var i = 1; + void Capture() + { + if (i > 0) + Console.WriteLine(i); // strictly positive + } + Capture(); + + if (i > 0) + Console.WriteLine(i); // strictly positive + } + + public struct MyStruct2 { public int F; } + void RefExpression(MyStruct2 s) + { + ref var x = ref s.F; + if (x < 0) + { + Console.WriteLine(x); // strictly negative + } + } + + enum MyEnum { A = 12, B, C } + void EnumOp(MyEnum x, MyEnum y) + { + var i = x - y; + if (i < 0) + { + System.Console.WriteLine(i); // strictly negative + } + } + + unsafe void PointerCast(byte* src, byte* dst) + { + var x = (int)(src - dst); + if (x < 0) + { + System.Console.WriteLine(x); // strictly negative + } + + byte[] buf = new byte[10]; + + fixed (byte* to = buf) + { + System.Console.WriteLine((int)to); + } + } + + uint Unsigned() { return 1; } + void UnsignedCheck(int i) + { + long l = Unsigned(); + if (l != 0) + { + System.Console.WriteLine(l); // strictly positive + } + + uint x = (uint)i; + x++; + System.Console.WriteLine(x); // strictly positive + } +} + +// semmle-extractor-options: /r:System.Linq.dll \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected new file mode 100644 index 000000000000..0e6cc20f05dc --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected @@ -0,0 +1,249 @@ +| SignAnalysis.cs:7:72:7:75 | 1000 | strictlyPositive | +| SignAnalysis.cs:7:80:7:82 | 500 | strictlyPositive | +| SignAnalysis.cs:18:20:18:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:24:20:24:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:30:20:30:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:36:20:36:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:42:20:42:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:45:24:45:24 | 1 | strictlyPositive | +| SignAnalysis.cs:49:20:49:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:59:17:59:25 | Int32 x = ... | strictlyNegative | +| SignAnalysis.cs:59:21:59:21 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:59:21:59:25 | ... + ... | strictlyNegative | +| SignAnalysis.cs:59:25:59:25 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:60:38:60:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:61:13:61:21 | ... = ... | strictlyPositive | +| SignAnalysis.cs:61:17:61:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:61:17:61:21 | ... * ... | strictlyPositive | +| SignAnalysis.cs:61:21:61:21 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:62:38:62:38 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:63:13:63:21 | ... = ... | positive | +| SignAnalysis.cs:63:17:63:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:63:17:63:21 | ... / ... | positive | +| SignAnalysis.cs:63:21:63:21 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:64:38:64:38 | access to local variable x | positive | +| SignAnalysis.cs:65:17:65:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:65:21:65:21 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:67:13:67:21 | ... = ... | negative | +| SignAnalysis.cs:67:17:67:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:67:17:67:21 | ... % ... | negative | +| SignAnalysis.cs:67:21:67:21 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:68:38:68:38 | access to local variable x | negative | +| SignAnalysis.cs:69:13:69:19 | ... = ... | strictlyNegative | +| SignAnalysis.cs:69:17:69:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:69:17:69:19 | ...++ | strictlyNegative | +| SignAnalysis.cs:70:38:70:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:71:13:71:19 | ... = ... | negative | +| SignAnalysis.cs:71:17:71:17 | access to parameter i | negative | +| SignAnalysis.cs:71:17:71:19 | ...-- | negative | +| SignAnalysis.cs:72:38:72:38 | access to local variable x | negative | +| SignAnalysis.cs:73:13:73:18 | ... = ... | strictlyPositive | +| SignAnalysis.cs:73:17:73:18 | -... | strictlyPositive | +| SignAnalysis.cs:73:18:73:18 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:74:38:74:38 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:75:13:75:18 | ... = ... | strictlyNegative | +| SignAnalysis.cs:75:17:75:18 | +... | strictlyNegative | +| SignAnalysis.cs:75:18:75:18 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:76:38:76:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:77:17:77:27 | Int64 l = ... | strictlyNegative | +| SignAnalysis.cs:77:21:77:27 | (...) ... | strictlyNegative | +| SignAnalysis.cs:77:27:77:27 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:78:38:78:38 | access to local variable l | strictlyNegative | +| SignAnalysis.cs:80:13:80:17 | ... = ... | strictlyNegative | +| SignAnalysis.cs:80:17:80:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:81:13:81:13 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:81:13:81:18 | ... + ... | strictlyNegative | +| SignAnalysis.cs:81:13:81:18 | ... += ... | strictlyNegative | +| SignAnalysis.cs:81:13:81:18 | ... = ... | strictlyNegative | +| SignAnalysis.cs:81:18:81:18 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:82:38:82:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:87:21:87:21 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:87:25:87:25 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:89:13:89:21 | ... = ... | strictlyNegative | +| SignAnalysis.cs:89:17:89:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:89:17:89:21 | ... * ... | strictlyNegative | +| SignAnalysis.cs:89:21:89:21 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:90:38:90:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:91:13:91:21 | ... = ... | negative | +| SignAnalysis.cs:91:17:91:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:91:17:91:21 | ... / ... | negative | +| SignAnalysis.cs:91:21:91:21 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:92:38:92:38 | access to local variable x | negative | +| SignAnalysis.cs:93:13:93:21 | ... = ... | strictlyNegative | +| SignAnalysis.cs:93:17:93:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:93:17:93:21 | ... - ... | strictlyNegative | +| SignAnalysis.cs:93:21:93:21 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:94:38:94:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:95:13:95:21 | ... = ... | negative | +| SignAnalysis.cs:95:17:95:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:95:17:95:21 | ... % ... | negative | +| SignAnalysis.cs:95:21:95:21 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:96:38:96:38 | access to local variable x | negative | +| SignAnalysis.cs:97:21:97:21 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:97:25:97:25 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:104:13:104:20 | Single f = ... | strictlyPositive | +| SignAnalysis.cs:104:17:104:20 | 4.2 | strictlyPositive | +| SignAnalysis.cs:105:34:105:34 | access to local variable f | strictlyPositive | +| SignAnalysis.cs:106:13:106:19 | Double d = ... | strictlyPositive | +| SignAnalysis.cs:106:17:106:19 | 4.2 | strictlyPositive | +| SignAnalysis.cs:107:34:107:34 | access to local variable d | strictlyPositive | +| SignAnalysis.cs:108:13:108:21 | Decimal de = ... | strictlyPositive | +| SignAnalysis.cs:108:18:108:21 | 4.2 | strictlyPositive | +| SignAnalysis.cs:109:34:109:35 | access to local variable de | strictlyPositive | +| SignAnalysis.cs:110:13:110:13 | access to local variable c | positive | +| SignAnalysis.cs:110:13:110:19 | Char c = ... | strictlyPositive | +| SignAnalysis.cs:110:17:110:19 | a | strictlyPositive | +| SignAnalysis.cs:111:34:111:34 | access to local variable c | strictlyPositive | +| SignAnalysis.cs:120:9:120:10 | access to field f0 | positive | +| SignAnalysis.cs:120:9:120:12 | ...++ | positive | +| SignAnalysis.cs:121:34:121:35 | access to field f0 | strictlyPositive | +| SignAnalysis.cs:122:9:122:10 | access to field f0 | positive | +| SignAnalysis.cs:129:9:129:16 | ... = ... | strictlyNegative | +| SignAnalysis.cs:129:14:129:16 | -... | strictlyNegative | +| SignAnalysis.cs:129:15:129:16 | 10 | strictlyPositive | +| SignAnalysis.cs:141:34:141:34 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:148:20:148:20 | access to parameter x | strictlyNegative | +| SignAnalysis.cs:151:18:151:18 | 1 | strictlyPositive | +| SignAnalysis.cs:153:20:153:20 | access to parameter y | strictlyPositive | +| SignAnalysis.cs:156:18:156:19 | -... | strictlyNegative | +| SignAnalysis.cs:156:19:156:19 | 1 | strictlyPositive | +| SignAnalysis.cs:158:20:158:20 | access to parameter y | strictlyNegative | +| SignAnalysis.cs:161:13:161:13 | access to parameter x | positive | +| SignAnalysis.cs:163:20:163:20 | access to parameter y | strictlyPositive | +| SignAnalysis.cs:166:22:166:22 | 1 | strictlyPositive | +| SignAnalysis.cs:169:20:169:20 | access to parameter y | strictlyPositive | +| SignAnalysis.cs:177:13:177:17 | Int32 i = ... | strictlyPositive | +| SignAnalysis.cs:177:17:177:17 | 1 | strictlyPositive | +| SignAnalysis.cs:178:13:178:13 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:180:38:180:38 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:186:34:186:44 | access to property Length | positive | +| SignAnalysis.cs:187:16:187:36 | 3 | strictlyPositive | +| SignAnalysis.cs:187:28:187:28 | 1 | strictlyPositive | +| SignAnalysis.cs:187:31:187:31 | 2 | strictlyPositive | +| SignAnalysis.cs:187:34:187:34 | 3 | strictlyPositive | +| SignAnalysis.cs:188:34:188:44 | access to property Length | strictlyPositive | +| SignAnalysis.cs:189:34:189:45 | call to method Count | positive | +| SignAnalysis.cs:190:34:190:55 | call to method Count | positive | +| SignAnalysis.cs:190:54:190:54 | 1 | strictlyPositive | +| SignAnalysis.cs:193:34:193:41 | access to property Length | positive | +| SignAnalysis.cs:196:34:196:51 | call to method Count | positive | +| SignAnalysis.cs:198:17:198:59 | 2 | strictlyPositive | +| SignAnalysis.cs:198:17:198:59 | 3 | strictlyPositive | +| SignAnalysis.cs:198:32:198:32 | 1 | strictlyPositive | +| SignAnalysis.cs:198:35:198:35 | 1 | strictlyPositive | +| SignAnalysis.cs:198:42:198:42 | 1 | strictlyPositive | +| SignAnalysis.cs:198:45:198:45 | 2 | strictlyPositive | +| SignAnalysis.cs:198:52:198:52 | 1 | strictlyPositive | +| SignAnalysis.cs:198:55:198:55 | 3 | strictlyPositive | +| SignAnalysis.cs:199:34:199:41 | access to property Length | strictlyPositive | +| SignAnalysis.cs:206:38:206:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:210:38:210:38 | access to parameter i | negative | +| SignAnalysis.cs:219:38:219:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:223:17:223:17 | access to parameter i | negative | +| SignAnalysis.cs:225:42:225:42 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:236:38:236:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:240:17:240:17 | access to parameter i | negative | +| SignAnalysis.cs:242:42:242:42 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:257:17:257:17 | access to parameter i | positive | +| SignAnalysis.cs:257:17:257:19 | ...-- | positive | +| SignAnalysis.cs:260:38:260:38 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:267:17:267:17 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:267:17:267:19 | ...-- | strictlyPositive | +| SignAnalysis.cs:268:42:268:42 | access to parameter j | positive | +| SignAnalysis.cs:270:38:270:38 | access to parameter j | negative | +| SignAnalysis.cs:277:17:277:17 | access to parameter k | strictlyPositive | +| SignAnalysis.cs:277:17:277:19 | ...-- | strictlyPositive | +| SignAnalysis.cs:278:42:278:42 | access to parameter k | positive | +| SignAnalysis.cs:280:21:280:21 | access to parameter k | positive | +| SignAnalysis.cs:280:26:280:26 | 5 | strictlyPositive | +| SignAnalysis.cs:292:34:292:34 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:295:38:295:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:300:27:300:28 | -... | strictlyNegative | +| SignAnalysis.cs:300:28:300:28 | 1 | strictlyPositive | +| SignAnalysis.cs:303:38:303:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:306:21:306:22 | -... | strictlyNegative | +| SignAnalysis.cs:306:22:306:22 | 1 | strictlyPositive | +| SignAnalysis.cs:309:38:309:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:315:13:315:15 | access to local variable min | positive | +| SignAnalysis.cs:316:13:316:15 | access to local variable max | positive | +| SignAnalysis.cs:316:13:316:31 | Char max = ... | strictlyPositive | +| SignAnalysis.cs:316:19:316:31 | access to constant MaxValue | strictlyPositive | +| SignAnalysis.cs:317:13:317:23 | Int32 c = ... | strictlyPositive | +| SignAnalysis.cs:317:17:317:23 | ... + ... | strictlyPositive | +| SignAnalysis.cs:317:23:317:23 | 1 | strictlyPositive | +| SignAnalysis.cs:318:34:318:34 | access to local variable c | strictlyPositive | +| SignAnalysis.cs:319:9:319:19 | ... = ... | strictlyNegative | +| SignAnalysis.cs:319:13:319:19 | ... - ... | strictlyNegative | +| SignAnalysis.cs:319:19:319:19 | 1 | strictlyPositive | +| SignAnalysis.cs:320:34:320:34 | access to local variable c | strictlyNegative | +| SignAnalysis.cs:321:9:321:19 | ... = ... | strictlyPositive | +| SignAnalysis.cs:321:13:321:15 | (...) ... | strictlyPositive | +| SignAnalysis.cs:321:13:321:15 | access to local variable max | strictlyPositive | +| SignAnalysis.cs:321:13:321:19 | ... + ... | strictlyPositive | +| SignAnalysis.cs:321:19:321:19 | 1 | strictlyPositive | +| SignAnalysis.cs:322:34:322:34 | access to local variable c | strictlyPositive | +| SignAnalysis.cs:329:17:329:26 | Int32 x = ... | strictlyPositive | +| SignAnalysis.cs:329:21:329:21 | access to parameter v | strictlyPositive | +| SignAnalysis.cs:329:21:329:26 | ... ?? ... | strictlyPositive | +| SignAnalysis.cs:329:26:329:26 | 1 | strictlyPositive | +| SignAnalysis.cs:330:38:330:38 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:335:17:335:26 | Int32 x = ... | strictlyPositive | +| SignAnalysis.cs:335:21:335:26 | ... ?? ... | strictlyPositive | +| SignAnalysis.cs:335:26:335:26 | 1 | strictlyPositive | +| SignAnalysis.cs:336:38:336:38 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:341:17:341:26 | Int32 x = ... | negative | +| SignAnalysis.cs:341:21:341:21 | access to parameter v | strictlyNegative | +| SignAnalysis.cs:341:21:341:26 | ... ?? ... | negative | +| SignAnalysis.cs:342:38:342:38 | access to local variable x | negative | +| SignAnalysis.cs:348:62:348:62 | 5 | strictlyPositive | +| SignAnalysis.cs:351:38:351:38 | access to local variable i | strictlyNegative | +| SignAnalysis.cs:357:13:357:13 | access to parameter i | positive | +| SignAnalysis.cs:359:38:359:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:371:38:371:38 | access to local variable y | strictlyNegative | +| SignAnalysis.cs:377:16:377:17 | access to local variable dp | positive | +| SignAnalysis.cs:377:16:377:22 | Single* dp = ... | positive | +| SignAnalysis.cs:377:21:377:22 | &... | positive | +| SignAnalysis.cs:378:18:378:19 | access to local variable dp | positive | +| SignAnalysis.cs:381:38:381:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:385:50:385:99 | access to constant Explicit | strictlyPositive | +| SignAnalysis.cs:385:109:385:110 | 15 | strictlyPositive | +| SignAnalysis.cs:390:13:390:32 | Int32 x = ... | strictlyPositive | +| SignAnalysis.cs:390:17:390:32 | sizeof(..) | strictlyPositive | +| SignAnalysis.cs:391:34:391:34 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:396:13:400:9 | Int32 x = ... | positive | +| SignAnalysis.cs:396:17:400:9 | ... switch { ... } | positive | +| SignAnalysis.cs:399:13:399:18 | ... => ... | strictlyPositive | +| SignAnalysis.cs:399:18:399:18 | 2 | strictlyPositive | +| SignAnalysis.cs:401:34:401:34 | access to local variable x | positive | +| SignAnalysis.cs:406:13:406:17 | Int32 i = ... | strictlyPositive | +| SignAnalysis.cs:406:17:406:17 | 1 | strictlyPositive | +| SignAnalysis.cs:410:35:410:35 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:414:13:414:13 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:415:31:415:31 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:424:31:424:31 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:428:19:428:19 | access to constant A | strictlyPositive | +| SignAnalysis.cs:428:19:428:24 | ... = ... | strictlyPositive | +| SignAnalysis.cs:428:23:428:24 | 12 | strictlyPositive | +| SignAnalysis.cs:434:38:434:38 | access to local variable i | strictlyNegative | +| SignAnalysis.cs:440:23:440:25 | access to parameter src | positive | +| SignAnalysis.cs:440:29:440:31 | access to parameter dst | positive | +| SignAnalysis.cs:443:38:443:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:446:31:446:32 | 10 | strictlyPositive | +| SignAnalysis.cs:448:22:448:23 | access to local variable to | positive | +| SignAnalysis.cs:448:22:448:29 | Byte* to = ... | positive | +| SignAnalysis.cs:448:27:448:29 | (...) ... | positive | +| SignAnalysis.cs:450:38:450:44 | (...) ... | positive | +| SignAnalysis.cs:450:43:450:44 | access to local variable to | positive | +| SignAnalysis.cs:454:30:454:30 | 1 | strictlyPositive | +| SignAnalysis.cs:454:30:454:30 | (...) ... | strictlyPositive | +| SignAnalysis.cs:457:14:457:27 | Int64 l = ... | positive | +| SignAnalysis.cs:457:18:457:27 | (...) ... | positive | +| SignAnalysis.cs:457:18:457:27 | call to method Unsigned | positive | +| SignAnalysis.cs:458:13:458:13 | access to local variable l | positive | +| SignAnalysis.cs:460:38:460:38 | access to local variable l | strictlyPositive | +| SignAnalysis.cs:463:14:463:14 | access to local variable x | positive | +| SignAnalysis.cs:463:14:463:24 | UInt32 x = ... | positive | +| SignAnalysis.cs:463:18:463:24 | (...) ... | positive | +| SignAnalysis.cs:464:9:464:9 | access to local variable x | positive | +| SignAnalysis.cs:464:9:464:11 | ...++ | positive | +| SignAnalysis.cs:465:34:465:34 | access to local variable x | strictlyPositive | diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql new file mode 100644 index 000000000000..4350e8f17427 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql @@ -0,0 +1,21 @@ +import csharp +import semmle.code.csharp.dataflow.SignAnalysis + +string getASignString(Expr e) { + positive(e) and + not strictlyPositive(e) and + result = "positive" + or + negative(e) and + not strictlyNegative(e) and + result = "negative" + or + strictlyPositive(e) and + result = "strictlyPositive" + or + strictlyNegative(e) and + result = "strictlyNegative" +} + +from Expr e +select e, strictconcat(string s | s = getASignString(e) | s, " ") diff --git a/csharp/ql/test/library-tests/dataflow/ssa/Capture.cs b/csharp/ql/test/library-tests/dataflow/ssa/Capture.cs index 0ac3945abaa6..d11947afed69 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/Capture.cs +++ b/csharp/ql/test/library-tests/dataflow/ssa/Capture.cs @@ -236,4 +236,22 @@ void M1() M3(); System.Console.WriteLine(i); } + + void M2() + { + int i = 0; + void CaptureWrite() + { + i = 1; + } + + void CaptureAndRef(ref int j) + { + CaptureWrite(); + j = 2; + } + + CaptureAndRef(ref i); // explicit definition only (no call definition) + System.Console.WriteLine(i); + } } diff --git a/csharp/ql/test/library-tests/dataflow/ssa/DefAdjacentRead.expected b/csharp/ql/test/library-tests/dataflow/ssa/DefAdjacentRead.expected index c10891ec2526..1a46a32d8fdf 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/DefAdjacentRead.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/DefAdjacentRead.expected @@ -30,6 +30,8 @@ | Capture.cs:203:28:203:30 | eh2 | Capture.cs:203:28:203:45 | MyEventHandler eh2 = ... | Capture.cs:204:27:204:29 | access to local variable eh2 | | Capture.cs:210:24:210:24 | p | Capture.cs:210:24:210:59 | Process p = ... | Capture.cs:213:17:213:17 | access to local variable p | | Capture.cs:212:30:212:35 | exited | Capture.cs:212:30:212:71 | EventHandler exited = ... | Capture.cs:213:29:213:34 | access to local variable exited | +| Capture.cs:242:13:242:13 | i | Capture.cs:242:13:242:17 | Int32 i = ... | Capture.cs:254:27:254:27 | access to local variable i | +| Capture.cs:242:13:242:13 | i | Capture.cs:254:27:254:27 | access to local variable i | Capture.cs:255:34:255:34 | access to local variable i | | Consistency.cs:7:25:7:25 | b | Consistency.cs:7:25:7:25 | b | Consistency.cs:11:17:11:17 | access to parameter b | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | Int32 i = ... | Consistency.cs:16:17:16:17 | access to local variable i | | Consistency.cs:25:29:25:29 | c | Consistency.cs:25:29:25:29 | Consistency c | Consistency.cs:26:13:26:13 | access to local variable c | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/IsLiveOutRefParameterDefinition.expected b/csharp/ql/test/library-tests/dataflow/ssa/IsLiveOutRefParameterDefinition.expected index fd9801dc6c62..7b1f6cb97693 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/IsLiveOutRefParameterDefinition.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/IsLiveOutRefParameterDefinition.expected @@ -1,4 +1,5 @@ | Capture.cs:81:28:81:28 | i | Capture.cs:81:34:81:36 | SSA def(i) | +| Capture.cs:248:36:248:36 | j | Capture.cs:251:13:251:17 | SSA def(j) | | Consistency.cs:30:30:30:30 | c | Consistency.cs:32:9:32:29 | SSA def(c) | | DefUse.cs:114:42:114:42 | i | DefUse.cs:114:47:114:52 | SSA def(i) | | DefUse.cs:116:42:116:42 | i | DefUse.cs:116:47:116:51 | SSA def(i) | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SsaCapturedVariableDef.expected b/csharp/ql/test/library-tests/dataflow/ssa/SsaCapturedVariableDef.expected index e1b4a0b52b2f..baa9311a09b7 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SsaCapturedVariableDef.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SsaCapturedVariableDef.expected @@ -4,11 +4,11 @@ | in | Capture.cs:8:13:8:13 | x | Capture.cs:15:13:15:17 | SSA def(x) | Capture.cs:19:24:23:13 | SSA capture def(x) | Capture.cs:25:13:25:15 | delegate call | false | | in | Capture.cs:8:13:8:13 | x | Capture.cs:43:9:43:13 | SSA def(x) | Capture.cs:19:24:23:13 | SSA capture def(x) | Capture.cs:44:9:44:12 | call to method M | true | | in | Capture.cs:17:17:17:17 | y | Capture.cs:17:17:17:21 | SSA def(y) | Capture.cs:19:24:23:13 | SSA capture def(y) | Capture.cs:25:13:25:15 | delegate call | false | -| in | Capture.cs:59:13:59:13 | i | Capture.cs:59:13:59:17 | SSA def(i) | Capture.cs:60:31:60:38 | SSA capture def(i) | Capture.cs:61:9:61:25 | call to method Select | false | -| in | Capture.cs:67:13:67:13 | c | Capture.cs:67:13:67:19 | SSA def(c) | Capture.cs:68:32:68:49 | SSA capture def(c) | Capture.cs:68:18:68:50 | call to method Where | false | -| in | Capture.cs:67:13:67:13 | c | Capture.cs:67:13:67:19 | SSA def(c) | Capture.cs:69:9:69:62 | SSA capture def(c) | Capture.cs:70:9:70:25 | call to method Select | false | -| in | Capture.cs:75:13:75:13 | i | Capture.cs:75:13:75:17 | SSA def(i) | Capture.cs:76:67:76:81 | SSA capture def(i) | Capture.cs:77:9:77:25 | call to method Select | false | -| in | Capture.cs:85:13:85:13 | b | Capture.cs:85:13:85:20 | SSA def(b) | Capture.cs:86:68:86:73 | SSA capture def(b) | Capture.cs:87:9:87:24 | call to method Where | false | +| in | Capture.cs:59:13:59:13 | i | Capture.cs:59:13:59:17 | SSA def(i) | Capture.cs:60:31:60:38 | SSA capture def(i) | Capture.cs:61:9:61:25 | call to method Select | true | +| in | Capture.cs:67:13:67:13 | c | Capture.cs:67:13:67:19 | SSA def(c) | Capture.cs:68:32:68:49 | SSA capture def(c) | Capture.cs:68:18:68:50 | call to method Where | true | +| in | Capture.cs:67:13:67:13 | c | Capture.cs:67:13:67:19 | SSA def(c) | Capture.cs:69:9:69:62 | SSA capture def(c) | Capture.cs:70:9:70:25 | call to method Select | true | +| in | Capture.cs:75:13:75:13 | i | Capture.cs:75:13:75:17 | SSA def(i) | Capture.cs:76:67:76:81 | SSA capture def(i) | Capture.cs:77:9:77:25 | call to method Select | true | +| in | Capture.cs:85:13:85:13 | b | Capture.cs:85:13:85:20 | SSA def(b) | Capture.cs:86:68:86:73 | SSA capture def(b) | Capture.cs:87:9:87:24 | call to method Where | true | | in | Capture.cs:94:13:94:13 | y | Capture.cs:94:13:94:18 | SSA def(y) | Capture.cs:96:12:100:9 | SSA capture def(y) | Capture.cs:96:9:100:10 | call to local function fn | true | | in | Capture.cs:94:13:94:13 | y | Capture.cs:94:13:94:18 | SSA def(y) | Capture.cs:96:12:100:9 | SSA capture def(y) | Capture.cs:103:9:107:10 | call to local function fn | true | | in | Capture.cs:114:13:114:13 | a | Capture.cs:114:13:114:18 | SSA def(a) | Capture.cs:115:9:119:9 | SSA capture def(a) | Capture.cs:120:9:120:12 | call to local function M1 | false | @@ -16,23 +16,23 @@ | in | Capture.cs:182:17:182:17 | i | Capture.cs:188:13:188:17 | SSA def(i) | Capture.cs:183:13:186:13 | SSA capture def(i) | Capture.cs:189:13:189:17 | call to local function M11 | false | | in | Capture.cs:197:17:197:17 | i | Capture.cs:197:17:197:21 | SSA def(i) | Capture.cs:198:33:198:44 | SSA capture def(i) | Capture.cs:200:13:200:19 | delegate call | false | | in | Capture.cs:197:17:197:17 | i | Capture.cs:197:17:197:21 | SSA def(i) | Capture.cs:203:34:203:45 | SSA capture def(i) | Capture.cs:200:13:200:19 | delegate call | false | -| in | Capture.cs:209:17:209:17 | i | Capture.cs:209:17:209:21 | SSA def(i) | Capture.cs:212:39:212:71 | SSA capture def(i) | Capture.cs:213:17:213:24 | access to event Exited | false | +| in | Capture.cs:209:17:209:17 | i | Capture.cs:209:17:209:21 | SSA def(i) | Capture.cs:212:39:212:71 | SSA capture def(i) | Capture.cs:213:17:213:24 | access to event Exited | true | | in | Capture.cs:229:13:229:13 | i | Capture.cs:232:9:232:13 | SSA def(i) | Capture.cs:231:9:231:49 | SSA capture def(i) | Capture.cs:233:9:233:12 | call to local function M2 | false | | in | Fields.cs:77:13:77:13 | f | Fields.cs:77:13:77:45 | SSA def(f) | Fields.cs:78:27:78:54 | SSA capture def(f) | Fields.cs:81:9:81:11 | delegate call | false | | in | Fields.cs:77:13:77:13 | f | Fields.cs:77:13:77:45 | SSA def(f) | Fields.cs:78:27:78:54 | SSA capture def(f) | Fields.cs:86:9:86:47 | call to method Select | true | -| in | Fields.cs:78:23:78:23 | a | Fields.cs:78:23:78:54 | SSA def(a) | Fields.cs:86:24:86:46 | SSA capture def(a) | Fields.cs:86:9:86:47 | call to method Select | false | -| in | Fields.cs:79:23:79:23 | b | Fields.cs:79:23:79:35 | SSA def(b) | Fields.cs:89:24:89:46 | SSA capture def(b) | Fields.cs:89:9:89:47 | call to method Select | false | +| in | Fields.cs:78:23:78:23 | a | Fields.cs:78:23:78:54 | SSA def(a) | Fields.cs:86:24:86:46 | SSA capture def(a) | Fields.cs:86:9:86:47 | call to method Select | true | +| in | Fields.cs:79:23:79:23 | b | Fields.cs:79:23:79:35 | SSA def(b) | Fields.cs:89:24:89:46 | SSA capture def(b) | Fields.cs:89:9:89:47 | call to method Select | true | | in | Properties.cs:73:13:73:13 | f | Properties.cs:73:13:73:32 | SSA def(f) | Properties.cs:74:27:74:54 | SSA capture def(f) | Properties.cs:77:9:77:11 | delegate call | false | | in | Properties.cs:73:13:73:13 | f | Properties.cs:73:13:73:32 | SSA def(f) | Properties.cs:74:27:74:54 | SSA capture def(f) | Properties.cs:82:9:82:47 | call to method Select | true | -| in | Properties.cs:74:23:74:23 | a | Properties.cs:74:23:74:54 | SSA def(a) | Properties.cs:82:24:82:46 | SSA capture def(a) | Properties.cs:82:9:82:47 | call to method Select | false | -| in | Properties.cs:75:23:75:23 | b | Properties.cs:75:23:75:35 | SSA def(b) | Properties.cs:85:24:85:46 | SSA capture def(b) | Properties.cs:85:9:85:47 | call to method Select | false | +| in | Properties.cs:74:23:74:23 | a | Properties.cs:74:23:74:54 | SSA def(a) | Properties.cs:82:24:82:46 | SSA capture def(a) | Properties.cs:82:9:82:47 | call to method Select | true | +| in | Properties.cs:75:23:75:23 | b | Properties.cs:75:23:75:35 | SSA def(b) | Properties.cs:85:24:85:46 | SSA capture def(b) | Properties.cs:85:9:85:47 | call to method Select | true | | out | Capture.cs:6:16:6:16 | i | Capture.cs:13:13:13:17 | SSA def(i) | Capture.cs:38:9:38:11 | SSA call def(i) | Capture.cs:38:9:38:11 | delegate call | false | | out | Capture.cs:8:13:8:13 | x | Capture.cs:15:13:15:17 | SSA def(x) | Capture.cs:38:9:38:11 | SSA call def(x) | Capture.cs:38:9:38:11 | delegate call | false | | out | Capture.cs:8:13:8:13 | x | Capture.cs:15:13:15:17 | SSA def(x) | Capture.cs:44:9:44:12 | SSA call def(x) | Capture.cs:44:9:44:12 | call to method M | true | | out | Capture.cs:29:13:29:13 | z | Capture.cs:30:28:30:32 | SSA def(z) | Capture.cs:32:9:32:11 | SSA call def(z) | Capture.cs:32:9:32:11 | delegate call | false | | out | Capture.cs:50:20:50:20 | a | Capture.cs:52:28:52:40 | SSA def(a) | Capture.cs:53:9:53:11 | SSA call def(a) | Capture.cs:53:9:53:11 | delegate call | false | -| out | Capture.cs:59:13:59:13 | i | Capture.cs:60:36:60:38 | SSA def(i) | Capture.cs:61:9:61:25 | SSA call def(i) | Capture.cs:61:9:61:25 | call to method Select | false | -| out | Capture.cs:75:13:75:13 | i | Capture.cs:76:80:76:80 | SSA def(i) | Capture.cs:77:9:77:25 | SSA call def(i) | Capture.cs:77:9:77:25 | call to method Select | false | +| out | Capture.cs:59:13:59:13 | i | Capture.cs:60:36:60:38 | SSA def(i) | Capture.cs:61:9:61:25 | SSA call def(i) | Capture.cs:61:9:61:25 | call to method Select | true | +| out | Capture.cs:75:13:75:13 | i | Capture.cs:76:80:76:80 | SSA def(i) | Capture.cs:77:9:77:25 | SSA call def(i) | Capture.cs:77:9:77:25 | call to method Select | true | | out | Capture.cs:130:13:130:13 | c | Capture.cs:133:13:133:17 | SSA def(c) | Capture.cs:136:9:136:12 | SSA call def(c) | Capture.cs:136:9:136:12 | call to local function M3 | false | | out | Capture.cs:139:13:139:13 | d | Capture.cs:142:13:142:17 | SSA def(d) | Capture.cs:144:9:144:12 | SSA call def(d) | Capture.cs:144:9:144:12 | call to local function M4 | false | | out | Capture.cs:168:13:168:13 | h | Capture.cs:174:17:174:21 | SSA def(h) | Capture.cs:176:13:176:16 | SSA call def(h) | Capture.cs:176:13:176:16 | call to local function M9 | false | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SsaDef.expected b/csharp/ql/test/library-tests/dataflow/ssa/SsaDef.expected index b8d50de56515..64b2ff156752 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SsaDef.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SsaDef.expected @@ -82,6 +82,9 @@ | Capture.cs:229:13:229:13 | i | Capture.cs:232:9:232:13 | SSA def(i) | | Capture.cs:229:13:229:13 | i | Capture.cs:235:21:235:25 | SSA def(i) | | Capture.cs:229:13:229:13 | i | Capture.cs:236:9:236:12 | SSA call def(i) | +| Capture.cs:242:13:242:13 | i | Capture.cs:242:13:242:17 | SSA def(i) | +| Capture.cs:242:13:242:13 | i | Capture.cs:254:27:254:27 | SSA def(i) | +| Capture.cs:248:36:248:36 | j | Capture.cs:251:13:251:17 | SSA def(j) | | Consistency.cs:7:25:7:25 | b | Consistency.cs:7:25:7:25 | SSA param(b) | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | SSA def(i) | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | [finally: exception(Exception)] SSA def(i) | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SsaDefElement.expected b/csharp/ql/test/library-tests/dataflow/ssa/SsaDefElement.expected index d824b98a50dc..bc176624df5c 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SsaDefElement.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SsaDefElement.expected @@ -82,6 +82,9 @@ | Capture.cs:232:9:232:13 | SSA def(i) | Capture.cs:232:9:232:13 | ... = ... | | Capture.cs:235:21:235:25 | SSA def(i) | Capture.cs:235:21:235:25 | ... = ... | | Capture.cs:236:9:236:12 | SSA call def(i) | Capture.cs:236:9:236:12 | call to local function M3 | +| Capture.cs:242:13:242:17 | SSA def(i) | Capture.cs:242:13:242:17 | Int32 i = ... | +| Capture.cs:251:13:251:17 | SSA def(j) | Capture.cs:251:13:251:17 | ... = ... | +| Capture.cs:254:27:254:27 | SSA def(i) | Capture.cs:254:9:254:28 | call to local function CaptureAndRef | | Consistency.cs:7:25:7:25 | SSA param(b) | Consistency.cs:7:25:7:25 | b | | Consistency.cs:15:17:15:21 | SSA def(i) | Consistency.cs:15:17:15:21 | Int32 i = ... | | Consistency.cs:15:17:15:21 | [finally: exception(Exception)] SSA def(i) | Consistency.cs:15:17:15:21 | Int32 i = ... | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SsaDefLastRead.expected b/csharp/ql/test/library-tests/dataflow/ssa/SsaDefLastRead.expected index 2dcd936457ec..72fc8e58359f 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SsaDefLastRead.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SsaDefLastRead.expected @@ -58,6 +58,8 @@ | Capture.cs:212:30:212:35 | exited | Capture.cs:212:30:212:71 | SSA def(exited) | Capture.cs:213:29:213:34 | access to local variable exited | | Capture.cs:229:13:229:13 | i | Capture.cs:231:9:231:49 | SSA capture def(i) | Capture.cs:231:47:231:47 | access to local variable i | | Capture.cs:229:13:229:13 | i | Capture.cs:236:9:236:12 | SSA call def(i) | Capture.cs:237:34:237:34 | access to local variable i | +| Capture.cs:242:13:242:13 | i | Capture.cs:242:13:242:17 | SSA def(i) | Capture.cs:254:27:254:27 | access to local variable i | +| Capture.cs:242:13:242:13 | i | Capture.cs:254:27:254:27 | SSA def(i) | Capture.cs:255:34:255:34 | access to local variable i | | Consistency.cs:7:25:7:25 | b | Consistency.cs:7:25:7:25 | SSA param(b) | Consistency.cs:11:17:11:17 | access to parameter b | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | SSA def(i) | Consistency.cs:16:17:16:17 | access to local variable i | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | [finally: exception(Exception)] SSA def(i) | Consistency.cs:16:17:16:17 | access to local variable i | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SsaExplicitDef.expected b/csharp/ql/test/library-tests/dataflow/ssa/SsaExplicitDef.expected index b25dbee614e4..6f5c8a4238b6 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SsaExplicitDef.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SsaExplicitDef.expected @@ -54,6 +54,9 @@ | Capture.cs:212:30:212:35 | exited | Capture.cs:212:30:212:71 | SSA def(exited) | Capture.cs:212:30:212:71 | EventHandler exited = ... | | Capture.cs:229:13:229:13 | i | Capture.cs:232:9:232:13 | SSA def(i) | Capture.cs:232:9:232:13 | ... = ... | | Capture.cs:229:13:229:13 | i | Capture.cs:235:21:235:25 | SSA def(i) | Capture.cs:235:21:235:25 | ... = ... | +| Capture.cs:242:13:242:13 | i | Capture.cs:242:13:242:17 | SSA def(i) | Capture.cs:242:13:242:17 | Int32 i = ... | +| Capture.cs:242:13:242:13 | i | Capture.cs:254:27:254:27 | SSA def(i) | Capture.cs:254:27:254:27 | access to local variable i | +| Capture.cs:248:36:248:36 | j | Capture.cs:251:13:251:17 | SSA def(j) | Capture.cs:251:13:251:17 | ... = ... | | Consistency.cs:7:25:7:25 | b | Consistency.cs:7:25:7:25 | SSA param(b) | Consistency.cs:7:25:7:25 | b | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | SSA def(i) | Consistency.cs:15:17:15:21 | Int32 i = ... | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | [finally: exception(Exception)] SSA def(i) | Consistency.cs:15:17:15:21 | Int32 i = ... | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SsaRead.expected b/csharp/ql/test/library-tests/dataflow/ssa/SsaRead.expected index 04134b73604f..1d236dd6588c 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SsaRead.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SsaRead.expected @@ -63,6 +63,8 @@ | Capture.cs:212:30:212:35 | exited | Capture.cs:212:30:212:71 | SSA def(exited) | Capture.cs:213:29:213:34 | access to local variable exited | | Capture.cs:229:13:229:13 | i | Capture.cs:231:9:231:49 | SSA capture def(i) | Capture.cs:231:47:231:47 | access to local variable i | | Capture.cs:229:13:229:13 | i | Capture.cs:236:9:236:12 | SSA call def(i) | Capture.cs:237:34:237:34 | access to local variable i | +| Capture.cs:242:13:242:13 | i | Capture.cs:242:13:242:17 | SSA def(i) | Capture.cs:254:27:254:27 | access to local variable i | +| Capture.cs:242:13:242:13 | i | Capture.cs:254:27:254:27 | SSA def(i) | Capture.cs:255:34:255:34 | access to local variable i | | Consistency.cs:7:25:7:25 | b | Consistency.cs:7:25:7:25 | SSA param(b) | Consistency.cs:11:17:11:17 | access to parameter b | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | SSA def(i) | Consistency.cs:16:17:16:17 | access to local variable i | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | [finally: exception(Exception)] SSA def(i) | Consistency.cs:16:17:16:17 | access to local variable i | diff --git a/csharp/ql/test/library-tests/dataflow/ssa/SsaUltimateDef.expected b/csharp/ql/test/library-tests/dataflow/ssa/SsaUltimateDef.expected index 91dce3c7740d..dd7aa8623b79 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/SsaUltimateDef.expected +++ b/csharp/ql/test/library-tests/dataflow/ssa/SsaUltimateDef.expected @@ -93,6 +93,9 @@ | Capture.cs:229:13:229:13 | i | Capture.cs:235:21:235:25 | SSA def(i) | Capture.cs:235:21:235:25 | SSA def(i) | | Capture.cs:229:13:229:13 | i | Capture.cs:236:9:236:12 | SSA call def(i) | Capture.cs:232:9:232:13 | SSA def(i) | | Capture.cs:229:13:229:13 | i | Capture.cs:236:9:236:12 | SSA call def(i) | Capture.cs:236:9:236:12 | SSA call def(i) | +| Capture.cs:242:13:242:13 | i | Capture.cs:242:13:242:17 | SSA def(i) | Capture.cs:242:13:242:17 | SSA def(i) | +| Capture.cs:242:13:242:13 | i | Capture.cs:254:27:254:27 | SSA def(i) | Capture.cs:254:27:254:27 | SSA def(i) | +| Capture.cs:248:36:248:36 | j | Capture.cs:251:13:251:17 | SSA def(j) | Capture.cs:251:13:251:17 | SSA def(j) | | Consistency.cs:7:25:7:25 | b | Consistency.cs:7:25:7:25 | SSA param(b) | Consistency.cs:7:25:7:25 | SSA param(b) | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | SSA def(i) | Consistency.cs:15:17:15:21 | SSA def(i) | | Consistency.cs:15:17:15:17 | i | Consistency.cs:15:17:15:21 | [finally: exception(Exception)] SSA def(i) | Consistency.cs:15:17:15:21 | [finally: exception(Exception)] SSA def(i) | diff --git a/csharp/ql/test/library-tests/dataflow/types/Types.cs b/csharp/ql/test/library-tests/dataflow/types/Types.cs index 92f60ec551e3..9cb505004b79 100644 --- a/csharp/ql/test/library-tests/dataflow/types/Types.cs +++ b/csharp/ql/test/library-tests/dataflow/types/Types.cs @@ -128,4 +128,28 @@ void M10() } static object Through(object x) => x; + + class FieldA + { + public object Field; + + public virtual void M() { } + + public void CallM() => this.M(); + + static void M1(FieldB b, FieldC c) + { + b.Field = new object(); + b.CallM(); // no flow + c.Field = new object(); + c.CallM(); // flow + } + } + + class FieldB : FieldA { } + + class FieldC : FieldA + { + public override void M() => Sink(this.Field); + } } diff --git a/csharp/ql/test/library-tests/dataflow/types/Types.expected b/csharp/ql/test/library-tests/dataflow/types/Types.expected index 3101be8d8540..43b06fb7cba8 100644 --- a/csharp/ql/test/library-tests/dataflow/types/Types.expected +++ b/csharp/ql/test/library-tests/dataflow/types/Types.expected @@ -43,6 +43,13 @@ edges | Types.cs:121:26:121:33 | object creation of type E2 : Types.E.E2 | Types.cs:123:30:123:31 | access to local variable e2 : Types.E.E2 | | Types.cs:122:30:122:30 | access to local variable a : A | Types.cs:122:22:122:31 | call to method Through | | Types.cs:123:30:123:31 | access to local variable e2 : Types.E.E2 | Types.cs:123:22:123:32 | call to method Through | +| Types.cs:138:21:138:25 | this [Field] : Object | Types.cs:138:32:138:35 | this access [Field] : Object | +| Types.cs:138:32:138:35 | this access [Field] : Object | Types.cs:153:30:153:30 | this [Field] : Object | +| Types.cs:144:13:144:13 | [post] access to parameter c [Field] : Object | Types.cs:145:13:145:13 | access to parameter c [Field] : Object | +| Types.cs:144:23:144:34 | object creation of type Object : Object | Types.cs:144:13:144:13 | [post] access to parameter c [Field] : Object | +| Types.cs:145:13:145:13 | access to parameter c [Field] : Object | Types.cs:138:21:138:25 | this [Field] : Object | +| Types.cs:153:30:153:30 | this [Field] : Object | Types.cs:153:42:153:45 | this access [Field] : Object | +| Types.cs:153:42:153:45 | this access [Field] : Object | Types.cs:153:42:153:51 | access to field Field | nodes | Types.cs:7:21:7:25 | this : D | semmle.label | this : D | | Types.cs:7:32:7:35 | this access : D | semmle.label | this access : D | @@ -100,21 +107,30 @@ nodes | Types.cs:122:30:122:30 | access to local variable a : A | semmle.label | access to local variable a : A | | Types.cs:123:22:123:32 | call to method Through | semmle.label | call to method Through | | Types.cs:123:30:123:31 | access to local variable e2 : Types.E.E2 | semmle.label | access to local variable e2 : Types.E.E2 | +| Types.cs:138:21:138:25 | this [Field] : Object | semmle.label | this [Field] : Object | +| Types.cs:138:32:138:35 | this access [Field] : Object | semmle.label | this access [Field] : Object | +| Types.cs:144:13:144:13 | [post] access to parameter c [Field] : Object | semmle.label | [post] access to parameter c [Field] : Object | +| Types.cs:144:23:144:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| Types.cs:145:13:145:13 | access to parameter c [Field] : Object | semmle.label | access to parameter c [Field] : Object | +| Types.cs:153:30:153:30 | this [Field] : Object | semmle.label | this [Field] : Object | +| Types.cs:153:42:153:45 | this access [Field] : Object | semmle.label | this access [Field] : Object | +| Types.cs:153:42:153:51 | access to field Field | semmle.label | access to field Field | #select -| Types.cs:23:12:23:18 | object creation of type C : C | Types.cs:50:18:50:18 | access to local variable c | Types.cs:50:18:50:18 | access to local variable c | $@ | Types.cs:50:18:50:18 | access to local variable c | access to local variable c | -| Types.cs:25:12:25:18 | object creation of type C : C | Types.cs:63:33:63:36 | (...) ... | Types.cs:63:33:63:36 | (...) ... | $@ | Types.cs:63:33:63:36 | (...) ... | (...) ... | -| Types.cs:26:12:26:18 | object creation of type C : C | Types.cs:65:36:65:36 | access to parameter x | Types.cs:65:36:65:36 | access to parameter x | $@ | Types.cs:65:36:65:36 | access to parameter x | access to parameter x | -| Types.cs:27:12:27:18 | object creation of type C : C | Types.cs:67:48:67:48 | access to parameter x | Types.cs:67:48:67:48 | access to parameter x | $@ | Types.cs:67:48:67:48 | access to parameter x | access to parameter x | -| Types.cs:28:12:28:18 | object creation of type C : C | Types.cs:69:52:69:52 | access to parameter x | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x | -| Types.cs:30:12:30:18 | object creation of type C : C | Types.cs:80:18:80:18 | access to local variable b | Types.cs:80:18:80:18 | access to local variable b | $@ | Types.cs:80:18:80:18 | access to local variable b | access to local variable b | -| Types.cs:32:9:32:15 | object creation of type D : D | Types.cs:16:42:16:45 | this access | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | -| Types.cs:33:9:33:15 | object creation of type D : D | Types.cs:16:42:16:45 | this access | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | -| Types.cs:35:12:35:18 | object creation of type D : D | Types.cs:58:22:58:22 | access to local variable d | Types.cs:58:22:58:22 | access to local variable d | $@ | Types.cs:58:22:58:22 | access to local variable d | access to local variable d | -| Types.cs:37:12:37:18 | object creation of type D : D | Types.cs:65:36:65:36 | access to parameter x | Types.cs:65:36:65:36 | access to parameter x | $@ | Types.cs:65:36:65:36 | access to parameter x | access to parameter x | -| Types.cs:38:12:38:18 | object creation of type D : D | Types.cs:67:48:67:48 | access to parameter x | Types.cs:67:48:67:48 | access to parameter x | $@ | Types.cs:67:48:67:48 | access to parameter x | access to parameter x | -| Types.cs:39:12:39:18 | object creation of type D : D | Types.cs:69:52:69:52 | access to parameter x | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x | -| Types.cs:40:12:40:18 | object creation of type D : D | Types.cs:16:42:16:45 | this access | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | -| Types.cs:43:20:43:23 | null : null | Types.cs:44:14:44:14 | access to local variable o | Types.cs:44:14:44:14 | access to local variable o | $@ | Types.cs:44:14:44:14 | access to local variable o | access to local variable o | -| Types.cs:110:25:110:32 | object creation of type E2 : Types.E.E2 | Types.cs:115:22:115:31 | access to field Field | Types.cs:115:22:115:31 | access to field Field | $@ | Types.cs:115:22:115:31 | access to field Field | access to field Field | -| Types.cs:120:25:120:31 | object creation of type A : A | Types.cs:122:22:122:31 | call to method Through | Types.cs:122:22:122:31 | call to method Through | $@ | Types.cs:122:22:122:31 | call to method Through | call to method Through | -| Types.cs:121:26:121:33 | object creation of type E2 : Types.E.E2 | Types.cs:123:22:123:32 | call to method Through | Types.cs:123:22:123:32 | call to method Through | $@ | Types.cs:123:22:123:32 | call to method Through | call to method Through | +| Types.cs:23:12:23:18 | object creation of type C : C | Types.cs:23:12:23:18 | object creation of type C : C | Types.cs:50:18:50:18 | access to local variable c | $@ | Types.cs:50:18:50:18 | access to local variable c | access to local variable c | +| Types.cs:25:12:25:18 | object creation of type C : C | Types.cs:25:12:25:18 | object creation of type C : C | Types.cs:63:33:63:36 | (...) ... | $@ | Types.cs:63:33:63:36 | (...) ... | (...) ... | +| Types.cs:26:12:26:18 | object creation of type C : C | Types.cs:26:12:26:18 | object creation of type C : C | Types.cs:65:36:65:36 | access to parameter x | $@ | Types.cs:65:36:65:36 | access to parameter x | access to parameter x | +| Types.cs:27:12:27:18 | object creation of type C : C | Types.cs:27:12:27:18 | object creation of type C : C | Types.cs:67:48:67:48 | access to parameter x | $@ | Types.cs:67:48:67:48 | access to parameter x | access to parameter x | +| Types.cs:28:12:28:18 | object creation of type C : C | Types.cs:28:12:28:18 | object creation of type C : C | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x | +| Types.cs:30:12:30:18 | object creation of type C : C | Types.cs:30:12:30:18 | object creation of type C : C | Types.cs:80:18:80:18 | access to local variable b | $@ | Types.cs:80:18:80:18 | access to local variable b | access to local variable b | +| Types.cs:32:9:32:15 | object creation of type D : D | Types.cs:32:9:32:15 | object creation of type D : D | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | +| Types.cs:33:9:33:15 | object creation of type D : D | Types.cs:33:9:33:15 | object creation of type D : D | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | +| Types.cs:35:12:35:18 | object creation of type D : D | Types.cs:35:12:35:18 | object creation of type D : D | Types.cs:58:22:58:22 | access to local variable d | $@ | Types.cs:58:22:58:22 | access to local variable d | access to local variable d | +| Types.cs:37:12:37:18 | object creation of type D : D | Types.cs:37:12:37:18 | object creation of type D : D | Types.cs:65:36:65:36 | access to parameter x | $@ | Types.cs:65:36:65:36 | access to parameter x | access to parameter x | +| Types.cs:38:12:38:18 | object creation of type D : D | Types.cs:38:12:38:18 | object creation of type D : D | Types.cs:67:48:67:48 | access to parameter x | $@ | Types.cs:67:48:67:48 | access to parameter x | access to parameter x | +| Types.cs:39:12:39:18 | object creation of type D : D | Types.cs:39:12:39:18 | object creation of type D : D | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x | +| Types.cs:40:12:40:18 | object creation of type D : D | Types.cs:40:12:40:18 | object creation of type D : D | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access | +| Types.cs:43:20:43:23 | null : null | Types.cs:43:20:43:23 | null : null | Types.cs:44:14:44:14 | access to local variable o | $@ | Types.cs:44:14:44:14 | access to local variable o | access to local variable o | +| Types.cs:110:25:110:32 | object creation of type E2 : Types.E.E2 | Types.cs:110:25:110:32 | object creation of type E2 : Types.E.E2 | Types.cs:115:22:115:31 | access to field Field | $@ | Types.cs:115:22:115:31 | access to field Field | access to field Field | +| Types.cs:120:25:120:31 | object creation of type A : A | Types.cs:120:25:120:31 | object creation of type A : A | Types.cs:122:22:122:31 | call to method Through | $@ | Types.cs:122:22:122:31 | call to method Through | call to method Through | +| Types.cs:121:26:121:33 | object creation of type E2 : Types.E.E2 | Types.cs:121:26:121:33 | object creation of type E2 : Types.E.E2 | Types.cs:123:22:123:32 | call to method Through | $@ | Types.cs:123:22:123:32 | call to method Through | call to method Through | +| Types.cs:144:23:144:34 | object creation of type Object : Object | Types.cs:144:23:144:34 | object creation of type Object : Object | Types.cs:153:42:153:51 | access to field Field | $@ | Types.cs:153:42:153:51 | access to field Field | access to field Field | diff --git a/csharp/ql/test/library-tests/dataflow/types/Types.ql b/csharp/ql/test/library-tests/dataflow/types/Types.ql index da3d8b63b616..57e42a5352ee 100644 --- a/csharp/ql/test/library-tests/dataflow/types/Types.ql +++ b/csharp/ql/test/library-tests/dataflow/types/Types.ql @@ -23,4 +23,4 @@ class Conf extends DataFlow::Configuration { from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf where conf.hasFlowPath(source, sink) -select source, sink, sink, "$@", sink, sink.toString() +select source, source, sink, "$@", sink, sink.toString() diff --git a/csharp/ql/test/library-tests/definitions/PrintAst.expected b/csharp/ql/test/library-tests/definitions/PrintAst.expected new file mode 100644 index 000000000000..5543a3363d5e --- /dev/null +++ b/csharp/ql/test/library-tests/definitions/PrintAst.expected @@ -0,0 +1,407 @@ +definitions.cs: +# 4| [NamespaceDeclaration] namespace ... { ... } +# 6| 1: [Class] StaticClass +# 8| 4: [ExtensionMethod] ExtensionMethod +#-----| 2: (Parameters) +# 8| 0: [Parameter] c1 +# 8| 1: [Parameter] args +# 9| 4: [BlockStmt] {...} +# 13| 2: [Enum] Enumeration +# 15| 5: [Field] e1 +# 15| 1: [AssignExpr] ... = ... +# 15| 0: [IntLiteral] 1 +# 15| 1: [MemberConstantAccess] access to constant e1 +# 15| 6: [Field] e2 +# 15| 1: [AssignExpr] ... = ... +# 15| 0: [IntLiteral] 2 +# 15| 1: [MemberConstantAccess] access to constant e2 +# 15| 7: [Field] e3 +# 18| 3: [Class] C1 +# 20| 4: [InstanceConstructor] C1 +#-----| 2: (Parameters) +# 20| 0: [Parameter] args +# 20| 4: [BlockStmt] {...} +# 22| 5: [Field] field1 +# 24| 6: [Property] property1 +# 26| 3: [Getter] get_property1 +# 26| 4: [BlockStmt] {...} +# 26| 0: [ReturnStmt] return ...; +# 26| 0: [FieldAccess] access to field field1 +# 27| 4: [Setter] set_property1 +#-----| 2: (Parameters) +# 27| 0: [Parameter] value +# 27| 4: [BlockStmt] {...} +# 27| 0: [ExprStmt] ...; +# 27| 0: [AssignExpr] ... = ... +# 27| 0: [ParameterAccess] access to parameter value +# 27| 1: [FieldAccess] access to field field1 +# 30| 7: [Method] f1 +#-----| 2: (Parameters) +# 30| 0: [Parameter] args +# 31| 4: [BlockStmt] {...} +# 32| 0: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclAndInitExpr] C1 qualifier = ... +# 32| 0: [ThisAccess] this access +# 32| 1: [LocalVariableAccess] access to local variable qualifier +# 35| 1: [ExprStmt] ...; +# 35| 0: [MethodCall] call to method f1 +# 36| 2: [ExprStmt] ...; +# 36| 0: [MethodCall] call to method f1 +# 36| 0: [IntLiteral] 1 +# 37| 3: [ExprStmt] ...; +# 37| 0: [MethodCall] call to method f1 +# 37| 0: [IntLiteral] 1 +# 37| 1: [IntLiteral] 2 +# 39| 4: [ExprStmt] ...; +# 39| 0: [MethodCall] call to method f1 +# 39| -1: [ThisAccess] this access +# 40| 5: [ExprStmt] ...; +# 40| 0: [MethodCall] call to method f1 +# 40| -1: [ThisAccess] this access +# 40| 0: [IntLiteral] 1 +# 41| 6: [ExprStmt] ...; +# 41| 0: [MethodCall] call to method f1 +# 41| -1: [ThisAccess] this access +# 41| 0: [IntLiteral] 1 +# 41| 1: [IntLiteral] 2 +# 43| 7: [ExprStmt] ...; +# 43| 0: [MethodCall] call to method ExtensionMethod +# 43| -1: [ThisAccess] this access +# 44| 8: [ExprStmt] ...; +# 44| 0: [MethodCall] call to method ExtensionMethod +# 44| -1: [ThisAccess] this access +# 44| 0: [CastExpr] (...) ... +# 44| 0: [IntLiteral] 1 +# 45| 9: [ExprStmt] ...; +# 45| 0: [MethodCall] call to method ExtensionMethod +# 45| -1: [ThisAccess] this access +# 45| 0: [CastExpr] (...) ... +# 45| 0: [IntLiteral] 1 +# 45| 1: [CastExpr] (...) ... +# 45| 0: [IntLiteral] 2 +# 47| 10: [ExprStmt] ...; +# 47| 0: [MethodCall] call to method GenericFn +# 47| 0: [IntLiteral] 1 +# 48| 11: [ExprStmt] ...; +# 48| 0: [MethodCall] call to method GenericFn +# 48| -1: [ThisAccess] this access +# 48| 0: [IntLiteral] 2 +# 51| 12: [ExprStmt] ...; +# 51| 0: [ObjectCreation] object creation of type C1 +# 52| 13: [ExprStmt] ...; +# 52| 0: [ObjectCreation] object creation of type C1 +# 52| 0: [IntLiteral] 1 +# 53| 14: [ExprStmt] ...; +# 53| 0: [ObjectCreation] object creation of type C1 +# 53| 0: [IntLiteral] 1 +# 53| 1: [IntLiteral] 2 +# 56| 15: [LocalVariableDeclStmt] ... ...; +# 56| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 56| 0: [IntLiteral] 2 +# 56| 1: [LocalVariableAccess] access to local variable x +# 56| 1: [LocalVariableDeclAndInitExpr] Int32 y = ... +# 56| 0: [LocalVariableAccess] access to local variable x +# 56| 1: [LocalVariableAccess] access to local variable y +# 56| 2: [LocalVariableDeclAndInitExpr] Int32 z = ... +# 56| 0: [FieldAccess] access to field field1 +# 56| 1: [LocalVariableAccess] access to local variable z +# 56| 3: [LocalVariableDeclAndInitExpr] Int32 w = ... +# 56| 0: [ArrayAccess] access to array element +# 56| -1: [ParameterAccess] access to parameter args +# 56| 0: [IntLiteral] 0 +# 56| 1: [LocalVariableAccess] access to local variable w +# 57| 16: [LocalVariableDeclStmt] ... ...; +# 57| 0: [LocalVariableDeclAndInitExpr] Enumeration e = ... +# 57| 0: [MemberConstantAccess] access to constant e1 +# 57| -1: [TypeAccess] access to type Enumeration +# 57| 1: [LocalVariableAccess] access to local variable e +# 60| 17: [ExprStmt] ...; +# 60| 0: [AssignExpr] ... = ... +# 60| 0: [AddExpr] ... + ... +# 60| 0: [PropertyCall] access to property property1 +# 60| 1: [IntLiteral] 1 +# 60| 1: [PropertyCall] access to property property1 +# 63| 18: [LocalVariableDeclStmt] ... ...; +# 63| 0: [LocalVariableDeclAndInitExpr] C1[] array = ... +# 63| 0: [NullLiteral] null +# 63| 1: [LocalVariableAccess] access to local variable array +# 64| 19: [LocalVariableDeclStmt] ... ...; +# 64| 0: [LocalVariableDeclAndInitExpr] Nullable nullable = ... +# 64| 0: [NullLiteral] null +# 64| 1: [LocalVariableAccess] access to local variable nullable +# 67| 20: [LocalVariableDeclStmt] ... ...; +# 67| 0: [LocalVariableDeclAndInitExpr] Action m1 = ... +# 67| 0: [ImplicitDelegateCreation] delegate creation of type Action +# 67| 0: [MethodAccess] access to method GenericFn +# 67| 1: [LocalVariableAccess] access to local variable m1 +# 70| 8: [Method] VariableTypeUse +#-----| 2: (Parameters) +# 70| 0: [Parameter] c1 +# 71| 4: [BlockStmt] {...} +# 72| 0: [LocalVariableDeclStmt] ... ...; +# 72| 0: [LocalVariableDeclAndInitExpr] C1 c2 = ... +# 72| 0: [NullLiteral] null +# 72| 1: [LocalVariableAccess] access to local variable c2 +# 75| 9: [Method] GenericFn +#-----| 1: (Type parameters) +# 75| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 75| 0: [Parameter] t +# 75| 4: [BlockStmt] {...} +# 78| 4: [Struct] S1 +# 80| 5: [Method] M +#-----| 2: (Parameters) +# 80| 0: [Parameter] ss +# 81| 4: [BlockStmt] {...} +# 82| 0: [TryStmt] try {...} ... +# 83| 0: [BlockStmt] {...} +# 84| 0: [LocalVariableDeclStmt] ... ...; +# 84| 0: [LocalVariableDeclAndInitExpr] String timestamp = ... +# 84| 0: [MethodCall] call to method ToString +# 84| -1: [PropertyCall] access to property Now +# 84| -1: [TypeAccess] access to type DateTime +# 84| 0: [StringLiteral] "HH:mm:ss" +# 84| 1: [LocalVariableAccess] access to local variable timestamp +# 86| 1: [SpecificCatchClause] catch (...) {...} +# 86| 0: [LocalVariableDeclExpr] Exception e +# 87| 1: [BlockStmt] {...} +# 88| 0: [ForeachStmt] foreach (... ... in ...) ... +# 88| 0: [LocalVariableDeclExpr] S1 s +# 88| 1: [ParameterAccess] access to parameter ss +# 89| 2: [BlockStmt] {...} +# 92| 1: [LocalVariableDeclStmt] ... ...; +# 92| 0: [LocalVariableDeclAndInitExpr] Type temp = ... +# 92| 0: [TypeofExpr] typeof(...) +# 92| 0: [TypeAccess] access to type S1[] +# 92| 1: [LocalVariableAccess] access to local variable temp +# 93| 2: [ReturnStmt] return ...; +# 93| 0: [ObjectCreation] object creation of type S1 +# 97| 5: [Class] A +# 99| 5: [DelegateType] EventHandler +# 101| 6: [Event] Click +# 101| 3: [AddEventAccessor] add_Click +#-----| 2: (Parameters) +# 101| 0: [Parameter] value +# 101| 4: [RemoveEventAccessor] remove_Click +#-----| 2: (Parameters) +# 101| 0: [Parameter] value +# 103| 7: [Method] M +# 104| 4: [BlockStmt] {...} +# 105| 0: [ExprStmt] ...; +# 105| 0: [AddEventExpr] ... += ... +# 105| 0: [ImplicitDelegateCreation] delegate creation of type EventHandler +# 105| 0: [MethodAccess] access to method M +# 105| 1: [EventAccess,EventCall] access to event Click +# 106| 1: [LocalFunctionStmt] LocalFunction(...) +# 106| 0: [LocalFunction] LocalFunction +# 106| 4: [BlockStmt] {...} +# 106| 2: [EmptyStmt] ; +# 107| 3: [ExprStmt] ...; +# 107| 0: [AddEventExpr] ... += ... +# 107| 0: [ImplicitDelegateCreation] delegate creation of type EventHandler +# 107| 0: [LocalFunctionAccess] access to local function LocalFunction +# 107| 1: [EventAccess,EventCall] access to event Click +# 108| 4: [ExprStmt] ...; +# 108| 0: [DelegateCall] delegate call +# 108| -1: [EventAccess,EventCall] access to event Click +# 112| 6: [Interface] I1 +# 114| 4: [Method] M2 +#-----| 1: (Type parameters) +# 114| 0: [TypeParameter] T +# 117| 7: [Interface] I2<> +#-----| 1: (Type parameters) +# 117| 0: [TypeParameter] T +# 119| 8: [Interface] I3 +#-----| 3: (Base types) +# 119| 1: [Interface] I2 +# 121| 9: [Class] B<> +#-----| 1: (Type parameters) +# 121| 0: [TypeParameter] T +#-----| 3: (Base types) +# 121| 0: [Class] A +# 121| 1: [Interface] I1 +# 121| 2: [Interface] I2 +# 123| 5: [Method] M +# 124| 4: [BlockStmt] {...} +# 125| 0: [ExprStmt] ...; +# 125| 0: [MethodCall] call to method M +# 125| -1: [BaseAccess] base access +# 128| 6: [Method] M2 +#-----| 1: (Type parameters) +# 128| 0: [TypeParameter] T +# 128| 4: [BlockStmt] {...} +# 130| 7: [Struct] S<> +#-----| 1: (Type parameters) +# 130| 0: [TypeParameter] T2 +#-----| 3: (Base types) +# 130| 1: [Interface] I3 +# 132| 8: [Method] Tuple +# 132| 4: [ThrowExpr] throw ... +# 132| 0: [ObjectCreation] object creation of type Exception +# 134| 9: [Indexer] Item +#-----| 1: (Parameters) +# 134| 0: [Parameter] a +# 134| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 134| 0: [Parameter] a +# 134| 4: [BlockStmt] {...} +# 134| 0: [ReturnStmt] return ...; +# 134| 0: [DefaultValueExpr] default(...) +# 134| 0: [TypeAccess] access to type B +# 137| 10: [Class] C +# 139| 5: [Enum] E +# 140| 6: [Method] Pointer +# 140| 4: [ThrowExpr] throw ... +# 140| 0: [ObjectCreation] object creation of type Exception +# 140| 0: [MethodCall] call to method ToString +# 140| -1: [SizeofExpr] sizeof(..) +# 140| 0: [TypeAccess] access to type E* +# 143| 11: [Interface] I4 +# 145| 4: [Event] EH +# 145| 3: [AddEventAccessor] add_EH +#-----| 2: (Parameters) +# 145| 0: [Parameter] value +# 145| 4: [RemoveEventAccessor] remove_EH +#-----| 2: (Parameters) +# 145| 0: [Parameter] value +# 146| 5: [Method] M +# 147| 6: [Property] P +# 147| 3: [Getter] get_P +# 148| 7: [Indexer] Item +#-----| 1: (Parameters) +# 148| 0: [Parameter] eh +# 148| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 148| 0: [Parameter] eh +# 151| 12: [Class] C4 +#-----| 3: (Base types) +# 151| 1: [Interface] I4 +# 153| 5: [Event] EH +# 153| 3: [AddEventAccessor] add_EH +#-----| 2: (Parameters) +# 153| 0: [Parameter] value +# 153| 4: [BlockStmt] {...} +# 153| 4: [RemoveEventAccessor] remove_EH +#-----| 2: (Parameters) +# 153| 0: [Parameter] value +# 153| 4: [BlockStmt] {...} +# 154| 6: [Method] M +# 154| 4: [ThrowExpr] throw ... +# 154| 0: [ObjectCreation] object creation of type Exception +# 155| 7: [Property] P +# 155| 3: [Getter] get_P +# 155| 4: [ThrowExpr] throw ... +# 155| 0: [ObjectCreation] object creation of type Exception +# 156| 8: [Indexer] Item +#-----| 1: (Parameters) +# 156| 0: [Parameter] eh +# 156| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 156| 0: [Parameter] eh +# 156| 4: [BlockStmt] {...} +# 156| 0: [ReturnStmt] return ...; +# 156| 0: [CastExpr] (...) ... +# 156| 0: [ObjectCreation] object creation of type S1 +# 156| 1: [TypeAccess] access to type S1 +# 158| 9: [Class] Nested<> +#-----| 1: (Type parameters) +# 158| 0: [TypeParameter] T +# 160| 5: [Method] Create +# 160| 4: [BlockStmt] {...} +# 160| 0: [ReturnStmt] return ...; +# 160| 0: [ObjectCreation] object creation of type Nested<> +# 164| 13: [Class] C5 +# 166| 5: [Field] f +# 166| 1: [AssignExpr] ... = ... +# 166| 0: [MethodCall] call to method Create +# 166| -1: [TypeAccess] access to type Nested +# 166| -1: [TypeAccess] access to type C4 +# 166| 1: [FieldAccess] access to field f +# 167| 6: [Field] c1 +# 169| 7: [Method] M +# 170| 4: [BlockStmt] {...} +# 171| 0: [LocalVariableDeclStmt] ... ...; +# 171| 0: [LocalVariableDeclAndInitExpr] C1 c = ... +# 171| 0: [ObjectCreation] object creation of type C1 +# 171| 1: [LocalVariableAccess] access to local variable c +# 172| 1: [ExprStmt] ...; +# 172| 0: [AssignExpr] ... = ... +# 172| 0: [PropertyCall] access to property property1 +# 172| -1: [LocalVariableAccess] access to local variable c +# 172| 1: [PropertyCall] access to property property1 +# 172| -1: [LocalVariableAccess] access to local variable c +# 173| 2: [LocalVariableDeclStmt] ... ...; +# 173| 0: [LocalVariableDeclAndInitExpr] C5 c5 = ... +# 173| 0: [AsExpr] ... as ... +# 173| 0: [ObjectCreation] object creation of type C5 +# 173| 1: [TypeAccess] access to type C5 +# 173| 1: [LocalVariableAccess] access to local variable c5 +# 174| 3: [ExprStmt] ...; +# 174| 0: [AssignExpr] ... = ... +# 174| 0: [IntLiteral] 0 +# 174| 1: [PropertyCall] access to property property1 +# 174| -1: [FieldAccess] access to field c1 +# 174| -1: [LocalVariableAccess] access to local variable c5 +# 175| 4: [LocalVariableDeclStmt] ... ...; +# 175| 0: [LocalVariableDeclAndInitExpr] Boolean temp = ... +# 175| 0: [IsExpr] ... is ... +# 175| 0: [LocalVariableAccess] access to local variable c5 +# 175| 1: [TypeAccessPatternExpr] access to type Nested +# 175| 1: [LocalVariableAccess] access to local variable temp +# 179| 14: [Class] C6 +# 181| 5: [ExplicitConversionOperator] explicit conversion +#-----| 2: (Parameters) +# 181| 0: [Parameter] c +#-----| 0: (Attributes) +# 181| 1: [Attribute] [My(...)] +# 182| 4: [BlockStmt] {...} +# 183| 0: [ReturnStmt] return ...; +# 183| 0: [NullLiteral] null +# 186| 6: [Method] M +# 187| 4: [BlockStmt] {...} +# 188| 0: [ReturnStmt] return ...; +# 188| 0: [OperatorCall] call to operator explicit conversion +# 188| 0: [ThisAccess] this access +# 191| 7: [AddOperator] + +#-----| 2: (Parameters) +# 191| 0: [Parameter] x +# 191| 1: [Parameter] y +# 191| 4: [ParameterAccess] access to parameter x +# 194| 15: [Class] MyAttribute +#-----| 3: (Base types) +# 194| 0: [Class] Attribute +# 196| 16: [Class] C7 +# 198| 5: [Method] M +# 198| 4: [BlockStmt] {...} +# 200| 6: [Method] M2 +# 201| 4: [BlockStmt] {...} +# 202| 0: [ExprStmt] ...; +# 202| 0: [MethodCall] call to method M +# 202| -1: [TypeAccess] access to type C7 +# 206| 17: [Class] C8 +# 208| 5: [Method] F +# 209| 4: [BlockStmt] {...} +# 210| 0: [LocalVariableDeclStmt] ... ...; +# 210| 0: [LocalVariableDeclAndInitExpr] C8 c8a = ... +# 210| 0: [NullLiteral] null +# 210| 1: [LocalVariableAccess] access to local variable c8a +# 211| 1: [IfStmt] if (...) ... +# 211| 0: [IsExpr] ... is ... +# 211| 0: [LocalVariableAccess] access to local variable c8a +# 211| 1: [VariablePatternExpr] C8 c8b +# 212| 1: [ExprStmt] ...; +# 212| 0: [AssignExpr] ... = ... +# 212| 0: [LocalVariableAccess] access to local variable c8b +# 212| 1: [LocalVariableAccess] access to local variable c8a +# 213| 2: [SwitchStmt] switch (...) {...} +# 213| 0: [LocalVariableAccess] access to local variable c8a +# 215| 0: [CaseStmt] case ...: +# 215| 0: [VariablePatternExpr] C8 c8c +# 215| 1: [NEExpr] ... != ... +# 215| 0: [LocalVariableAccess] access to local variable c8c +# 215| 1: [NullLiteral] null +# 216| 1: [ExprStmt] ...; +# 216| 0: [AssignExpr] ... = ... +# 216| 0: [LocalVariableAccess] access to local variable c8c +# 216| 1: [LocalVariableAccess] access to local variable c8a +# 217| 2: [BreakStmt] break; diff --git a/csharp/ql/test/library-tests/definitions/PrintAst.qlref b/csharp/ql/test/library-tests/definitions/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/definitions/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/delegates/PrintAst.expected b/csharp/ql/test/library-tests/delegates/PrintAst.expected new file mode 100644 index 000000000000..0c73aa05ae10 --- /dev/null +++ b/csharp/ql/test/library-tests/delegates/PrintAst.expected @@ -0,0 +1,190 @@ +delegates.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [DelegateType] FooDelegate +#-----| 2: (Parameters) +# 7| 0: [Parameter] param +# 7| 1: [Parameter] condition +# 7| 2: [Parameter] args +# 9| 2: [DelegateType] D1 +#-----| 2: (Parameters) +# 9| 0: [Parameter] i +# 9| 1: [Parameter] d +# 11| 3: [Class] A +# 14| 5: [Method] M1 +#-----| 2: (Parameters) +# 14| 0: [Parameter] a +# 14| 1: [Parameter] b +# 14| 4: [BlockStmt] {...} +# 14| 0: [ReturnStmt] return ...; +# 14| 0: [CastExpr] (...) ... +# 14| 0: [AddExpr] ... + ... +# 14| 0: [CastExpr] (...) ... +# 14| 0: [ParameterAccess] access to parameter a +# 14| 1: [ParameterAccess] access to parameter b +# 14| 1: [TypeAccess] access to type Int32 +# 18| 4: [Class] B +# 21| 5: [DelegateType] D2 +#-----| 2: (Parameters) +# 21| 0: [Parameter] c +# 21| 1: [Parameter] d +# 23| 6: [Method] M1 +#-----| 2: (Parameters) +# 23| 0: [Parameter] f +# 23| 1: [Parameter] g +# 23| 4: [BlockStmt] {...} +# 23| 0: [ReturnStmt] return ...; +# 23| 0: [SubExpr] ... - ... +# 23| 0: [ParameterAccess] access to parameter f +# 23| 1: [CastExpr] (...) ... +# 23| 0: [ParameterAccess] access to parameter g +# 23| 1: [TypeAccess] access to type Int32 +# 25| 7: [Method] M2 +#-----| 2: (Parameters) +# 25| 0: [Parameter] k +# 25| 1: [Parameter] l +# 25| 4: [BlockStmt] {...} +# 27| 8: [Method] M3 +#-----| 2: (Parameters) +# 27| 0: [Parameter] g +# 27| 4: [BlockStmt] {...} +# 27| 0: [ReturnStmt] return ...; +# 27| 0: [AddExpr] ... + ... +# 27| 0: [UnaryMinusExpr] -... +# 27| 0: [ParameterAccess] access to parameter g +# 27| 1: [UnaryPlusExpr] +... +# 27| 0: [ParameterAccess] access to parameter g +# 29| 9: [Method] M4 +#-----| 2: (Parameters) +# 29| 0: [Parameter] g +# 29| 4: [BlockStmt] {...} +# 33| 5: [DelegateType] Predicate<> +#-----| 1: (Type parameters) +# 33| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 33| 0: [Parameter] value +# 35| 6: [Class] X +# 38| 5: [Method] F +#-----| 2: (Parameters) +# 38| 0: [Parameter] i +# 38| 4: [BlockStmt] {...} +# 38| 0: [ReturnStmt] return ...; +# 38| 0: [LTExpr] ... < ... +# 38| 0: [ParameterAccess] access to parameter i +# 38| 1: [IntLiteral] 2 +# 40| 6: [Method] G +#-----| 2: (Parameters) +# 40| 0: [Parameter] s +# 40| 4: [BlockStmt] {...} +# 40| 0: [ReturnStmt] return ...; +# 40| 0: [BoolLiteral] false +# 44| 7: [DelegateType] D +#-----| 2: (Parameters) +# 44| 0: [Parameter] x +# 46| 8: [Class] C +# 49| 5: [Method] M1 +#-----| 2: (Parameters) +# 49| 0: [Parameter] i +# 49| 4: [BlockStmt] {...} +# 50| 6: [Method] M2 +#-----| 2: (Parameters) +# 50| 0: [Parameter] i +# 50| 4: [BlockStmt] {...} +# 51| 7: [Method] M3 +#-----| 2: (Parameters) +# 51| 0: [Parameter] i +# 51| 4: [BlockStmt] {...} +# 55| 9: [Class] Test +# 58| 5: [Method] Main +# 59| 4: [BlockStmt] {...} +# 60| 0: [LocalVariableDeclStmt] ... ...; +# 60| 0: [LocalVariableDeclAndInitExpr] D cd1 = ... +# 60| 0: [ExplicitDelegateCreation] delegate creation of type D +# 60| 0: [MethodAccess] access to method M1 +# 60| -1: [TypeAccess] access to type C +# 60| 1: [LocalVariableAccess] access to local variable cd1 +# 61| 1: [LocalVariableDeclStmt] ... ...; +# 61| 0: [LocalVariableDeclAndInitExpr] D cd2 = ... +# 61| 0: [ImplicitDelegateCreation] delegate creation of type D +# 61| 0: [MethodAccess] access to method M2 +# 61| -1: [TypeAccess] access to type C +# 61| 1: [LocalVariableAccess] access to local variable cd2 +# 62| 2: [LocalVariableDeclStmt] ... ...; +# 62| 0: [LocalVariableDeclAndInitExpr] D cd3 = ... +# 62| 0: [OperatorCall] call to operator + +# 62| 0: [LocalVariableAccess] access to local variable cd1 +# 62| 1: [LocalVariableAccess] access to local variable cd2 +# 62| 1: [LocalVariableAccess] access to local variable cd3 +# 63| 3: [LocalVariableDeclStmt] ... ...; +# 63| 0: [LocalVariableDeclAndInitExpr] D cd4 = ... +# 63| 0: [OperatorCall] call to operator + +# 63| 0: [LocalVariableAccess] access to local variable cd3 +# 63| 1: [LocalVariableAccess] access to local variable cd1 +# 63| 1: [LocalVariableAccess] access to local variable cd4 +# 64| 4: [LocalVariableDeclStmt] ... ...; +# 64| 0: [LocalVariableDeclAndInitExpr] D cd5 = ... +# 64| 0: [OperatorCall] call to operator - +# 64| 0: [LocalVariableAccess] access to local variable cd4 +# 64| 1: [LocalVariableAccess] access to local variable cd3 +# 64| 1: [LocalVariableAccess] access to local variable cd5 +# 65| 5: [ExprStmt] ...; +# 65| 0: [AssignAddExpr] ... += ... +# 65| 0: [LocalVariableAccess] access to local variable cd5 +# 65| 1: [LocalVariableAccess] access to local variable cd4 +# 66| 6: [ExprStmt] ...; +# 66| 0: [AssignSubExpr] ... -= ... +# 66| 0: [LocalVariableAccess] access to local variable cd1 +# 66| 1: [LocalVariableAccess] access to local variable cd4 +# 68| 7: [LocalVariableDeclStmt] ... ...; +# 68| 0: [LocalVariableDeclAndInitExpr] C c = ... +# 68| 0: [ObjectCreation] object creation of type C +# 68| 1: [LocalVariableAccess] access to local variable c +# 69| 8: [LocalVariableDeclStmt] ... ...; +# 69| 0: [LocalVariableDeclAndInitExpr] D cd6 = ... +# 69| 0: [ExplicitDelegateCreation] delegate creation of type D +# 69| 0: [MethodAccess] access to method M3 +# 69| -1: [LocalVariableAccess] access to local variable c +# 69| 1: [LocalVariableAccess] access to local variable cd6 +# 70| 9: [LocalVariableDeclStmt] ... ...; +# 70| 0: [LocalVariableDeclAndInitExpr] D cd7 = ... +# 70| 0: [ExplicitDelegateCreation] delegate creation of type D +# 70| 0: [LocalVariableAccess] access to local variable cd6 +# 70| 1: [LocalVariableAccess] access to local variable cd7 +# 72| 10: [ExprStmt] ...; +# 72| 0: [DelegateCall] delegate call +# 72| -1: [LocalVariableAccess] access to local variable cd1 +# 72| 0: [UnaryMinusExpr] -... +# 72| 0: [IntLiteral] 40 +# 73| 11: [LocalVariableDeclStmt] ... ...; +# 73| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 73| 0: [IntLiteral] 0 +# 73| 1: [LocalVariableAccess] access to local variable x +# 74| 12: [ExprStmt] ...; +# 74| 0: [DelegateCall] delegate call +# 74| -1: [LocalVariableAccess] access to local variable cd7 +# 74| 0: [AddExpr] ... + ... +# 74| 0: [IntLiteral] 34 +# 74| 1: [LocalVariableAccess] access to local variable x +# 76| 13: [LocalVariableDeclStmt] ... ...; +# 76| 0: [LocalVariableDeclAndInitExpr] Predicate pi = ... +# 76| 0: [ExplicitDelegateCreation] delegate creation of type Predicate +# 76| 0: [MethodAccess] access to method F +# 76| -1: [TypeAccess] access to type X +# 76| 1: [LocalVariableAccess] access to local variable pi +# 77| 14: [LocalVariableDeclStmt] ... ...; +# 77| 0: [LocalVariableDeclAndInitExpr] Predicate ps = ... +# 77| 0: [ImplicitDelegateCreation] delegate creation of type Predicate +# 77| 0: [MethodAccess] access to method G +# 77| -1: [TypeAccess] access to type X +# 77| 1: [LocalVariableAccess] access to local variable ps +# 79| 15: [LocalVariableDeclStmt] ... ...; +# 79| 0: [LocalVariableDeclAndInitExpr] Boolean b = ... +# 79| 0: [BitwiseAndExpr] ... & ... +# 79| 0: [DelegateCall] delegate call +# 79| -1: [LocalVariableAccess] access to local variable pi +# 79| 0: [IntLiteral] 3 +# 79| 1: [DelegateCall] delegate call +# 79| -1: [LocalVariableAccess] access to local variable ps +# 79| 0: [StringLiteral] "" +# 79| 1: [LocalVariableAccess] access to local variable b +# 81| 16: [LocalVariableDeclStmt] ... ...; +# 81| 0: [LocalVariableDeclExpr] ContextCallback d diff --git a/csharp/ql/test/library-tests/delegates/PrintAst.qlref b/csharp/ql/test/library-tests/delegates/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/delegates/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dispatch/CallContext.expected b/csharp/ql/test/library-tests/dispatch/CallContext.expected new file mode 100644 index 000000000000..988fa363b9b9 --- /dev/null +++ b/csharp/ql/test/library-tests/dispatch/CallContext.expected @@ -0,0 +1,25 @@ +getADynamicTargetInCallContext +| TypeFlow.cs:33:9:33:18 | call to method Method | TypeFlow.cs:12:29:12:34 | Method | TypeFlow.cs:7:7:7:23 | call to method Run | +| TypeFlow.cs:33:9:33:18 | call to method Method | TypeFlow.cs:17:30:17:35 | Method | TypeFlow.cs:7:7:7:23 | call to method Run | +mayBenefitFromCallContext +| TypeFlow.cs:33:9:33:18 | call to method Method | +| ViableCallable.cs:12:9:12:28 | call to method M | +| ViableCallable.cs:14:9:14:15 | access to property Prop | +| ViableCallable.cs:14:19:14:25 | access to property Prop | +| ViableCallable.cs:16:9:16:23 | access to indexer | +| ViableCallable.cs:16:27:16:41 | access to indexer | +| ViableCallable.cs:18:9:18:16 | access to event Event | +| ViableCallable.cs:19:9:19:16 | access to event Event | +| ViableCallable.cs:22:9:22:30 | call to method M | +| ViableCallable.cs:24:9:24:15 | access to property Prop | +| ViableCallable.cs:24:19:24:25 | access to property Prop | +| ViableCallable.cs:26:9:26:23 | access to indexer | +| ViableCallable.cs:26:27:26:41 | access to indexer | +| ViableCallable.cs:28:9:28:16 | access to event Event | +| ViableCallable.cs:29:9:29:16 | access to event Event | +| ViableCallable.cs:235:9:235:15 | call to method M | +| ViableCallable.cs:284:9:284:15 | call to method M | +| ViableCallable.cs:287:9:287:20 | call to method M | +| ViableCallable.cs:412:9:412:18 | call to method M | +| ViableCallable.cs:456:9:456:30 | call to method M2 | +| ViableCallable.cs:462:9:462:30 | call to method M2 | diff --git a/csharp/ql/test/library-tests/dispatch/CallContext.ql b/csharp/ql/test/library-tests/dispatch/CallContext.ql new file mode 100644 index 000000000000..c21274b00950 --- /dev/null +++ b/csharp/ql/test/library-tests/dispatch/CallContext.ql @@ -0,0 +1,10 @@ +import csharp +import semmle.code.csharp.dispatch.Dispatch + +query predicate getADynamicTargetInCallContext( + DispatchCall call, Callable callable, DispatchCall ctx +) { + callable = call.getADynamicTargetInCallContext(ctx) +} + +query predicate mayBenefitFromCallContext(DispatchCall call) { call.mayBenefitFromCallContext() } diff --git a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs index bcc68034b717..7fdd307edc80 100644 --- a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs +++ b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs @@ -438,7 +438,7 @@ void M1(int i) // Viable callables: C16.M1() this.M1(""); - // Viable callables: C16.M2() + // Viable callables: C17.M2() this.M2(() => i); } diff --git a/csharp/ql/test/library-tests/dynamic/PrintAst.expected b/csharp/ql/test/library-tests/dynamic/PrintAst.expected new file mode 100644 index 000000000000..adaec26beb3b --- /dev/null +++ b/csharp/ql/test/library-tests/dynamic/PrintAst.expected @@ -0,0 +1,252 @@ +dynamic.cs: +# 4| [Class] DynamicTest +# 6| 4: [InstanceConstructor] DynamicTest +#-----| 2: (Parameters) +# 6| 0: [Parameter] x +# 6| 4: [BlockStmt] {...} +# 8| 5: [Method] Main +#-----| 2: (Parameters) +# 8| 0: [Parameter] args +# 9| 4: [BlockStmt] {...} +# 10| 0: [LocalVariableDeclStmt] ... ...; +# 10| 0: [LocalVariableDeclAndInitExpr] DynamicTest dt = ... +# 10| 0: [ObjectCreation] object creation of type DynamicTest +# 10| 0: [IntLiteral] 0 +# 10| 1: [LocalVariableAccess] access to local variable dt +# 11| 1: [LocalVariableDeclStmt] ... ...; +# 11| 0: [LocalVariableDeclAndInitExpr] Int32[] array = ... +# 11| 0: [ArrayCreation] array creation of type Int32[] +# 11| -1: [ArrayInitializer] { ..., ... } +# 11| 0: [IntLiteral] 42 +# 11| 1: [LocalVariableAccess] access to local variable array +# 12| 2: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclAndInitExpr] Action action = ... +# 12| 0: [LambdaExpr] (...) => ... +#-----| 2: (Parameters) +# 12| 0: [Parameter] x +# 12| 4: [BlockStmt] {...} +# 12| 1: [LocalVariableAccess] access to local variable action +# 13| 3: [LocalVariableDeclStmt] ... ...; +# 13| 0: [LocalVariableDeclAndInitExpr] dynamic d = ... +# 13| 0: [CastExpr] (...) ... +# 13| 0: [IntLiteral] 0 +# 13| 1: [LocalVariableAccess] access to local variable d +# 16| 4: [ExprStmt] ...; +# 16| 0: [DynamicObjectCreation] dynamic object creation of type DynamicTest +# 16| 0: [LocalVariableAccess] access to local variable d +# 17| 5: [ExprStmt] ...; +# 17| 0: [DynamicObjectCreation] dynamic object creation of type DynamicTest +# 17| -1: [ObjectInitializer] { ..., ... } +# 17| 0: [MemberInitializer] ... = ... +# 17| 0: [CastExpr] (...) ... +# 17| 0: [LocalVariableAccess] access to local variable d +# 17| 1: [FieldAccess] access to field Field +# 17| 0: [LocalVariableAccess] access to local variable d +# 18| 6: [ExprStmt] ...; +# 18| 0: [DynamicObjectCreation] dynamic object creation of type KeyValuePair +# 18| 0: [StringLiteral] "" +# 18| 1: [LocalVariableAccess] access to local variable d +# 19| 7: [ExprStmt] ...; +# 19| 0: [DynamicObjectCreation] dynamic object creation of type KeyValuePair +# 19| 0: [StringLiteral] "" +# 19| 1: [LocalVariableAccess] access to local variable d +# 22| 8: [ExprStmt] ...; +# 22| 0: [ObjectCreation] object creation of type DynamicTest +# 22| 0: [IntLiteral] 0 +# 23| 9: [ExprStmt] ...; +# 23| 0: [ObjectCreation] object creation of type DynamicTest +# 23| -1: [ObjectInitializer] { ..., ... } +# 23| 0: [MemberInitializer] ... = ... +# 23| 0: [CastExpr] (...) ... +# 23| 0: [LocalVariableAccess] access to local variable d +# 23| 1: [FieldAccess] access to field Field +# 23| 0: [IntLiteral] 0 +# 26| 10: [ExprStmt] ...; +# 26| 0: [DynamicMethodCall] dynamic call to method Bar +# 26| -1: [LocalVariableAccess] access to local variable d +# 26| 0: [StringLiteral] "" +# 27| 11: [ExprStmt] ...; +# 27| 0: [DynamicMethodCall] dynamic call to method Foo +# 27| 0: [LocalVariableAccess] access to local variable d +# 30| 12: [ExprStmt] ...; +# 30| 0: [MethodCall] call to method Bar +# 30| -1: [LocalVariableAccess] access to local variable dt +# 30| 0: [StringLiteral] "" +# 31| 13: [ExprStmt] ...; +# 31| 0: [MethodCall] call to method Foo +# 31| 0: [IntLiteral] 0 +# 34| 14: [ExprStmt] ...; +# 34| 0: [AssignExpr] ... = ... +# 34| 0: [CastExpr] (...) ... +# 34| 0: [IntLiteral] 0 +# 34| 1: [LocalVariableAccess] access to local variable d +# 35| 15: [ExprStmt] ...; +# 35| 0: [AssignExpr] ... = ... +# 35| 0: [DynamicOperatorCall] dynamic call to operator - +# 35| 0: [LocalVariableAccess] access to local variable d +# 35| 1: [LocalVariableAccess] access to local variable d +# 36| 16: [ExprStmt] ...; +# 36| 0: [AssignExpr] ... = ... +# 36| 0: [DynamicOperatorCall] dynamic call to operator + +# 36| 0: [LocalVariableAccess] access to local variable d +# 36| 1: [LocalVariableAccess] access to local variable d +# 36| 1: [LocalVariableAccess] access to local variable d +# 37| 17: [ExprStmt] ...; +# 37| 0: [AssignAddExpr] ... += ... +# 37| 0: [LocalVariableAccess] access to local variable d +# 37| 1: [LocalVariableAccess] access to local variable d +# 40| 18: [LocalVariableDeclStmt] ... ...; +# 40| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 40| 0: [IntLiteral] 0 +# 40| 1: [LocalVariableAccess] access to local variable i +# 41| 19: [ExprStmt] ...; +# 41| 0: [AssignExpr] ... = ... +# 41| 0: [UnaryMinusExpr] -... +# 41| 0: [LocalVariableAccess] access to local variable i +# 41| 1: [LocalVariableAccess] access to local variable i +# 42| 20: [ExprStmt] ...; +# 42| 0: [AssignExpr] ... = ... +# 42| 0: [AddExpr] ... + ... +# 42| 0: [LocalVariableAccess] access to local variable i +# 42| 1: [LocalVariableAccess] access to local variable i +# 42| 1: [LocalVariableAccess] access to local variable i +# 43| 21: [ExprStmt] ...; +# 43| 0: [AssignAddExpr] ... += ... +# 43| 0: [LocalVariableAccess] access to local variable i +# 43| 1: [LocalVariableAccess] access to local variable i +# 44| 22: [ExprStmt] ...; +# 44| 0: [PostIncrExpr] ...++ +# 44| 0: [LocalVariableAccess] access to local variable i +# 47| 23: [ExprStmt] ...; +# 47| 0: [DynamicOperatorCall] dynamic call to operator ++ +# 47| 0: [LocalVariableAccess] access to local variable d +# 50| 24: [ExprStmt] ...; +# 50| 0: [PostIncrExpr] ...++ +# 50| 0: [LocalVariableAccess] access to local variable i +# 53| 25: [ExprStmt] ...; +# 53| 0: [AssignExpr] ... = ... +# 53| 0: [IntLiteral] 0 +# 53| 1: [DynamicMemberAccess] dynamic access to member Field +# 53| -1: [LocalVariableAccess] access to local variable d +# 54| 26: [ExprStmt] ...; +# 54| 0: [AssignExpr] ... = ... +# 54| 0: [DynamicMemberAccess] dynamic access to member Prop +# 54| -1: [LocalVariableAccess] access to local variable d +# 54| 1: [DynamicMemberAccess] dynamic access to member Prop +# 54| -1: [LocalVariableAccess] access to local variable d +# 57| 27: [ExprStmt] ...; +# 57| 0: [AssignExpr] ... = ... +# 57| 0: [IntLiteral] 0 +# 57| 1: [FieldAccess] access to field Field +# 57| -1: [LocalVariableAccess] access to local variable dt +# 58| 28: [ExprStmt] ...; +# 58| 0: [AssignExpr] ... = ... +# 58| 0: [PropertyCall] access to property Prop +# 58| -1: [LocalVariableAccess] access to local variable dt +# 58| 1: [PropertyCall] access to property Prop +# 58| -1: [LocalVariableAccess] access to local variable dt +# 61| 29: [ExprStmt] ...; +# 61| 0: [AssignExpr] ... = ... +# 61| 0: [LocalVariableAccess] access to local variable array +# 61| 1: [LocalVariableAccess] access to local variable d +# 62| 30: [ExprStmt] ...; +# 62| 0: [AssignExpr] ... = ... +# 62| 0: [DynamicElementAccess] dynamic access to element +# 62| -1: [LocalVariableAccess] access to local variable d +# 62| 0: [IntLiteral] 0 +# 62| 1: [DynamicElementAccess] dynamic access to element +# 62| -1: [LocalVariableAccess] access to local variable d +# 62| 0: [IntLiteral] 0 +# 63| 31: [ExprStmt] ...; +# 63| 0: [AssignExpr] ... = ... +# 63| 0: [DynamicElementAccess] dynamic access to element +# 63| -1: [LocalVariableAccess] access to local variable d +# 63| 0: [IntLiteral] 0 +# 63| 1: [LocalVariableAccess] access to local variable d +# 66| 32: [ExprStmt] ...; +# 66| 0: [AssignExpr] ... = ... +# 66| 0: [CastExpr] (...) ... +# 66| 0: [IntLiteral] 0 +# 66| 1: [LocalVariableAccess] access to local variable d +# 67| 33: [ExprStmt] ...; +# 67| 0: [AssignExpr] ... = ... +# 67| 0: [CastExpr] (...) ... +# 67| 0: [IndexerCall] access to indexer +# 67| -1: [LocalVariableAccess] access to local variable dt +# 67| 0: [LocalVariableAccess] access to local variable d +# 67| 1: [IndexerCall] access to indexer +# 67| -1: [LocalVariableAccess] access to local variable dt +# 67| 0: [IntLiteral] 0 +# 68| 34: [ExprStmt] ...; +# 68| 0: [AssignExpr] ... = ... +# 68| 0: [CastExpr] (...) ... +# 68| 0: [IndexerCall] access to indexer +# 68| -1: [LocalVariableAccess] access to local variable dt +# 68| 0: [IntLiteral] 0 +# 68| 1: [LocalVariableAccess] access to local variable d +# 69| 35: [ExprStmt] ...; +# 69| 0: [AssignExpr] ... = ... +# 69| 0: [ArrayAccess] access to array element +# 69| -1: [LocalVariableAccess] access to local variable array +# 69| 0: [CastExpr] (...) ... +# 69| 0: [LocalVariableAccess] access to local variable d +# 69| 1: [ArrayAccess] access to array element +# 69| -1: [LocalVariableAccess] access to local variable array +# 69| 0: [IntLiteral] 0 +# 70| 36: [ExprStmt] ...; +# 70| 0: [AssignExpr] ... = ... +# 70| 0: [CastExpr] (...) ... +# 70| 0: [ArrayAccess] access to array element +# 70| -1: [LocalVariableAccess] access to local variable array +# 70| 0: [IntLiteral] 0 +# 70| 1: [LocalVariableAccess] access to local variable d +# 73| 37: [ExprStmt] ...; +# 73| 0: [DelegateCall] delegate call +# 73| -1: [LocalVariableAccess] access to local variable action +# 73| 0: [IntLiteral] 3 +# 74| 38: [ExprStmt] ...; +# 74| 0: [AssignExpr] ... = ... +# 74| 0: [LocalVariableAccess] access to local variable action +# 74| 1: [LocalVariableAccess] access to local variable d +# 75| 39: [ExprStmt] ...; +# 75| 0: [DelegateCall] delegate call +# 75| -1: [LocalVariableAccess] access to local variable d +# 75| 0: [IntLiteral] 42 +# 78| 6: [Method] Foo +#-----| 2: (Parameters) +# 78| 0: [Parameter] x +# 78| 4: [BlockStmt] {...} +# 79| 7: [Method] Foo +#-----| 2: (Parameters) +# 79| 0: [Parameter] x +# 79| 4: [BlockStmt] {...} +# 81| 8: [Method] Bar +#-----| 2: (Parameters) +# 81| 0: [Parameter] x +# 81| 4: [BlockStmt] {...} +# 83| 9: [IncrementOperator] ++ +#-----| 2: (Parameters) +# 83| 0: [Parameter] dt +# 84| 4: [BlockStmt] {...} +# 85| 0: [ReturnStmt] return ...; +# 85| 0: [ParameterAccess] access to parameter dt +# 88| 10: [Field] Field +# 90| 11: [Property] Prop +# 90| 3: [Getter] get_Prop +# 90| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 90| 0: [Parameter] value +# 92| 12: [Indexer] Item +#-----| 1: (Parameters) +# 92| 0: [Parameter] x +# 92| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 92| 0: [Parameter] x +# 92| 4: [BlockStmt] {...} +# 92| 0: [ReturnStmt] return ...; +# 92| 0: [ParameterAccess] access to parameter x +# 92| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 92| 0: [Parameter] x +# 92| 1: [Parameter] value +# 92| 4: [BlockStmt] {...} diff --git a/csharp/ql/test/library-tests/dynamic/PrintAst.qlref b/csharp/ql/test/library-tests/dynamic/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/dynamic/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/enums/Enums11.expected b/csharp/ql/test/library-tests/enums/Enums11.expected new file mode 100644 index 000000000000..715beb400ff5 --- /dev/null +++ b/csharp/ql/test/library-tests/enums/Enums11.expected @@ -0,0 +1,5 @@ +| enums.cs:28:18:28:18 | (...) ... | 1 | +| enums.cs:29:20:29:20 | (...) ... | 2 | +| enums.cs:30:20:30:20 | (...) ... | 4 | +| enums.cs:38:17:38:18 | 10 | 10 | +| enums.cs:40:23:40:32 | ... + ... | 11 | diff --git a/csharp/ql/test/library-tests/enums/Enums11.ql b/csharp/ql/test/library-tests/enums/Enums11.ql new file mode 100644 index 000000000000..36b2c005a213 --- /dev/null +++ b/csharp/ql/test/library-tests/enums/Enums11.ql @@ -0,0 +1,12 @@ +/** + * @name Test for enums + */ + +import csharp + +from Expr e +where + exists(Assignment a | a.getRValue() = e | + a.getParent().(Field).getDeclaringType() instanceof Enum + ) +select e, e.getValue() diff --git a/csharp/ql/test/library-tests/enums/PrintAst.expected b/csharp/ql/test/library-tests/enums/PrintAst.expected new file mode 100644 index 000000000000..446b4c018565 --- /dev/null +++ b/csharp/ql/test/library-tests/enums/PrintAst.expected @@ -0,0 +1,105 @@ +enums.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [Enum] Color +# 10| 5: [Field] Red +# 10| 6: [Field] Green +# 10| 7: [Field] Blue +# 14| 2: [Enum] LongColor +# 17| 5: [Field] Red +# 18| 6: [Field] Green +# 19| 7: [Field] Blue +# 23| 3: [Enum] E +# 25| 4: [Enum] ValueColor +# 28| 5: [Field] OneRed +# 28| 1: [AssignExpr] ... = ... +# 28| 0: [CastExpr] (...) ... +# 28| 0: [IntLiteral] 1 +# 28| 1: [MemberConstantAccess] access to constant OneRed +# 29| 6: [Field] TwoGreen +# 29| 1: [AssignExpr] ... = ... +# 29| 0: [CastExpr] (...) ... +# 29| 0: [IntLiteral] 2 +# 29| 1: [MemberConstantAccess] access to constant TwoGreen +# 30| 7: [Field] FourBlue +# 30| 1: [AssignExpr] ... = ... +# 30| 0: [CastExpr] (...) ... +# 30| 0: [IntLiteral] 4 +# 30| 1: [MemberConstantAccess] access to constant FourBlue +# 34| 5: [Enum] SparseColor +# 37| 5: [Field] Red +# 38| 6: [Field] Green +# 38| 1: [AssignExpr] ... = ... +# 38| 0: [IntLiteral] 10 +# 38| 1: [MemberConstantAccess] access to constant Green +# 39| 7: [Field] Blue +# 40| 8: [Field] AnotherBlue +# 40| 1: [AssignExpr] ... = ... +# 40| 0: [AddExpr] ... + ... +# 40| 0: [CastExpr] (...) ... +# 40| 0: [MemberConstantAccess] access to constant Blue +# 40| 1: [CastExpr] (...) ... +# 40| 0: [MemberConstantAccess] access to constant Red +# 40| 1: [MemberConstantAccess] access to constant AnotherBlue +# 44| 6: [Class] Test +# 47| 5: [Method] Main +# 48| 4: [BlockStmt] {...} +# 49| 0: [ExprStmt] ...; +# 49| 0: [MethodCall] call to method WriteLine +# 49| -1: [TypeAccess] access to type Console +# 49| 0: [MethodCall] call to method StringFromColor +# 49| 0: [MemberConstantAccess] access to constant Red +# 49| -1: [TypeAccess] access to type SparseColor +# 50| 1: [ExprStmt] ...; +# 50| 0: [MethodCall] call to method WriteLine +# 50| -1: [TypeAccess] access to type Console +# 50| 0: [MethodCall] call to method StringFromColor +# 50| 0: [MemberConstantAccess] access to constant Green +# 50| -1: [TypeAccess] access to type SparseColor +# 51| 2: [ExprStmt] ...; +# 51| 0: [MethodCall] call to method WriteLine +# 51| -1: [TypeAccess] access to type Console +# 51| 0: [MethodCall] call to method StringFromColor +# 51| 0: [MemberConstantAccess] access to constant Blue +# 51| -1: [TypeAccess] access to type SparseColor +# 54| 6: [Method] StringFromColor +#-----| 2: (Parameters) +# 54| 0: [Parameter] c +# 55| 4: [BlockStmt] {...} +# 56| 0: [SwitchStmt] switch (...) {...} +# 56| 0: [ParameterAccess] access to parameter c +# 58| 0: [ConstCase] case ...: +# 58| 0: [ConstantPatternExpr,MemberConstantAccess] access to constant Red +# 58| -1: [TypeAccess] access to type SparseColor +# 58| 1: [ReturnStmt] return ...; +# 58| 0: [MethodCall] call to method Format +# 58| -1: [TypeAccess] access to type String +# 58| 0: [StringLiteral] "Red = {0}" +# 58| 1: [CastExpr] (...) ... +# 58| 0: [CastExpr] (...) ... +# 58| 0: [ParameterAccess] access to parameter c +# 58| 1: [TypeAccess] access to type Int32 +# 59| 2: [ConstCase] case ...: +# 59| 0: [ConstantPatternExpr,MemberConstantAccess] access to constant Green +# 59| -1: [TypeAccess] access to type SparseColor +# 59| 3: [ReturnStmt] return ...; +# 59| 0: [MethodCall] call to method Format +# 59| -1: [TypeAccess] access to type String +# 59| 0: [StringLiteral] "Green = {0}" +# 59| 1: [CastExpr] (...) ... +# 59| 0: [CastExpr] (...) ... +# 59| 0: [ParameterAccess] access to parameter c +# 59| 1: [TypeAccess] access to type Int32 +# 60| 4: [ConstCase] case ...: +# 60| 0: [ConstantPatternExpr,MemberConstantAccess] access to constant Blue +# 60| -1: [TypeAccess] access to type SparseColor +# 60| 5: [ReturnStmt] return ...; +# 60| 0: [MethodCall] call to method Format +# 60| -1: [TypeAccess] access to type String +# 60| 0: [StringLiteral] "Blue = {0}" +# 60| 1: [CastExpr] (...) ... +# 60| 0: [CastExpr] (...) ... +# 60| 0: [ParameterAccess] access to parameter c +# 60| 1: [TypeAccess] access to type Int32 +# 61| 6: [DefaultCase] default: +# 61| 7: [ReturnStmt] return ...; +# 61| 0: [StringLiteral] "Invalid color" diff --git a/csharp/ql/test/library-tests/enums/PrintAst.qlref b/csharp/ql/test/library-tests/enums/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/enums/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/enums/enums.cs b/csharp/ql/test/library-tests/enums/enums.cs index df7df79c066d..784d4f64114e 100644 --- a/csharp/ql/test/library-tests/enums/enums.cs +++ b/csharp/ql/test/library-tests/enums/enums.cs @@ -37,7 +37,7 @@ enum SparseColor Red, Green = 10, Blue, - AnotherBlue = Blue + AnotherBlue = Blue + Red } diff --git a/csharp/ql/test/library-tests/events/PrintAst.expected b/csharp/ql/test/library-tests/events/PrintAst.expected new file mode 100644 index 000000000000..ca2c497bd7cd --- /dev/null +++ b/csharp/ql/test/library-tests/events/PrintAst.expected @@ -0,0 +1,149 @@ +events.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [DelegateType] EventHandler +#-----| 2: (Parameters) +# 7| 0: [Parameter] sender +# 7| 1: [Parameter] e +# 10| 2: [Class] Button +# 13| 5: [Event] Click +# 13| 3: [AddEventAccessor] add_Click +#-----| 2: (Parameters) +# 13| 0: [Parameter] value +# 13| 4: [RemoveEventAccessor] remove_Click +#-----| 2: (Parameters) +# 13| 0: [Parameter] value +# 15| 6: [Method] OnClick +#-----| 2: (Parameters) +# 15| 0: [Parameter] e +# 16| 4: [BlockStmt] {...} +# 17| 0: [IfStmt] if (...) ... +# 17| 0: [OperatorCall] call to operator != +# 17| 0: [EventAccess,EventCall] access to event Click +# 17| 1: [NullLiteral] null +# 18| 1: [ExprStmt] ...; +# 18| 0: [DelegateCall] delegate call +# 18| -1: [EventAccess,EventCall] access to event Click +# 18| 0: [ThisAccess] this access +# 18| 1: [ParameterAccess] access to parameter e +# 21| 7: [Method] Reset +# 22| 4: [BlockStmt] {...} +# 23| 0: [ExprStmt] ...; +# 23| 0: [AssignExpr] ... = ... +# 23| 0: [NullLiteral] null +# 23| 1: [EventAccess,EventCall] access to event Click +# 27| 3: [Class] LoginDialog +# 30| 4: [Field] OkButton +# 31| 5: [Field] CancelButton +# 33| 6: [InstanceConstructor] LoginDialog +# 34| 4: [BlockStmt] {...} +# 35| 0: [ExprStmt] ...; +# 35| 0: [AssignExpr] ... = ... +# 35| 0: [ObjectCreation] object creation of type Button +# 35| 1: [FieldAccess] access to field OkButton +# 36| 1: [ExprStmt] ...; +# 36| 0: [AddEventExpr] ... += ... +# 36| 0: [ExplicitDelegateCreation] delegate creation of type EventHandler +# 36| 0: [MethodAccess] access to method OkButtonClick +# 36| 1: [EventAccess,EventCall] access to event Click +# 36| -1: [FieldAccess] access to field OkButton +# 37| 2: [ExprStmt] ...; +# 37| 0: [AssignExpr] ... = ... +# 37| 0: [ObjectCreation] object creation of type Button +# 37| 1: [FieldAccess] access to field CancelButton +# 38| 3: [ExprStmt] ...; +# 38| 0: [RemoveEventExpr] ... -= ... +# 38| 0: [ExplicitDelegateCreation] delegate creation of type EventHandler +# 38| 0: [MethodAccess] access to method CancelButtonClick +# 38| 1: [EventAccess,EventCall] access to event Click +# 38| -1: [FieldAccess] access to field CancelButton +# 41| 7: [Method] OkButtonClick +#-----| 2: (Parameters) +# 41| 0: [Parameter] sender +# 41| 1: [Parameter] e +# 42| 4: [BlockStmt] {...} +# 45| 8: [Method] CancelButtonClick +#-----| 2: (Parameters) +# 45| 0: [Parameter] sender +# 45| 1: [Parameter] e +# 46| 4: [BlockStmt] {...} +# 51| 4: [Class] Control +# 54| 6: [Field] mouseDownEventKey +# 54| 1: [AssignExpr] ... = ... +# 54| 0: [ObjectCreation] object creation of type Object +# 54| 1: [FieldAccess] access to field mouseDownEventKey +# 55| 7: [Field] mouseUpEventKey +# 55| 1: [AssignExpr] ... = ... +# 55| 0: [ObjectCreation] object creation of type Object +# 55| 1: [FieldAccess] access to field mouseUpEventKey +# 58| 8: [Method] GetEventHandler +#-----| 2: (Parameters) +# 58| 0: [Parameter] key +# 58| 4: [BlockStmt] {...} +# 58| 0: [ReturnStmt] return ...; +# 58| 0: [NullLiteral] null +# 61| 9: [Method] AddEventHandler +#-----| 2: (Parameters) +# 61| 0: [Parameter] key +# 61| 1: [Parameter] handler +# 61| 4: [BlockStmt] {...} +# 64| 10: [Method] RemoveEventHandler +#-----| 2: (Parameters) +# 64| 0: [Parameter] key +# 64| 1: [Parameter] handler +# 64| 4: [BlockStmt] {...} +# 67| 11: [Event] MouseDown +# 69| 3: [AddEventAccessor] add_MouseDown +#-----| 2: (Parameters) +# 69| 0: [Parameter] value +# 69| 4: [BlockStmt] {...} +# 69| 0: [ExprStmt] ...; +# 69| 0: [MethodCall] call to method AddEventHandler +# 69| 0: [FieldAccess] access to field mouseDownEventKey +# 69| 1: [ParameterAccess] access to parameter value +# 70| 4: [RemoveEventAccessor] remove_MouseDown +#-----| 2: (Parameters) +# 70| 0: [Parameter] value +# 70| 4: [BlockStmt] {...} +# 70| 0: [ExprStmt] ...; +# 70| 0: [MethodCall] call to method RemoveEventHandler +# 70| 0: [FieldAccess] access to field mouseDownEventKey +# 70| 1: [ParameterAccess] access to parameter value +# 74| 12: [Event] MouseUp +# 76| 3: [AddEventAccessor] add_MouseUp +#-----| 2: (Parameters) +# 76| 0: [Parameter] value +# 76| 4: [BlockStmt] {...} +# 76| 0: [ExprStmt] ...; +# 76| 0: [MethodCall] call to method AddEventHandler +# 76| 0: [FieldAccess] access to field mouseUpEventKey +# 76| 1: [ParameterAccess] access to parameter value +# 77| 4: [RemoveEventAccessor] remove_MouseUp +#-----| 2: (Parameters) +# 77| 0: [Parameter] value +# 77| 4: [BlockStmt] {...} +# 77| 0: [ExprStmt] ...; +# 77| 0: [MethodCall] call to method RemoveEventHandler +# 77| 0: [FieldAccess] access to field mouseUpEventKey +# 77| 1: [ParameterAccess] access to parameter value +# 81| 13: [Method] OnMouseUp +#-----| 2: (Parameters) +# 81| 0: [Parameter] args +# 82| 4: [BlockStmt] {...} +# 83| 0: [LocalVariableDeclStmt] ... ...; +# 83| 0: [LocalVariableDeclExpr] EventHandler handler +# 84| 1: [ExprStmt] ...; +# 84| 0: [AssignExpr] ... = ... +# 84| 0: [CastExpr] (...) ... +# 84| 0: [MethodCall] call to method GetEventHandler +# 84| 0: [FieldAccess] access to field mouseUpEventKey +# 84| 1: [TypeAccess] access to type EventHandler +# 84| 1: [LocalVariableAccess] access to local variable handler +# 85| 2: [IfStmt] if (...) ... +# 85| 0: [OperatorCall] call to operator != +# 85| 0: [LocalVariableAccess] access to local variable handler +# 85| 1: [NullLiteral] null +# 86| 1: [ExprStmt] ...; +# 86| 0: [DelegateCall] delegate call +# 86| -1: [LocalVariableAccess] access to local variable handler +# 86| 0: [ThisAccess] this access +# 86| 1: [ParameterAccess] access to parameter args diff --git a/csharp/ql/test/library-tests/events/PrintAst.qlref b/csharp/ql/test/library-tests/events/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/events/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/exceptions/PrintAst.expected b/csharp/ql/test/library-tests/exceptions/PrintAst.expected new file mode 100644 index 000000000000..abcca7b9d3a1 --- /dev/null +++ b/csharp/ql/test/library-tests/exceptions/PrintAst.expected @@ -0,0 +1,503 @@ +exceptions.cs: +# 3| [Class] Class1 +# 5| 5: [Method] G +# 6| 4: [BlockStmt] {...} +# 9| 6: [Field] p +# 11| 7: [Method] TestNoThrow +# 12| 4: [BlockStmt] {...} +# 13| 0: [TryStmt] try {...} ... +# 38| -1: [BlockStmt] {...} +# 39| 0: [EmptyStmt] ; +# 14| 0: [BlockStmt] {...} +# 15| 0: [EmptyStmt] ; +# 17| 1: [SpecificCatchClause] catch (...) {...} +# 17| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 18| 1: [BlockStmt] {...} +# 19| 0: [EmptyStmt] ; +# 21| 2: [SpecificCatchClause] catch (...) {...} +# 21| 0: [LocalVariableDeclExpr] OverflowException ex +# 22| 1: [BlockStmt] {...} +# 23| 0: [EmptyStmt] ; +# 25| 3: [SpecificCatchClause] catch (...) {...} +# 25| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 26| 1: [BlockStmt] {...} +# 27| 0: [EmptyStmt] ; +# 29| 4: [SpecificCatchClause] catch (...) {...} +# 29| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 30| 1: [BlockStmt] {...} +# 31| 0: [EmptyStmt] ; +# 33| 5: [SpecificCatchClause] catch (...) {...} +# 33| 0: [LocalVariableDeclExpr] Exception ex +# 34| 1: [BlockStmt] {...} +# 35| 0: [EmptyStmt] ; +# 43| 8: [Method] TestCall +# 44| 4: [BlockStmt] {...} +# 45| 0: [TryStmt] try {...} ... +# 46| 0: [BlockStmt] {...} +# 47| 0: [EmptyStmt] ; +# 48| 1: [ExprStmt] ...; +# 48| 0: [MethodCall] call to method G +# 50| 1: [SpecificCatchClause] catch (...) {...} +# 50| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 51| 1: [BlockStmt] {...} +# 52| 0: [EmptyStmt] ; +# 54| 2: [SpecificCatchClause] catch (...) {...} +# 54| 0: [LocalVariableDeclExpr] OverflowException ex +# 55| 1: [BlockStmt] {...} +# 56| 0: [EmptyStmt] ; +# 58| 3: [SpecificCatchClause] catch (...) {...} +# 58| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 59| 1: [BlockStmt] {...} +# 60| 0: [EmptyStmt] ; +# 62| 4: [SpecificCatchClause] catch (...) {...} +# 62| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 63| 1: [BlockStmt] {...} +# 64| 0: [EmptyStmt] ; +# 66| 5: [GeneralCatchClause] catch {...} +# 67| 1: [BlockStmt] {...} +# 68| 0: [EmptyStmt] ; +# 72| 9: [Method] TestCreation +# 73| 4: [BlockStmt] {...} +# 74| 0: [TryStmt] try {...} ... +# 75| 0: [BlockStmt] {...} +# 76| 0: [EmptyStmt] ; +# 77| 1: [LocalVariableDeclStmt] ... ...; +# 77| 0: [LocalVariableDeclAndInitExpr] Class1 v = ... +# 77| 0: [ObjectCreation] object creation of type Class1 +# 77| 1: [LocalVariableAccess] access to local variable v +# 79| 1: [SpecificCatchClause] catch (...) {...} +# 79| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 80| 1: [BlockStmt] {...} +# 81| 0: [EmptyStmt] ; +# 83| 2: [SpecificCatchClause] catch (...) {...} +# 83| 0: [LocalVariableDeclExpr] OverflowException ex +# 84| 1: [BlockStmt] {...} +# 85| 0: [EmptyStmt] ; +# 87| 3: [SpecificCatchClause] catch (...) {...} +# 87| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 88| 1: [BlockStmt] {...} +# 89| 0: [EmptyStmt] ; +# 91| 4: [SpecificCatchClause] catch (...) {...} +# 91| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 92| 1: [BlockStmt] {...} +# 93| 0: [EmptyStmt] ; +# 95| 5: [SpecificCatchClause] catch (...) {...} +# 95| 0: [LocalVariableDeclExpr] Exception ex +# 96| 1: [BlockStmt] {...} +# 97| 0: [EmptyStmt] ; +# 101| 10: [Method] TestIntAdd +# 102| 4: [BlockStmt] {...} +# 103| 0: [TryStmt] try {...} ... +# 104| 0: [BlockStmt] {...} +# 105| 0: [EmptyStmt] ; +# 106| 1: [LocalVariableDeclStmt] ... ...; +# 106| 0: [LocalVariableDeclAndInitExpr] Int32 v = ... +# 106| 0: [AddExpr] ... + ... +# 106| 0: [IntLiteral] 1 +# 106| 1: [IntLiteral] 2 +# 106| 1: [LocalVariableAccess] access to local variable v +# 108| 1: [SpecificCatchClause] catch (...) {...} +# 108| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 109| 1: [BlockStmt] {...} +# 110| 0: [EmptyStmt] ; +# 112| 2: [SpecificCatchClause] catch (...) {...} +# 112| 0: [LocalVariableDeclExpr] OverflowException ex +# 113| 1: [BlockStmt] {...} +# 114| 0: [EmptyStmt] ; +# 116| 3: [SpecificCatchClause] catch (...) {...} +# 116| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 117| 1: [BlockStmt] {...} +# 118| 0: [EmptyStmt] ; +# 120| 4: [SpecificCatchClause] catch (...) {...} +# 120| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 121| 1: [BlockStmt] {...} +# 122| 0: [EmptyStmt] ; +# 124| 5: [SpecificCatchClause] catch (...) {...} +# 124| 0: [LocalVariableDeclExpr] Exception ex +# 125| 1: [BlockStmt] {...} +# 126| 0: [EmptyStmt] ; +# 130| 11: [Method] TestIntSub +# 131| 4: [BlockStmt] {...} +# 132| 0: [TryStmt] try {...} ... +# 133| 0: [BlockStmt] {...} +# 134| 0: [EmptyStmt] ; +# 135| 1: [LocalVariableDeclStmt] ... ...; +# 135| 0: [LocalVariableDeclAndInitExpr] Int32 v = ... +# 135| 0: [SubExpr] ... - ... +# 135| 0: [IntLiteral] 1 +# 135| 1: [IntLiteral] 2 +# 135| 1: [LocalVariableAccess] access to local variable v +# 137| 1: [SpecificCatchClause] catch (...) {...} +# 137| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 138| 1: [BlockStmt] {...} +# 139| 0: [EmptyStmt] ; +# 141| 2: [SpecificCatchClause] catch (...) {...} +# 141| 0: [LocalVariableDeclExpr] OverflowException ex +# 142| 1: [BlockStmt] {...} +# 143| 0: [EmptyStmt] ; +# 145| 3: [SpecificCatchClause] catch (...) {...} +# 145| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 146| 1: [BlockStmt] {...} +# 147| 0: [EmptyStmt] ; +# 149| 4: [SpecificCatchClause] catch (...) {...} +# 149| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 150| 1: [BlockStmt] {...} +# 151| 0: [EmptyStmt] ; +# 153| 5: [SpecificCatchClause] catch (...) {...} +# 153| 0: [LocalVariableDeclExpr] Exception ex +# 154| 1: [BlockStmt] {...} +# 155| 0: [EmptyStmt] ; +# 159| 12: [Method] TestIntMul +# 160| 4: [BlockStmt] {...} +# 161| 0: [TryStmt] try {...} ... +# 162| 0: [BlockStmt] {...} +# 163| 0: [EmptyStmt] ; +# 164| 1: [LocalVariableDeclStmt] ... ...; +# 164| 0: [LocalVariableDeclAndInitExpr] Int32 v = ... +# 164| 0: [MulExpr] ... * ... +# 164| 0: [IntLiteral] 1 +# 164| 1: [IntLiteral] 2 +# 164| 1: [LocalVariableAccess] access to local variable v +# 166| 1: [SpecificCatchClause] catch (...) {...} +# 166| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 167| 1: [BlockStmt] {...} +# 168| 0: [EmptyStmt] ; +# 170| 2: [SpecificCatchClause] catch (...) {...} +# 170| 0: [LocalVariableDeclExpr] OverflowException ex +# 171| 1: [BlockStmt] {...} +# 172| 0: [EmptyStmt] ; +# 174| 3: [SpecificCatchClause] catch (...) {...} +# 174| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 175| 1: [BlockStmt] {...} +# 176| 0: [EmptyStmt] ; +# 178| 4: [SpecificCatchClause] catch (...) {...} +# 178| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 179| 1: [BlockStmt] {...} +# 180| 0: [EmptyStmt] ; +# 182| 5: [SpecificCatchClause] catch (...) {...} +# 182| 0: [LocalVariableDeclExpr] Exception ex +# 183| 1: [BlockStmt] {...} +# 184| 0: [EmptyStmt] ; +# 188| 13: [Method] TestStringLiteral +# 189| 4: [BlockStmt] {...} +# 190| 0: [TryStmt] try {...} ... +# 191| 0: [BlockStmt] {...} +# 192| 0: [EmptyStmt] ; +# 193| 1: [LocalVariableDeclStmt] ... ...; +# 193| 0: [LocalVariableDeclAndInitExpr] String v = ... +# 193| 0: [StringLiteral] "" +# 193| 1: [LocalVariableAccess] access to local variable v +# 195| 1: [SpecificCatchClause] catch (...) {...} +# 195| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 196| 1: [BlockStmt] {...} +# 197| 0: [EmptyStmt] ; +# 199| 2: [SpecificCatchClause] catch (...) {...} +# 199| 0: [LocalVariableDeclExpr] OverflowException ex +# 200| 1: [BlockStmt] {...} +# 201| 0: [EmptyStmt] ; +# 203| 3: [SpecificCatchClause] catch (...) {...} +# 203| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 204| 1: [BlockStmt] {...} +# 205| 0: [EmptyStmt] ; +# 207| 4: [SpecificCatchClause] catch (...) {...} +# 207| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 208| 1: [BlockStmt] {...} +# 209| 0: [EmptyStmt] ; +# 211| 5: [SpecificCatchClause] catch (...) {...} +# 211| 0: [LocalVariableDeclExpr] Exception ex +# 212| 1: [BlockStmt] {...} +# 213| 0: [EmptyStmt] ; +# 217| 14: [Method] TestStringAdd +# 218| 4: [BlockStmt] {...} +# 219| 0: [TryStmt] try {...} ... +# 220| 0: [BlockStmt] {...} +# 221| 0: [LocalVariableDeclStmt] ... ...; +# 221| 0: [LocalVariableDeclAndInitExpr] String s = ... +# 221| 0: [StringLiteral] "" +# 221| 1: [LocalVariableAccess] access to local variable s +# 222| 1: [EmptyStmt] ; +# 223| 2: [LocalVariableDeclStmt] ... ...; +# 223| 0: [LocalVariableDeclAndInitExpr] String v = ... +# 223| 0: [AddExpr] ... + ... +# 223| 0: [LocalVariableAccess] access to local variable s +# 223| 1: [LocalVariableAccess] access to local variable s +# 223| 1: [LocalVariableAccess] access to local variable v +# 225| 1: [SpecificCatchClause] catch (...) {...} +# 225| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 226| 1: [BlockStmt] {...} +# 227| 0: [EmptyStmt] ; +# 229| 2: [SpecificCatchClause] catch (...) {...} +# 229| 0: [LocalVariableDeclExpr] OverflowException ex +# 230| 1: [BlockStmt] {...} +# 231| 0: [EmptyStmt] ; +# 233| 3: [SpecificCatchClause] catch (...) {...} +# 233| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 234| 1: [BlockStmt] {...} +# 235| 0: [EmptyStmt] ; +# 237| 4: [SpecificCatchClause] catch (...) {...} +# 237| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 238| 1: [BlockStmt] {...} +# 239| 0: [EmptyStmt] ; +# 241| 5: [SpecificCatchClause] catch (...) {...} +# 241| 0: [LocalVariableDeclExpr] Exception ex +# 242| 1: [BlockStmt] {...} +# 243| 0: [EmptyStmt] ; +# 247| 15: [Method] TestDivide +# 248| 4: [BlockStmt] {...} +# 249| 0: [TryStmt] try {...} ... +# 250| 0: [BlockStmt] {...} +# 251| 0: [EmptyStmt] ; +# 252| 1: [LocalVariableDeclStmt] ... ...; +# 252| 0: [LocalVariableDeclAndInitExpr] Int32 v = ... +# 252| 0: [DivExpr] ... / ... +# 252| 0: [IntLiteral] 1 +# 252| 1: [IntLiteral] 2 +# 252| 1: [LocalVariableAccess] access to local variable v +# 254| 1: [SpecificCatchClause] catch (...) {...} +# 254| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 255| 1: [BlockStmt] {...} +# 256| 0: [EmptyStmt] ; +# 258| 2: [SpecificCatchClause] catch (...) {...} +# 258| 0: [LocalVariableDeclExpr] OverflowException ex +# 259| 1: [BlockStmt] {...} +# 260| 0: [EmptyStmt] ; +# 262| 3: [SpecificCatchClause] catch (...) {...} +# 262| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 263| 1: [BlockStmt] {...} +# 264| 0: [EmptyStmt] ; +# 266| 4: [SpecificCatchClause] catch (...) {...} +# 266| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 267| 1: [BlockStmt] {...} +# 268| 0: [EmptyStmt] ; +# 270| 5: [SpecificCatchClause] catch (...) {...} +# 270| 0: [LocalVariableDeclExpr] Exception ex +# 271| 1: [BlockStmt] {...} +# 272| 0: [EmptyStmt] ; +# 276| 16: [Method] TestRemainder +# 277| 4: [BlockStmt] {...} +# 278| 0: [TryStmt] try {...} ... +# 279| 0: [BlockStmt] {...} +# 280| 0: [EmptyStmt] ; +# 281| 1: [LocalVariableDeclStmt] ... ...; +# 281| 0: [LocalVariableDeclAndInitExpr] Int32 v = ... +# 281| 0: [RemExpr] ... % ... +# 281| 0: [IntLiteral] 1 +# 281| 1: [IntLiteral] 2 +# 281| 1: [LocalVariableAccess] access to local variable v +# 283| 1: [SpecificCatchClause] catch (...) {...} +# 283| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 284| 1: [BlockStmt] {...} +# 285| 0: [EmptyStmt] ; +# 287| 2: [SpecificCatchClause] catch (...) {...} +# 287| 0: [LocalVariableDeclExpr] OverflowException ex +# 288| 1: [BlockStmt] {...} +# 289| 0: [EmptyStmt] ; +# 291| 3: [SpecificCatchClause] catch (...) {...} +# 291| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 292| 1: [BlockStmt] {...} +# 293| 0: [EmptyStmt] ; +# 295| 4: [SpecificCatchClause] catch (...) {...} +# 295| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 296| 1: [BlockStmt] {...} +# 297| 0: [EmptyStmt] ; +# 299| 5: [SpecificCatchClause] catch (...) {...} +# 299| 0: [LocalVariableDeclExpr] Exception ex +# 300| 1: [BlockStmt] {...} +# 301| 0: [EmptyStmt] ; +# 305| 17: [Method] TestMemberAccess +# 306| 4: [BlockStmt] {...} +# 307| 0: [TryStmt] try {...} ... +# 308| 0: [BlockStmt] {...} +# 309| 0: [EmptyStmt] ; +# 310| 1: [LocalVariableDeclStmt] ... ...; +# 310| 0: [LocalVariableDeclAndInitExpr] Int32 v = ... +# 310| 0: [FieldAccess] access to field p +# 310| -1: [ThisAccess] this access +# 310| 1: [LocalVariableAccess] access to local variable v +# 312| 1: [SpecificCatchClause] catch (...) {...} +# 312| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 313| 1: [BlockStmt] {...} +# 314| 0: [EmptyStmt] ; +# 316| 2: [SpecificCatchClause] catch (...) {...} +# 316| 0: [LocalVariableDeclExpr] OverflowException ex +# 317| 1: [BlockStmt] {...} +# 318| 0: [EmptyStmt] ; +# 320| 3: [SpecificCatchClause] catch (...) {...} +# 320| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 321| 1: [BlockStmt] {...} +# 322| 0: [EmptyStmt] ; +# 324| 4: [SpecificCatchClause] catch (...) {...} +# 324| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 325| 1: [BlockStmt] {...} +# 326| 0: [EmptyStmt] ; +# 328| 5: [SpecificCatchClause] catch (...) {...} +# 328| 0: [LocalVariableDeclExpr] Exception ex +# 329| 1: [BlockStmt] {...} +# 330| 0: [EmptyStmt] ; +# 334| 18: [Method] TestCast +# 335| 4: [BlockStmt] {...} +# 336| 0: [TryStmt] try {...} ... +# 337| 0: [BlockStmt] {...} +# 338| 0: [EmptyStmt] ; +# 339| 1: [LocalVariableDeclStmt] ... ...; +# 339| 0: [LocalVariableDeclAndInitExpr] Int16 v = ... +# 339| 0: [CastExpr] (...) ... +# 339| 0: [IntLiteral] 1 +# 339| 1: [TypeAccess] access to type Int16 +# 339| 1: [LocalVariableAccess] access to local variable v +# 341| 1: [SpecificCatchClause] catch (...) {...} +# 341| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 342| 1: [BlockStmt] {...} +# 343| 0: [EmptyStmt] ; +# 345| 2: [SpecificCatchClause] catch (...) {...} +# 345| 0: [LocalVariableDeclExpr] OverflowException ex +# 346| 1: [BlockStmt] {...} +# 347| 0: [EmptyStmt] ; +# 349| 3: [SpecificCatchClause] catch (...) {...} +# 349| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 350| 1: [BlockStmt] {...} +# 351| 0: [EmptyStmt] ; +# 353| 4: [SpecificCatchClause] catch (...) {...} +# 353| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 354| 1: [BlockStmt] {...} +# 355| 0: [EmptyStmt] ; +# 357| 5: [SpecificCatchClause] catch (...) {...} +# 357| 0: [LocalVariableDeclExpr] Exception ex +# 358| 1: [BlockStmt] {...} +# 359| 0: [EmptyStmt] ; +# 363| 19: [Method] TestThrow +# 364| 4: [BlockStmt] {...} +# 365| 0: [TryStmt] try {...} ... +# 366| 0: [BlockStmt] {...} +# 367| 0: [LocalVariableDeclStmt] ... ...; +# 367| 0: [LocalVariableDeclAndInitExpr] DivideByZeroException e = ... +# 367| 0: [ObjectCreation] object creation of type DivideByZeroException +# 367| 1: [LocalVariableAccess] access to local variable e +# 368| 1: [EmptyStmt] ; +# 369| 2: [ThrowStmt] throw ...; +# 369| 0: [LocalVariableAccess] access to local variable e +# 371| 1: [SpecificCatchClause] catch (...) {...} +# 371| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 372| 1: [BlockStmt] {...} +# 373| 0: [EmptyStmt] ; +# 375| 2: [SpecificCatchClause] catch (...) {...} +# 375| 0: [LocalVariableDeclExpr] OverflowException ex +# 376| 1: [BlockStmt] {...} +# 377| 0: [EmptyStmt] ; +# 379| 3: [SpecificCatchClause] catch (...) {...} +# 379| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 380| 1: [BlockStmt] {...} +# 381| 0: [EmptyStmt] ; +# 383| 4: [SpecificCatchClause] catch (...) {...} +# 383| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 384| 1: [BlockStmt] {...} +# 385| 0: [EmptyStmt] ; +# 387| 5: [SpecificCatchClause] catch (...) {...} +# 387| 0: [LocalVariableDeclExpr] Exception ex +# 388| 1: [BlockStmt] {...} +# 389| 0: [EmptyStmt] ; +# 393| 20: [Method] TestUnaryOperation +# 394| 4: [BlockStmt] {...} +# 395| 0: [TryStmt] try {...} ... +# 396| 0: [BlockStmt] {...} +# 397| 0: [LocalVariableDeclStmt] ... ...; +# 397| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 397| 0: [IntLiteral] 1 +# 397| 1: [LocalVariableAccess] access to local variable a +# 398| 1: [EmptyStmt] ; +# 399| 2: [ExprStmt] ...; +# 399| 0: [PreIncrExpr] ++... +# 399| 0: [LocalVariableAccess] access to local variable a +# 401| 1: [SpecificCatchClause] catch (...) {...} +# 401| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 402| 1: [BlockStmt] {...} +# 403| 0: [EmptyStmt] ; +# 405| 2: [SpecificCatchClause] catch (...) {...} +# 405| 0: [LocalVariableDeclExpr] OverflowException ex +# 406| 1: [BlockStmt] {...} +# 407| 0: [EmptyStmt] ; +# 409| 3: [SpecificCatchClause] catch (...) {...} +# 409| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 410| 1: [BlockStmt] {...} +# 411| 0: [EmptyStmt] ; +# 413| 4: [SpecificCatchClause] catch (...) {...} +# 413| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 414| 1: [BlockStmt] {...} +# 415| 0: [EmptyStmt] ; +# 417| 5: [SpecificCatchClause] catch (...) {...} +# 417| 0: [LocalVariableDeclExpr] Exception ex +# 418| 1: [BlockStmt] {...} +# 419| 0: [EmptyStmt] ; +# 423| 21: [Method] TestRethrow +# 424| 4: [BlockStmt] {...} +# 425| 0: [TryStmt] try {...} ... +# 426| 0: [BlockStmt] {...} +# 427| 0: [TryStmt] try {...} ... +# 428| 0: [BlockStmt] {...} +# 430| 1: [GeneralCatchClause] catch {...} +# 431| 1: [BlockStmt] {...} +# 432| 0: [EmptyStmt] ; +# 433| 1: [ThrowStmt] throw ...; +# 436| 1: [SpecificCatchClause] catch (...) {...} +# 436| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 437| 1: [BlockStmt] {...} +# 438| 0: [EmptyStmt] ; +# 440| 2: [GeneralCatchClause] catch {...} +# 441| 1: [BlockStmt] {...} +# 442| 0: [EmptyStmt] ; +# 446| 22: [Method] TestSubtypeCast +# 447| 4: [BlockStmt] {...} +# 448| 0: [TryStmt] try {...} ... +# 449| 0: [BlockStmt] {...} +# 450| 0: [LocalVariableDeclStmt] ... ...; +# 450| 0: [LocalVariableDeclAndInitExpr] Object o = ... +# 450| 0: [NullLiteral] null +# 450| 1: [LocalVariableAccess] access to local variable o +# 451| 1: [EmptyStmt] ; +# 452| 2: [LocalVariableDeclStmt] ... ...; +# 452| 0: [LocalVariableDeclAndInitExpr] Class1 p = ... +# 452| 0: [CastExpr] (...) ... +# 452| 0: [LocalVariableAccess] access to local variable o +# 452| 1: [TypeAccess] access to type Class1 +# 452| 1: [LocalVariableAccess] access to local variable p +# 454| 1: [SpecificCatchClause] catch (...) {...} +# 454| 0: [LocalVariableDeclExpr] InvalidCastException ex +# 455| 1: [BlockStmt] {...} +# 456| 0: [EmptyStmt] ; +# 458| 2: [SpecificCatchClause] catch (...) {...} +# 458| 0: [LocalVariableDeclExpr] Exception ex +# 459| 1: [BlockStmt] {...} +# 460| 0: [EmptyStmt] ; +# 464| 23: [Method] TestDivideMaybeZero +#-----| 2: (Parameters) +# 464| 0: [Parameter] i +# 465| 4: [BlockStmt] {...} +# 466| 0: [TryStmt] try {...} ... +# 467| 0: [BlockStmt] {...} +# 468| 0: [EmptyStmt] ; +# 469| 1: [LocalVariableDeclStmt] ... ...; +# 469| 0: [LocalVariableDeclAndInitExpr] Int32 v = ... +# 469| 0: [DivExpr] ... / ... +# 469| 0: [IntLiteral] 1 +# 469| 1: [ParameterAccess] access to parameter i +# 469| 1: [LocalVariableAccess] access to local variable v +# 471| 1: [SpecificCatchClause] catch (...) {...} +# 471| 0: [LocalVariableDeclExpr] NullReferenceException ex +# 472| 1: [BlockStmt] {...} +# 473| 0: [EmptyStmt] ; +# 475| 2: [SpecificCatchClause] catch (...) {...} +# 475| 0: [LocalVariableDeclExpr] OverflowException ex +# 476| 1: [BlockStmt] {...} +# 477| 0: [EmptyStmt] ; +# 479| 3: [SpecificCatchClause] catch (...) {...} +# 479| 0: [LocalVariableDeclExpr] OutOfMemoryException ex +# 480| 1: [BlockStmt] {...} +# 481| 0: [EmptyStmt] ; +# 483| 4: [SpecificCatchClause] catch (...) {...} +# 483| 0: [LocalVariableDeclExpr] DivideByZeroException ex +# 484| 1: [BlockStmt] {...} +# 485| 0: [EmptyStmt] ; +# 487| 5: [SpecificCatchClause] catch (...) {...} +# 487| 0: [LocalVariableDeclExpr] Exception ex +# 488| 1: [BlockStmt] {...} +# 489| 0: [EmptyStmt] ; diff --git a/csharp/ql/test/library-tests/exceptions/PrintAst.qlref b/csharp/ql/test/library-tests/exceptions/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/exceptions/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod1.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod1.expected index 99cbc7b72803..a443b2e5f587 100644 --- a/csharp/ql/test/library-tests/expressions/AnonymousMethod1.expected +++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod1.expected @@ -1 +1 @@ -| expressions.cs:443:33:443:66 | delegate(...) { ... } | +| expressions.cs:455:33:455:66 | delegate(...) { ... } | diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod2.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod2.expected index 71d45f3859be..a4eb1c44ada0 100644 --- a/csharp/ql/test/library-tests/expressions/AnonymousMethod2.expected +++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod2.expected @@ -1 +1 @@ -| expressions.cs:443:33:443:66 | delegate(...) { ... } | expressions.cs:443:47:443:47 | x | +| expressions.cs:455:33:455:66 | delegate(...) { ... } | expressions.cs:455:47:455:47 | x | diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod3.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod3.expected index 99cbc7b72803..a443b2e5f587 100644 --- a/csharp/ql/test/library-tests/expressions/AnonymousMethod3.expected +++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod3.expected @@ -1 +1 @@ -| expressions.cs:443:33:443:66 | delegate(...) { ... } | +| expressions.cs:455:33:455:66 | delegate(...) { ... } | diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod4.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod4.expected index f1ea5fb30213..650c309ba907 100644 --- a/csharp/ql/test/library-tests/expressions/AnonymousMethod4.expected +++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod4.expected @@ -1 +1 @@ -| expressions.cs:445:28:445:53 | delegate(...) { ... } | expressions.cs:445:28:445:53 | delegate(...) { ... } | +| expressions.cs:457:28:457:53 | delegate(...) { ... } | expressions.cs:457:28:457:53 | delegate(...) { ... } | diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod5.expected b/csharp/ql/test/library-tests/expressions/AnonymousMethod5.expected index aa2ee29ffda7..2f79004d8efe 100644 --- a/csharp/ql/test/library-tests/expressions/AnonymousMethod5.expected +++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod5.expected @@ -1 +1 @@ -| expressions.cs:445:28:445:53 | delegate(...) { ... } | expressions.cs:445:46:445:46 | access to local variable j | +| expressions.cs:457:28:457:53 | delegate(...) { ... } | expressions.cs:457:46:457:46 | access to local variable j | diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected new file mode 100644 index 000000000000..2fa6120d6953 --- /dev/null +++ b/csharp/ql/test/library-tests/expressions/ArrayCreation11.expected @@ -0,0 +1,27 @@ +| expressions.cs:168:27:168:44 | array creation of type Object[] | expressions.cs:168:27:168:44 | 1 | true | +| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 2 | true | +| expressions.cs:409:23:409:65 | array creation of type Int32[,] | expressions.cs:409:23:409:65 | 3 | true | +| expressions.cs:437:24:437:66 | array creation of type Int32[,] | expressions.cs:437:24:437:66 | 2 | true | +| expressions.cs:437:24:437:66 | array creation of type Int32[,] | expressions.cs:437:24:437:66 | 3 | true | +| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 2 | true | +| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 2 | true | +| expressions.cs:438:17:438:93 | array creation of type Int32[,,] | expressions.cs:438:17:438:93 | 3 | true | +| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 2 | true | +| expressions.cs:439:17:443:13 | array creation of type Int32[,][,] | expressions.cs:439:17:443:13 | 3 | true | +| expressions.cs:441:19:441:45 | array creation of type Int32[,] | expressions.cs:441:19:441:45 | 2 | true | +| expressions.cs:441:19:441:45 | array creation of type Int32[,] | expressions.cs:441:19:441:45 | 2 | true | +| expressions.cs:441:48:441:82 | array creation of type Int32[,] | expressions.cs:441:48:441:82 | 2 | true | +| expressions.cs:441:48:441:82 | array creation of type Int32[,] | expressions.cs:441:48:441:82 | 3 | true | +| expressions.cs:441:85:441:122 | array creation of type Int32[,] | expressions.cs:441:85:441:122 | 2 | true | +| expressions.cs:441:85:441:122 | array creation of type Int32[,] | expressions.cs:441:85:441:122 | 3 | true | +| expressions.cs:442:19:442:45 | array creation of type Int32[,] | expressions.cs:442:19:442:45 | 2 | true | +| expressions.cs:442:19:442:45 | array creation of type Int32[,] | expressions.cs:442:19:442:45 | 2 | true | +| expressions.cs:442:48:442:82 | array creation of type Int32[,] | expressions.cs:442:48:442:82 | 2 | true | +| expressions.cs:442:48:442:82 | array creation of type Int32[,] | expressions.cs:442:48:442:82 | 3 | true | +| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 2 | true | +| expressions.cs:442:85:442:122 | array creation of type Int32[,] | expressions.cs:442:85:442:122 | 3 | true | +| expressions.cs:444:17:444:123 | array creation of type Int32[,][] | expressions.cs:444:17:444:123 | 2 | true | +| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 1 | true | +| expressions.cs:444:32:444:54 | array creation of type Int32[,] | expressions.cs:444:32:444:54 | 2 | true | +| expressions.cs:444:57:444:121 | array creation of type Int32[,] | expressions.cs:444:57:444:121 | 3 | true | +| expressions.cs:444:57:444:121 | array creation of type Int32[,] | expressions.cs:444:57:444:121 | 4 | true | diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation11.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation11.ql new file mode 100644 index 000000000000..6d7a501fefaa --- /dev/null +++ b/csharp/ql/test/library-tests/expressions/ArrayCreation11.ql @@ -0,0 +1,13 @@ +/** + * @name Test for array creations + */ + +import csharp + +private boolean isImplicit(Expr expr) { + if expr.isImplicit() then result = true else result = false +} + +from ArrayCreation ac, Expr expr +where ac.isImplicitlySized() and not ac.isImplicitlyTyped() and expr = ac.getALengthArgument() +select ac, expr, isImplicit(expr) diff --git a/csharp/ql/test/library-tests/expressions/Lambda1.expected b/csharp/ql/test/library-tests/expressions/Lambda1.expected index 030ff70fac43..34e99d531ad5 100644 --- a/csharp/ql/test/library-tests/expressions/Lambda1.expected +++ b/csharp/ql/test/library-tests/expressions/Lambda1.expected @@ -1 +1 @@ -| expressions.cs:437:36:437:53 | (...) => ... | +| expressions.cs:449:36:449:53 | (...) => ... | diff --git a/csharp/ql/test/library-tests/expressions/Lambda2.expected b/csharp/ql/test/library-tests/expressions/Lambda2.expected index 6b9ab4d13af2..a59313cf13e6 100644 --- a/csharp/ql/test/library-tests/expressions/Lambda2.expected +++ b/csharp/ql/test/library-tests/expressions/Lambda2.expected @@ -1 +1 @@ -| expressions.cs:438:38:438:59 | (...) => ... | +| expressions.cs:450:38:450:59 | (...) => ... | diff --git a/csharp/ql/test/library-tests/expressions/Lambda3.expected b/csharp/ql/test/library-tests/expressions/Lambda3.expected index 0a17c1914d6f..f00475e2298d 100644 --- a/csharp/ql/test/library-tests/expressions/Lambda3.expected +++ b/csharp/ql/test/library-tests/expressions/Lambda3.expected @@ -1 +1 @@ -| expressions.cs:439:33:439:48 | (...) => ... | +| expressions.cs:451:33:451:48 | (...) => ... | diff --git a/csharp/ql/test/library-tests/expressions/Lambda4.expected b/csharp/ql/test/library-tests/expressions/Lambda4.expected index da7c72353aec..92b0dea6fa52 100644 --- a/csharp/ql/test/library-tests/expressions/Lambda4.expected +++ b/csharp/ql/test/library-tests/expressions/Lambda4.expected @@ -1 +1 @@ -| expressions.cs:440:36:440:64 | (...) => ... | +| expressions.cs:452:36:452:64 | (...) => ... | diff --git a/csharp/ql/test/library-tests/expressions/Lambda5.expected b/csharp/ql/test/library-tests/expressions/Lambda5.expected index cc846823df46..08d4b5546ae2 100644 --- a/csharp/ql/test/library-tests/expressions/Lambda5.expected +++ b/csharp/ql/test/library-tests/expressions/Lambda5.expected @@ -1 +1 @@ -| expressions.cs:441:20:441:34 | (...) => ... | +| expressions.cs:453:20:453:34 | (...) => ... | diff --git a/csharp/ql/test/library-tests/expressions/Lambda6.expected b/csharp/ql/test/library-tests/expressions/Lambda6.expected index 83d1caa259dc..55427d92103e 100644 --- a/csharp/ql/test/library-tests/expressions/Lambda6.expected +++ b/csharp/ql/test/library-tests/expressions/Lambda6.expected @@ -1 +1 @@ -| expressions.cs:442:23:442:47 | (...) => ... | +| expressions.cs:454:23:454:47 | (...) => ... | diff --git a/csharp/ql/test/library-tests/expressions/OperatorCall6.expected b/csharp/ql/test/library-tests/expressions/OperatorCall6.expected index 208e70975f36..4f483ef92e22 100644 --- a/csharp/ql/test/library-tests/expressions/OperatorCall6.expected +++ b/csharp/ql/test/library-tests/expressions/OperatorCall6.expected @@ -1,4 +1,4 @@ -| expressions.cs:458:20:458:27 | addition | expressions.cs:460:26:460:26 | access to parameter a | expressions.cs:473:40:473:40 | + | -| expressions.cs:458:20:458:27 | addition | expressions.cs:460:30:460:30 | access to parameter b | expressions.cs:473:40:473:40 | + | -| expressions.cs:458:20:458:27 | addition | expressions.cs:461:13:461:18 | access to local variable result | expressions.cs:473:40:473:40 | + | -| expressions.cs:458:20:458:27 | addition | expressions.cs:461:23:461:23 | access to parameter c | expressions.cs:473:40:473:40 | + | +| expressions.cs:470:20:470:27 | addition | expressions.cs:472:26:472:26 | access to parameter a | expressions.cs:485:40:485:40 | + | +| expressions.cs:470:20:470:27 | addition | expressions.cs:472:30:472:30 | access to parameter b | expressions.cs:485:40:485:40 | + | +| expressions.cs:470:20:470:27 | addition | expressions.cs:473:13:473:18 | access to local variable result | expressions.cs:485:40:485:40 | + | +| expressions.cs:470:20:470:27 | addition | expressions.cs:473:23:473:23 | access to parameter c | expressions.cs:485:40:485:40 | + | diff --git a/csharp/ql/test/library-tests/expressions/OperatorCall7.expected b/csharp/ql/test/library-tests/expressions/OperatorCall7.expected index b59bcd180fa9..46f56cef5ac9 100644 --- a/csharp/ql/test/library-tests/expressions/OperatorCall7.expected +++ b/csharp/ql/test/library-tests/expressions/OperatorCall7.expected @@ -1,2 +1,2 @@ -| expressions.cs:452:21:452:35 | delegateCombine | expressions.cs:450:11:450:23 | OperatorCalls | expressions.cs:455:13:455:27 | access to local variable PropertyChanged | expressions.cs:479:30:479:39 | MyDelegate | -| expressions.cs:452:21:452:35 | delegateCombine | expressions.cs:450:11:450:23 | OperatorCalls | expressions.cs:455:32:455:34 | access to parameter fun | expressions.cs:479:30:479:39 | MyDelegate | +| expressions.cs:464:21:464:35 | delegateCombine | expressions.cs:462:11:462:23 | OperatorCalls | expressions.cs:467:13:467:27 | access to local variable PropertyChanged | expressions.cs:491:30:491:39 | MyDelegate | +| expressions.cs:464:21:464:35 | delegateCombine | expressions.cs:462:11:462:23 | OperatorCalls | expressions.cs:467:32:467:34 | access to parameter fun | expressions.cs:491:30:491:39 | MyDelegate | diff --git a/csharp/ql/test/library-tests/expressions/PrintAst.expected b/csharp/ql/test/library-tests/expressions/PrintAst.expected new file mode 100644 index 000000000000..9e654a6eeb84 --- /dev/null +++ b/csharp/ql/test/library-tests/expressions/PrintAst.expected @@ -0,0 +1,1957 @@ +FoldedLiterals.cs: +# 1| [Class] FoldedLiterals +# 3| 5: [Method] Test +# 4| 4: [BlockStmt] {...} +# 6| 0: [LocalVariableDeclStmt] ... ...; +# 6| 0: [LocalVariableDeclAndInitExpr] Boolean b1 = ... +# 6| 0: [BoolLiteral] false +# 6| 1: [LocalVariableAccess] access to local variable b1 +# 7| 1: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclAndInitExpr] Boolean b2 = ... +# 7| 0: [LogicalNotExpr] !... +# 7| 0: [BoolLiteral] false +# 7| 1: [LocalVariableAccess] access to local variable b2 +# 10| 2: [LocalVariableDeclStmt] ... ...; +# 10| 0: [LocalVariableDeclAndInitExpr] Char c0 = ... +# 10| 0: [CharLiteral] +# 10| 1: [LocalVariableAccess] access to local variable c0 +# 11| 3: [LocalVariableDeclStmt] ... ...; +# 11| 0: [LocalVariableDeclAndInitExpr] Int32 c1 = ... +# 11| 0: [UnaryPlusExpr] +... +# 11| 0: [CastExpr] (...) ... +# 11| 0: [CharLiteral] +# 11| 1: [LocalVariableAccess] access to local variable c1 +# 12| 4: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclAndInitExpr] Int32 c2 = ... +# 12| 0: [UnaryMinusExpr] -... +# 13| 0: [CastExpr] (...) ... +# 13| 0: [CharLiteral] +# 12| 1: [LocalVariableAccess] access to local variable c2 +# 14| 5: [LocalVariableDeclStmt] ... ...; +# 14| 0: [LocalVariableDeclAndInitExpr] Int32 c3 = ... +# 14| 0: [ComplementExpr] ~... +# 14| 0: [CastExpr] (...) ... +# 14| 0: [CharLiteral] +# 14| 1: [LocalVariableAccess] access to local variable c3 +# 15| 6: [LocalVariableDeclStmt] ... ...; +# 15| 0: [LocalVariableDeclAndInitExpr] Char c4 = ... +# 15| 0: [CharLiteral] \ +# 15| 1: [LocalVariableAccess] access to local variable c4 +# 18| 7: [LocalVariableDeclStmt] ... ...; +# 18| 0: [LocalVariableDeclAndInitExpr] SByte sb0 = ... +# 18| 0: [CastExpr] (...) ... +# 18| 0: [IntLiteral] 1 +# 18| 1: [TypeAccess] access to type SByte +# 18| 1: [LocalVariableAccess] access to local variable sb0 +# 19| 8: [LocalVariableDeclStmt] ... ...; +# 19| 0: [LocalVariableDeclAndInitExpr] Int32 sb1 = ... +# 19| 0: [UnaryPlusExpr] +... +# 19| 0: [CastExpr] (...) ... +# 19| 0: [CastExpr] (...) ... +# 19| 0: [IntLiteral] 1 +# 19| 1: [TypeAccess] access to type SByte +# 19| 1: [LocalVariableAccess] access to local variable sb1 +# 20| 9: [LocalVariableDeclStmt] ... ...; +# 20| 0: [LocalVariableDeclAndInitExpr] Int32 sb2 = ... +# 20| 0: [UnaryMinusExpr] -... +# 20| 0: [CastExpr] (...) ... +# 20| 0: [CastExpr] (...) ... +# 20| 0: [IntLiteral] 1 +# 20| 1: [TypeAccess] access to type SByte +# 20| 1: [LocalVariableAccess] access to local variable sb2 +# 21| 10: [LocalVariableDeclStmt] ... ...; +# 21| 0: [LocalVariableDeclAndInitExpr] Int32 sb3 = ... +# 21| 0: [ComplementExpr] ~... +# 21| 0: [CastExpr] (...) ... +# 21| 0: [CastExpr] (...) ... +# 21| 0: [IntLiteral] 1 +# 21| 1: [TypeAccess] access to type SByte +# 21| 1: [LocalVariableAccess] access to local variable sb3 +# 24| 11: [LocalVariableDeclStmt] ... ...; +# 24| 0: [LocalVariableDeclAndInitExpr] Byte ub0 = ... +# 24| 0: [CastExpr] (...) ... +# 24| 0: [IntLiteral] 2 +# 24| 1: [TypeAccess] access to type Byte +# 24| 1: [LocalVariableAccess] access to local variable ub0 +# 25| 12: [LocalVariableDeclStmt] ... ...; +# 25| 0: [LocalVariableDeclAndInitExpr] Int32 ub1 = ... +# 25| 0: [UnaryPlusExpr] +... +# 25| 0: [CastExpr] (...) ... +# 25| 0: [CastExpr] (...) ... +# 25| 0: [IntLiteral] 2 +# 25| 1: [TypeAccess] access to type Byte +# 25| 1: [LocalVariableAccess] access to local variable ub1 +# 26| 13: [LocalVariableDeclStmt] ... ...; +# 26| 0: [LocalVariableDeclAndInitExpr] Int32 ub2 = ... +# 26| 0: [UnaryMinusExpr] -... +# 26| 0: [CastExpr] (...) ... +# 26| 0: [CastExpr] (...) ... +# 26| 0: [IntLiteral] 2 +# 26| 1: [TypeAccess] access to type Byte +# 26| 1: [LocalVariableAccess] access to local variable ub2 +# 27| 14: [LocalVariableDeclStmt] ... ...; +# 27| 0: [LocalVariableDeclAndInitExpr] Int32 ub3 = ... +# 27| 0: [ComplementExpr] ~... +# 27| 0: [CastExpr] (...) ... +# 27| 0: [CastExpr] (...) ... +# 27| 0: [IntLiteral] 2 +# 27| 1: [TypeAccess] access to type Byte +# 27| 1: [LocalVariableAccess] access to local variable ub3 +# 30| 15: [LocalVariableDeclStmt] ... ...; +# 30| 0: [LocalVariableDeclAndInitExpr] Int16 ss0 = ... +# 30| 0: [CastExpr] (...) ... +# 30| 0: [IntLiteral] 3 +# 30| 1: [TypeAccess] access to type Int16 +# 30| 1: [LocalVariableAccess] access to local variable ss0 +# 31| 16: [LocalVariableDeclStmt] ... ...; +# 31| 0: [LocalVariableDeclAndInitExpr] Int32 ss1 = ... +# 31| 0: [UnaryPlusExpr] +... +# 31| 0: [CastExpr] (...) ... +# 31| 0: [CastExpr] (...) ... +# 31| 0: [IntLiteral] 3 +# 31| 1: [TypeAccess] access to type Int16 +# 31| 1: [LocalVariableAccess] access to local variable ss1 +# 32| 17: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclAndInitExpr] Int32 ss2 = ... +# 32| 0: [UnaryMinusExpr] -... +# 32| 0: [CastExpr] (...) ... +# 32| 0: [CastExpr] (...) ... +# 32| 0: [IntLiteral] 3 +# 32| 1: [TypeAccess] access to type Int16 +# 32| 1: [LocalVariableAccess] access to local variable ss2 +# 33| 18: [LocalVariableDeclStmt] ... ...; +# 33| 0: [LocalVariableDeclAndInitExpr] Int32 ss3 = ... +# 33| 0: [ComplementExpr] ~... +# 33| 0: [CastExpr] (...) ... +# 33| 0: [CastExpr] (...) ... +# 33| 0: [IntLiteral] 3 +# 33| 1: [TypeAccess] access to type Int16 +# 33| 1: [LocalVariableAccess] access to local variable ss3 +# 36| 19: [LocalVariableDeclStmt] ... ...; +# 36| 0: [LocalVariableDeclAndInitExpr] UInt16 us0 = ... +# 36| 0: [CastExpr] (...) ... +# 36| 0: [IntLiteral] 4 +# 36| 1: [TypeAccess] access to type UInt16 +# 36| 1: [LocalVariableAccess] access to local variable us0 +# 37| 20: [LocalVariableDeclStmt] ... ...; +# 37| 0: [LocalVariableDeclAndInitExpr] Int32 us1 = ... +# 37| 0: [UnaryPlusExpr] +... +# 37| 0: [CastExpr] (...) ... +# 37| 0: [CastExpr] (...) ... +# 37| 0: [IntLiteral] 4 +# 37| 1: [TypeAccess] access to type UInt16 +# 37| 1: [LocalVariableAccess] access to local variable us1 +# 38| 21: [LocalVariableDeclStmt] ... ...; +# 38| 0: [LocalVariableDeclAndInitExpr] Int32 us2 = ... +# 38| 0: [UnaryMinusExpr] -... +# 38| 0: [CastExpr] (...) ... +# 38| 0: [CastExpr] (...) ... +# 38| 0: [IntLiteral] 4 +# 38| 1: [TypeAccess] access to type UInt16 +# 38| 1: [LocalVariableAccess] access to local variable us2 +# 39| 22: [LocalVariableDeclStmt] ... ...; +# 39| 0: [LocalVariableDeclAndInitExpr] Int32 us3 = ... +# 39| 0: [ComplementExpr] ~... +# 39| 0: [CastExpr] (...) ... +# 39| 0: [CastExpr] (...) ... +# 39| 0: [IntLiteral] 4 +# 39| 1: [TypeAccess] access to type UInt16 +# 39| 1: [LocalVariableAccess] access to local variable us3 +# 42| 23: [LocalVariableDeclStmt] ... ...; +# 42| 0: [LocalVariableDeclAndInitExpr] Int32 i0 = ... +# 42| 0: [IntLiteral] 5 +# 42| 1: [LocalVariableAccess] access to local variable i0 +# 43| 24: [LocalVariableDeclStmt] ... ...; +# 43| 0: [LocalVariableDeclAndInitExpr] Int32 i1 = ... +# 43| 0: [UnaryPlusExpr] +... +# 43| 0: [IntLiteral] 5 +# 43| 1: [LocalVariableAccess] access to local variable i1 +# 44| 25: [LocalVariableDeclStmt] ... ...; +# 44| 0: [LocalVariableDeclAndInitExpr] Int32 i2 = ... +# 44| 0: [UnaryMinusExpr] -... +# 44| 0: [IntLiteral] 5 +# 44| 1: [LocalVariableAccess] access to local variable i2 +# 45| 26: [LocalVariableDeclStmt] ... ...; +# 45| 0: [LocalVariableDeclAndInitExpr] Int32 i3 = ... +# 45| 0: [ComplementExpr] ~... +# 45| 0: [IntLiteral] 5 +# 45| 1: [LocalVariableAccess] access to local variable i3 +# 48| 27: [LocalVariableDeclStmt] ... ...; +# 48| 0: [LocalVariableDeclAndInitExpr] UInt32 ui0 = ... +# 48| 0: [CastExpr] (...) ... +# 48| 0: [IntLiteral] 6 +# 48| 1: [LocalVariableAccess] access to local variable ui0 +# 49| 28: [LocalVariableDeclStmt] ... ...; +# 49| 0: [LocalVariableDeclAndInitExpr] Int32 ui1 = ... +# 49| 0: [UnaryPlusExpr] +... +# 49| 0: [IntLiteral] 6 +# 49| 1: [LocalVariableAccess] access to local variable ui1 +# 50| 29: [LocalVariableDeclStmt] ... ...; +# 50| 0: [LocalVariableDeclAndInitExpr] Int32 ui2 = ... +# 50| 0: [UnaryMinusExpr] -... +# 50| 0: [IntLiteral] 6 +# 50| 1: [LocalVariableAccess] access to local variable ui2 +# 51| 30: [LocalVariableDeclStmt] ... ...; +# 51| 0: [LocalVariableDeclAndInitExpr] Int32 ui3 = ... +# 51| 0: [ComplementExpr] ~... +# 51| 0: [IntLiteral] 6 +# 51| 1: [LocalVariableAccess] access to local variable ui3 +# 54| 31: [LocalVariableDeclStmt] ... ...; +# 54| 0: [LocalVariableDeclAndInitExpr] Int64 l0 = ... +# 54| 0: [LongLiteral] 7 +# 54| 1: [LocalVariableAccess] access to local variable l0 +# 55| 32: [LocalVariableDeclStmt] ... ...; +# 55| 0: [LocalVariableDeclAndInitExpr] Int64 l1 = ... +# 55| 0: [UnaryPlusExpr] +... +# 55| 0: [LongLiteral] 7 +# 55| 1: [LocalVariableAccess] access to local variable l1 +# 55| 33: [EmptyStmt] ; +# 56| 34: [LocalVariableDeclStmt] ... ...; +# 56| 0: [LocalVariableDeclAndInitExpr] Int64 l2 = ... +# 56| 0: [UnaryMinusExpr] -... +# 56| 0: [LongLiteral] 7 +# 56| 1: [LocalVariableAccess] access to local variable l2 +# 57| 35: [LocalVariableDeclStmt] ... ...; +# 57| 0: [LocalVariableDeclAndInitExpr] Int64 l3 = ... +# 57| 0: [ComplementExpr] ~... +# 57| 0: [LongLiteral] 7 +# 57| 1: [LocalVariableAccess] access to local variable l3 +# 60| 36: [LocalVariableDeclStmt] ... ...; +# 60| 0: [LocalVariableDeclAndInitExpr] UInt64 ul0 = ... +# 60| 0: [ULongLiteral] 8 +# 60| 1: [LocalVariableAccess] access to local variable ul0 +# 61| 37: [LocalVariableDeclStmt] ... ...; +# 61| 0: [LocalVariableDeclAndInitExpr] UInt64 ul1 = ... +# 61| 0: [UnaryPlusExpr] +... +# 61| 0: [ULongLiteral] 8 +# 61| 1: [LocalVariableAccess] access to local variable ul1 +# 62| 38: [LocalVariableDeclStmt] ... ...; +# 62| 0: [LocalVariableDeclAndInitExpr] UInt64 ul3 = ... +# 62| 0: [ComplementExpr] ~... +# 62| 0: [ULongLiteral] 8 +# 62| 1: [LocalVariableAccess] access to local variable ul3 +# 65| 39: [LocalVariableDeclStmt] ... ...; +# 65| 0: [LocalVariableDeclAndInitExpr] Single f0 = ... +# 65| 0: [FloatLiteral] 9 +# 65| 1: [LocalVariableAccess] access to local variable f0 +# 66| 40: [LocalVariableDeclStmt] ... ...; +# 66| 0: [LocalVariableDeclAndInitExpr] Single f1 = ... +# 66| 0: [UnaryPlusExpr] +... +# 66| 0: [FloatLiteral] 9 +# 66| 1: [LocalVariableAccess] access to local variable f1 +# 67| 41: [LocalVariableDeclStmt] ... ...; +# 67| 0: [LocalVariableDeclAndInitExpr] Single f2 = ... +# 67| 0: [UnaryMinusExpr] -... +# 67| 0: [FloatLiteral] 9 +# 67| 1: [LocalVariableAccess] access to local variable f2 +# 70| 42: [LocalVariableDeclStmt] ... ...; +# 70| 0: [LocalVariableDeclAndInitExpr] Double d0 = ... +# 70| 0: [DoubleLiteral] 10 +# 70| 1: [LocalVariableAccess] access to local variable d0 +# 71| 43: [LocalVariableDeclStmt] ... ...; +# 71| 0: [LocalVariableDeclAndInitExpr] Double d1 = ... +# 71| 0: [UnaryPlusExpr] +... +# 71| 0: [DoubleLiteral] 10 +# 71| 1: [LocalVariableAccess] access to local variable d1 +# 72| 44: [LocalVariableDeclStmt] ... ...; +# 72| 0: [LocalVariableDeclAndInitExpr] Double d2 = ... +# 72| 0: [UnaryMinusExpr] -... +# 72| 0: [DoubleLiteral] 10 +# 72| 1: [LocalVariableAccess] access to local variable d2 +# 75| 45: [LocalVariableDeclStmt] ... ...; +# 75| 0: [LocalVariableDeclAndInitExpr] Decimal m0 = ... +# 75| 0: [DecimalLiteral] 11 +# 75| 1: [LocalVariableAccess] access to local variable m0 +# 76| 46: [LocalVariableDeclStmt] ... ...; +# 76| 0: [LocalVariableDeclAndInitExpr] Decimal m1 = ... +# 76| 0: [UnaryPlusExpr] +... +# 76| 0: [DecimalLiteral] 11 +# 76| 1: [LocalVariableAccess] access to local variable m1 +# 77| 47: [LocalVariableDeclStmt] ... ...; +# 77| 0: [LocalVariableDeclAndInitExpr] Decimal m2 = ... +# 77| 0: [UnaryMinusExpr] -... +# 77| 0: [DecimalLiteral] 11 +# 77| 1: [LocalVariableAccess] access to local variable m2 +MethodAccess.cs: +# 3| [Class] MethodAccess +# 5| 5: [Method] M +# 6| 4: [BlockStmt] {...} +# 7| 0: [LocalFunctionStmt] M1(...) +# 7| 0: [LocalFunction] M1 +# 7| 4: [BlockStmt] {...} +# 7| 1: [EmptyStmt] ; +# 8| 2: [LocalVariableDeclStmt] ... ...; +# 8| 0: [LocalVariableDeclAndInitExpr] Action a = ... +# 8| 0: [ImplicitDelegateCreation] delegate creation of type Action +# 8| 0: [LocalFunctionAccess] access to local function M1 +# 8| 1: [LocalVariableAccess] access to local variable a +# 9| 3: [ExprStmt] ...; +# 9| 0: [AssignExpr] ... = ... +# 9| 0: [ImplicitDelegateCreation] delegate creation of type Action +# 9| 0: [MethodAccess] access to method M2 +# 9| 1: [LocalVariableAccess] access to local variable a +# 10| 4: [ExprStmt] ...; +# 10| 0: [AssignExpr] ... = ... +# 10| 0: [ImplicitDelegateCreation] delegate creation of type Action +# 10| 0: [MethodAccess] access to method M2 +# 10| -1: [ThisAccess] this access +# 10| 1: [LocalVariableAccess] access to local variable a +# 11| 5: [ExprStmt] ...; +# 11| 0: [AssignExpr] ... = ... +# 11| 0: [ImplicitDelegateCreation] delegate creation of type Action +# 11| 0: [MethodAccess] access to method M3 +# 11| 1: [LocalVariableAccess] access to local variable a +# 12| 6: [ExprStmt] ...; +# 12| 0: [AssignExpr] ... = ... +# 12| 0: [ImplicitDelegateCreation] delegate creation of type Action +# 12| 0: [MethodAccess] access to method M3 +# 12| -1: [TypeAccess] access to type MethodAccess +# 12| 1: [LocalVariableAccess] access to local variable a +# 15| 6: [Method] M2 +# 15| 4: [BlockStmt] {...} +# 17| 7: [Method] M3 +# 17| 4: [BlockStmt] {...} +Qualifiers.cs: +# 3| [Class] Qualifiers +# 5| 5: [Property] S +# 5| 3: [Getter] get_S +# 5| 4: [MethodCall] call to method Static +# 5| 0: [NullLiteral] null +# 7| 6: [Property] I +# 7| 3: [Getter] get_I +# 7| 4: [MethodCall] call to method Instance +# 9| 7: [Property] B +# 9| 3: [Getter] get_B +# 9| 4: [MethodCall] call to method Instance +# 9| -1: [ThisAccess] this access +# 11| 8: [Method] Static +#-----| 1: (Type parameters) +# 11| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 11| 0: [Parameter] o +# 11| 4: [DefaultValueExpr] default(...) +# 11| 0: [TypeAccess] access to type T +# 13| 10: [Method] Instance +#-----| 1: (Type parameters) +# 13| 0: [TypeParameter] T +# 13| 4: [DefaultValueExpr] default(...) +# 13| 0: [TypeAccess] access to type T +ReducedExpression.cs: +# 2| [Class] ReducedClass +# 5| 5: [Field] ReducedExpression +# 5| 1: [AssignExpr] ... = ... +# 5| 0: [ConditionalExpr] ... ? ... : ... +# 5| 0: [BoolLiteral] true +# 5| 1: [IntLiteral] 10 +# 5| 2: [IntLiteral] 12 +# 5| 1: [MemberConstantAccess] access to constant ReducedExpression +expressions.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [Class] Class +# 10| 4: [Method] MainLiterals +# 11| 4: [BlockStmt] {...} +# 12| 0: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclExpr] Boolean b +# 13| 1: [ExprStmt] ...; +# 13| 0: [AssignExpr] ... = ... +# 13| 0: [BoolLiteral] true +# 13| 1: [LocalVariableAccess] access to local variable b +# 14| 2: [ExprStmt] ...; +# 14| 0: [AssignExpr] ... = ... +# 14| 0: [BoolLiteral] false +# 14| 1: [LocalVariableAccess] access to local variable b +# 15| 3: [ExprStmt] ...; +# 15| 0: [AssignExpr] ... = ... +# 15| 0: [LogicalNotExpr] !... +# 15| 0: [LocalVariableAccess] access to local variable b +# 15| 1: [LocalVariableAccess] access to local variable b +# 16| 4: [LocalVariableDeclStmt] ... ...; +# 16| 0: [LocalVariableDeclAndInitExpr] Char c = ... +# 16| 0: [CharLiteral] @ +# 16| 1: [LocalVariableAccess] access to local variable c +# 17| 5: [LocalVariableDeclStmt] ... ...; +# 17| 0: [LocalVariableDeclExpr] Int32 i +# 18| 6: [ExprStmt] ...; +# 18| 0: [AssignExpr] ... = ... +# 18| 0: [IntLiteral] 1 +# 18| 1: [LocalVariableAccess] access to local variable i +# 19| 7: [ExprStmt] ...; +# 19| 0: [AssignExpr] ... = ... +# 19| 0: [UnaryMinusExpr] -... +# 19| 0: [LocalVariableAccess] access to local variable i +# 19| 1: [LocalVariableAccess] access to local variable i +# 20| 8: [LocalVariableDeclStmt] ... ...; +# 20| 0: [LocalVariableDeclExpr] Int64 l +# 21| 9: [ExprStmt] ...; +# 21| 0: [AssignExpr] ... = ... +# 21| 0: [LongLiteral] 8989898 +# 21| 1: [LocalVariableAccess] access to local variable l +# 22| 10: [LocalVariableDeclStmt] ... ...; +# 22| 0: [LocalVariableDeclExpr] UInt32 ui +# 23| 11: [ExprStmt] ...; +# 23| 0: [AssignExpr] ... = ... +# 23| 0: [UIntLiteral] 3 +# 23| 1: [LocalVariableAccess] access to local variable ui +# 24| 12: [LocalVariableDeclStmt] ... ...; +# 24| 0: [LocalVariableDeclExpr] UInt64 ul +# 25| 13: [ExprStmt] ...; +# 25| 0: [AssignExpr] ... = ... +# 25| 0: [ULongLiteral] 89898787897 +# 25| 1: [LocalVariableAccess] access to local variable ul +# 26| 14: [LocalVariableDeclStmt] ... ...; +# 26| 0: [LocalVariableDeclAndInitExpr] Single f = ... +# 26| 0: [FloatLiteral] 4.5 +# 26| 1: [LocalVariableAccess] access to local variable f +# 27| 15: [LocalVariableDeclStmt] ... ...; +# 27| 0: [LocalVariableDeclExpr] Double d +# 28| 16: [ExprStmt] ...; +# 28| 0: [AssignExpr] ... = ... +# 28| 0: [DoubleLiteral] 4.565 +# 28| 1: [LocalVariableAccess] access to local variable d +# 29| 17: [LocalVariableDeclStmt] ... ...; +# 29| 0: [LocalVariableDeclExpr] Decimal m +# 30| 18: [ExprStmt] ...; +# 30| 0: [AssignExpr] ... = ... +# 30| 0: [DecimalLiteral] 123.456 +# 30| 1: [LocalVariableAccess] access to local variable m +# 31| 19: [LocalVariableDeclStmt] ... ...; +# 31| 0: [LocalVariableDeclExpr] String s +# 32| 20: [ExprStmt] ...; +# 32| 0: [AssignExpr] ... = ... +# 32| 0: [StringLiteral] "test" +# 32| 1: [LocalVariableAccess] access to local variable s +# 33| 21: [LocalVariableDeclStmt] ... ...; +# 33| 0: [LocalVariableDeclExpr] Object o +# 34| 22: [ExprStmt] ...; +# 34| 0: [AssignExpr] ... = ... +# 34| 0: [NullLiteral] null +# 34| 1: [LocalVariableAccess] access to local variable o +# 37| 5: [Method] LogicalOperators +#-----| 2: (Parameters) +# 37| 0: [Parameter] a +# 37| 1: [Parameter] b +# 38| 4: [BlockStmt] {...} +# 39| 0: [LocalVariableDeclStmt] ... ...; +# 39| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 39| 0: [ConditionalExpr] ... ? ... : ... +# 39| 0: [ParameterAccess] access to parameter a +# 39| 1: [IntLiteral] 0 +# 39| 2: [IntLiteral] 42 +# 39| 1: [LocalVariableAccess] access to local variable x +# 40| 1: [LocalVariableDeclStmt] ... ...; +# 40| 0: [LocalVariableDeclAndInitExpr] Boolean c = ... +# 40| 0: [LogicalOrExpr] ... || ... +# 40| 0: [ParameterAccess] access to parameter b +# 40| 1: [GTExpr] ... > ... +# 40| 0: [LocalVariableAccess] access to local variable x +# 40| 1: [IntLiteral] 3 +# 40| 1: [LocalVariableAccess] access to local variable c +# 41| 2: [ReturnStmt] return ...; +# 41| 0: [LogicalAndExpr] ... && ... +# 41| 0: [LogicalAndExpr] ... && ... +# 41| 0: [ParameterAccess] access to parameter a +# 41| 1: [ParameterAccess] access to parameter b +# 41| 1: [LogicalNotExpr] !... +# 41| 0: [LocalVariableAccess] access to local variable c +# 44| 6: [Field] constant +# 44| 1: [AssignExpr] ... = ... +# 44| 0: [StringLiteral] "constant" +# 44| 1: [MemberConstantAccess] access to constant constant +# 45| 7: [Field] f +# 45| 1: [AssignExpr] ... = ... +# 45| 0: [IntLiteral] 0 +# 45| 1: [FieldAccess] access to field f +# 46| 8: [Field] name +# 48| 9: [StaticConstructor] Class +# 49| 4: [BlockStmt] {...} +# 51| 0: [ExprStmt] ...; +# 51| 0: [AssignExpr] ... = ... +# 51| 0: [StringLiteral] "" +# 51| 1: [FieldAccess] access to field name +# 51| -1: [TypeAccess] access to type Class +# 52| 1: [ExprStmt] ...; +# 52| 0: [MethodCall] call to method Foo +# 52| -1: [TypeAccess] access to type Class +# 53| 2: [ExprStmt] ...; +# 53| 0: [MethodCall] call to method Foo +# 56| 10: [InstanceConstructor] Class +# 56| 3: [ConstructorInitializer] call to constructor Class +# 56| 0: [IntLiteral] 0 +# 56| 4: [BlockStmt] {...} +# 58| 11: [InstanceConstructor] Class +#-----| 2: (Parameters) +# 58| 0: [Parameter] i +# 58| 4: [BlockStmt] {...} +# 60| 12: [Method] Foo +# 60| 4: [BlockStmt] {...} +# 62| 13: [Method] Bar +#-----| 2: (Parameters) +# 62| 0: [Parameter] x +# 62| 1: [Parameter] s +# 63| 4: [BlockStmt] {...} +# 64| 0: [ReturnStmt] return ...; +# 64| 0: [AddExpr] ... + ... +# 64| 0: [PropertyCall] access to property Length +# 64| -1: [ParameterAccess] access to parameter s +# 64| 1: [ParameterAccess] access to parameter x +# 67| 14: [IndexerProperty] Name +# 69| 3: [Getter] get_Name +# 69| 4: [BlockStmt] {...} +# 69| 0: [ReturnStmt] return ...; +# 69| 0: [FieldAccess] access to field name +# 70| 4: [Setter] set_Name +#-----| 2: (Parameters) +# 70| 0: [Parameter] value +# 70| 4: [BlockStmt] {...} +# 70| 0: [ExprStmt] ...; +# 70| 0: [AssignExpr] ... = ... +# 70| 0: [ParameterAccess] access to parameter value +# 70| 1: [FieldAccess] access to field name +# 73| 15: [Indexer] Item +#-----| 1: (Parameters) +# 73| 0: [Parameter] i +# 73| 1: [Parameter] s +# 75| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 73| 0: [Parameter] i +# 73| 1: [Parameter] s +# 75| 4: [BlockStmt] {...} +# 75| 0: [ReturnStmt] return ...; +# 75| 0: [LogicalAndExpr] ... && ... +# 75| 0: [GTExpr] ... > ... +# 75| 0: [ParameterAccess] access to parameter i +# 75| 1: [IntLiteral] 2 +# 75| 1: [MethodCall] call to method Equals +# 75| -1: [ParameterAccess] access to parameter s +# 75| 0: [StringLiteral] "" +# 76| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 73| 0: [Parameter] i +# 73| 1: [Parameter] s +# 76| 2: [Parameter] value +# 76| 4: [BlockStmt] {...} +# 76| 0: [IfStmt] if (...) ... +# 76| 0: [ParameterAccess] access to parameter value +# 76| 1: [ExprStmt] ...; +# 76| 0: [PostIncrExpr] ...++ +# 76| 0: [FieldAccess] access to field f +# 79| 16: [Method] MainAccesses +#-----| 2: (Parameters) +# 79| 0: [Parameter] other +# 79| 1: [Parameter] args +# 80| 4: [BlockStmt] {...} +# 81| 0: [ExprStmt] ...; +# 81| 0: [AssignExpr] ... = ... +# 81| 0: [StringLiteral] "aName" +# 81| 1: [PropertyCall] access to property Name +# 82| 1: [LocalVariableDeclStmt] ... ...; +# 82| 0: [LocalVariableDeclAndInitExpr] String n = ... +# 82| 0: [PropertyCall] access to property Name +# 82| -1: [ThisAccess] this access +# 82| 1: [LocalVariableAccess] access to local variable n +# 83| 2: [LocalVariableDeclStmt] ... ...; +# 83| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 83| 0: [MethodCall] call to method Bar +# 83| -1: [ObjectCreation] object creation of type Class +# 83| 0: [AddExpr] ... + ... +# 83| 0: [IntLiteral] 4 +# 83| 1: [IntLiteral] 2 +# 83| 1: [PropertyCall] access to property Name +# 83| 1: [LocalVariableAccess] access to local variable i +# 84| 3: [ExprStmt] ...; +# 84| 0: [AssignExpr] ... = ... +# 84| 0: [MemberConstantAccess] access to constant constant +# 84| 1: [PropertyCall] access to property Name +# 84| -1: [ParameterAccess] access to parameter other +# 85| 4: [ExprStmt] ...; +# 85| 0: [AssignExpr] ... = ... +# 85| 0: [IndexerCall] access to indexer +# 85| -1: [ThisAccess] this access +# 85| 0: [IntLiteral] 0 +# 85| 1: [StringLiteral] "" +# 85| 1: [IndexerCall] access to indexer +# 85| -1: [ParameterAccess] access to parameter other +# 85| 0: [LocalVariableAccess] access to local variable i +# 85| 1: [MemberConstantAccess] access to constant constant +# 85| -1: [TypeAccess] access to type Nested +# 86| 5: [LocalVariableDeclStmt] ... ...; +# 86| 0: [LocalVariableDeclAndInitExpr] Int32[] array = ... +# 86| 0: [ArrayCreation] array creation of type Int32[] +# 86| -1: [ArrayInitializer] { ..., ... } +# 86| 0: [CastExpr] (...) ... +# 86| 0: [ULongLiteral] 4 +# 86| 1: [TypeAccess] access to type Int32 +# 86| 1: [CastExpr] (...) ... +# 86| 0: [LongLiteral] 3 +# 86| 1: [TypeAccess] access to type Int32 +# 86| 1: [LocalVariableAccess] access to local variable array +# 87| 6: [ExprStmt] ...; +# 87| 0: [AssignExpr] ... = ... +# 87| 0: [IntLiteral] 5 +# 87| 1: [ArrayAccess] access to array element +# 87| -1: [LocalVariableAccess] access to local variable array +# 87| 0: [IntLiteral] 1 +# 91| 17: [Method] MainIsAsCast +#-----| 2: (Parameters) +# 91| 0: [Parameter] s +# 91| 1: [Parameter] o +# 91| 2: [Parameter] p +# 92| 4: [BlockStmt] {...} +# 93| 0: [IfStmt] if (...) ... +# 93| 0: [IsExpr] ... is ... +# 93| 0: [ParameterAccess] access to parameter o +# 93| 1: [TypeAccessPatternExpr] access to type Class +# 94| 1: [BlockStmt] {...} +# 95| 0: [LocalVariableDeclStmt] ... ...; +# 95| 0: [LocalVariableDeclAndInitExpr] Class c = ... +# 95| 0: [AsExpr] ... as ... +# 95| 0: [ParameterAccess] access to parameter o +# 95| 1: [TypeAccess] access to type Class +# 95| 1: [LocalVariableAccess] access to local variable c +# 96| 1: [LocalVariableDeclStmt] ... ...; +# 96| 0: [LocalVariableDeclAndInitExpr] Class d = ... +# 96| 0: [CastExpr] (...) ... +# 96| 0: [ParameterAccess] access to parameter p +# 96| 1: [TypeAccess] access to type Class +# 96| 1: [LocalVariableAccess] access to local variable d +# 98| 1: [LocalVariableDeclStmt] ... ...; +# 98| 0: [LocalVariableDeclAndInitExpr] Class x = ... +# 98| 0: [CastExpr] (...) ... +# 98| 0: [AsExpr] ... as ... +# 98| 0: [CastExpr] (...) ... +# 98| 0: [ParameterAccess] access to parameter o +# 98| 1: [TypeAccess] access to type Class +# 98| 1: [TypeAccess] access to type Object +# 98| 1: [TypeAccess] access to type Class +# 98| 1: [LocalVariableAccess] access to local variable x +# 99| 2: [LocalVariableDeclStmt] ... ...; +# 99| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 99| 0: [DefaultValueExpr] default(...) +# 99| 0: [TypeAccess] access to type Int32 +# 99| 1: [LocalVariableAccess] access to local variable i +# 100| 3: [ExprStmt] ...; +# 100| 0: [AssignExpr] ... = ... +# 100| 0: [AddExpr] ... + ... +# 100| 0: [AddExpr] ... + ... +# 100| 0: [ParameterAccess] access to parameter s +# 100| 1: [StringLiteral] " " +# 100| 1: [CastExpr] (...) ... +# 100| 0: [LocalVariableAccess] access to local variable i +# 100| 1: [ParameterAccess] access to parameter s +# 103| 18: [Class] Y<,> +#-----| 1: (Type parameters) +# 103| 0: [TypeParameter] T +# 103| 1: [TypeParameter] U +# 107| 19: [Class] X<> +#-----| 1: (Type parameters) +# 107| 0: [TypeParameter] T +# 110| 5: [Method] PrintTypes +# 111| 4: [BlockStmt] {...} +# 112| 0: [LocalVariableDeclStmt] ... ...; +# 112| 0: [LocalVariableDeclAndInitExpr] Type[] t = ... +# 112| 0: [ArrayCreation] array creation of type Type[] +# 112| -1: [ArrayInitializer] { ..., ... } +# 113| 0: [TypeofExpr] typeof(...) +# 113| 0: [TypeAccess] access to type Void +# 114| 1: [TypeofExpr] typeof(...) +# 114| 0: [TypeAccess] access to type Int32 +# 115| 2: [TypeofExpr] typeof(...) +# 115| 0: [TypeAccess] access to type Int32 +# 116| 3: [TypeofExpr] typeof(...) +# 116| 0: [TypeAccess] access to type String +# 117| 4: [TypeofExpr] typeof(...) +# 117| 0: [TypeAccess] access to type Double[] +# 118| 5: [TypeofExpr] typeof(...) +# 118| 0: [TypeAccess] access to type Void +# 119| 6: [TypeofExpr] typeof(...) +# 119| 0: [TypeAccess] access to type T +# 120| 7: [TypeofExpr] typeof(...) +# 120| 0: [TypeAccess] access to type X<> +# 121| 8: [TypeofExpr] typeof(...) +# 121| 0: [TypeAccess] access to type X> +# 122| 9: [TypeofExpr] typeof(...) +# 122| 0: [TypeAccess] access to type Y<,> +# 112| 1: [LocalVariableAccess] access to local variable t +# 124| 1: [LocalVariableDeclStmt] ... ...; +# 124| 0: [LocalVariableDeclAndInitExpr] T e = ... +# 124| 0: [DefaultValueExpr] default(...) +# 124| 0: [TypeAccess] access to type T +# 124| 1: [LocalVariableAccess] access to local variable e +# 129| 21: [Class] Nested +#-----| 3: (Base types) +# 129| 0: [Class] Class +# 133| 4: [InstanceConstructor] Nested +#-----| 2: (Parameters) +# 133| 0: [Parameter] i +# 133| 3: [ConstructorInitializer] call to constructor Class +# 133| 0: [AddExpr] ... + ... +# 133| 0: [ParameterAccess] access to parameter i +# 133| 1: [IntLiteral] 1 +# 133| 4: [BlockStmt] {...} +# 135| 5: [Method] OtherAccesses +# 136| 4: [BlockStmt] {...} +# 137| 0: [ExprStmt] ...; +# 137| 0: [AssignExpr] ... = ... +# 137| 0: [IntLiteral] 0 +# 137| 1: [FieldAccess] access to field f +# 137| -1: [ThisAccess] this access +# 138| 1: [ExprStmt] ...; +# 138| 0: [MethodCall] call to method MainAccesses +# 138| -1: [BaseAccess] base access +# 138| 0: [ThisAccess] this access +# 138| 1: [CastExpr] (...) ... +# 138| 0: [IntLiteral] 1 +# 138| 2: [CastExpr] (...) ... +# 138| 0: [IntLiteral] 2 +# 138| 3: [CastExpr] (...) ... +# 138| 0: [IntLiteral] 3 +# 138| 4: [CastExpr] (...) ... +# 138| 0: [IntLiteral] 4 +# 138| 5: [StringLiteral] "" +# 143| 22: [Method] MainLocalVarDecl +# 144| 4: [BlockStmt] {...} +# 145| 0: [LocalVariableDeclStmt] ... ...; +# 145| 0: [LocalVariableDeclExpr] Int32 a +# 146| 1: [LocalVariableDeclStmt] ... ...; +# 146| 0: [LocalVariableDeclAndInitExpr] Int32 b = ... +# 146| 0: [IntLiteral] 2 +# 146| 1: [LocalVariableAccess] access to local variable b +# 146| 1: [LocalVariableDeclAndInitExpr] Int32 c = ... +# 146| 0: [IntLiteral] 3 +# 146| 1: [LocalVariableAccess] access to local variable c +# 147| 2: [ExprStmt] ...; +# 147| 0: [AssignExpr] ... = ... +# 147| 0: [IntLiteral] 1 +# 147| 1: [LocalVariableAccess] access to local variable a +# 148| 3: [ExprStmt] ...; +# 148| 0: [MethodCall] call to method WriteLine +# 148| -1: [TypeAccess] access to type Console +# 148| 0: [AddExpr] ... + ... +# 148| 0: [AddExpr] ... + ... +# 148| 0: [LocalVariableAccess] access to local variable a +# 148| 1: [LocalVariableAccess] access to local variable b +# 148| 1: [LocalVariableAccess] access to local variable c +# 149| 4: [LocalVariableDeclStmt] ... ...; +# 149| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 149| 0: [IntLiteral] 45 +# 149| 1: [LocalVariableAccess] access to local variable x +# 150| 5: [LocalVariableDeclStmt] ... ...; +# 150| 0: [LocalVariableDeclAndInitExpr] String y = ... +# 150| 0: [StringLiteral] "test" +# 150| 1: [LocalVariableAccess] access to local variable y +# 153| 23: [Method] MainLocalConstDecl +# 154| 4: [BlockStmt] {...} +# 155| 0: [LocalConstantDeclStmt] const ... ...; +# 155| 0: [LocalVariableDeclAndInitExpr] Single pi = ... +# 155| 0: [FloatLiteral] 3.1415927 +# 155| 1: [LocalVariableAccess] access to local variable pi +# 156| 1: [LocalConstantDeclStmt] const ... ...; +# 156| 0: [LocalVariableDeclAndInitExpr] Int32 r = ... +# 156| 0: [AddExpr] ... + ... +# 156| 0: [IntLiteral] 10 +# 156| 1: [IntLiteral] 15 +# 156| 1: [LocalVariableAccess] access to local variable r +# 157| 2: [ExprStmt] ...; +# 157| 0: [MethodCall] call to method WriteLine +# 157| -1: [TypeAccess] access to type Console +# 157| 0: [MulExpr] ... * ... +# 157| 0: [MulExpr] ... * ... +# 157| 0: [LocalVariableAccess] access to local variable pi +# 157| 1: [CastExpr] (...) ... +# 157| 0: [LocalVariableAccess] access to local variable r +# 157| 1: [CastExpr] (...) ... +# 157| 0: [LocalVariableAccess] access to local variable r +# 160| 24: [Method] MainChecked +# 161| 4: [BlockStmt] {...} +# 162| 0: [LocalVariableDeclStmt] ... ...; +# 162| 0: [LocalVariableDeclAndInitExpr] String s = ... +# 162| 0: [CheckedExpr] checked (...) +# 162| 0: [PropertyCall] access to property Name +# 162| 1: [LocalVariableAccess] access to local variable s +# 163| 1: [LocalVariableDeclStmt] ... ...; +# 163| 0: [LocalVariableDeclAndInitExpr] Int32 t = ... +# 163| 0: [UncheckedExpr] unchecked (...) +# 163| 0: [AddExpr] ... + ... +# 163| 0: [FieldAccess] access to field f +# 163| 1: [IntLiteral] 20 +# 163| 1: [LocalVariableAccess] access to local variable t +# 166| 25: [Method] MainElementAccess +#-----| 2: (Parameters) +# 166| 0: [Parameter] i +# 167| 4: [BlockStmt] {...} +# 168| 0: [LocalVariableDeclStmt] ... ...; +# 168| 0: [LocalVariableDeclAndInitExpr] Object[] os = ... +# 168| 0: [ArrayCreation] array creation of type Object[] +# 168| -1: [ArrayInitializer] { ..., ... } +# 168| 0: [CastExpr] (...) ... +# 168| 0: [ParameterAccess] access to parameter i +# 168| 1: [LocalVariableAccess] access to local variable os +# 171| 26: [Method] MainDelegateAndMethodAccesses +# 172| 4: [BlockStmt] {...} +# 173| 0: [LocalVariableDeclStmt] ... ...; +# 173| 0: [LocalVariableDeclAndInitExpr] D cd1 = ... +# 173| 0: [ExplicitDelegateCreation] delegate creation of type D +# 173| 0: [MethodAccess] access to method M1 +# 173| -1: [TypeAccess] access to type C +# 173| 1: [LocalVariableAccess] access to local variable cd1 +# 174| 1: [LocalVariableDeclStmt] ... ...; +# 174| 0: [LocalVariableDeclAndInitExpr] D cd2 = ... +# 174| 0: [ImplicitDelegateCreation] delegate creation of type D +# 174| 0: [MethodAccess] access to method M2 +# 174| -1: [TypeAccess] access to type C +# 174| 1: [LocalVariableAccess] access to local variable cd2 +# 175| 2: [LocalVariableDeclStmt] ... ...; +# 175| 0: [LocalVariableDeclAndInitExpr] D cd3 = ... +# 175| 0: [OperatorCall] call to operator + +# 175| 0: [LocalVariableAccess] access to local variable cd1 +# 175| 1: [LocalVariableAccess] access to local variable cd2 +# 175| 1: [LocalVariableAccess] access to local variable cd3 +# 176| 3: [LocalVariableDeclStmt] ... ...; +# 176| 0: [LocalVariableDeclAndInitExpr] D cd4 = ... +# 176| 0: [OperatorCall] call to operator + +# 176| 0: [LocalVariableAccess] access to local variable cd3 +# 176| 1: [LocalVariableAccess] access to local variable cd1 +# 176| 1: [LocalVariableAccess] access to local variable cd4 +# 177| 4: [LocalVariableDeclStmt] ... ...; +# 177| 0: [LocalVariableDeclAndInitExpr] D cd5 = ... +# 177| 0: [OperatorCall] call to operator - +# 177| 0: [LocalVariableAccess] access to local variable cd4 +# 177| 1: [LocalVariableAccess] access to local variable cd3 +# 177| 1: [LocalVariableAccess] access to local variable cd5 +# 178| 5: [ExprStmt] ...; +# 178| 0: [AssignAddExpr] ... += ... +# 178| 0: [LocalVariableAccess] access to local variable cd5 +# 178| 1: [LocalVariableAccess] access to local variable cd4 +# 179| 6: [ExprStmt] ...; +# 179| 0: [AssignSubExpr] ... -= ... +# 179| 0: [LocalVariableAccess] access to local variable cd1 +# 179| 1: [LocalVariableAccess] access to local variable cd4 +# 181| 7: [LocalVariableDeclStmt] ... ...; +# 181| 0: [LocalVariableDeclAndInitExpr] C c = ... +# 181| 0: [ObjectCreation] object creation of type C +# 181| 1: [LocalVariableAccess] access to local variable c +# 182| 8: [LocalVariableDeclStmt] ... ...; +# 182| 0: [LocalVariableDeclAndInitExpr] D cd6 = ... +# 182| 0: [ExplicitDelegateCreation] delegate creation of type D +# 182| 0: [MethodAccess] access to method M3 +# 182| -1: [LocalVariableAccess] access to local variable c +# 182| 1: [LocalVariableAccess] access to local variable cd6 +# 183| 9: [LocalVariableDeclStmt] ... ...; +# 183| 0: [LocalVariableDeclAndInitExpr] D cd7 = ... +# 183| 0: [ExplicitDelegateCreation] delegate creation of type D +# 183| 0: [LocalVariableAccess] access to local variable cd6 +# 183| 1: [LocalVariableAccess] access to local variable cd7 +# 185| 10: [ExprStmt] ...; +# 185| 0: [DelegateCall] delegate call +# 185| -1: [LocalVariableAccess] access to local variable cd1 +# 185| 0: [UnaryMinusExpr] -... +# 185| 0: [IntLiteral] 40 +# 186| 11: [LocalVariableDeclStmt] ... ...; +# 186| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 186| 0: [IntLiteral] 0 +# 186| 1: [LocalVariableAccess] access to local variable x +# 187| 12: [ExprStmt] ...; +# 187| 0: [DelegateCall] delegate call +# 187| -1: [LocalVariableAccess] access to local variable cd7 +# 187| 0: [AddExpr] ... + ... +# 187| 0: [IntLiteral] 34 +# 187| 1: [LocalVariableAccess] access to local variable x +# 189| 13: [LocalVariableDeclStmt] ... ...; +# 189| 0: [LocalVariableDeclAndInitExpr] Predicate pi = ... +# 189| 0: [ExplicitDelegateCreation] delegate creation of type Predicate +# 189| 0: [MethodAccess] access to method F +# 189| -1: [TypeAccess] access to type X +# 189| 1: [LocalVariableAccess] access to local variable pi +# 190| 14: [LocalVariableDeclStmt] ... ...; +# 190| 0: [LocalVariableDeclAndInitExpr] Predicate ps = ... +# 190| 0: [ImplicitDelegateCreation] delegate creation of type Predicate +# 190| 0: [MethodAccess] access to method G +# 190| -1: [TypeAccess] access to type X +# 190| 1: [LocalVariableAccess] access to local variable ps +# 192| 15: [LocalVariableDeclStmt] ... ...; +# 192| 0: [LocalVariableDeclAndInitExpr] Boolean b = ... +# 192| 0: [BitwiseAndExpr] ... & ... +# 192| 0: [DelegateCall] delegate call +# 192| -1: [LocalVariableAccess] access to local variable pi +# 192| 0: [IntLiteral] 3 +# 192| 1: [DelegateCall] delegate call +# 192| -1: [LocalVariableAccess] access to local variable ps +# 192| 0: [StringLiteral] "" +# 192| 1: [LocalVariableAccess] access to local variable b +# 194| 16: [LocalVariableDeclStmt] ... ...; +# 194| 0: [LocalVariableDeclExpr] ContextCallback d +# 196| 17: [LocalFunctionStmt] LocalFunction(...) +# 196| 0: [LocalFunction] LocalFunction +#-----| 2: (Parameters) +# 196| 0: [Parameter] i +# 196| 4: [BlockStmt] {...} +# 196| 18: [EmptyStmt] ; +# 197| 19: [ExprStmt] ...; +# 197| 0: [AssignExpr] ... = ... +# 197| 0: [ExplicitDelegateCreation] delegate creation of type D +# 197| 0: [LocalFunctionAccess] access to local function LocalFunction +# 197| 1: [LocalVariableAccess] access to local variable cd1 +# 198| 20: [ExprStmt] ...; +# 198| 0: [AssignExpr] ... = ... +# 198| 0: [ImplicitDelegateCreation] delegate creation of type D +# 198| 0: [LocalFunctionAccess] access to local function LocalFunction +# 198| 1: [LocalVariableAccess] access to local variable cd1 +# 202| 2: [DelegateType] Predicate<> +#-----| 1: (Type parameters) +# 202| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 202| 0: [Parameter] value +# 204| 3: [DelegateType] D +#-----| 2: (Parameters) +# 204| 0: [Parameter] x +# 206| 4: [Class] C +# 209| 5: [Method] M1 +#-----| 2: (Parameters) +# 209| 0: [Parameter] i +# 209| 4: [BlockStmt] {...} +# 210| 6: [Method] M2 +#-----| 2: (Parameters) +# 210| 0: [Parameter] i +# 210| 4: [BlockStmt] {...} +# 211| 7: [Method] M3 +#-----| 2: (Parameters) +# 211| 0: [Parameter] i +# 211| 4: [BlockStmt] {...} +# 215| 5: [Class] X +# 218| 5: [Method] F +#-----| 2: (Parameters) +# 218| 0: [Parameter] i +# 218| 4: [BlockStmt] {...} +# 218| 0: [ReturnStmt] return ...; +# 218| 0: [LTExpr] ... < ... +# 218| 0: [ParameterAccess] access to parameter i +# 218| 1: [IntLiteral] 2 +# 220| 6: [Method] G +#-----| 2: (Parameters) +# 220| 0: [Parameter] s +# 220| 4: [BlockStmt] {...} +# 220| 0: [ReturnStmt] return ...; +# 220| 0: [BoolLiteral] false +# 224| 6: [DelegateType] EventHandler +#-----| 2: (Parameters) +# 224| 0: [Parameter] sender +# 224| 1: [Parameter] e +# 226| 7: [Class] Button +# 229| 5: [Event] Click +# 229| 3: [AddEventAccessor] add_Click +#-----| 2: (Parameters) +# 229| 0: [Parameter] value +# 229| 4: [RemoveEventAccessor] remove_Click +#-----| 2: (Parameters) +# 229| 0: [Parameter] value +# 231| 6: [Method] OnClick +#-----| 2: (Parameters) +# 231| 0: [Parameter] e +# 232| 4: [BlockStmt] {...} +# 233| 0: [IfStmt] if (...) ... +# 233| 0: [OperatorCall] call to operator != +# 233| 0: [EventAccess,EventCall] access to event Click +# 233| 1: [NullLiteral] null +# 234| 1: [ExprStmt] ...; +# 234| 0: [DelegateCall] delegate call +# 234| -1: [EventAccess,EventCall] access to event Click +# 234| 0: [ThisAccess] this access +# 234| 1: [ParameterAccess] access to parameter e +# 237| 7: [Method] Reset +# 238| 4: [BlockStmt] {...} +# 239| 0: [ExprStmt] ...; +# 239| 0: [AssignExpr] ... = ... +# 239| 0: [NullLiteral] null +# 239| 1: [EventAccess,EventCall] access to event Click +# 243| 8: [Class] LoginDialog +# 246| 4: [Field] OkButton +# 247| 5: [Field] CancelButton +# 249| 6: [InstanceConstructor] LoginDialog +# 250| 4: [BlockStmt] {...} +# 251| 0: [ExprStmt] ...; +# 251| 0: [AssignExpr] ... = ... +# 251| 0: [ObjectCreation] object creation of type Button +# 251| 1: [FieldAccess] access to field OkButton +# 252| 1: [ExprStmt] ...; +# 252| 0: [AddEventExpr] ... += ... +# 252| 0: [ExplicitDelegateCreation] delegate creation of type EventHandler +# 252| 0: [MethodAccess] access to method OkButtonClick +# 252| 1: [EventAccess,EventCall] access to event Click +# 252| -1: [FieldAccess] access to field OkButton +# 253| 2: [ExprStmt] ...; +# 253| 0: [AssignExpr] ... = ... +# 253| 0: [ObjectCreation] object creation of type Button +# 253| 1: [FieldAccess] access to field CancelButton +# 254| 3: [ExprStmt] ...; +# 254| 0: [RemoveEventExpr] ... -= ... +# 254| 0: [ExplicitDelegateCreation] delegate creation of type EventHandler +# 254| 0: [MethodAccess] access to method CancelButtonClick +# 254| 1: [EventAccess,EventCall] access to event Click +# 254| -1: [FieldAccess] access to field CancelButton +# 257| 7: [Method] OkButtonClick +#-----| 2: (Parameters) +# 257| 0: [Parameter] sender +# 257| 1: [Parameter] e +# 258| 4: [BlockStmt] {...} +# 261| 8: [Method] CancelButtonClick +#-----| 2: (Parameters) +# 261| 0: [Parameter] sender +# 261| 1: [Parameter] e +# 262| 4: [BlockStmt] {...} +# 267| 9: [Class] IntVector +# 270| 4: [InstanceConstructor] IntVector +#-----| 2: (Parameters) +# 270| 0: [Parameter] length +# 270| 4: [BlockStmt] {...} +# 272| 5: [Property] Length +# 272| 3: [Getter] get_Length +# 272| 4: [BlockStmt] {...} +# 272| 0: [ReturnStmt] return ...; +# 272| 0: [IntLiteral] 4 +# 274| 6: [Indexer] Item +#-----| 1: (Parameters) +# 274| 0: [Parameter] index +# 274| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 274| 0: [Parameter] index +# 274| 4: [BlockStmt] {...} +# 274| 0: [ReturnStmt] return ...; +# 274| 0: [IntLiteral] 0 +# 274| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 274| 0: [Parameter] index +# 274| 1: [Parameter] value +# 274| 4: [BlockStmt] {...} +# 276| 7: [IncrementOperator] ++ +#-----| 2: (Parameters) +# 276| 0: [Parameter] iv +# 277| 4: [BlockStmt] {...} +# 278| 0: [LocalVariableDeclStmt] ... ...; +# 278| 0: [LocalVariableDeclAndInitExpr] IntVector temp = ... +# 278| 0: [ObjectCreation] object creation of type IntVector +# 278| 0: [PropertyCall] access to property Length +# 278| -1: [ParameterAccess] access to parameter iv +# 278| 1: [LocalVariableAccess] access to local variable temp +# 279| 1: [ForStmt] for (...;...;...) ... +# 279| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 279| 0: [IntLiteral] 0 +# 279| 1: [LocalVariableAccess] access to local variable i +# 279| 0: [LTExpr] ... < ... +# 279| 0: [LocalVariableAccess] access to local variable i +# 279| 1: [PropertyCall] access to property Length +# 279| -1: [ParameterAccess] access to parameter iv +# 279| 1: [PostIncrExpr] ...++ +# 279| 0: [LocalVariableAccess] access to local variable i +# 280| 2: [ExprStmt] ...; +# 280| 0: [AssignExpr] ... = ... +# 280| 0: [AddExpr] ... + ... +# 280| 0: [IndexerCall] access to indexer +# 280| -1: [ParameterAccess] access to parameter iv +# 280| 0: [LocalVariableAccess] access to local variable i +# 280| 1: [IntLiteral] 1 +# 280| 1: [IndexerCall] access to indexer +# 280| -1: [LocalVariableAccess] access to local variable temp +# 280| 0: [LocalVariableAccess] access to local variable i +# 281| 2: [ReturnStmt] return ...; +# 281| 0: [LocalVariableAccess] access to local variable temp +# 284| 8: [AddOperator] + +#-----| 2: (Parameters) +# 284| 0: [Parameter] iv1 +# 284| 1: [Parameter] iv2 +# 285| 4: [BlockStmt] {...} +# 286| 0: [ReturnStmt] return ...; +# 286| 0: [ParameterAccess] access to parameter iv1 +# 291| 10: [Class] TestUnaryOperator +# 294| 5: [Method] MainUnaryOperator +# 295| 4: [BlockStmt] {...} +# 296| 0: [LocalVariableDeclStmt] ... ...; +# 296| 0: [LocalVariableDeclAndInitExpr] IntVector iv1 = ... +# 296| 0: [ObjectCreation] object creation of type IntVector +# 296| 0: [IntLiteral] 4 +# 296| 1: [LocalVariableAccess] access to local variable iv1 +# 297| 1: [LocalVariableDeclStmt] ... ...; +# 297| 0: [LocalVariableDeclExpr] IntVector iv2 +# 298| 2: [ExprStmt] ...; +# 298| 0: [AssignExpr] ... = ... +# 298| 0: [OperatorCall] call to operator ++ +# 298| 0: [LocalVariableAccess] access to local variable iv1 +# 298| 1: [LocalVariableAccess] access to local variable iv2 +# 299| 3: [ExprStmt] ...; +# 299| 0: [AssignExpr] ... = ... +# 299| 0: [OperatorCall] call to operator ++ +# 299| 0: [LocalVariableAccess] access to local variable iv1 +# 299| 1: [LocalVariableAccess] access to local variable iv2 +# 300| 4: [LocalVariableDeclStmt] ... ...; +# 300| 0: [LocalVariableDeclAndInitExpr] IntVector iv3 = ... +# 300| 0: [OperatorCall] call to operator + +# 300| 0: [LocalVariableAccess] access to local variable iv1 +# 300| 1: [LocalVariableAccess] access to local variable iv2 +# 300| 1: [LocalVariableAccess] access to local variable iv3 +# 305| 11: [Struct] Digit +# 308| 5: [Field] value +# 310| 6: [InstanceConstructor] Digit +#-----| 2: (Parameters) +# 310| 0: [Parameter] value +# 311| 4: [BlockStmt] {...} +# 312| 0: [IfStmt] if (...) ... +# 312| 0: [LogicalOrExpr] ... || ... +# 312| 0: [LTExpr] ... < ... +# 312| 0: [CastExpr] (...) ... +# 312| 0: [ParameterAccess] access to parameter value +# 312| 1: [IntLiteral] 0 +# 312| 1: [GTExpr] ... > ... +# 312| 0: [CastExpr] (...) ... +# 312| 0: [ParameterAccess] access to parameter value +# 312| 1: [IntLiteral] 9 +# 313| 1: [ThrowStmt] throw ...; +# 313| 0: [ObjectCreation] object creation of type ArgumentException +# 314| 1: [ExprStmt] ...; +# 314| 0: [AssignExpr] ... = ... +# 314| 0: [ParameterAccess] access to parameter value +# 314| 1: [FieldAccess] access to field value +# 314| -1: [ThisAccess] this access +# 317| 7: [ImplicitConversionOperator] implicit conversion +#-----| 2: (Parameters) +# 317| 0: [Parameter] d +# 318| 4: [BlockStmt] {...} +# 319| 0: [ReturnStmt] return ...; +# 319| 0: [FieldAccess] access to field value +# 319| -1: [ParameterAccess] access to parameter d +# 322| 8: [ExplicitConversionOperator] explicit conversion +#-----| 2: (Parameters) +# 322| 0: [Parameter] b +# 323| 4: [BlockStmt] {...} +# 324| 0: [ReturnStmt] return ...; +# 324| 0: [ObjectCreation] object creation of type Digit +# 324| 0: [ParameterAccess] access to parameter b +# 329| 12: [Class] TestConversionOperator +# 332| 5: [Method] MainConversionOperator +# 333| 4: [BlockStmt] {...} +# 334| 0: [LocalVariableDeclStmt] ... ...; +# 334| 0: [LocalVariableDeclAndInitExpr] Digit d = ... +# 334| 0: [OperatorCall] call to operator explicit conversion +# 334| 0: [CastExpr] (...) ... +# 334| 0: [IntLiteral] 8 +# 334| 1: [LocalVariableAccess] access to local variable d +# 335| 1: [LocalVariableDeclStmt] ... ...; +# 335| 0: [LocalVariableDeclAndInitExpr] Byte b = ... +# 335| 0: [OperatorCall] call to operator implicit conversion +# 335| 0: [LocalVariableAccess] access to local variable d +# 335| 1: [LocalVariableAccess] access to local variable b +# 340| 13: [Class] Point +# 343| 5: [Field] x +# 343| 6: [Field] y +# 345| 7: [Property] X +# 345| 3: [Getter] get_X +# 345| 4: [BlockStmt] {...} +# 345| 0: [ReturnStmt] return ...; +# 345| 0: [FieldAccess] access to field x +# 345| 4: [Setter] set_X +#-----| 2: (Parameters) +# 345| 0: [Parameter] value +# 345| 4: [BlockStmt] {...} +# 345| 0: [ExprStmt] ...; +# 345| 0: [AssignExpr] ... = ... +# 345| 0: [ParameterAccess] access to parameter value +# 345| 1: [FieldAccess] access to field x +# 346| 8: [Property] Y +# 346| 3: [Getter] get_Y +# 346| 4: [BlockStmt] {...} +# 346| 0: [ReturnStmt] return ...; +# 346| 0: [FieldAccess] access to field y +# 346| 4: [Setter] set_Y +#-----| 2: (Parameters) +# 346| 0: [Parameter] value +# 346| 4: [BlockStmt] {...} +# 346| 0: [ExprStmt] ...; +# 346| 0: [AssignExpr] ... = ... +# 346| 0: [ParameterAccess] access to parameter value +# 346| 1: [FieldAccess] access to field y +# 350| 14: [Class] Rectangle +# 353| 5: [Field] p1 +# 353| 6: [Field] p2 +# 355| 7: [Property] P1 +# 355| 3: [Getter] get_P1 +# 355| 4: [BlockStmt] {...} +# 355| 0: [ReturnStmt] return ...; +# 355| 0: [FieldAccess] access to field p1 +# 355| 4: [Setter] set_P1 +#-----| 2: (Parameters) +# 355| 0: [Parameter] value +# 355| 4: [BlockStmt] {...} +# 355| 0: [ExprStmt] ...; +# 355| 0: [AssignExpr] ... = ... +# 355| 0: [ParameterAccess] access to parameter value +# 355| 1: [FieldAccess] access to field p1 +# 356| 8: [Property] P2 +# 356| 3: [Getter] get_P2 +# 356| 4: [BlockStmt] {...} +# 356| 0: [ReturnStmt] return ...; +# 356| 0: [FieldAccess] access to field p2 +# 356| 4: [Setter] set_P2 +#-----| 2: (Parameters) +# 356| 0: [Parameter] value +# 356| 4: [BlockStmt] {...} +# 356| 0: [ExprStmt] ...; +# 356| 0: [AssignExpr] ... = ... +# 356| 0: [ParameterAccess] access to parameter value +# 356| 1: [FieldAccess] access to field p2 +# 360| 15: [Class] Rectangle2 +# 363| 5: [Field] p1 +# 363| 1: [AssignExpr] ... = ... +# 363| 0: [ObjectCreation] object creation of type Point +# 363| 1: [FieldAccess] access to field p1 +# 364| 6: [Field] p2 +# 364| 1: [AssignExpr] ... = ... +# 364| 0: [ObjectCreation] object creation of type Point +# 364| 1: [FieldAccess] access to field p2 +# 366| 7: [Property] P1 +# 366| 3: [Getter] get_P1 +# 366| 4: [BlockStmt] {...} +# 366| 0: [ReturnStmt] return ...; +# 366| 0: [FieldAccess] access to field p1 +# 367| 8: [Property] P2 +# 367| 3: [Getter] get_P2 +# 367| 4: [BlockStmt] {...} +# 367| 0: [ReturnStmt] return ...; +# 367| 0: [FieldAccess] access to field p2 +# 371| 16: [Class] Contact +# 374| 5: [Field] name +# 375| 6: [Field] phoneNumbers +# 375| 1: [AssignExpr] ... = ... +# 375| 0: [ObjectCreation] object creation of type List +# 375| 1: [FieldAccess] access to field phoneNumbers +# 377| 7: [IndexerProperty] Name +# 377| 3: [Getter] get_Name +# 377| 4: [BlockStmt] {...} +# 377| 0: [ReturnStmt] return ...; +# 377| 0: [FieldAccess] access to field name +# 377| 4: [Setter] set_Name +#-----| 2: (Parameters) +# 377| 0: [Parameter] value +# 377| 4: [BlockStmt] {...} +# 377| 0: [ExprStmt] ...; +# 377| 0: [AssignExpr] ... = ... +# 377| 0: [ParameterAccess] access to parameter value +# 377| 1: [FieldAccess] access to field name +# 378| 8: [IndexerProperty] PhoneNumbers +# 378| 3: [Getter] get_PhoneNumbers +# 378| 4: [BlockStmt] {...} +# 378| 0: [ReturnStmt] return ...; +# 378| 0: [FieldAccess] access to field phoneNumbers +# 382| 17: [Class] TestCreations +# 385| 5: [Method] MainCreations +# 386| 4: [BlockStmt] {...} +# 387| 0: [LocalVariableDeclStmt] ... ...; +# 387| 0: [LocalVariableDeclAndInitExpr] Point a = ... +# 387| 0: [ObjectCreation] object creation of type Point +# 387| -1: [ObjectInitializer] { ..., ... } +# 387| 0: [MemberInitializer] ... = ... +# 387| 0: [IntLiteral] 0 +# 387| 1: [PropertyCall] access to property X +# 387| 1: [MemberInitializer] ... = ... +# 387| 0: [IntLiteral] 1 +# 387| 1: [PropertyCall] access to property Y +# 387| 1: [LocalVariableAccess] access to local variable a +# 388| 1: [LocalVariableDeclStmt] ... ...; +# 388| 0: [LocalVariableDeclAndInitExpr] Rectangle r = ... +# 388| 0: [ObjectCreation] object creation of type Rectangle +# 389| -1: [ObjectInitializer] { ..., ... } +# 390| 0: [MemberInitializer] ... = ... +# 390| 0: [ObjectCreation] object creation of type Point +# 390| -1: [ObjectInitializer] { ..., ... } +# 390| 0: [MemberInitializer] ... = ... +# 390| 0: [IntLiteral] 0 +# 390| 1: [PropertyCall] access to property X +# 390| 1: [MemberInitializer] ... = ... +# 390| 0: [IntLiteral] 1 +# 390| 1: [PropertyCall] access to property Y +# 390| 1: [PropertyCall] access to property P1 +# 391| 1: [MemberInitializer] ... = ... +# 391| 0: [ObjectCreation] object creation of type Point +# 391| -1: [ObjectInitializer] { ..., ... } +# 391| 0: [MemberInitializer] ... = ... +# 391| 0: [IntLiteral] 2 +# 391| 1: [PropertyCall] access to property X +# 391| 1: [MemberInitializer] ... = ... +# 391| 0: [IntLiteral] 3 +# 391| 1: [PropertyCall] access to property Y +# 391| 1: [PropertyCall] access to property P2 +# 388| 1: [LocalVariableAccess] access to local variable r +# 393| 2: [LocalVariableDeclStmt] ... ...; +# 393| 0: [LocalVariableDeclAndInitExpr] Rectangle2 r2 = ... +# 393| 0: [ObjectCreation] object creation of type Rectangle2 +# 394| -1: [ObjectInitializer] { ..., ... } +# 395| 0: [MemberInitializer] ... = ... +# 395| 0: [ObjectInitializer] { ..., ... } +# 395| 0: [MemberInitializer] ... = ... +# 395| 0: [IntLiteral] 0 +# 395| 1: [PropertyCall] access to property X +# 395| 1: [MemberInitializer] ... = ... +# 395| 0: [IntLiteral] 1 +# 395| 1: [PropertyCall] access to property Y +# 395| 1: [PropertyCall] access to property P1 +# 396| 1: [MemberInitializer] ... = ... +# 396| 0: [ObjectInitializer] { ..., ... } +# 396| 0: [MemberInitializer] ... = ... +# 396| 0: [IntLiteral] 2 +# 396| 1: [PropertyCall] access to property X +# 396| 1: [MemberInitializer] ... = ... +# 396| 0: [IntLiteral] 3 +# 396| 1: [PropertyCall] access to property Y +# 396| 1: [PropertyCall] access to property P2 +# 393| 1: [LocalVariableAccess] access to local variable r2 +# 398| 3: [LocalVariableDeclStmt] ... ...; +# 398| 0: [LocalVariableDeclAndInitExpr] List digits = ... +# 398| 0: [ObjectCreation] object creation of type List +# 398| -1: [CollectionInitializer] { ..., ... } +# 398| 0: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 0 +# 398| 1: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 1 +# 398| 2: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 2 +# 398| 3: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 3 +# 398| 4: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 4 +# 398| 5: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 5 +# 398| 6: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 6 +# 398| 7: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 7 +# 398| 8: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 8 +# 398| 9: [ElementInitializer] call to method Add +# 398| 0: [IntLiteral] 9 +# 398| 1: [LocalVariableAccess] access to local variable digits +# 399| 4: [LocalVariableDeclStmt] ... ...; +# 399| 0: [LocalVariableDeclAndInitExpr] List contacts = ... +# 399| 0: [ObjectCreation] object creation of type List +# 399| -1: [CollectionInitializer] { ..., ... } +# 400| 0: [ElementInitializer] call to method Add +# 400| 0: [ObjectCreation] object creation of type Contact +# 400| -1: [ObjectInitializer] { ..., ... } +# 401| 0: [MemberInitializer] ... = ... +# 401| 0: [StringLiteral] "Chris Smith" +# 401| 1: [PropertyCall] access to property Name +# 402| 1: [MemberInitializer] ... = ... +# 402| 0: [CollectionInitializer] { ..., ... } +# 402| 0: [ElementInitializer] call to method Add +# 402| 0: [StringLiteral] "206-555-0101" +# 402| 1: [ElementInitializer] call to method Add +# 402| 0: [StringLiteral] "425-882-8080" +# 402| 1: [PropertyCall] access to property PhoneNumbers +# 404| 1: [ElementInitializer] call to method Add +# 404| 0: [ObjectCreation] object creation of type Contact +# 404| -1: [ObjectInitializer] { ..., ... } +# 405| 0: [MemberInitializer] ... = ... +# 405| 0: [StringLiteral] "Bob Harris" +# 405| 1: [PropertyCall] access to property Name +# 406| 1: [MemberInitializer] ... = ... +# 406| 0: [CollectionInitializer] { ..., ... } +# 406| 0: [ElementInitializer] call to method Add +# 406| 0: [StringLiteral] "650-555-0199" +# 406| 1: [PropertyCall] access to property PhoneNumbers +# 399| 1: [LocalVariableAccess] access to local variable contacts +# 409| 5: [LocalVariableDeclStmt] ... ...; +# 409| 0: [LocalVariableDeclAndInitExpr] Int32[,] is1 = ... +# 409| 0: [ArrayCreation] array creation of type Int32[,] +# 409| -1: [ArrayInitializer] { ..., ... } +# 409| 0: [ArrayInitializer] { ..., ... } +# 409| 0: [IntLiteral] 0 +# 409| 1: [IntLiteral] 1 +# 409| 1: [ArrayInitializer] { ..., ... } +# 409| 0: [IntLiteral] 2 +# 409| 1: [IntLiteral] 3 +# 409| 2: [ArrayInitializer] { ..., ... } +# 409| 0: [IntLiteral] 4 +# 409| 1: [IntLiteral] 5 +# 409| 1: [LocalVariableAccess] access to local variable is1 +# 410| 6: [LocalVariableDeclStmt] ... ...; +# 410| 0: [LocalVariableDeclAndInitExpr] Int32[,] is2 = ... +# 410| 0: [ArrayCreation] array creation of type Int32[,] +# 410| -1: [ArrayInitializer] { ..., ... } +# 410| 0: [ArrayInitializer] { ..., ... } +# 410| 0: [IntLiteral] 0 +# 410| 1: [IntLiteral] 1 +# 410| 1: [ArrayInitializer] { ..., ... } +# 410| 0: [IntLiteral] 2 +# 410| 1: [IntLiteral] 3 +# 410| 2: [ArrayInitializer] { ..., ... } +# 410| 0: [IntLiteral] 4 +# 410| 1: [IntLiteral] 5 +# 410| 0: [IntLiteral] 3 +# 410| 1: [IntLiteral] 2 +# 410| 1: [LocalVariableAccess] access to local variable is2 +# 411| 7: [LocalVariableDeclStmt] ... ...; +# 411| 0: [LocalVariableDeclAndInitExpr] Int32[][] is3 = ... +# 411| 0: [ArrayCreation] array creation of type Int32[][] +# 411| 0: [IntLiteral] 100 +# 411| 1: [LocalVariableAccess] access to local variable is3 +# 412| 8: [LocalVariableDeclStmt] ... ...; +# 412| 0: [LocalVariableDeclAndInitExpr] Int32[,] is4 = ... +# 412| 0: [ArrayCreation] array creation of type Int32[,] +# 412| 0: [IntLiteral] 100 +# 412| 1: [IntLiteral] 5 +# 412| 1: [LocalVariableAccess] access to local variable is4 +# 413| 9: [LocalVariableDeclStmt] ... ...; +# 413| 0: [LocalVariableDeclAndInitExpr] Int32[] is5 = ... +# 413| 0: [ArrayCreation] array creation of type Int32[] +# 413| -1: [ArrayInitializer] { ..., ... } +# 413| 0: [IntLiteral] 1 +# 413| 1: [IntLiteral] 10 +# 413| 2: [IntLiteral] 100 +# 413| 3: [IntLiteral] 1000 +# 413| 1: [LocalVariableAccess] access to local variable is5 +# 414| 10: [LocalVariableDeclStmt] ... ...; +# 414| 0: [LocalVariableDeclAndInitExpr] Double[] is6 = ... +# 414| 0: [ArrayCreation] array creation of type Double[] +# 414| -1: [ArrayInitializer] { ..., ... } +# 414| 0: [CastExpr] (...) ... +# 414| 0: [IntLiteral] 1 +# 414| 1: [DoubleLiteral] 1.5 +# 414| 2: [CastExpr] (...) ... +# 414| 0: [IntLiteral] 2 +# 414| 3: [DoubleLiteral] 2.5 +# 414| 1: [LocalVariableAccess] access to local variable is6 +# 415| 11: [LocalVariableDeclStmt] ... ...; +# 415| 0: [LocalVariableDeclAndInitExpr] String[,] is7 = ... +# 415| 0: [ArrayCreation] array creation of type String[,] +# 415| -1: [ArrayInitializer] { ..., ... } +# 415| 0: [ArrayInitializer] { ..., ... } +# 415| 0: [StringLiteral] "hello" +# 415| 1: [NullLiteral] null +# 415| 1: [ArrayInitializer] { ..., ... } +# 415| 0: [StringLiteral] "world" +# 415| 1: [StringLiteral] "!" +# 415| 1: [LocalVariableAccess] access to local variable is7 +# 416| 12: [LocalVariableDeclStmt] ... ...; +# 416| 0: [LocalVariableDeclAndInitExpr] <>__AnonType0[] contacts2 = ... +# 416| 0: [ArrayCreation] array creation of type <>__AnonType0[] +# 416| -1: [ArrayInitializer] { ..., ... } +# 417| 0: [AnonymousObjectCreation] object creation of type <>__AnonType0 +# 417| -1: [ObjectInitializer] { ..., ... } +# 418| 0: [MemberInitializer] ... = ... +# 418| 0: [StringLiteral] "Chris Smith" +# 418| 1: [PropertyCall] access to property Name +# 419| 1: [MemberInitializer] ... = ... +# 419| 0: [ArrayCreation] array creation of type String[] +# 419| -1: [ArrayInitializer] { ..., ... } +# 419| 0: [StringLiteral] "206-555-0101" +# 419| 1: [StringLiteral] "425-882-8080" +# 419| 1: [PropertyCall] access to property PhoneNumbers +# 421| 1: [AnonymousObjectCreation] object creation of type <>__AnonType0 +# 421| -1: [ObjectInitializer] { ..., ... } +# 422| 0: [MemberInitializer] ... = ... +# 422| 0: [StringLiteral] "Bob Harris" +# 422| 1: [PropertyCall] access to property Name +# 423| 1: [MemberInitializer] ... = ... +# 423| 0: [ArrayCreation] array creation of type String[] +# 423| -1: [ArrayInitializer] { ..., ... } +# 423| 0: [StringLiteral] "650-555-0199" +# 423| 1: [PropertyCall] access to property PhoneNumbers +# 416| 1: [LocalVariableAccess] access to local variable contacts2 +# 426| 13: [LocalVariableDeclStmt] ... ...; +# 426| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 426| 0: [IntLiteral] 1 +# 426| 1: [LocalVariableAccess] access to local variable i +# 427| 14: [LocalVariableDeclStmt] ... ...; +# 427| 0: [LocalVariableDeclAndInitExpr] List list1 = ... +# 427| 0: [ObjectCreation] object creation of type List +# 427| -1: [CollectionInitializer] { ..., ... } +# 427| 0: [ElementInitializer] call to method Add +# 427| 0: [AssignExpr] ... = ... +# 427| 0: [IntLiteral] 2 +# 427| 1: [LocalVariableAccess] access to local variable i +# 427| 1: [LocalVariableAccess] access to local variable list1 +# 428| 15: [LocalVariableDeclStmt] ... ...; +# 428| 0: [LocalVariableDeclAndInitExpr] List list2 = ... +# 428| 0: [ObjectCreation] object creation of type List +# 428| -1: [CollectionInitializer] { ..., ... } +# 428| 0: [ElementInitializer] call to method Add +# 428| 0: [AnonymousObjectCreation] object creation of type <>__AnonType1 +# 428| -1: [ObjectInitializer] { ..., ... } +# 428| 0: [MemberInitializer] ... = ... +# 428| 0: [IntLiteral] 2 +# 428| 1: [PropertyCall] access to property i +# 428| 1: [LocalVariableAccess] access to local variable list2 +# 429| 16: [LocalVariableDeclStmt] ... ...; +# 429| 0: [LocalVariableDeclAndInitExpr] List list3 = ... +# 429| 0: [ObjectCreation] object creation of type List +# 429| -1: [CollectionInitializer] { ..., ... } +# 429| 0: [ElementInitializer] call to method Add +# 429| 0: [EQExpr] ... == ... +# 429| 0: [LocalVariableAccess] access to local variable i +# 429| 1: [IntLiteral] 2 +# 429| 1: [LocalVariableAccess] access to local variable list3 +# 432| 6: [DelegateType] S +#-----| 2: (Parameters) +# 432| 0: [Parameter] x +# 432| 1: [Parameter] y +# 433| 7: [DelegateType] Unit +# 435| 8: [Method] MultiDimensionalArrayCreations +# 436| 4: [BlockStmt] {...} +# 437| 0: [LocalVariableDeclStmt] ... ...; +# 437| 0: [LocalVariableDeclAndInitExpr] Object o = ... +# 437| 0: [ArrayCreation] array creation of type Int32[,] +# 437| -1: [ArrayInitializer] { ..., ... } +# 437| 0: [ArrayInitializer] { ..., ... } +# 437| 0: [IntLiteral] 1 +# 437| 1: [IntLiteral] 2 +# 437| 1: [ArrayInitializer] { ..., ... } +# 437| 0: [IntLiteral] 3 +# 437| 1: [IntLiteral] 4 +# 437| 2: [ArrayInitializer] { ..., ... } +# 437| 0: [IntLiteral] 5 +# 437| 1: [IntLiteral] 6 +# 437| 1: [LocalVariableAccess] access to local variable o +# 438| 1: [ExprStmt] ...; +# 438| 0: [AssignExpr] ... = ... +# 438| 0: [ArrayCreation] array creation of type Int32[,,] +# 438| -1: [ArrayInitializer] { ..., ... } +# 438| 0: [ArrayInitializer] { ..., ... } +# 438| 0: [ArrayInitializer] { ..., ... } +# 438| 0: [IntLiteral] 1 +# 438| 1: [IntLiteral] 2 +# 438| 2: [IntLiteral] 3 +# 438| 1: [ArrayInitializer] { ..., ... } +# 438| 0: [IntLiteral] 4 +# 438| 1: [IntLiteral] 5 +# 438| 2: [IntLiteral] 6 +# 438| 1: [ArrayInitializer] { ..., ... } +# 438| 0: [ArrayInitializer] { ..., ... } +# 438| 0: [IntLiteral] 7 +# 438| 1: [IntLiteral] 8 +# 438| 2: [IntLiteral] 9 +# 438| 1: [ArrayInitializer] { ..., ... } +# 438| 0: [IntLiteral] 10 +# 438| 1: [IntLiteral] 11 +# 438| 2: [IntLiteral] 12 +# 438| 1: [LocalVariableAccess] access to local variable o +# 439| 2: [ExprStmt] ...; +# 439| 0: [AssignExpr] ... = ... +# 439| 0: [ArrayCreation] array creation of type Int32[,][,] +# 440| -1: [ArrayInitializer] { ..., ... } +# 441| 0: [ArrayInitializer] { ..., ... } +# 441| 0: [ArrayCreation] array creation of type Int32[,] +# 441| -1: [ArrayInitializer] { ..., ... } +# 441| 0: [ArrayInitializer] { ..., ... } +# 441| 0: [IntLiteral] 1 +# 441| 1: [IntLiteral] 3 +# 441| 1: [ArrayInitializer] { ..., ... } +# 441| 0: [IntLiteral] 5 +# 441| 1: [IntLiteral] 7 +# 441| 1: [ArrayCreation] array creation of type Int32[,] +# 441| -1: [ArrayInitializer] { ..., ... } +# 441| 0: [ArrayInitializer] { ..., ... } +# 441| 0: [IntLiteral] 0 +# 441| 1: [IntLiteral] 2 +# 441| 1: [ArrayInitializer] { ..., ... } +# 441| 0: [IntLiteral] 4 +# 441| 1: [IntLiteral] 6 +# 441| 2: [ArrayInitializer] { ..., ... } +# 441| 0: [IntLiteral] 8 +# 441| 1: [IntLiteral] 10 +# 441| 2: [ArrayCreation] array creation of type Int32[,] +# 441| -1: [ArrayInitializer] { ..., ... } +# 441| 0: [ArrayInitializer] { ..., ... } +# 441| 0: [IntLiteral] 11 +# 441| 1: [IntLiteral] 22 +# 441| 1: [ArrayInitializer] { ..., ... } +# 441| 0: [IntLiteral] 99 +# 441| 1: [IntLiteral] 88 +# 441| 2: [ArrayInitializer] { ..., ... } +# 441| 0: [IntLiteral] 0 +# 441| 1: [IntLiteral] 9 +# 442| 1: [ArrayInitializer] { ..., ... } +# 442| 0: [ArrayCreation] array creation of type Int32[,] +# 442| -1: [ArrayInitializer] { ..., ... } +# 442| 0: [ArrayInitializer] { ..., ... } +# 442| 0: [IntLiteral] 1 +# 442| 1: [IntLiteral] 3 +# 442| 1: [ArrayInitializer] { ..., ... } +# 442| 0: [IntLiteral] 5 +# 442| 1: [IntLiteral] 7 +# 442| 1: [ArrayCreation] array creation of type Int32[,] +# 442| -1: [ArrayInitializer] { ..., ... } +# 442| 0: [ArrayInitializer] { ..., ... } +# 442| 0: [IntLiteral] 0 +# 442| 1: [IntLiteral] 2 +# 442| 1: [ArrayInitializer] { ..., ... } +# 442| 0: [IntLiteral] 4 +# 442| 1: [IntLiteral] 6 +# 442| 2: [ArrayInitializer] { ..., ... } +# 442| 0: [IntLiteral] 8 +# 442| 1: [IntLiteral] 10 +# 442| 2: [ArrayCreation] array creation of type Int32[,] +# 442| -1: [ArrayInitializer] { ..., ... } +# 442| 0: [ArrayInitializer] { ..., ... } +# 442| 0: [IntLiteral] 11 +# 442| 1: [IntLiteral] 22 +# 442| 1: [ArrayInitializer] { ..., ... } +# 442| 0: [IntLiteral] 99 +# 442| 1: [IntLiteral] 88 +# 442| 2: [ArrayInitializer] { ..., ... } +# 442| 0: [IntLiteral] 0 +# 442| 1: [IntLiteral] 9 +# 439| 1: [LocalVariableAccess] access to local variable o +# 444| 3: [ExprStmt] ...; +# 444| 0: [AssignExpr] ... = ... +# 444| 0: [ArrayCreation] array creation of type Int32[,][] +# 444| -1: [ArrayInitializer] { ..., ... } +# 444| 0: [ArrayCreation] array creation of type Int32[,] +# 444| -1: [ArrayInitializer] { ..., ... } +# 444| 0: [ArrayInitializer] { ..., ... } +# 444| 0: [IntLiteral] 1 +# 444| 1: [IntLiteral] 2 +# 444| 1: [ArrayCreation] array creation of type Int32[,] +# 444| -1: [ArrayInitializer] { ..., ... } +# 444| 0: [ArrayInitializer] { ..., ... } +# 444| 0: [IntLiteral] 1 +# 444| 1: [IntLiteral] 2 +# 444| 2: [IntLiteral] 3 +# 444| 1: [ArrayInitializer] { ..., ... } +# 444| 0: [IntLiteral] 1 +# 444| 1: [IntLiteral] 2 +# 444| 2: [IntLiteral] 3 +# 444| 2: [ArrayInitializer] { ..., ... } +# 444| 0: [IntLiteral] 1 +# 444| 1: [IntLiteral] 2 +# 444| 2: [IntLiteral] 3 +# 444| 3: [ArrayInitializer] { ..., ... } +# 444| 0: [IntLiteral] 1 +# 444| 1: [IntLiteral] 2 +# 444| 2: [IntLiteral] 3 +# 444| 1: [LocalVariableAccess] access to local variable o +# 447| 9: [Method] MainAnonymousFunctions +# 448| 4: [BlockStmt] {...} +# 449| 0: [LocalVariableDeclStmt] ... ...; +# 449| 0: [LocalVariableDeclAndInitExpr] Func f1 = ... +# 449| 0: [LambdaExpr] (...) => ... +# 449| 0: [CastExpr] (...) ... +# 449| 0: [AddExpr] ... + ... +# 449| 0: [CastExpr] (...) ... +# 449| 0: [ParameterAccess] access to parameter x +# 449| 1: [IntLiteral] 1 +# 449| 1: [TypeAccess] access to type Byte +#-----| 2: (Parameters) +# 449| 0: [Parameter] x +# 449| 1: [LocalVariableAccess] access to local variable f1 +# 450| 1: [LocalVariableDeclStmt] ... ...; +# 450| 0: [LocalVariableDeclAndInitExpr] Func f2 = ... +# 450| 0: [LambdaExpr] (...) => ... +# 450| 0: [BlockStmt] {...} +# 450| 0: [ReturnStmt] return ...; +# 450| 0: [CastExpr] (...) ... +# 450| 0: [AddExpr] ... + ... +# 450| 0: [ParameterAccess] access to parameter x +# 450| 1: [IntLiteral] 1 +#-----| 2: (Parameters) +# 450| 0: [Parameter] x +# 450| 1: [LocalVariableAccess] access to local variable f2 +# 451| 2: [LocalVariableDeclStmt] ... ...; +# 451| 0: [LocalVariableDeclAndInitExpr] Func f3 = ... +# 451| 0: [LambdaExpr] (...) => ... +# 451| 0: [AddExpr] ... + ... +# 451| 0: [ParameterAccess] access to parameter x +# 451| 1: [IntLiteral] 1 +#-----| 2: (Parameters) +# 451| 0: [Parameter] x +# 451| 1: [LocalVariableAccess] access to local variable f3 +# 452| 3: [LocalVariableDeclStmt] ... ...; +# 452| 0: [LocalVariableDeclAndInitExpr] Func f4 = ... +# 452| 0: [LambdaExpr] (...) => ... +# 452| 0: [BlockStmt] {...} +# 452| 0: [ReturnStmt] return ...; +# 452| 0: [AddExpr] ... + ... +# 452| 0: [CastExpr] (...) ... +# 452| 0: [ParameterAccess] access to parameter x +# 452| 1: [StringLiteral] "" +#-----| 2: (Parameters) +# 452| 0: [Parameter] x +# 452| 1: [LocalVariableAccess] access to local variable f4 +# 453| 4: [LocalVariableDeclStmt] ... ...; +# 453| 0: [LocalVariableDeclAndInitExpr] S f5 = ... +# 453| 0: [LambdaExpr] (...) => ... +# 453| 0: [MulExpr] ... * ... +# 453| 0: [ParameterAccess] access to parameter x +# 453| 1: [ParameterAccess] access to parameter y +#-----| 2: (Parameters) +# 453| 0: [Parameter] x +# 453| 1: [Parameter] y +# 453| 1: [LocalVariableAccess] access to local variable f5 +# 454| 5: [LocalVariableDeclStmt] ... ...; +# 454| 0: [LocalVariableDeclAndInitExpr] Unit f6 = ... +# 454| 0: [LambdaExpr] (...) => ... +# 454| 0: [MethodCall] call to method WriteLine +# 454| -1: [TypeAccess] access to type Console +# 454| 1: [LocalVariableAccess] access to local variable f6 +# 455| 6: [LocalVariableDeclStmt] ... ...; +# 455| 0: [LocalVariableDeclAndInitExpr] Func f7 = ... +# 455| 0: [AnonymousMethodExpr] delegate(...) { ... } +# 455| 0: [BlockStmt] {...} +# 455| 0: [ReturnStmt] return ...; +# 455| 0: [AddExpr] ... + ... +# 455| 0: [ParameterAccess] access to parameter x +# 455| 1: [IntLiteral] 1 +#-----| 2: (Parameters) +# 455| 0: [Parameter] x +# 455| 1: [LocalVariableAccess] access to local variable f7 +# 456| 7: [LocalVariableDeclStmt] ... ...; +# 456| 0: [LocalVariableDeclAndInitExpr] Int32 j = ... +# 456| 0: [IntLiteral] 0 +# 456| 1: [LocalVariableAccess] access to local variable j +# 457| 8: [LocalVariableDeclStmt] ... ...; +# 457| 0: [LocalVariableDeclAndInitExpr] Func f8 = ... +# 457| 0: [AnonymousMethodExpr] delegate(...) { ... } +# 457| 0: [BlockStmt] {...} +# 457| 0: [ReturnStmt] return ...; +# 457| 0: [AddExpr] ... + ... +# 457| 0: [LocalVariableAccess] access to local variable j +# 457| 1: [IntLiteral] 1 +# 457| 1: [LocalVariableAccess] access to local variable f8 +# 462| 18: [Class] OperatorCalls +# 464| 5: [Method] delegateCombine +#-----| 2: (Parameters) +# 464| 0: [Parameter] fun +# 465| 4: [BlockStmt] {...} +# 466| 0: [LocalVariableDeclStmt] ... ...; +# 466| 0: [LocalVariableDeclAndInitExpr] MyDelegate PropertyChanged = ... +# 466| 0: [NullLiteral] null +# 466| 1: [LocalVariableAccess] access to local variable PropertyChanged +# 467| 1: [ExprStmt] ...; +# 467| 0: [AssignAddExpr] ... += ... +# 467| 0: [ParameterAccess] access to parameter fun +# 467| 1: [LocalVariableAccess] access to local variable PropertyChanged +# 470| 6: [Method] addition +#-----| 2: (Parameters) +# 470| 0: [Parameter] a +# 470| 1: [Parameter] b +# 470| 2: [Parameter] c +# 471| 4: [BlockStmt] {...} +# 472| 0: [LocalVariableDeclStmt] ... ...; +# 472| 0: [LocalVariableDeclAndInitExpr] Num result = ... +# 472| 0: [OperatorCall] call to operator + +# 472| 0: [ParameterAccess] access to parameter a +# 472| 1: [ParameterAccess] access to parameter b +# 472| 1: [LocalVariableAccess] access to local variable result +# 473| 1: [ExprStmt] ...; +# 473| 0: [AssignAddExpr] ... += ... +# 473| 0: [ParameterAccess] access to parameter c +# 473| 1: [LocalVariableAccess] access to local variable result +# 474| 2: [ReturnStmt] return ...; +# 474| 0: [LocalVariableAccess] access to local variable result +# 476| 7: [Class] Num +# 478| 4: [Field] value +# 480| 5: [InstanceConstructor] Num +#-----| 2: (Parameters) +# 480| 0: [Parameter] value +# 481| 4: [BlockStmt] {...} +# 482| 0: [ExprStmt] ...; +# 482| 0: [AssignExpr] ... = ... +# 482| 0: [ParameterAccess] access to parameter value +# 482| 1: [FieldAccess] access to field value +# 482| -1: [ThisAccess] this access +# 485| 6: [AddOperator] + +#-----| 2: (Parameters) +# 485| 0: [Parameter] c1 +# 485| 1: [Parameter] c2 +# 486| 4: [BlockStmt] {...} +# 487| 0: [ReturnStmt] return ...; +# 487| 0: [ObjectCreation] object creation of type Num +# 487| 0: [AddExpr] ... + ... +# 487| 0: [FieldAccess] access to field value +# 487| -1: [ParameterAccess] access to parameter c1 +# 487| 1: [FieldAccess] access to field value +# 487| -1: [ParameterAccess] access to parameter c2 +# 491| 8: [DelegateType] MyDelegate +#-----| 2: (Parameters) +# 491| 0: [Parameter] e +# 494| 19: [Class] ExpressionDepth +# 496| 5: [Field] d +# 496| 1: [AssignExpr] ... = ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [AddExpr] ... + ... +# 496| 0: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 496| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 497| 1: [IntLiteral] 1 +# 496| 1: [MemberConstantAccess] access to constant d +# 500| 20: [Class] TupleExprs +# 502| 5: [Method] Test +# 503| 4: [BlockStmt] {...} +# 504| 0: [LocalVariableDeclStmt] ... ...; +# 504| 0: [LocalVariableDeclAndInitExpr] (Int32,String) a = ... +# 504| 0: [DefaultValueExpr] default(...) +# 504| 0: [TypeAccess] access to type (Int32,String) +# 504| 1: [LocalVariableAccess] access to local variable a +# 505| 1: [LocalVariableDeclStmt] ... ...; +# 505| 0: [LocalVariableDeclAndInitExpr] (Boolean,Int32[],Object) b = ... +# 505| 0: [DefaultValueExpr] default(...) +# 505| 0: [TypeAccess] access to type (Boolean,Int32[],Object) +# 505| 1: [LocalVariableAccess] access to local variable b +# 506| 2: [LocalVariableDeclStmt] ... ...; +# 506| 0: [LocalVariableDeclAndInitExpr] Type x = ... +# 506| 0: [TypeofExpr] typeof(...) +# 506| 0: [TypeAccess] access to type (Int32,String) +# 506| 1: [LocalVariableAccess] access to local variable x +# 507| 3: [LocalVariableDeclStmt] ... ...; +# 507| 0: [LocalVariableDeclAndInitExpr] Type y = ... +# 507| 0: [TypeofExpr] typeof(...) +# 507| 0: [TypeAccess] access to type (Boolean,Int32[],dynamic) +# 507| 1: [LocalVariableAccess] access to local variable y diff --git a/csharp/ql/test/library-tests/expressions/PrintAst.qlref b/csharp/ql/test/library-tests/expressions/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/expressions/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/expressions/QualifiableExpr.expected b/csharp/ql/test/library-tests/expressions/QualifiableExpr.expected index 0013cee52a94..f5b5c0aa892c 100644 --- a/csharp/ql/test/library-tests/expressions/QualifiableExpr.expected +++ b/csharp/ql/test/library-tests/expressions/QualifiableExpr.expected @@ -65,7 +65,7 @@ | expressions.cs:377:43:377:46 | access to field name | expressions.cs:377:43:377:46 | this access | | expressions.cs:377:57:377:60 | access to field name | expressions.cs:377:57:377:60 | this access | | expressions.cs:378:57:378:68 | access to field phoneNumbers | expressions.cs:378:57:378:68 | this access | -| expressions.cs:442:29:442:47 | call to method WriteLine | expressions.cs:442:29:442:35 | access to type Console | -| expressions.cs:470:17:470:26 | access to field value | expressions.cs:470:17:470:20 | this access | -| expressions.cs:475:32:475:39 | access to field value | expressions.cs:475:32:475:33 | access to parameter c1 | -| expressions.cs:475:43:475:50 | access to field value | expressions.cs:475:43:475:44 | access to parameter c2 | +| expressions.cs:454:29:454:47 | call to method WriteLine | expressions.cs:454:29:454:35 | access to type Console | +| expressions.cs:482:17:482:26 | access to field value | expressions.cs:482:17:482:20 | this access | +| expressions.cs:487:32:487:39 | access to field value | expressions.cs:487:32:487:33 | access to parameter c1 | +| expressions.cs:487:43:487:50 | access to field value | expressions.cs:487:43:487:44 | access to parameter c2 | diff --git a/csharp/ql/test/library-tests/expressions/StripCasts.expected b/csharp/ql/test/library-tests/expressions/StripCasts.expected index bf16559809a8..6fdfa4be3d59 100644 --- a/csharp/ql/test/library-tests/expressions/StripCasts.expected +++ b/csharp/ql/test/library-tests/expressions/StripCasts.expected @@ -50,7 +50,7 @@ | expressions.cs:334:30:334:30 | (...) ... | expressions.cs:334:30:334:30 | 8 | | expressions.cs:414:31:414:31 | (...) ... | expressions.cs:414:31:414:31 | 1 | | expressions.cs:414:39:414:39 | (...) ... | expressions.cs:414:39:414:39 | 2 | -| expressions.cs:437:41:437:53 | (...) ... | expressions.cs:437:48:437:52 | ... + ... | -| expressions.cs:437:48:437:48 | (...) ... | expressions.cs:437:48:437:48 | access to parameter x | -| expressions.cs:438:52:438:56 | (...) ... | expressions.cs:438:52:438:56 | ... + ... | -| expressions.cs:440:56:440:56 | (...) ... | expressions.cs:440:56:440:56 | access to parameter x | +| expressions.cs:449:41:449:53 | (...) ... | expressions.cs:449:48:449:52 | ... + ... | +| expressions.cs:449:48:449:48 | (...) ... | expressions.cs:449:48:449:48 | access to parameter x | +| expressions.cs:450:52:450:56 | (...) ... | expressions.cs:450:52:450:56 | ... + ... | +| expressions.cs:452:56:452:56 | (...) ... | expressions.cs:452:56:452:56 | access to parameter x | diff --git a/csharp/ql/test/library-tests/expressions/Tuples1.expected b/csharp/ql/test/library-tests/expressions/Tuples1.expected index 70210471b036..ea05f07d8eb5 100644 --- a/csharp/ql/test/library-tests/expressions/Tuples1.expected +++ b/csharp/ql/test/library-tests/expressions/Tuples1.expected @@ -1,4 +1,4 @@ -| expressions.cs:492:29:492:41 | access to type (Int32,String) | expressions.cs:492:29:492:41 | (Int32,String) | -| expressions.cs:493:29:493:49 | access to type (Boolean,Int32[],Object) | expressions.cs:493:29:493:49 | (Boolean,Int32[],Object) | -| expressions.cs:494:28:494:40 | access to type (Int32,String) | expressions.cs:492:29:492:41 | (Int32,String) | -| expressions.cs:495:28:495:49 | access to type (Boolean,Int32[],dynamic) | expressions.cs:495:28:495:49 | (Boolean,Int32[],dynamic) | +| expressions.cs:504:29:504:41 | access to type (Int32,String) | expressions.cs:504:29:504:41 | (Int32,String) | +| expressions.cs:505:29:505:49 | access to type (Boolean,Int32[],Object) | expressions.cs:505:29:505:49 | (Boolean,Int32[],Object) | +| expressions.cs:506:28:506:40 | access to type (Int32,String) | expressions.cs:504:29:504:41 | (Int32,String) | +| expressions.cs:507:28:507:49 | access to type (Boolean,Int32[],dynamic) | expressions.cs:507:28:507:49 | (Boolean,Int32[],dynamic) | diff --git a/csharp/ql/test/library-tests/expressions/expressions.cs b/csharp/ql/test/library-tests/expressions/expressions.cs index aea2d80729c2..5d995e109857 100644 --- a/csharp/ql/test/library-tests/expressions/expressions.cs +++ b/csharp/ql/test/library-tests/expressions/expressions.cs @@ -432,6 +432,18 @@ void MainCreations() delegate int S(int x, int y); delegate void Unit(); + void MultiDimensionalArrayCreations() + { + object o = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } }; + o = new int[,,] { { { 1, 2, 3 }, { 4, 5, 6 } }, { { 7, 8, 9 }, { 10, 11, 12 } } }; + o = new int[,][,] + { + { new int[,] { {1,3}, {5,7} }, new int[,] { {0,2}, {4,6}, {8,10} }, new int[,] { {11,22}, {99,88}, {0,9} } }, + { new int[,] { {1,3}, {5,7} }, new int[,] { {0,2}, {4,6}, {8,10} }, new int[,] { {11,22}, {99,88}, {0,9} } } + }; + o = new int[][,] { new int[,] { { 1, 2 } }, new int[,] { { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 } } }; + } + void MainAnonymousFunctions() { Func f1 = x => (byte)(x + 1); // Implicitly typed, expression body diff --git a/csharp/ql/test/library-tests/exprorstmtparent/Callable.expected b/csharp/ql/test/library-tests/exprorstmtparent/Callable.expected index a1449d3574f2..be7a918193f0 100644 --- a/csharp/ql/test/library-tests/exprorstmtparent/Callable.expected +++ b/csharp/ql/test/library-tests/exprorstmtparent/Callable.expected @@ -1,16 +1,60 @@ +| A.cs:6:22:6:31 | get_P1 | A.cs:6:22:6:31 | throw ... | +| A.cs:6:22:6:31 | get_P1 | B.cs:5:22:5:22 | 0 | +| A.cs:7:21:7:23 | get_P2 | A.cs:7:25:7:39 | {...} | +| A.cs:7:21:7:23 | get_P2 | B.cs:6:25:6:37 | {...} | +| A.cs:7:41:7:43 | set_P2 | A.cs:7:45:7:59 | {...} | +| A.cs:7:41:7:43 | set_P2 | B.cs:6:43:6:45 | {...} | +| A.cs:8:16:8:16 | M | A.cs:8:23:8:32 | throw ... | +| A.cs:8:16:8:16 | M | B.cs:7:23:7:23 | 2 | | A.cs:14:31:14:31 | get_Item | A.cs:14:31:14:31 | access to parameter i | +| A.cs:14:31:14:31 | get_Item | B.cs:14:31:14:40 | throw ... | | A.cs:15:36:15:38 | get_Item | A.cs:15:40:15:52 | {...} | +| A.cs:15:36:15:38 | get_Item | B.cs:15:40:15:54 | {...} | | A.cs:15:54:15:56 | set_Item | A.cs:15:58:15:60 | {...} | +| A.cs:15:54:15:56 | set_Item | B.cs:15:60:15:62 | {...} | | A.cs:16:17:16:18 | M1 | A.cs:17:5:19:5 | {...} | +| A.cs:16:17:16:18 | M1 | B.cs:17:5:19:5 | {...} | | A.cs:18:9:18:22 | M2 | A.cs:18:21:18:21 | 0 | | A.cs:20:12:20:13 | C2 | A.cs:20:22:20:31 | {...} | +| A.cs:20:12:20:13 | C2 | B.cs:20:22:20:36 | {...} | | A.cs:21:12:21:13 | C2 | A.cs:21:27:21:29 | {...} | +| A.cs:21:12:21:13 | C2 | B.cs:21:27:21:29 | {...} | | A.cs:22:6:22:7 | ~C2 | A.cs:22:11:22:13 | {...} | +| A.cs:22:6:22:7 | ~C2 | B.cs:22:11:22:25 | {...} | | A.cs:23:28:23:35 | implicit conversion | A.cs:23:50:23:53 | null | +| A.cs:23:28:23:35 | implicit conversion | B.cs:23:50:23:59 | throw ... | +| A.cs:30:21:30:23 | get_P3 | A.cs:30:28:30:37 | throw ... | +| A.cs:36:9:36:10 | M1 | A.cs:36:14:36:28 | {...} | +| A.cs:36:9:36:10 | M1 | B.cs:34:17:34:17 | 0 | +| A.cs:37:9:37:10 | M2 | A.cs:37:14:37:28 | {...} | +| A.cs:37:9:37:10 | M2 | C.cs:3:17:3:17 | 0 | +| B.cs:5:22:5:22 | get_P1 | A.cs:6:22:6:31 | throw ... | | B.cs:5:22:5:22 | get_P1 | B.cs:5:22:5:22 | 0 | +| B.cs:6:21:6:23 | get_P2 | A.cs:7:25:7:39 | {...} | | B.cs:6:21:6:23 | get_P2 | B.cs:6:25:6:37 | {...} | +| B.cs:6:39:6:41 | set_P2 | A.cs:7:45:7:59 | {...} | | B.cs:6:39:6:41 | set_P2 | B.cs:6:43:6:45 | {...} | +| B.cs:7:16:7:16 | M | A.cs:8:23:8:32 | throw ... | | B.cs:7:16:7:16 | M | B.cs:7:23:7:23 | 2 | +| B.cs:14:31:14:40 | get_Item | A.cs:14:31:14:31 | access to parameter i | +| B.cs:14:31:14:40 | get_Item | B.cs:14:31:14:40 | throw ... | +| B.cs:15:36:15:38 | get_Item | A.cs:15:40:15:52 | {...} | +| B.cs:15:36:15:38 | get_Item | B.cs:15:40:15:54 | {...} | +| B.cs:15:56:15:58 | set_Item | A.cs:15:58:15:60 | {...} | +| B.cs:15:56:15:58 | set_Item | B.cs:15:60:15:62 | {...} | +| B.cs:16:17:16:18 | M1 | A.cs:17:5:19:5 | {...} | +| B.cs:16:17:16:18 | M1 | B.cs:17:5:19:5 | {...} | | B.cs:18:9:18:31 | M2 | B.cs:18:21:18:30 | throw ... | +| B.cs:20:12:20:13 | C2 | A.cs:20:22:20:31 | {...} | +| B.cs:20:12:20:13 | C2 | B.cs:20:22:20:36 | {...} | +| B.cs:21:12:21:13 | C2 | A.cs:21:27:21:29 | {...} | +| B.cs:21:12:21:13 | C2 | B.cs:21:27:21:29 | {...} | +| B.cs:22:6:22:7 | ~C2 | A.cs:22:11:22:13 | {...} | +| B.cs:22:6:22:7 | ~C2 | B.cs:22:11:22:25 | {...} | +| B.cs:23:28:23:35 | implicit conversion | A.cs:23:50:23:53 | null | +| B.cs:23:28:23:35 | implicit conversion | B.cs:23:50:23:59 | throw ... | +| B.cs:29:21:29:23 | get_P3 | A.cs:30:28:30:37 | throw ... | +| B.cs:34:9:34:10 | M1 | A.cs:36:14:36:28 | {...} | | B.cs:34:9:34:10 | M1 | B.cs:34:17:34:17 | 0 | +| C.cs:3:9:3:10 | M2 | A.cs:37:14:37:28 | {...} | | C.cs:3:9:3:10 | M2 | C.cs:3:17:3:17 | 0 | diff --git a/csharp/ql/test/library-tests/exprorstmtparent/Declaration.expected b/csharp/ql/test/library-tests/exprorstmtparent/Declaration.expected index 8faac77089e9..23072367947c 100644 --- a/csharp/ql/test/library-tests/exprorstmtparent/Declaration.expected +++ b/csharp/ql/test/library-tests/exprorstmtparent/Declaration.expected @@ -1,3 +1,11 @@ +| A.cs:4:7:4:8 | C1 | +| A.cs:6:16:6:17 | P1 | +| A.cs:6:22:6:31 | get_P1 | +| A.cs:7:16:7:17 | P2 | +| A.cs:7:21:7:23 | get_P2 | +| A.cs:7:41:7:43 | set_P2 | +| A.cs:7:41:7:43 | value | +| A.cs:8:16:8:16 | M | | A.cs:11:7:11:8 | C2 | | A.cs:13:16:13:16 | F | | A.cs:14:16:14:19 | Item | @@ -24,6 +32,12 @@ | A.cs:24:20:24:22 | get_P | | A.cs:24:25:24:27 | set_P | | A.cs:24:25:24:27 | value | +| A.cs:28:7:28:8 | C3 | +| A.cs:30:16:30:17 | P3 | +| A.cs:30:21:30:23 | get_P3 | +| A.cs:34:15:34:16 | C4 | +| A.cs:36:9:36:10 | M1 | +| A.cs:37:9:37:10 | M2 | | B.cs:3:7:3:8 | C1 | | B.cs:5:16:5:17 | P1 | | B.cs:5:22:5:22 | get_P1 | @@ -32,7 +46,32 @@ | B.cs:6:39:6:41 | set_P2 | | B.cs:6:39:6:41 | value | | B.cs:7:16:7:16 | M | +| B.cs:11:7:11:8 | C2 | +| B.cs:13:16:13:16 | F | +| B.cs:14:16:14:19 | Item | +| B.cs:14:25:14:25 | i | +| B.cs:14:25:14:25 | i | +| B.cs:14:31:14:40 | get_Item | +| B.cs:15:19:15:22 | Item | +| B.cs:15:31:15:31 | s | +| B.cs:15:31:15:31 | s | +| B.cs:15:31:15:31 | s | +| B.cs:15:36:15:38 | get_Item | +| B.cs:15:56:15:58 | set_Item | +| B.cs:15:56:15:58 | value | +| B.cs:16:17:16:18 | M1 | +| B.cs:16:24:16:24 | i | | B.cs:18:9:18:31 | M2 | +| B.cs:20:12:20:13 | C2 | +| B.cs:20:19:20:19 | i | +| B.cs:21:12:21:13 | C2 | +| B.cs:22:6:22:7 | ~C2 | +| B.cs:23:28:23:35 | implicit conversion | +| B.cs:23:44:23:44 | i | +| B.cs:24:16:24:16 | P | +| B.cs:24:20:24:22 | get_P | +| B.cs:24:25:24:27 | set_P | +| B.cs:24:25:24:27 | value | | B.cs:27:7:27:8 | C3 | | B.cs:29:16:29:17 | P3 | | B.cs:29:21:29:23 | get_P3 | diff --git a/csharp/ql/test/library-tests/exprorstmtparent/GetABody.expected b/csharp/ql/test/library-tests/exprorstmtparent/GetABody.expected deleted file mode 100644 index 591ffbb71f9f..000000000000 --- a/csharp/ql/test/library-tests/exprorstmtparent/GetABody.expected +++ /dev/null @@ -1,31 +0,0 @@ -| A.cs:14:31:14:31 | get_Item | A.cs:14:31:14:31 | access to parameter i | -| A.cs:14:31:14:31 | get_Item | B.cs:14:31:14:40 | throw ... | -| A.cs:15:36:15:38 | get_Item | A.cs:15:40:15:52 | {...} | -| A.cs:15:36:15:38 | get_Item | B.cs:15:40:15:54 | {...} | -| A.cs:15:54:15:56 | set_Item | A.cs:15:58:15:60 | {...} | -| A.cs:15:54:15:56 | set_Item | B.cs:15:60:15:62 | {...} | -| A.cs:16:17:16:18 | M1 | A.cs:17:5:19:5 | {...} | -| A.cs:16:17:16:18 | M1 | B.cs:17:5:19:5 | {...} | -| A.cs:18:9:18:22 | M2 | A.cs:18:21:18:21 | 0 | -| A.cs:20:12:20:13 | C2 | A.cs:20:22:20:31 | {...} | -| A.cs:20:12:20:13 | C2 | B.cs:20:22:20:36 | {...} | -| A.cs:21:12:21:13 | C2 | A.cs:21:27:21:29 | {...} | -| A.cs:21:12:21:13 | C2 | B.cs:21:27:21:29 | {...} | -| A.cs:22:6:22:7 | ~C2 | A.cs:22:11:22:13 | {...} | -| A.cs:22:6:22:7 | ~C2 | B.cs:22:11:22:25 | {...} | -| A.cs:23:28:23:35 | implicit conversion | A.cs:23:50:23:53 | null | -| A.cs:23:28:23:35 | implicit conversion | B.cs:23:50:23:59 | throw ... | -| B.cs:5:22:5:22 | get_P1 | A.cs:6:22:6:31 | throw ... | -| B.cs:5:22:5:22 | get_P1 | B.cs:5:22:5:22 | 0 | -| B.cs:6:21:6:23 | get_P2 | A.cs:7:25:7:39 | {...} | -| B.cs:6:21:6:23 | get_P2 | B.cs:6:25:6:37 | {...} | -| B.cs:6:39:6:41 | set_P2 | A.cs:7:45:7:59 | {...} | -| B.cs:6:39:6:41 | set_P2 | B.cs:6:43:6:45 | {...} | -| B.cs:7:16:7:16 | M | A.cs:8:23:8:32 | throw ... | -| B.cs:7:16:7:16 | M | B.cs:7:23:7:23 | 2 | -| B.cs:18:9:18:31 | M2 | B.cs:18:21:18:30 | throw ... | -| B.cs:29:21:29:23 | get_P3 | A.cs:30:28:30:37 | throw ... | -| B.cs:34:9:34:10 | M1 | A.cs:36:14:36:28 | {...} | -| B.cs:34:9:34:10 | M1 | B.cs:34:17:34:17 | 0 | -| C.cs:3:9:3:10 | M2 | A.cs:37:14:37:28 | {...} | -| C.cs:3:9:3:10 | M2 | C.cs:3:17:3:17 | 0 | diff --git a/csharp/ql/test/library-tests/exprorstmtparent/GetABody.ql b/csharp/ql/test/library-tests/exprorstmtparent/GetABody.ql deleted file mode 100644 index 4ad64da8618e..000000000000 --- a/csharp/ql/test/library-tests/exprorstmtparent/GetABody.ql +++ /dev/null @@ -1,4 +0,0 @@ -import csharp - -from Callable c -select c, c.getABody() diff --git a/csharp/ql/test/library-tests/exprorstmtparent/Indexer.expected b/csharp/ql/test/library-tests/exprorstmtparent/Indexer.expected index 552e5e3874d3..0ab6193a8e01 100644 --- a/csharp/ql/test/library-tests/exprorstmtparent/Indexer.expected +++ b/csharp/ql/test/library-tests/exprorstmtparent/Indexer.expected @@ -1 +1,4 @@ | A.cs:14:16:14:19 | Item | A.cs:14:31:14:31 | access to parameter i | +| A.cs:14:16:14:19 | Item | B.cs:14:31:14:40 | throw ... | +| B.cs:14:16:14:19 | Item | A.cs:14:31:14:31 | access to parameter i | +| B.cs:14:16:14:19 | Item | B.cs:14:31:14:40 | throw ... | diff --git a/csharp/ql/test/library-tests/exprorstmtparent/MultiImplementationsParent.expected b/csharp/ql/test/library-tests/exprorstmtparent/MultiImplementationsParent.expected deleted file mode 100644 index 19ecdd658110..000000000000 --- a/csharp/ql/test/library-tests/exprorstmtparent/MultiImplementationsParent.expected +++ /dev/null @@ -1,60 +0,0 @@ -| C2 | A.cs:20:12:20:13 | A.cs:20:12:20:13 | -| C2 | A.cs:21:12:21:13 | A.cs:21:12:21:13 | -| C2 | B.cs:20:12:20:13 | B.cs:20:12:20:13 | -| C2 | B.cs:21:12:21:13 | B.cs:21:12:21:13 | -| F | A.cs:13:16:13:16 | A.cs:13:16:13:16 | -| F | B.cs:13:16:13:16 | B.cs:13:16:13:16 | -| F | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| Item | A.cs:14:16:14:19 | A.cs:14:16:14:19 | -| Item | B.cs:14:16:14:19 | B.cs:14:16:14:19 | -| Item | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| M | A.cs:8:16:8:16 | A.cs:8:16:8:16 | -| M | B.cs:7:16:7:16 | B.cs:7:16:7:16 | -| M | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| M1 | A.cs:16:17:16:18 | A.cs:16:17:16:18 | -| M1 | A.cs:36:9:36:10 | A.cs:36:9:36:10 | -| M1 | B.cs:16:17:16:18 | B.cs:16:17:16:18 | -| M1 | B.cs:34:9:34:10 | B.cs:34:9:34:10 | -| M1 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| M2 | A.cs:37:9:37:10 | A.cs:37:9:37:10 | -| M2 | C.cs:3:9:3:10 | C.cs:3:9:3:10 | -| M2 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| P | A.cs:24:16:24:16 | A.cs:24:16:24:16 | -| P | B.cs:24:16:24:16 | B.cs:24:16:24:16 | -| P | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| P1 | A.cs:6:16:6:17 | A.cs:6:16:6:17 | -| P1 | B.cs:5:16:5:17 | B.cs:5:16:5:17 | -| P1 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| get_Item | A.cs:14:31:14:31 | A.cs:14:31:14:31 | -| get_Item | A.cs:15:36:15:38 | A.cs:15:36:15:38 | -| get_Item | B.cs:14:31:14:40 | B.cs:14:31:14:40 | -| get_Item | B.cs:15:36:15:38 | B.cs:15:36:15:38 | -| get_Item | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| get_P | A.cs:24:20:24:22 | A.cs:24:20:24:22 | -| get_P | B.cs:24:20:24:22 | B.cs:24:20:24:22 | -| get_P | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| get_P1 | A.cs:6:22:6:31 | A.cs:6:22:6:31 | -| get_P1 | B.cs:5:22:5:22 | B.cs:5:22:5:22 | -| get_P1 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| get_P2 | A.cs:7:21:7:23 | A.cs:7:21:7:23 | -| get_P2 | B.cs:6:21:6:23 | B.cs:6:21:6:23 | -| get_P2 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| get_P3 | A.cs:30:21:30:23 | A.cs:30:21:30:23 | -| get_P3 | B.cs:29:21:29:23 | B.cs:29:21:29:23 | -| get_P3 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| i | A.cs:16:24:16:24 | A.cs:16:24:16:24 | -| i | B.cs:16:24:16:24 | B.cs:16:24:16:24 | -| implicit conversion | A.cs:23:28:23:35 | A.cs:23:28:23:35 | -| implicit conversion | B.cs:23:28:23:35 | B.cs:23:28:23:35 | -| implicit conversion | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| set_Item | A.cs:15:54:15:56 | A.cs:15:54:15:56 | -| set_Item | B.cs:15:56:15:58 | B.cs:15:56:15:58 | -| set_Item | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| set_P | A.cs:24:25:24:27 | A.cs:24:25:24:27 | -| set_P | B.cs:24:25:24:27 | B.cs:24:25:24:27 | -| set_P | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| set_P2 | A.cs:7:41:7:43 | A.cs:7:41:7:43 | -| set_P2 | B.cs:6:39:6:41 | B.cs:6:39:6:41 | -| set_P2 | test.dll:0:0:0:0 | test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null | -| ~C2 | A.cs:22:6:22:7 | A.cs:22:6:22:7 | -| ~C2 | B.cs:22:6:22:7 | B.cs:22:6:22:7 | diff --git a/csharp/ql/test/library-tests/exprorstmtparent/MultiImplementationsParent.ql b/csharp/ql/test/library-tests/exprorstmtparent/MultiImplementationsParent.ql deleted file mode 100644 index 92cb1c32fdb5..000000000000 --- a/csharp/ql/test/library-tests/exprorstmtparent/MultiImplementationsParent.ql +++ /dev/null @@ -1,5 +0,0 @@ -import csharp -import semmle.code.csharp.ExprOrStmtParent - -from MultiImplementationsParent p -select p.toString(), p.getALocation() diff --git a/csharp/ql/test/library-tests/exprorstmtparent/Parameter.expected b/csharp/ql/test/library-tests/exprorstmtparent/Parameter.expected index 2bbf43a054a7..c17d356f4773 100644 --- a/csharp/ql/test/library-tests/exprorstmtparent/Parameter.expected +++ b/csharp/ql/test/library-tests/exprorstmtparent/Parameter.expected @@ -1 +1,4 @@ | A.cs:16:24:16:24 | i | A.cs:16:28:16:28 | 0 | +| A.cs:16:24:16:24 | i | B.cs:16:28:16:28 | 1 | +| B.cs:16:24:16:24 | i | A.cs:16:28:16:28 | 0 | +| B.cs:16:24:16:24 | i | B.cs:16:28:16:28 | 1 | diff --git a/csharp/ql/test/library-tests/exprorstmtparent/Property.expected b/csharp/ql/test/library-tests/exprorstmtparent/Property.expected index 4f04105b9181..4f71b86ae9d6 100644 --- a/csharp/ql/test/library-tests/exprorstmtparent/Property.expected +++ b/csharp/ql/test/library-tests/exprorstmtparent/Property.expected @@ -1,2 +1,8 @@ +| A.cs:6:16:6:17 | P1 | A.cs:6:22:6:31 | throw ... | body | +| A.cs:6:16:6:17 | P1 | B.cs:5:22:5:22 | 0 | body | | A.cs:24:16:24:16 | P | A.cs:24:34:24:34 | 0 | initializer | +| A.cs:24:16:24:16 | P | B.cs:24:34:24:34 | 1 | initializer | +| B.cs:5:16:5:17 | P1 | A.cs:6:22:6:31 | throw ... | body | | B.cs:5:16:5:17 | P1 | B.cs:5:22:5:22 | 0 | body | +| B.cs:24:16:24:16 | P | A.cs:24:34:24:34 | 0 | initializer | +| B.cs:24:16:24:16 | P | B.cs:24:34:24:34 | 1 | initializer | diff --git a/csharp/ql/test/library-tests/extractor/tagstack/Bodies.expected b/csharp/ql/test/library-tests/extractor/tagstack/Bodies.expected index e69de29bb2d1..147cd9914254 100644 --- a/csharp/ql/test/library-tests/extractor/tagstack/Bodies.expected +++ b/csharp/ql/test/library-tests/extractor/tagstack/Bodies.expected @@ -0,0 +1,2 @@ +| A.cs:5:12:5:13 | M1 | This method has multiple bodies. | +| B.cs:5:12:5:13 | M1 | This method has multiple bodies. | diff --git a/csharp/ql/test/library-tests/extractor/tagstack/Classes.expected b/csharp/ql/test/library-tests/extractor/tagstack/Classes.expected index 3163ed48abe9..33c9f5a6b3d5 100644 --- a/csharp/ql/test/library-tests/extractor/tagstack/Classes.expected +++ b/csharp/ql/test/library-tests/extractor/tagstack/Classes.expected @@ -1 +1,2 @@ | A.cs:3:7:3:7 | C | +| B.cs:3:7:3:7 | C | diff --git a/csharp/ql/test/library-tests/extractor/tagstack/Methods.expected b/csharp/ql/test/library-tests/extractor/tagstack/Methods.expected index bab64e4d86a6..27a65e160d5d 100644 --- a/csharp/ql/test/library-tests/extractor/tagstack/Methods.expected +++ b/csharp/ql/test/library-tests/extractor/tagstack/Methods.expected @@ -1,2 +1,4 @@ | A.cs:5:12:5:13 | M1 | | A.cs:6:10:6:11 | M2 | +| B.cs:5:12:5:13 | M1 | +| B.cs:6:10:6:11 | M3 | diff --git a/csharp/ql/test/library-tests/fields/PrintAst.expected b/csharp/ql/test/library-tests/fields/PrintAst.expected new file mode 100644 index 000000000000..b87bd043db2b --- /dev/null +++ b/csharp/ql/test/library-tests/fields/PrintAst.expected @@ -0,0 +1,185 @@ +fields.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [Class] A +# 10| 6: [Field] X +# 10| 1: [AssignExpr] ... = ... +# 10| 0: [IntLiteral] 1 +# 10| 1: [FieldAccess] access to field X +# 10| 7: [Field] Y +# 10| 8: [Field] Z +# 10| 1: [AssignExpr] ... = ... +# 10| 0: [IntLiteral] 100 +# 10| 1: [FieldAccess] access to field Z +# 13| 2: [Class] B +# 15| 6: [Field] X +# 15| 1: [AssignExpr] ... = ... +# 15| 0: [IntLiteral] 1 +# 15| 1: [FieldAccess] access to field X +# 16| 7: [Field] Y +# 17| 8: [Field] Z +# 17| 1: [AssignExpr] ... = ... +# 17| 0: [IntLiteral] 100 +# 17| 1: [FieldAccess] access to field Z +# 20| 3: [Class] C<> +#-----| 1: (Type parameters) +# 20| 0: [TypeParameter] V +# 23| 5: [Field] count +# 23| 1: [AssignExpr] ... = ... +# 23| 0: [IntLiteral] 0 +# 23| 1: [FieldAccess] access to field count +# 25| 6: [InstanceConstructor] C +# 25| 4: [BlockStmt] {...} +# 25| 0: [ExprStmt] ...; +# 25| 0: [PostIncrExpr] ...++ +# 25| 0: [FieldAccess] access to field count +# 27| 7: [Property] Count +# 27| 3: [Getter] get_Count +# 27| 4: [BlockStmt] {...} +# 27| 0: [ReturnStmt] return ...; +# 27| 0: [FieldAccess] access to field count +# 31| 4: [Class] Application +# 34| 6: [Field] finished +# 35| 7: [Field] x +# 35| 1: [AssignExpr] ... = ... +# 35| 0: [MethodCall] call to method Sqrt +# 35| -1: [TypeAccess] access to type Math +# 35| 0: [DoubleLiteral] 2 +# 35| 1: [FieldAccess] access to field x +# 36| 8: [Field] i +# 36| 1: [AssignExpr] ... = ... +# 36| 0: [IntLiteral] 100 +# 36| 1: [FieldAccess] access to field i +# 37| 9: [Field] s +# 37| 1: [AssignExpr] ... = ... +# 37| 0: [StringLiteral] "Hello" +# 37| 1: [FieldAccess] access to field s +# 39| 10: [Method] Main +# 40| 4: [BlockStmt] {...} +# 41| 0: [LocalVariableDeclStmt] ... ...; +# 41| 0: [LocalVariableDeclAndInitExpr] Decimal d = ... +# 41| 0: [MemberConstantAccess] access to constant MaxValue +# 41| -1: [TypeAccess] access to type Decimal +# 41| 1: [LocalVariableAccess] access to local variable d +# 42| 1: [LocalVariableDeclStmt] ... ...; +# 42| 0: [LocalVariableDeclAndInitExpr] C x1 = ... +# 42| 0: [ObjectCreation] object creation of type C +# 42| 1: [LocalVariableAccess] access to local variable x1 +# 43| 2: [ExprStmt] ...; +# 43| 0: [MethodCall] call to method WriteLine +# 43| -1: [TypeAccess] access to type Console +# 43| 0: [PropertyCall] access to property Count +# 43| -1: [TypeAccess] access to type C +# 44| 3: [LocalVariableDeclStmt] ... ...; +# 44| 0: [LocalVariableDeclAndInitExpr] C x2 = ... +# 44| 0: [ObjectCreation] object creation of type C +# 44| 1: [LocalVariableAccess] access to local variable x2 +# 45| 4: [ExprStmt] ...; +# 45| 0: [MethodCall] call to method WriteLine +# 45| -1: [TypeAccess] access to type Console +# 45| 0: [PropertyCall] access to property Count +# 45| -1: [TypeAccess] access to type C +# 50| 5: [Class] Color +# 53| 5: [Field] Black +# 53| 1: [AssignExpr] ... = ... +# 53| 0: [ObjectCreation] object creation of type Color +# 53| 0: [CastExpr] (...) ... +# 53| 0: [IntLiteral] 0 +# 53| 1: [CastExpr] (...) ... +# 53| 0: [IntLiteral] 0 +# 53| 2: [CastExpr] (...) ... +# 53| 0: [IntLiteral] 0 +# 53| 1: [FieldAccess] access to field Black +# 54| 6: [Field] White +# 54| 1: [AssignExpr] ... = ... +# 54| 0: [ObjectCreation] object creation of type Color +# 54| 0: [CastExpr] (...) ... +# 54| 0: [IntLiteral] 255 +# 54| 1: [CastExpr] (...) ... +# 54| 0: [IntLiteral] 255 +# 54| 2: [CastExpr] (...) ... +# 54| 0: [IntLiteral] 255 +# 54| 1: [FieldAccess] access to field White +# 56| 7: [InstanceConstructor] Color +#-----| 2: (Parameters) +# 56| 0: [Parameter] r +# 56| 1: [Parameter] g +# 56| 2: [Parameter] b +# 56| 4: [BlockStmt] {...} +# 60| 6: [Class] TestBindings +# 63| 6: [Field] a +# 63| 1: [AssignExpr] ... = ... +# 63| 0: [AddExpr] ... + ... +# 63| 0: [FieldAccess] access to field b +# 63| 1: [IntLiteral] 1 +# 63| 1: [FieldAccess] access to field a +# 64| 7: [Field] b +# 64| 1: [AssignExpr] ... = ... +# 64| 0: [AddExpr] ... + ... +# 64| 0: [FieldAccess] access to field a +# 64| 1: [IntLiteral] 1 +# 64| 1: [FieldAccess] access to field b +# 70| [NamespaceDeclaration] namespace ... { ... } +# 72| 1: [Class] A +# 74| 5: [Field] X +# 74| 1: [AssignExpr] ... = ... +# 74| 0: [AddExpr] ... + ... +# 74| 0: [MemberConstantAccess] access to constant Z +# 74| -1: [TypeAccess] access to type B +# 74| 1: [IntLiteral] 1 +# 74| 1: [MemberConstantAccess] access to constant X +# 75| 6: [Field] Y +# 75| 1: [AssignExpr] ... = ... +# 75| 0: [IntLiteral] 10 +# 75| 1: [MemberConstantAccess] access to constant Y +# 78| 2: [Class] B +# 80| 5: [Field] Z +# 80| 1: [AssignExpr] ... = ... +# 80| 0: [AddExpr] ... + ... +# 80| 0: [MemberConstantAccess] access to constant Y +# 80| -1: [TypeAccess] access to type A +# 80| 1: [IntLiteral] 1 +# 80| 1: [MemberConstantAccess] access to constant Z +# 83| 3: [Class] C +# 85| 4: [Field] Foo +# 85| 1: [AssignExpr] ... = ... +# 85| 0: [IntLiteral] 1 +# 85| 1: [MemberConstantAccess] access to constant Foo +# 86| 5: [Field] x +# 87| 6: [InstanceConstructor] C +# 88| 4: [BlockStmt] {...} +# 89| 0: [ExprStmt] ...; +# 89| 0: [AssignExpr] ... = ... +# 89| 0: [CastExpr] (...) ... +# 89| 0: [MemberConstantAccess] access to constant Foo +# 89| 1: [FieldAccess] access to field x +# 90| 1: [LocalVariableDeclStmt] ... ...; +# 90| 0: [LocalVariableDeclAndInitExpr] dynamic dyn = ... +# 90| 0: [CastExpr] (...) ... +# 90| 0: [MemberConstantAccess] access to constant Foo +# 90| 1: [LocalVariableAccess] access to local variable dyn +# 91| 2: [LocalVariableDeclStmt] ... ...; +# 91| 0: [LocalVariableDeclAndInitExpr] D d = ... +# 91| 0: [OperatorCall] call to operator implicit conversion +# 91| 0: [MemberConstantAccess] access to constant Foo +# 91| 1: [LocalVariableAccess] access to local variable d +# 92| 3: [LocalVariableDeclStmt] ... ...; +# 92| 0: [LocalVariableDeclAndInitExpr] C c = ... +# 92| 0: [ObjectCreation] object creation of type C +# 92| -1: [ObjectInitializer] { ..., ... } +# 92| 0: [MemberInitializer] ... = ... +# 92| 0: [CastExpr] (...) ... +# 92| 0: [MemberConstantAccess] access to constant Foo +# 92| 1: [FieldAccess] access to field x +# 92| 1: [LocalVariableAccess] access to local variable c +# 96| 4: [Class] D +# 98| 4: [InstanceConstructor] D +#-----| 2: (Parameters) +# 98| 0: [Parameter] d +# 99| 4: [BlockStmt] {...} +# 101| 5: [ImplicitConversionOperator] implicit conversion +#-----| 2: (Parameters) +# 101| 0: [Parameter] d +# 101| 4: [BlockStmt] {...} +# 101| 0: [ReturnStmt] return ...; +# 101| 0: [ObjectCreation] object creation of type D +# 101| 0: [ParameterAccess] access to parameter d diff --git a/csharp/ql/test/library-tests/fields/PrintAst.qlref b/csharp/ql/test/library-tests/fields/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/fields/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/generics/PrintAst.expected b/csharp/ql/test/library-tests/generics/PrintAst.expected new file mode 100644 index 000000000000..9d50bca4d68d --- /dev/null +++ b/csharp/ql/test/library-tests/generics/PrintAst.expected @@ -0,0 +1,547 @@ +Nesting.cs: +# 1| [Class] A<> +#-----| 1: (Type parameters) +# 1| 0: [TypeParameter] T1 +# 3| 5: [Method] MA1 +#-----| 2: (Parameters) +# 3| 0: [Parameter] x +# 3| 4: [BlockStmt] {...} +# 4| 6: [Method] MA2 +#-----| 1: (Type parameters) +# 4| 0: [TypeParameter] T2 +#-----| 2: (Parameters) +# 4| 0: [Parameter] x +# 4| 1: [Parameter] y +# 4| 4: [BlockStmt] {...} +# 6| 7: [Class] B<> +#-----| 1: (Type parameters) +# 6| 0: [TypeParameter] T3 +# 8| 5: [Method] MB1 +#-----| 2: (Parameters) +# 8| 0: [Parameter] x +# 8| 1: [Parameter] y +# 8| 4: [BlockStmt] {...} +# 9| 6: [Method] MB2 +#-----| 1: (Type parameters) +# 9| 0: [TypeParameter] T4 +#-----| 2: (Parameters) +# 9| 0: [Parameter] x +# 9| 1: [Parameter] y +# 9| 2: [Parameter] z +# 9| 4: [BlockStmt] {...} +# 12| 8: [Class] C +# 14| 5: [Method] MC1 +#-----| 2: (Parameters) +# 14| 0: [Parameter] x +# 14| 4: [BlockStmt] {...} +# 15| 6: [Method] MC2 +#-----| 1: (Type parameters) +# 15| 0: [TypeParameter] T5 +#-----| 2: (Parameters) +# 15| 0: [Parameter] x +# 15| 1: [Parameter] y +# 15| 4: [BlockStmt] {...} +# 17| 7: [Class] D<> +#-----| 1: (Type parameters) +# 17| 0: [TypeParameter] T6 +# 19| 5: [Method] MD1 +#-----| 2: (Parameters) +# 19| 0: [Parameter] x +# 19| 1: [Parameter] y +# 19| 4: [BlockStmt] {...} +# 20| 6: [Method] MD2 +#-----| 1: (Type parameters) +# 20| 0: [TypeParameter] T7 +#-----| 2: (Parameters) +# 20| 0: [Parameter] x +# 20| 1: [Parameter] y +# 20| 2: [Parameter] z +# 20| 4: [BlockStmt] {...} +# 24| 9: [Method] Construct +# 25| 4: [BlockStmt] {...} +# 26| 0: [LocalVariableDeclStmt] ... ...; +# 26| 0: [LocalVariableDeclAndInitExpr] A a1 = ... +# 26| 0: [ObjectCreation] object creation of type A +# 26| 1: [LocalVariableAccess] access to local variable a1 +# 27| 1: [ExprStmt] ...; +# 27| 0: [MethodCall] call to method MA1 +# 27| -1: [LocalVariableAccess] access to local variable a1 +# 27| 0: [IntLiteral] 0 +# 28| 2: [ExprStmt] ...; +# 28| 0: [MethodCall] call to method MA2 +# 28| -1: [LocalVariableAccess] access to local variable a1 +# 28| 0: [IntLiteral] 0 +# 28| 1: [StringLiteral] "" +# 30| 3: [LocalVariableDeclStmt] ... ...; +# 30| 0: [LocalVariableDeclAndInitExpr] A a2 = ... +# 30| 0: [ObjectCreation] object creation of type A +# 30| 1: [LocalVariableAccess] access to local variable a2 +# 31| 4: [ExprStmt] ...; +# 31| 0: [MethodCall] call to method MA1 +# 31| -1: [LocalVariableAccess] access to local variable a2 +# 31| 0: [StringLiteral] "" +# 32| 5: [ExprStmt] ...; +# 32| 0: [MethodCall] call to method MA2 +# 32| -1: [LocalVariableAccess] access to local variable a2 +# 32| 0: [StringLiteral] "" +# 32| 1: [IntLiteral] 0 +# 34| 6: [LocalVariableDeclStmt] ... ...; +# 34| 0: [LocalVariableDeclAndInitExpr] B b1 = ... +# 34| 0: [ObjectCreation] object creation of type B +# 34| 1: [LocalVariableAccess] access to local variable b1 +# 35| 7: [ExprStmt] ...; +# 35| 0: [MethodCall] call to method MB1 +# 35| -1: [LocalVariableAccess] access to local variable b1 +# 35| 0: [IntLiteral] 0 +# 35| 1: [StringLiteral] "" +# 36| 8: [ExprStmt] ...; +# 36| 0: [MethodCall] call to method MB2 +# 36| -1: [LocalVariableAccess] access to local variable b1 +# 36| 0: [IntLiteral] 0 +# 36| 1: [StringLiteral] "" +# 36| 2: [BoolLiteral] false +# 38| 9: [LocalVariableDeclStmt] ... ...; +# 38| 0: [LocalVariableDeclAndInitExpr] B b2 = ... +# 38| 0: [ObjectCreation] object creation of type B +# 38| 1: [LocalVariableAccess] access to local variable b2 +# 39| 10: [ExprStmt] ...; +# 39| 0: [MethodCall] call to method MB1 +# 39| -1: [LocalVariableAccess] access to local variable b2 +# 39| 0: [StringLiteral] "" +# 39| 1: [IntLiteral] 0 +# 40| 11: [ExprStmt] ...; +# 40| 0: [MethodCall] call to method MB2 +# 40| -1: [LocalVariableAccess] access to local variable b2 +# 40| 0: [StringLiteral] "" +# 40| 1: [IntLiteral] 0 +# 40| 2: [BoolLiteral] false +# 42| 12: [LocalVariableDeclStmt] ... ...; +# 42| 0: [LocalVariableDeclAndInitExpr] C c1 = ... +# 42| 0: [ObjectCreation] object creation of type C +# 42| 1: [LocalVariableAccess] access to local variable c1 +# 43| 13: [ExprStmt] ...; +# 43| 0: [MethodCall] call to method MC1 +# 43| -1: [LocalVariableAccess] access to local variable c1 +# 43| 0: [IntLiteral] 0 +# 44| 14: [ExprStmt] ...; +# 44| 0: [MethodCall] call to method MC2 +# 44| -1: [LocalVariableAccess] access to local variable c1 +# 44| 0: [IntLiteral] 0 +# 44| 1: [BoolLiteral] false +# 46| 15: [LocalVariableDeclStmt] ... ...; +# 46| 0: [LocalVariableDeclAndInitExpr] C c2 = ... +# 46| 0: [ObjectCreation] object creation of type C +# 46| 1: [LocalVariableAccess] access to local variable c2 +# 47| 16: [ExprStmt] ...; +# 47| 0: [MethodCall] call to method MC1 +# 47| -1: [LocalVariableAccess] access to local variable c2 +# 47| 0: [StringLiteral] "" +# 48| 17: [ExprStmt] ...; +# 48| 0: [MethodCall] call to method MC2 +# 48| -1: [LocalVariableAccess] access to local variable c2 +# 48| 0: [StringLiteral] "" +# 48| 1: [BoolLiteral] false +# 50| 18: [LocalVariableDeclStmt] ... ...; +# 50| 0: [LocalVariableDeclAndInitExpr] D d1 = ... +# 50| 0: [ObjectCreation] object creation of type D +# 50| 1: [LocalVariableAccess] access to local variable d1 +# 51| 19: [ExprStmt] ...; +# 51| 0: [MethodCall] call to method MD1 +# 51| -1: [LocalVariableAccess] access to local variable d1 +# 51| 0: [IntLiteral] 0 +# 51| 1: [BoolLiteral] false +# 52| 20: [ExprStmt] ...; +# 52| 0: [MethodCall] call to method MD2 +# 52| -1: [LocalVariableAccess] access to local variable d1 +# 52| 0: [IntLiteral] 0 +# 52| 1: [BoolLiteral] false +# 52| 2: [StringLiteral] "" +# 54| 21: [LocalVariableDeclStmt] ... ...; +# 54| 0: [LocalVariableDeclAndInitExpr] D d2 = ... +# 54| 0: [ObjectCreation] object creation of type D +# 54| 1: [LocalVariableAccess] access to local variable d2 +# 55| 22: [ExprStmt] ...; +# 55| 0: [MethodCall] call to method MD1 +# 55| -1: [LocalVariableAccess] access to local variable d2 +# 55| 0: [StringLiteral] "" +# 55| 1: [DecimalLiteral] 0 +# 56| 23: [ExprStmt] ...; +# 56| 0: [MethodCall] call to method MD2 +# 56| -1: [LocalVariableAccess] access to local variable d2 +# 56| 0: [StringLiteral] "" +# 56| 1: [DecimalLiteral] 0 +# 56| 2: [BoolLiteral] false +generics.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [DelegateType] GenericDelegate<> +#-----| 1: (Type parameters) +# 7| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 7| 0: [Parameter] t +# 9| 2: [Class] A +# 13| 3: [Class] A<> +#-----| 1: (Type parameters) +# 13| 0: [TypeParameter] T +# 16| 5: [DelegateType] GenericDelegateInGenericClass<> +#-----| 1: (Type parameters) +# 16| 0: [TypeParameter] U +#-----| 2: (Parameters) +# 16| 0: [Parameter] t +# 16| 1: [Parameter] u +# 18| 6: [Method] bar +#-----| 1: (Type parameters) +# 18| 0: [TypeParameter] X +#-----| 2: (Parameters) +# 18| 0: [Parameter] x +# 18| 1: [Parameter] t +# 18| 4: [BlockStmt] {...} +# 18| 0: [LocalVariableDeclStmt] ... ...; +# 18| 0: [LocalVariableDeclExpr] A<> a +# 18| 1: [ReturnStmt] return ...; +# 18| 0: [ParameterAccess] access to parameter t +# 22| 4: [Class] B<> +#-----| 1: (Type parameters) +# 22| 0: [TypeParameter] T +# 25| 5: [Field] at +# 27| 6: [Field] name +# 29| 7: [Method] foo +# 29| 4: [BlockStmt] {...} +# 31| 8: [Method] fooParams +#-----| 2: (Parameters) +# 31| 0: [Parameter] ts +# 31| 4: [BlockStmt] {...} +# 33| 9: [Method] staticFoo +# 33| 4: [BlockStmt] {...} +# 35| 10: [IndexerProperty] Name +# 35| 3: [Getter] get_Name +# 35| 4: [BlockStmt] {...} +# 35| 0: [ReturnStmt] return ...; +# 35| 0: [FieldAccess] access to field name +# 35| 4: [Setter] set_Name +#-----| 2: (Parameters) +# 35| 0: [Parameter] value +# 35| 4: [BlockStmt] {...} +# 35| 0: [ExprStmt] ...; +# 35| 0: [AssignExpr] ... = ... +# 35| 0: [ParameterAccess] access to parameter value +# 35| 1: [FieldAccess] access to field name +# 37| 11: [Event] myEvent +# 37| 3: [AddEventAccessor] add_myEvent +#-----| 2: (Parameters) +# 37| 0: [Parameter] value +# 37| 4: [RemoveEventAccessor] remove_myEvent +#-----| 2: (Parameters) +# 37| 0: [Parameter] value +# 39| 12: [IncrementOperator] ++ +#-----| 2: (Parameters) +# 39| 0: [Parameter] a +# 40| 4: [BlockStmt] {...} +# 41| 0: [ReturnStmt] return ...; +# 41| 0: [ObjectCreation] object creation of type B<> +# 44| 13: [Destructor] ~B +# 44| 4: [BlockStmt] {...} +# 45| 14: [Method] f +#-----| 1: (Type parameters) +# 45| 0: [TypeParameter] X +# 45| 4: [BlockStmt] {...} +# 45| 0: [ExprStmt] ...; +# 45| 0: [ObjectCreation] object creation of type B +# 48| 5: [Class] Outer<> +#-----| 1: (Type parameters) +# 48| 0: [TypeParameter] T +# 51| 5: [Class] Inner<> +#-----| 1: (Type parameters) +# 51| 0: [TypeParameter] U +# 54| 5: [Field] t +# 55| 6: [Field] myFunc +# 60| 6: [Class] Grid<> +#-----| 1: (Type parameters) +# 60| 0: [TypeParameter] T +# 63| 5: [Field] NumRows +# 63| 1: [AssignExpr] ... = ... +# 63| 0: [IntLiteral] 26 +# 63| 1: [MemberConstantAccess] access to constant NumRows +# 64| 6: [Field] NumCols +# 64| 1: [AssignExpr] ... = ... +# 64| 0: [IntLiteral] 10 +# 64| 1: [MemberConstantAccess] access to constant NumCols +# 66| 7: [Field] cells +# 66| 1: [AssignExpr] ... = ... +# 66| 0: [ArrayCreation] array creation of type T[,] +# 66| 0: [MemberConstantAccess] access to constant NumRows +# 66| 1: [MemberConstantAccess] access to constant NumCols +# 66| 1: [FieldAccess] access to field cells +# 68| 8: [Indexer] Item +#-----| 1: (Parameters) +# 68| 0: [Parameter] i +# 70| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 68| 0: [Parameter] i +# 70| 4: [BlockStmt] {...} +# 70| 0: [ReturnStmt] return ...; +# 70| 0: [ParameterAccess] access to parameter i +# 73| 9: [Indexer] Item +#-----| 1: (Parameters) +# 73| 0: [Parameter] c +# 73| 1: [Parameter] col +# 75| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 73| 0: [Parameter] c +# 73| 1: [Parameter] col +# 76| 4: [BlockStmt] {...} +# 77| 0: [ExprStmt] ...; +# 77| 0: [AssignExpr] ... = ... +# 77| 0: [MethodCall] call to method ToUpper +# 77| -1: [TypeAccess] access to type Char +# 77| 0: [ParameterAccess] access to parameter c +# 77| 1: [ParameterAccess] access to parameter c +# 78| 1: [IfStmt] if (...) ... +# 78| 0: [LogicalOrExpr] ... || ... +# 78| 0: [LTExpr] ... < ... +# 78| 0: [CastExpr] (...) ... +# 78| 0: [ParameterAccess] access to parameter c +# 78| 1: [CastExpr] (...) ... +# 78| 0: [CharLiteral] A +# 78| 1: [GTExpr] ... > ... +# 78| 0: [CastExpr] (...) ... +# 78| 0: [ParameterAccess] access to parameter c +# 78| 1: [CastExpr] (...) ... +# 78| 0: [CharLiteral] Z +# 79| 1: [BlockStmt] {...} +# 80| 0: [ThrowStmt] throw ...; +# 80| 0: [ObjectCreation] object creation of type ArgumentException +# 82| 2: [IfStmt] if (...) ... +# 82| 0: [LogicalOrExpr] ... || ... +# 82| 0: [LTExpr] ... < ... +# 82| 0: [ParameterAccess] access to parameter col +# 82| 1: [IntLiteral] 0 +# 82| 1: [GEExpr] ... >= ... +# 82| 0: [ParameterAccess] access to parameter col +# 82| 1: [MemberConstantAccess] access to constant NumCols +# 83| 1: [BlockStmt] {...} +# 84| 0: [ThrowStmt] throw ...; +# 84| 0: [ObjectCreation] object creation of type IndexOutOfRangeException +# 86| 3: [ReturnStmt] return ...; +# 86| 0: [ArrayAccess] access to array element +# 86| -1: [FieldAccess] access to field cells +# 86| 0: [SubExpr] ... - ... +# 86| 0: [CastExpr] (...) ... +# 86| 0: [ParameterAccess] access to parameter c +# 86| 1: [CastExpr] (...) ... +# 86| 0: [CharLiteral] A +# 86| 1: [ParameterAccess] access to parameter col +# 88| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 73| 0: [Parameter] c +# 73| 1: [Parameter] col +# 88| 2: [Parameter] value +# 89| 4: [BlockStmt] {...} +# 90| 0: [ExprStmt] ...; +# 90| 0: [AssignExpr] ... = ... +# 90| 0: [MethodCall] call to method ToUpper +# 90| -1: [TypeAccess] access to type Char +# 90| 0: [ParameterAccess] access to parameter c +# 90| 1: [ParameterAccess] access to parameter c +# 91| 1: [IfStmt] if (...) ... +# 91| 0: [LogicalOrExpr] ... || ... +# 91| 0: [LTExpr] ... < ... +# 91| 0: [CastExpr] (...) ... +# 91| 0: [ParameterAccess] access to parameter c +# 91| 1: [CastExpr] (...) ... +# 91| 0: [CharLiteral] A +# 91| 1: [GTExpr] ... > ... +# 91| 0: [CastExpr] (...) ... +# 91| 0: [ParameterAccess] access to parameter c +# 91| 1: [CastExpr] (...) ... +# 91| 0: [CharLiteral] Z +# 92| 1: [BlockStmt] {...} +# 93| 0: [ThrowStmt] throw ...; +# 93| 0: [ObjectCreation] object creation of type ArgumentException +# 95| 2: [IfStmt] if (...) ... +# 95| 0: [LogicalOrExpr] ... || ... +# 95| 0: [LTExpr] ... < ... +# 95| 0: [ParameterAccess] access to parameter col +# 95| 1: [IntLiteral] 0 +# 95| 1: [GEExpr] ... >= ... +# 95| 0: [ParameterAccess] access to parameter col +# 95| 1: [MemberConstantAccess] access to constant NumCols +# 96| 1: [BlockStmt] {...} +# 97| 0: [ThrowStmt] throw ...; +# 97| 0: [ObjectCreation] object creation of type IndexOutOfRangeException +# 99| 3: [ExprStmt] ...; +# 99| 0: [AssignExpr] ... = ... +# 99| 0: [ParameterAccess] access to parameter value +# 99| 1: [ArrayAccess] access to array element +# 99| -1: [FieldAccess] access to field cells +# 99| 0: [SubExpr] ... - ... +# 99| 0: [CastExpr] (...) ... +# 99| 0: [ParameterAccess] access to parameter c +# 99| 1: [CastExpr] (...) ... +# 99| 0: [CharLiteral] A +# 99| 1: [ParameterAccess] access to parameter col +# 105| 7: [Class] Test +# 108| 5: [Method] Main +# 109| 4: [BlockStmt] {...} +# 110| 0: [LocalVariableDeclStmt] ... ...; +# 110| 0: [LocalVariableDeclAndInitExpr] B bs = ... +# 110| 0: [ObjectCreation] object creation of type B +# 110| 1: [LocalVariableAccess] access to local variable bs +# 111| 1: [ExprStmt] ...; +# 111| 0: [AssignExpr] ... = ... +# 111| 0: [ObjectCreation] object creation of type A +# 111| 1: [FieldAccess] access to field at +# 111| -1: [LocalVariableAccess] access to local variable bs +# 112| 2: [ExprStmt] ...; +# 112| 0: [MethodCall] call to method foo +# 112| -1: [LocalVariableAccess] access to local variable bs +# 113| 3: [ExprStmt] ...; +# 113| 0: [MethodCall] call to method fooParams +# 113| -1: [LocalVariableAccess] access to local variable bs +# 113| 0: [StringLiteral] "a" +# 113| 1: [StringLiteral] "b" +# 115| 4: [ExprStmt] ...; +# 115| 0: [MethodCall] call to method staticFoo +# 115| -1: [TypeAccess] access to type B +# 117| 5: [ExprStmt] ...; +# 117| 0: [AssignExpr] ... = ... +# 117| 0: [StringLiteral] "" +# 117| 1: [PropertyCall] access to property Name +# 117| -1: [LocalVariableAccess] access to local variable bs +# 118| 6: [ExprStmt] ...; +# 118| 0: [AddEventExpr] ... += ... +# 118| 0: [ExplicitDelegateCreation] delegate creation of type GenericDelegate +# 118| 0: [MethodAccess] access to method f +# 118| 1: [EventAccess,EventCall] access to event myEvent +# 118| -1: [LocalVariableAccess] access to local variable bs +# 119| 7: [ExprStmt] ...; +# 119| 0: [OperatorCall] call to operator ++ +# 119| 0: [LocalVariableAccess] access to local variable bs +# 121| 8: [LocalVariableDeclStmt] ... ...; +# 121| 0: [LocalVariableDeclAndInitExpr] Grid g = ... +# 121| 0: [ObjectCreation] object creation of type Grid +# 121| 1: [LocalVariableAccess] access to local variable g +# 122| 9: [LocalVariableDeclStmt] ... ...; +# 122| 0: [LocalVariableDeclAndInitExpr] Int32 j = ... +# 122| 0: [IndexerCall] access to indexer +# 122| -1: [LocalVariableAccess] access to local variable g +# 122| 0: [CharLiteral] e +# 122| 1: [IntLiteral] 1 +# 122| 1: [LocalVariableAccess] access to local variable j +# 124| 10: [ExprStmt] ...; +# 124| 0: [AssignExpr] ... = ... +# 124| 0: [CastExpr] (...) ... +# 124| 0: [IntLiteral] 3 +# 124| 1: [FieldAccess] access to field t +# 124| -1: [ObjectCreation] object creation of type Inner +# 126| 11: [ExprStmt] ...; +# 126| 0: [MethodCall] call to method bar +# 126| -1: [ObjectCreation] object creation of type A +# 126| 0: [IntLiteral] 2 +# 126| 1: [StringLiteral] "" +# 127| 12: [ExprStmt] ...; +# 127| 0: [MethodCall] call to method bar +# 127| -1: [ObjectCreation] object creation of type A +# 127| 0: [ObjectCreation] object creation of type Test +# 127| 1: [IntLiteral] 2 +# 130| 6: [Method] f +#-----| 2: (Parameters) +# 130| 0: [Parameter] s +# 130| 4: [BlockStmt] {...} +# 130| 0: [ReturnStmt] return ...; +# 130| 0: [ParameterAccess] access to parameter s +# 134| 8: [Class] Subtle +# 137| 5: [Method] fs +#-----| 1: (Type parameters) +# 137| 0: [TypeParameter] X +#-----| 2: (Parameters) +# 137| 0: [Parameter] i +# 137| 4: [BlockStmt] {...} +# 139| 6: [Method] fs +#-----| 1: (Type parameters) +# 139| 0: [TypeParameter] X +#-----| 2: (Parameters) +# 139| 0: [Parameter] i +# 139| 1: [Parameter] j +# 139| 4: [BlockStmt] {...} +# 141| 7: [Method] fs +#-----| 2: (Parameters) +# 141| 0: [Parameter] i +# 141| 4: [BlockStmt] {...} +# 145| 9: [Class] Param<> +#-----| 1: (Type parameters) +# 145| 0: [TypeParameter] T +# 147| 5: [Enum] E +# 147| 5: [Field] x +# 150| 10: [Class] ConstructedMethods +# 152| 5: [Method] CM1 +#-----| 1: (Type parameters) +# 152| 0: [TypeParameter] T +# 152| 4: [BlockStmt] {...} +# 153| 8: [Method] CM2 +#-----| 1: (Type parameters) +# 153| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 153| 0: [Parameter] t +# 153| 4: [BlockStmt] {...} +# 153| 0: [ReturnStmt] return ...; +# 153| 0: [ParameterAccess] access to parameter t +# 155| 11: [Class] Class<> +#-----| 1: (Type parameters) +# 155| 0: [TypeParameter] T1 +# 157| 5: [Method] CM3 +#-----| 1: (Type parameters) +# 157| 0: [TypeParameter] T2 +#-----| 2: (Parameters) +# 157| 0: [Parameter] t +# 157| 1: [Parameter] t1 +# 157| 4: [BlockStmt] {...} +# 157| 0: [ReturnStmt] return ...; +# 157| 0: [ParameterAccess] access to parameter t +# 160| 14: [Method] NonCM +# 160| 4: [BlockStmt] {...} +# 162| 15: [Method] CM +# 163| 4: [BlockStmt] {...} +# 164| 0: [ExprStmt] ...; +# 164| 0: [MethodCall] call to method CM1 +# 165| 1: [ExprStmt] ...; +# 165| 0: [MethodCall] call to method CM1 +# 166| 2: [ExprStmt] ...; +# 166| 0: [MethodCall] call to method CM2 +# 166| 0: [IntLiteral] 4 +# 167| 3: [ExprStmt] ...; +# 167| 0: [MethodCall] call to method CM2 +# 167| 0: [DoubleLiteral] 2 +# 168| 4: [ExprStmt] ...; +# 168| 0: [MethodCall] call to method CM3 +# 168| -1: [ObjectCreation] object creation of type Class +# 168| 0: [DoubleLiteral] 1 +# 168| 1: [IntLiteral] 2 +# 169| 5: [ExprStmt] ...; +# 169| 0: [MethodCall] call to method CM3 +# 169| -1: [ObjectCreation] object creation of type Class +# 169| 0: [DoubleLiteral] 1 +# 169| 1: [DoubleLiteral] 2 +# 173| 11: [Interface] Interface<> +#-----| 1: (Type parameters) +# 173| 0: [TypeParameter] T +# 175| 4: [Method] set +#-----| 2: (Parameters) +# 175| 0: [Parameter] t +# 178| 12: [Class] Inheritance<> +#-----| 1: (Type parameters) +# 178| 0: [TypeParameter] T +#-----| 3: (Base types) +# 178| 1: [Interface] Interface +# 180| 5: [Method] set +#-----| 2: (Parameters) +# 180| 0: [Parameter] t +# 180| 4: [BlockStmt] {...} +# 183| 13: [Class] InheritanceTest +# 185| 5: [Field] member +# 188| 14: [Interface] Interface2<,> +#-----| 1: (Type parameters) +# 188| 0: [TypeParameter] T1 +# 188| 1: [TypeParameter] T2 +# 190| 4: [Method] M +#-----| 2: (Parameters) +# 190| 0: [Parameter] x diff --git a/csharp/ql/test/library-tests/generics/PrintAst.qlref b/csharp/ql/test/library-tests/generics/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/generics/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/goto/PrintAst.expected b/csharp/ql/test/library-tests/goto/PrintAst.expected new file mode 100644 index 000000000000..7ee79e8ded20 --- /dev/null +++ b/csharp/ql/test/library-tests/goto/PrintAst.expected @@ -0,0 +1,42 @@ +goto.cs: +# 2| [Class] Goto +# 4| 5: [Method] Main +# 5| 4: [BlockStmt] {...} +# 6| 0: [BlockStmt] {...} +# 7| 0: [LabelStmt] s1: +# 7| 1: [GotoLabelStmt] goto ...; +# 9| 1: [LabelStmt] s2: +# 9| 2: [LocalVariableDeclStmt] ... ...; +# 9| 0: [LocalVariableDeclAndInitExpr] String s = ... +# 9| 0: [StringLiteral] "5" +# 9| 1: [LocalVariableAccess] access to local variable s +# 10| 3: [SwitchStmt] switch (...) {...} +# 10| 0: [LocalVariableAccess] access to local variable s +# 12| 0: [ConstCase] case ...: +# 12| 0: [ConstantPatternExpr,NullLiteral] null +# 12| 1: [LabelStmt] s3: +# 12| 2: [GotoCaseStmt] goto case ...; +# 12| 0: [StringLiteral] "1" +# 13| 3: [ConstCase] case ...: +# 13| 0: [ConstantPatternExpr,StringLiteral] "1" +# 13| 4: [LabelStmt] s4: +# 13| 5: [GotoCaseStmt] goto case ...; +# 13| 0: [StringLiteral] "2" +# 14| 6: [ConstCase] case ...: +# 14| 0: [ConstantPatternExpr,StringLiteral] "2" +# 14| 7: [LabelStmt] s5: +# 14| 8: [GotoLabelStmt] goto ...; +# 15| 9: [ConstCase] case ...: +# 15| 0: [ConstantPatternExpr,StringLiteral] "3" +# 15| 10: [LabelStmt] s6: +# 15| 11: [GotoDefaultStmt] goto default; +# 16| 12: [ConstCase] case ...: +# 16| 0: [ConstantPatternExpr,StringLiteral] "4" +# 16| 13: [LabelStmt] s7: +# 16| 14: [BreakStmt] break; +# 17| 15: [DefaultCase] default: +# 17| 16: [LabelStmt] s8: +# 17| 17: [GotoCaseStmt] goto case ...; +# 17| 0: [NullLiteral] null +# 19| 4: [LabelStmt] s9: +# 19| 5: [EmptyStmt] ; diff --git a/csharp/ql/test/library-tests/goto/PrintAst.qlref b/csharp/ql/test/library-tests/goto/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/goto/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/indexers/Indexers12.expected b/csharp/ql/test/library-tests/indexers/Indexers12.expected new file mode 100644 index 000000000000..a97b8216ef31 --- /dev/null +++ b/csharp/ql/test/library-tests/indexers/Indexers12.expected @@ -0,0 +1 @@ +| Item[int] | get_Item(int) | diff --git a/csharp/ql/test/library-tests/indexers/Indexers12.ql b/csharp/ql/test/library-tests/indexers/Indexers12.ql new file mode 100644 index 000000000000..189c239edb1e --- /dev/null +++ b/csharp/ql/test/library-tests/indexers/Indexers12.ql @@ -0,0 +1,8 @@ +import csharp + +from Indexer i, Accessor a, string s +where + i.getDeclaringType().hasQualifiedName("System", s) and + s.regexpMatch("Tuple<,*>") and + a = i.getAnAccessor() +select i.toStringWithTypes(), a.toStringWithTypes() diff --git a/csharp/ql/test/library-tests/indexers/PrintAst.expected b/csharp/ql/test/library-tests/indexers/PrintAst.expected new file mode 100644 index 000000000000..57f4bac0ef9d --- /dev/null +++ b/csharp/ql/test/library-tests/indexers/PrintAst.expected @@ -0,0 +1,322 @@ +indexers.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 8| 1: [Class] BitArray +# 11| 4: [Field] bits +# 12| 5: [Field] length +# 14| 6: [InstanceConstructor] BitArray +#-----| 2: (Parameters) +# 14| 0: [Parameter] length +# 15| 4: [BlockStmt] {...} +# 16| 0: [IfStmt] if (...) ... +# 16| 0: [LTExpr] ... < ... +# 16| 0: [ParameterAccess] access to parameter length +# 16| 1: [IntLiteral] 0 +# 17| 1: [ThrowStmt] throw ...; +# 17| 0: [ObjectCreation] object creation of type ArgumentException +# 18| 1: [ExprStmt] ...; +# 18| 0: [AssignExpr] ... = ... +# 18| 0: [ArrayCreation] array creation of type Int32[] +# 18| 0: [AddExpr] ... + ... +# 18| 0: [RShiftExpr] ... >> ... +# 18| 0: [SubExpr] ... - ... +# 18| 0: [ParameterAccess] access to parameter length +# 18| 1: [IntLiteral] 1 +# 18| 1: [IntLiteral] 5 +# 18| 1: [IntLiteral] 1 +# 18| 1: [FieldAccess] access to field bits +# 19| 2: [ExprStmt] ...; +# 19| 0: [AssignExpr] ... = ... +# 19| 0: [ParameterAccess] access to parameter length +# 19| 1: [FieldAccess] access to field length +# 19| -1: [ThisAccess] this access +# 22| 7: [Property] Length +# 22| 3: [Getter] get_Length +# 22| 4: [BlockStmt] {...} +# 22| 0: [ReturnStmt] return ...; +# 22| 0: [FieldAccess] access to field length +# 24| 8: [Indexer] Item +#-----| 1: (Parameters) +# 24| 0: [Parameter] index +# 26| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 24| 0: [Parameter] index +# 27| 4: [BlockStmt] {...} +# 28| 0: [IfStmt] if (...) ... +# 28| 0: [LogicalOrExpr] ... || ... +# 28| 0: [LTExpr] ... < ... +# 28| 0: [ParameterAccess] access to parameter index +# 28| 1: [IntLiteral] 0 +# 28| 1: [GEExpr] ... >= ... +# 28| 0: [ParameterAccess] access to parameter index +# 28| 1: [FieldAccess] access to field length +# 29| 1: [BlockStmt] {...} +# 30| 0: [ThrowStmt] throw ...; +# 30| 0: [ObjectCreation] object creation of type IndexOutOfRangeException +# 32| 1: [ReturnStmt] return ...; +# 32| 0: [NEExpr] ... != ... +# 32| 0: [BitwiseAndExpr] ... & ... +# 32| 0: [ArrayAccess] access to array element +# 32| -1: [FieldAccess] access to field bits +# 32| 0: [RShiftExpr] ... >> ... +# 32| 0: [ParameterAccess] access to parameter index +# 32| 1: [IntLiteral] 5 +# 32| 1: [LShiftExpr] ... << ... +# 32| 0: [IntLiteral] 1 +# 32| 1: [ParameterAccess] access to parameter index +# 32| 1: [IntLiteral] 0 +# 34| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 24| 0: [Parameter] index +# 34| 1: [Parameter] value +# 35| 4: [BlockStmt] {...} +# 36| 0: [IfStmt] if (...) ... +# 36| 0: [LogicalOrExpr] ... || ... +# 36| 0: [LTExpr] ... < ... +# 36| 0: [ParameterAccess] access to parameter index +# 36| 1: [IntLiteral] 0 +# 36| 1: [GEExpr] ... >= ... +# 36| 0: [ParameterAccess] access to parameter index +# 36| 1: [FieldAccess] access to field length +# 37| 1: [BlockStmt] {...} +# 38| 0: [ThrowStmt] throw ...; +# 38| 0: [ObjectCreation] object creation of type IndexOutOfRangeException +# 40| 1: [IfStmt] if (...) ... +# 40| 0: [ParameterAccess] access to parameter value +# 41| 1: [BlockStmt] {...} +# 42| 0: [ExprStmt] ...; +# 42| 0: [AssignOrExpr] ... |= ... +# 42| 0: [LShiftExpr] ... << ... +# 42| 0: [IntLiteral] 1 +# 42| 1: [ParameterAccess] access to parameter index +# 42| 1: [ArrayAccess] access to array element +# 42| -1: [FieldAccess] access to field bits +# 42| 0: [RShiftExpr] ... >> ... +# 42| 0: [ParameterAccess] access to parameter index +# 42| 1: [IntLiteral] 5 +# 45| 2: [BlockStmt] {...} +# 46| 0: [ExprStmt] ...; +# 46| 0: [AssignAndExpr] ... &= ... +# 46| 0: [ComplementExpr] ~... +# 46| 0: [LShiftExpr] ... << ... +# 46| 0: [IntLiteral] 1 +# 46| 1: [ParameterAccess] access to parameter index +# 46| 1: [ArrayAccess] access to array element +# 46| -1: [FieldAccess] access to field bits +# 46| 0: [RShiftExpr] ... >> ... +# 46| 0: [ParameterAccess] access to parameter index +# 46| 1: [IntLiteral] 5 +# 53| 2: [Class] CountPrimes +# 56| 5: [Method] Count +#-----| 2: (Parameters) +# 56| 0: [Parameter] max +# 57| 4: [BlockStmt] {...} +# 58| 0: [LocalVariableDeclStmt] ... ...; +# 58| 0: [LocalVariableDeclAndInitExpr] BitArray flags = ... +# 58| 0: [ObjectCreation] object creation of type BitArray +# 58| 0: [AddExpr] ... + ... +# 58| 0: [ParameterAccess] access to parameter max +# 58| 1: [IntLiteral] 1 +# 58| 1: [LocalVariableAccess] access to local variable flags +# 59| 1: [LocalVariableDeclStmt] ... ...; +# 59| 0: [LocalVariableDeclAndInitExpr] Int32 count = ... +# 59| 0: [IntLiteral] 1 +# 59| 1: [LocalVariableAccess] access to local variable count +# 60| 2: [ForStmt] for (...;...;...) ... +# 60| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 60| 0: [IntLiteral] 2 +# 60| 1: [LocalVariableAccess] access to local variable i +# 60| 0: [LEExpr] ... <= ... +# 60| 0: [LocalVariableAccess] access to local variable i +# 60| 1: [ParameterAccess] access to parameter max +# 60| 1: [PostIncrExpr] ...++ +# 60| 0: [LocalVariableAccess] access to local variable i +# 61| 2: [BlockStmt] {...} +# 62| 0: [IfStmt] if (...) ... +# 62| 0: [LogicalNotExpr] !... +# 62| 0: [IndexerCall] access to indexer +# 62| -1: [LocalVariableAccess] access to local variable flags +# 62| 0: [LocalVariableAccess] access to local variable i +# 63| 1: [BlockStmt] {...} +# 64| 0: [ForStmt] for (...;...;...) ... +# 64| -1: [LocalVariableDeclAndInitExpr] Int32 j = ... +# 64| 0: [MulExpr] ... * ... +# 64| 0: [LocalVariableAccess] access to local variable i +# 64| 1: [IntLiteral] 2 +# 64| 1: [LocalVariableAccess] access to local variable j +# 64| 0: [LEExpr] ... <= ... +# 64| 0: [LocalVariableAccess] access to local variable j +# 64| 1: [ParameterAccess] access to parameter max +# 64| 1: [AssignAddExpr] ... += ... +# 64| 0: [LocalVariableAccess] access to local variable i +# 64| 1: [LocalVariableAccess] access to local variable j +# 65| 2: [ExprStmt] ...; +# 65| 0: [AssignExpr] ... = ... +# 65| 0: [BoolLiteral] true +# 65| 1: [IndexerCall] access to indexer +# 65| -1: [LocalVariableAccess] access to local variable flags +# 65| 0: [LocalVariableAccess] access to local variable j +# 66| 1: [ExprStmt] ...; +# 66| 0: [PostIncrExpr] ...++ +# 66| 0: [LocalVariableAccess] access to local variable count +# 69| 3: [ReturnStmt] return ...; +# 69| 0: [LocalVariableAccess] access to local variable count +# 72| 6: [Method] Main +#-----| 2: (Parameters) +# 72| 0: [Parameter] args +# 73| 4: [BlockStmt] {...} +# 74| 0: [LocalVariableDeclStmt] ... ...; +# 74| 0: [LocalVariableDeclAndInitExpr] Int32 max = ... +# 74| 0: [MethodCall] call to method Parse +# 74| -1: [TypeAccess] access to type Int32 +# 74| 0: [ArrayAccess] access to array element +# 74| -1: [ParameterAccess] access to parameter args +# 74| 0: [IntLiteral] 0 +# 74| 1: [LocalVariableAccess] access to local variable max +# 75| 1: [LocalVariableDeclStmt] ... ...; +# 75| 0: [LocalVariableDeclAndInitExpr] Int32 count = ... +# 75| 0: [MethodCall] call to method Count +# 75| 0: [LocalVariableAccess] access to local variable max +# 75| 1: [LocalVariableAccess] access to local variable count +# 76| 2: [ExprStmt] ...; +# 76| 0: [MethodCall] call to method WriteLine +# 76| -1: [TypeAccess] access to type Console +# 76| 0: [StringLiteral] "Found {0} primes between 1 and {1}" +# 76| 1: [CastExpr] (...) ... +# 76| 0: [LocalVariableAccess] access to local variable count +# 76| 2: [CastExpr] (...) ... +# 76| 0: [LocalVariableAccess] access to local variable max +# 81| 3: [Class] Grid +# 84| 5: [Field] NumRows +# 84| 1: [AssignExpr] ... = ... +# 84| 0: [IntLiteral] 26 +# 84| 1: [MemberConstantAccess] access to constant NumRows +# 85| 6: [Field] NumCols +# 85| 1: [AssignExpr] ... = ... +# 85| 0: [IntLiteral] 10 +# 85| 1: [MemberConstantAccess] access to constant NumCols +# 87| 7: [Field] cells +# 87| 1: [AssignExpr] ... = ... +# 87| 0: [ArrayCreation] array creation of type Int32[,] +# 87| 0: [MemberConstantAccess] access to constant NumRows +# 87| 1: [MemberConstantAccess] access to constant NumCols +# 87| 1: [FieldAccess] access to field cells +# 89| 8: [Indexer] Item +#-----| 1: (Parameters) +# 89| 0: [Parameter] c +# 89| 1: [Parameter] col +# 91| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 89| 0: [Parameter] c +# 89| 1: [Parameter] col +# 92| 4: [BlockStmt] {...} +# 93| 0: [ExprStmt] ...; +# 93| 0: [AssignExpr] ... = ... +# 93| 0: [MethodCall] call to method ToUpper +# 93| -1: [TypeAccess] access to type Char +# 93| 0: [ParameterAccess] access to parameter c +# 93| 1: [ParameterAccess] access to parameter c +# 94| 1: [IfStmt] if (...) ... +# 94| 0: [LogicalOrExpr] ... || ... +# 94| 0: [LTExpr] ... < ... +# 94| 0: [CastExpr] (...) ... +# 94| 0: [ParameterAccess] access to parameter c +# 94| 1: [CastExpr] (...) ... +# 94| 0: [CharLiteral] A +# 94| 1: [GTExpr] ... > ... +# 94| 0: [CastExpr] (...) ... +# 94| 0: [ParameterAccess] access to parameter c +# 94| 1: [CastExpr] (...) ... +# 94| 0: [CharLiteral] Z +# 95| 1: [BlockStmt] {...} +# 96| 0: [ThrowStmt] throw ...; +# 96| 0: [ObjectCreation] object creation of type ArgumentException +# 98| 2: [IfStmt] if (...) ... +# 98| 0: [LogicalOrExpr] ... || ... +# 98| 0: [LTExpr] ... < ... +# 98| 0: [ParameterAccess] access to parameter col +# 98| 1: [IntLiteral] 0 +# 98| 1: [GEExpr] ... >= ... +# 98| 0: [ParameterAccess] access to parameter col +# 98| 1: [MemberConstantAccess] access to constant NumCols +# 99| 1: [BlockStmt] {...} +# 100| 0: [ThrowStmt] throw ...; +# 100| 0: [ObjectCreation] object creation of type IndexOutOfRangeException +# 102| 3: [ReturnStmt] return ...; +# 102| 0: [ArrayAccess] access to array element +# 102| -1: [FieldAccess] access to field cells +# 102| 0: [SubExpr] ... - ... +# 102| 0: [CastExpr] (...) ... +# 102| 0: [ParameterAccess] access to parameter c +# 102| 1: [CastExpr] (...) ... +# 102| 0: [CharLiteral] A +# 102| 1: [ParameterAccess] access to parameter col +# 104| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 89| 0: [Parameter] c +# 89| 1: [Parameter] col +# 104| 2: [Parameter] value +# 105| 4: [BlockStmt] {...} +# 106| 0: [ExprStmt] ...; +# 106| 0: [AssignExpr] ... = ... +# 106| 0: [MethodCall] call to method ToUpper +# 106| -1: [TypeAccess] access to type Char +# 106| 0: [ParameterAccess] access to parameter c +# 106| 1: [ParameterAccess] access to parameter c +# 107| 1: [IfStmt] if (...) ... +# 107| 0: [LogicalOrExpr] ... || ... +# 107| 0: [LTExpr] ... < ... +# 107| 0: [CastExpr] (...) ... +# 107| 0: [ParameterAccess] access to parameter c +# 107| 1: [CastExpr] (...) ... +# 107| 0: [CharLiteral] A +# 107| 1: [GTExpr] ... > ... +# 107| 0: [CastExpr] (...) ... +# 107| 0: [ParameterAccess] access to parameter c +# 107| 1: [CastExpr] (...) ... +# 107| 0: [CharLiteral] Z +# 108| 1: [BlockStmt] {...} +# 109| 0: [ThrowStmt] throw ...; +# 109| 0: [ObjectCreation] object creation of type ArgumentException +# 111| 2: [IfStmt] if (...) ... +# 111| 0: [LogicalOrExpr] ... || ... +# 111| 0: [LTExpr] ... < ... +# 111| 0: [ParameterAccess] access to parameter col +# 111| 1: [IntLiteral] 0 +# 111| 1: [GEExpr] ... >= ... +# 111| 0: [ParameterAccess] access to parameter col +# 111| 1: [MemberConstantAccess] access to constant NumCols +# 112| 1: [BlockStmt] {...} +# 113| 0: [ThrowStmt] throw ...; +# 113| 0: [ObjectCreation] object creation of type IndexOutOfRangeException +# 115| 3: [ExprStmt] ...; +# 115| 0: [AssignExpr] ... = ... +# 115| 0: [ParameterAccess] access to parameter value +# 115| 1: [ArrayAccess] access to array element +# 115| -1: [FieldAccess] access to field cells +# 115| 0: [SubExpr] ... - ... +# 115| 0: [CastExpr] (...) ... +# 115| 0: [ParameterAccess] access to parameter c +# 115| 1: [CastExpr] (...) ... +# 115| 0: [CharLiteral] A +# 115| 1: [ParameterAccess] access to parameter col +# 121| 4: [Class] DuplicateIndexerSignatures +# 123| 5: [Indexer] Item +#-----| 1: (Parameters) +# 123| 0: [Parameter] index +# 125| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 123| 0: [Parameter] index +# 125| 4: [BlockStmt] {...} +# 125| 0: [ReturnStmt] return ...; +# 125| 0: [BoolLiteral] false +# 128| 6: [Indexer] Item +#-----| 1: (Parameters) +# 128| 0: [Parameter] c +# 128| 1: [Parameter] col +# 130| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 128| 0: [Parameter] c +# 128| 1: [Parameter] col +# 130| 4: [BlockStmt] {...} +# 130| 0: [ReturnStmt] return ...; +# 130| 0: [IntLiteral] 0 diff --git a/csharp/ql/test/library-tests/indexers/PrintAst.qlref b/csharp/ql/test/library-tests/indexers/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/indexers/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/initializers/PrintAst.expected b/csharp/ql/test/library-tests/initializers/PrintAst.expected new file mode 100644 index 000000000000..b80326a17515 --- /dev/null +++ b/csharp/ql/test/library-tests/initializers/PrintAst.expected @@ -0,0 +1,58 @@ +initializers.cs: +# 3| [Class] S1 +# 5| 5: [Field] P1 +# 6| 6: [Property] P2 +# 6| 3: [Getter] get_P2 +# 6| 4: [Setter] set_P2 +#-----| 2: (Parameters) +# 6| 0: [Parameter] value +# 7| 7: [Property] P3 +# 7| 3: [Setter] set_P3 +#-----| 2: (Parameters) +# 7| 0: [Parameter] value +# 7| 4: [BlockStmt] {...} +# 10| [Class] S2 +#-----| 3: (Base types) +# 10| 1: [Interface] IEnumerable +# 12| 5: [Method] Add +#-----| 2: (Parameters) +# 12| 0: [Parameter] x +# 12| 4: [BlockStmt] {...} +# 13| 6: [Method] Add +#-----| 2: (Parameters) +# 13| 0: [Parameter] x +# 13| 1: [Parameter] y +# 13| 4: [BlockStmt] {...} +# 14| 7: [Method] GetEnumerator +# 14| 4: [BlockStmt] {...} +# 14| 0: [ReturnStmt] return ...; +# 14| 0: [NullLiteral] null +# 17| [Class] Test +# 19| 5: [Method] Main +#-----| 2: (Parameters) +# 19| 0: [Parameter] args +# 20| 4: [BlockStmt] {...} +# 21| 0: [ExprStmt] ...; +# 21| 0: [ObjectCreation] object creation of type S1 +# 21| -1: [ObjectInitializer] { ..., ... } +# 21| 0: [MemberInitializer] ... = ... +# 21| 0: [IntLiteral] 1 +# 21| 1: [FieldAccess] access to field P1 +# 21| 1: [MemberInitializer] ... = ... +# 21| 0: [IntLiteral] 2 +# 21| 1: [PropertyCall] access to property P2 +# 21| 2: [MemberInitializer] ... = ... +# 21| 0: [IntLiteral] 3 +# 21| 1: [PropertyCall] access to property P3 +# 22| 1: [ExprStmt] ...; +# 22| 0: [ObjectCreation] object creation of type S2 +# 22| -1: [CollectionInitializer] { ..., ... } +# 22| 0: [ElementInitializer] call to method Add +# 22| 0: [IntLiteral] 1 +# 22| 1: [ElementInitializer] call to method Add +# 22| 0: [IntLiteral] 2 +# 22| 2: [ElementInitializer] call to method Add +# 22| 0: [IntLiteral] 3 +# 22| 3: [ElementInitializer] call to method Add +# 22| 0: [IntLiteral] 4 +# 22| 1: [IntLiteral] 5 diff --git a/csharp/ql/test/library-tests/initializers/PrintAst.qlref b/csharp/ql/test/library-tests/initializers/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/initializers/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/ir/ir/raw_ir.qlref b/csharp/ql/test/library-tests/ir/ir/raw_ir.qlref deleted file mode 100644 index 1f503d46e017..000000000000 --- a/csharp/ql/test/library-tests/ir/ir/raw_ir.qlref +++ /dev/null @@ -1 +0,0 @@ -semmle/code/csharp/ir/implementation/raw/PrintIR.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/ir/ir/raw_ir_consistency.qlref b/csharp/ql/test/library-tests/ir/ir/raw_ir_consistency.qlref deleted file mode 100644 index 5a11b73c5443..000000000000 --- a/csharp/ql/test/library-tests/ir/ir/raw_ir_consistency.qlref +++ /dev/null @@ -1 +0,0 @@ -semmle/code/csharp/ir/implementation/raw/IRConsistency.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.qlref b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.qlref deleted file mode 100644 index 90b5e8129f86..000000000000 --- a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.qlref +++ /dev/null @@ -1 +0,0 @@ -semmle/code/csharp/ir/implementation/unaliased_ssa/IRConsistency.ql diff --git a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_consistency.qlref b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_consistency.qlref deleted file mode 100644 index ae9b0e757b9f..000000000000 --- a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_ssa_consistency.qlref +++ /dev/null @@ -1 +0,0 @@ -semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/ir/rangeanalysis/RangeAnalysis.ql b/csharp/ql/test/library-tests/ir/rangeanalysis/RangeAnalysis.ql deleted file mode 100644 index 0eed018495ad..000000000000 --- a/csharp/ql/test/library-tests/ir/rangeanalysis/RangeAnalysis.ql +++ /dev/null @@ -1,25 +0,0 @@ -import semmle.code.csharp.ir.rangeanalysis.RangeAnalysis -import semmle.code.csharp.ir.IR -import semmle.code.csharp.ir.internal.IRGuards -import semmle.code.csharp.ir.ValueNumbering - -query predicate instructionBounds( - Instruction i, Bound b, int delta, boolean upper, Reason reason, Location reasonLoc -) { - ( - i.getAUse() instanceof ArgumentOperand - or - exists(ReturnValueInstruction retInstr | retInstr.getReturnValueOperand() = i.getAUse()) - ) and - ( - upper = true and - delta = min(int d | boundedInstruction(i, b, d, upper, reason)) - or - upper = false and - delta = max(int d | boundedInstruction(i, b, d, upper, reason)) - ) and - not valueNumber(b.getInstruction()) = valueNumber(i) and - if reason instanceof CondReason - then reasonLoc = reason.(CondReason).getCond().getLocation() - else reasonLoc instanceof EmptyLocation -} diff --git a/csharp/ql/test/library-tests/linq/PrintAst.expected b/csharp/ql/test/library-tests/linq/PrintAst.expected new file mode 100644 index 000000000000..bf38619f4d7e --- /dev/null +++ b/csharp/ql/test/library-tests/linq/PrintAst.expected @@ -0,0 +1,194 @@ +queries.cs: +# 5| [Class] Queries +# 7| 5: [Method] Queries1 +# 8| 4: [BlockStmt] {...} +# 9| 0: [LocalVariableDeclStmt] ... ...; +# 9| 0: [LocalVariableDeclAndInitExpr] IList list1 = ... +# 9| 0: [ObjectCreation] object creation of type List +# 9| -1: [CollectionInitializer] { ..., ... } +# 9| 0: [ElementInitializer] call to method Add +# 9| 0: [IntLiteral] 1 +# 9| 1: [ElementInitializer] call to method Add +# 9| 0: [IntLiteral] 2 +# 9| 2: [ElementInitializer] call to method Add +# 9| 0: [IntLiteral] 3 +# 9| 1: [LocalVariableAccess] access to local variable list1 +# 10| 1: [LocalVariableDeclStmt] ... ...; +# 10| 0: [LocalVariableDeclAndInitExpr] IList> list2 = ... +# 10| 0: [ObjectCreation] object creation of type List> +# 10| -1: [CollectionInitializer] { ..., ... } +# 10| 0: [ElementInitializer] call to method Add +# 10| 0: [LocalVariableAccess] access to local variable list1 +# 10| 1: [ElementInitializer] call to method Add +# 10| 0: [LocalVariableAccess] access to local variable list1 +# 10| 1: [LocalVariableAccess] access to local variable list2 +# 12| 2: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclAndInitExpr] IEnumerable list3 = ... +# 16| 0: [MethodCall] call to method Select +# 15| 0: [MethodCall] call to method OrderBy +# 14| 0: [MethodCall] call to method Where +# 13| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 13| 0: [LocalVariableAccess] access to local variable list1 +# 13| 1: [LocalVariableAccess] access to local variable a +# 14| 1: [LogicalOrExpr] ... || ... +# 14| 0: [EQExpr] ... == ... +# 14| 0: [LocalVariableAccess] access to local variable a +# 14| 1: [IntLiteral] 2 +# 14| 1: [EQExpr] ... == ... +# 14| 0: [LocalVariableAccess] access to local variable a +# 14| 1: [IntLiteral] 3 +# 15| 1: [LocalVariableAccess] access to local variable a +# 16| 1: [AddExpr] ... + ... +# 16| 0: [LocalVariableAccess] access to local variable a +# 16| 1: [IntLiteral] 1 +# 12| 1: [LocalVariableAccess] access to local variable list3 +# 18| 3: [LocalVariableDeclStmt] ... ...; +# 18| 0: [LocalVariableDeclAndInitExpr] IEnumerable list4 = ... +# 18| 0: [MethodCall] call to method Select +# 18| -1: [MethodCall] call to method OrderBy +# 18| -1: [MethodCall] call to method Where +# 18| -1: [LocalVariableAccess] access to local variable list1 +# 19| 0: [LambdaExpr] (...) => ... +#-----| 2: (Parameters) +# 19| 0: [Parameter] a +# 19| 4: [LogicalOrExpr] ... || ... +# 19| 0: [EQExpr] ... == ... +# 19| 0: [ParameterAccess] access to parameter a +# 19| 1: [IntLiteral] 2 +# 19| 1: [EQExpr] ... == ... +# 19| 0: [ParameterAccess] access to parameter a +# 19| 1: [IntLiteral] 3 +# 20| 0: [LambdaExpr] (...) => ... +#-----| 2: (Parameters) +# 20| 0: [Parameter] a +# 20| 4: [ParameterAccess] access to parameter a +# 21| 0: [LambdaExpr] (...) => ... +#-----| 2: (Parameters) +# 21| 0: [Parameter] a +# 21| 4: [AddExpr] ... + ... +# 21| 0: [ParameterAccess] access to parameter a +# 21| 1: [IntLiteral] 1 +# 18| 1: [LocalVariableAccess] access to local variable list4 +# 23| 4: [LocalVariableDeclStmt] ... ...; +# 23| 0: [LocalVariableDeclAndInitExpr] IEnumerable list5 = ... +# 28| 0: [MethodCall] call to method +# 27| 0: [MethodCall] call to method Join +# 26| 0: [MethodCall] call to method Select +# 25| 0: [MethodCall] call to method SelectMany +# 24| 0: [LocalVariableDeclAndInitExpr] IList a = ... +# 24| 0: [LocalVariableAccess] access to local variable list2 +# 24| 1: [LocalVariableAccess] access to local variable a +# 25| 1: [LocalVariableDeclAndInitExpr] IList b = ... +# 25| 0: [LocalVariableAccess] access to local variable a +# 25| 1: [LocalVariableAccess] access to local variable b +# 25| 2: [LocalVariableAccess] access to local variable a +# 26| 1: [LocalVariableDeclAndInitExpr] Int32 next = ... +# 26| 0: [AddExpr] ... + ... +# 26| 0: [LocalVariableAccess] access to local variable b +# 26| 1: [IntLiteral] 1 +# 26| 1: [LocalVariableAccess] access to local variable next +# 26| 2: [AddExpr] ... + ... +# 26| 0: [LocalVariableAccess] access to local variable b +# 26| 1: [IntLiteral] 1 +# 27| 1: [LocalVariableDeclAndInitExpr] IList c = ... +# 27| 0: [LocalVariableAccess] access to local variable list1 +# 27| 1: [LocalVariableAccess] access to local variable c +# 27| 2: [LocalVariableAccess] access to local variable list1 +# 27| 3: [LocalVariableAccess] access to local variable next +# 27| 4: [LocalVariableAccess] access to local variable c +# 28| 1: [IntLiteral] 1 +# 23| 1: [LocalVariableAccess] access to local variable list5 +# 30| 5: [LocalVariableDeclStmt] ... ...; +# 30| 0: [LocalVariableDeclAndInitExpr] IEnumerable> list6 = ... +# 35| 0: [MethodCall] call to method GroupBy +# 34| 0: [MethodCall] call to method OrderByDescending +# 33| 0: [MethodCall] call to method Select +# 32| 0: [MethodCall] call to method SelectMany +# 31| 0: [LocalVariableDeclAndInitExpr] IList a = ... +# 31| 0: [LocalVariableAccess] access to local variable list2 +# 31| 1: [LocalVariableAccess] access to local variable a +# 32| 1: [LocalVariableDeclAndInitExpr] IList b = ... +# 32| 0: [LocalVariableAccess] access to local variable a +# 32| 1: [LocalVariableAccess] access to local variable b +# 32| 2: [LocalVariableAccess] access to local variable a +# 33| 1: [LocalVariableDeclAndInitExpr] Int32 next = ... +# 33| 0: [AddExpr] ... + ... +# 33| 0: [LocalVariableAccess] access to local variable b +# 33| 1: [IntLiteral] 1 +# 33| 1: [LocalVariableAccess] access to local variable next +# 33| 2: [AddExpr] ... + ... +# 33| 0: [LocalVariableAccess] access to local variable b +# 33| 1: [IntLiteral] 1 +# 34| 1: [MulExpr] ... * ... +# 34| 0: [LocalVariableAccess] access to local variable next +# 34| 1: [IntLiteral] 2 +# 35| 1: [LocalVariableAccess] access to local variable b +# 35| 2: [LocalVariableAccess] access to local variable next +# 30| 1: [LocalVariableAccess] access to local variable list6 +# 37| 6: [LocalVariableDeclStmt] ... ...; +# 37| 0: [LocalVariableDeclAndInitExpr] B list7 = ... +# 37| 0: [ObjectCreation] object creation of type B +# 37| 1: [LocalVariableAccess] access to local variable list7 +# 39| 7: [LocalVariableDeclStmt] ... ...; +# 39| 0: [LocalVariableDeclAndInitExpr] IEnumerable list8 = ... +# 41| 0: [MethodCall] call to method Select +# 40| 0: [LocalVariableDeclAndInitExpr] Object a = ... +# 40| 0: [LocalVariableAccess] access to local variable list7 +# 40| 1: [LocalVariableAccess] access to local variable a +# 41| 1: [LocalVariableAccess] access to local variable a +# 39| 1: [LocalVariableAccess] access to local variable list8 +# 43| 8: [LocalVariableDeclStmt] ... ...; +# 43| 0: [LocalVariableDeclAndInitExpr] C list9 = ... +# 43| 0: [ObjectCreation] object creation of type C +# 43| 1: [LocalVariableAccess] access to local variable list9 +# 45| 9: [LocalVariableDeclStmt] ... ...; +# 45| 0: [LocalVariableDeclAndInitExpr] IEnumerable list10 = ... +# 47| 0: [MethodCall] call to method Select +# 46| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 46| 0: [LocalVariableAccess] access to local variable list9 +# 46| 1: [LocalVariableAccess] access to local variable a +# 47| 1: [LocalVariableAccess] access to local variable a +# 45| 1: [LocalVariableAccess] access to local variable list10 +# 49| 10: [LocalVariableDeclStmt] ... ...; +# 49| 0: [LocalVariableDeclAndInitExpr] IEnumerable list11 = ... +# 51| 0: [MethodCall] call to method Select +# 50| 0: [LocalVariableDeclAndInitExpr] String a = ... +# 50| 0: [LocalVariableAccess] access to local variable list7 +# 50| 1: [LocalVariableAccess] access to local variable a +# 51| 1: [LocalVariableAccess] access to local variable a +# 49| 1: [LocalVariableAccess] access to local variable list11 +# 53| 11: [LocalVariableDeclStmt] ... ...; +# 53| 0: [LocalVariableDeclAndInitExpr] IEnumerable<(Int32,IEnumerable>)> list12 = ... +# 56| 0: [MethodCall] call to method +# 55| 0: [MethodCall] call to method GroupJoin +# 54| 0: [LocalVariableDeclAndInitExpr] Int32 a = ... +# 54| 0: [LocalVariableAccess] access to local variable list1 +# 54| 1: [LocalVariableAccess] access to local variable a +# 55| 1: [LocalVariableDeclAndInitExpr] IList> c = ... +# 55| 0: [LocalVariableAccess] access to local variable list2 +# 55| 1: [LocalVariableAccess] access to local variable c +# 55| 2: [LocalVariableAccess] access to local variable list2 +# 55| 3: [LocalVariableAccess] access to local variable a +# 55| 4: [IndexerCall] access to indexer +# 55| -1: [LocalVariableAccess] access to local variable c +# 55| 0: [IntLiteral] 0 +# 55| 5: [LocalVariableDeclAndInitExpr] IList> d = ... +# 55| 0: [LocalVariableAccess] access to local variable list2 +# 55| 1: [LocalVariableAccess] access to local variable d +# 56| 1: [TupleExpr] (..., ...) +# 56| 0: [LocalVariableAccess] access to local variable a +# 56| 1: [LocalVariableAccess] access to local variable d +# 53| 1: [LocalVariableAccess] access to local variable list12 +# 59| 6: [Class] A +#-----| 3: (Base types) +# 59| 1: [Interface] IEnumerable +# 61| 5: [Method] GetEnumerator +# 62| 4: [BlockStmt] {...} +# 63| 0: [ThrowStmt] throw ...; +# 63| 0: [ObjectCreation] object creation of type NotImplementedException +# 67| 7: [Class] B +#-----| 3: (Base types) +# 67| 0: [Class] A +# 69| 8: [Class] C +#-----| 3: (Base types) +# 69| 0: [Class] List diff --git a/csharp/ql/test/library-tests/linq/PrintAst.qlref b/csharp/ql/test/library-tests/linq/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/linq/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/members/PrintAst.expected b/csharp/ql/test/library-tests/members/PrintAst.expected new file mode 100644 index 000000000000..3a69ff413dff --- /dev/null +++ b/csharp/ql/test/library-tests/members/PrintAst.expected @@ -0,0 +1,184 @@ +Members.cs: +# 1| [NamespaceDeclaration] namespace ... { ... } +# 3| 1: [DelegateType] EventHandler +#-----| 2: (Parameters) +# 3| 0: [Parameter] sender +# 3| 1: [Parameter] e +# 6| 2: [Class] Class +# 9| 5: [Class] NestedClass +# 12| 5: [Method] Method +#-----| 1: (Type parameters) +# 12| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 12| 0: [Parameter] t +# 12| 4: [MethodCall] call to method ToString +# 12| -1: [ParameterAccess] access to parameter t +# 14| 6: [Indexer] Item +#-----| 1: (Parameters) +# 14| 0: [Parameter] i +# 14| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 14| 0: [Parameter] i +# 14| 4: [MethodCall] call to method ToString +# 14| -1: [ParameterAccess] access to parameter i +# 14| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 14| 0: [Parameter] i +# 14| 1: [Parameter] value +# 14| 4: [BlockStmt] {...} +# 16| 7: [Field] Field +# 18| 8: [IndexerProperty] Prop +# 18| 3: [Getter] get_Prop +# 18| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 18| 0: [Parameter] value +# 20| 9: [Event] Event +# 20| 3: [AddEventAccessor] add_Event +#-----| 2: (Parameters) +# 20| 0: [Parameter] value +# 20| 4: [RemoveEventAccessor] remove_Event +#-----| 2: (Parameters) +# 20| 0: [Parameter] value +# 24| 6: [Method] Method +# 24| 4: [BlockStmt] {...} +# 26| 7: [Indexer] Item +#-----| 1: (Parameters) +# 26| 0: [Parameter] i +# 26| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 26| 0: [Parameter] i +# 26| 4: [MethodCall] call to method ToString +# 26| -1: [ParameterAccess] access to parameter i +# 26| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 26| 0: [Parameter] i +# 26| 1: [Parameter] value +# 26| 4: [BlockStmt] {...} +# 28| 8: [Field] Field +# 30| 9: [IndexerProperty] Prop +# 30| 3: [Getter] get_Prop +# 30| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 30| 0: [Parameter] value +# 32| 10: [Event] Event +# 32| 3: [AddEventAccessor] add_Event +#-----| 2: (Parameters) +# 32| 0: [Parameter] value +# 32| 4: [RemoveEventAccessor] remove_Event +#-----| 2: (Parameters) +# 32| 0: [Parameter] value +# 35| 3: [Class] Class2 +# 37| 5: [Class] NestedClass2 +# 39| 5: [Method] Method +#-----| 1: (Type parameters) +# 39| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 39| 0: [Parameter] t +# 39| 4: [MethodCall] call to method ToString +# 39| -1: [ParameterAccess] access to parameter t +# 40| 6: [Indexer] Item +#-----| 1: (Parameters) +# 40| 0: [Parameter] i +# 40| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 40| 0: [Parameter] i +# 40| 4: [MethodCall] call to method ToString +# 40| -1: [ParameterAccess] access to parameter i +# 40| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 40| 0: [Parameter] i +# 40| 1: [Parameter] value +# 40| 4: [BlockStmt] {...} +# 41| 7: [Field] Field +# 42| 8: [IndexerProperty] Prop +# 42| 3: [Getter] get_Prop +# 42| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 42| 0: [Parameter] value +# 43| 9: [Event] Event +# 43| 3: [AddEventAccessor] add_Event +#-----| 2: (Parameters) +# 43| 0: [Parameter] value +# 43| 4: [RemoveEventAccessor] remove_Event +#-----| 2: (Parameters) +# 43| 0: [Parameter] value +# 46| 6: [Method] Method +# 46| 4: [BlockStmt] {...} +# 47| 7: [Indexer] Item +#-----| 1: (Parameters) +# 47| 0: [Parameter] i +# 47| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 47| 0: [Parameter] i +# 47| 4: [MethodCall] call to method ToString +# 47| -1: [ParameterAccess] access to parameter i +# 47| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 47| 0: [Parameter] i +# 47| 1: [Parameter] value +# 47| 4: [BlockStmt] {...} +# 48| 8: [Field] Field +# 49| 9: [IndexerProperty] Prop +# 49| 3: [Getter] get_Prop +# 49| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 49| 0: [Parameter] value +# 50| 10: [Event] Event +# 50| 3: [AddEventAccessor] add_Event +#-----| 2: (Parameters) +# 50| 0: [Parameter] value +# 50| 4: [RemoveEventAccessor] remove_Event +#-----| 2: (Parameters) +# 50| 0: [Parameter] value +# 54| 4: [Interface] Interface +# 56| 4: [Method] Method +# 57| 5: [Indexer] Item +#-----| 1: (Parameters) +# 57| 0: [Parameter] i +# 57| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 57| 0: [Parameter] i +# 57| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 57| 0: [Parameter] i +# 57| 1: [Parameter] value +# 58| 6: [IndexerProperty] Prop +# 58| 3: [Getter] get_Prop +# 58| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 58| 0: [Parameter] value +# 59| 7: [Event] Event +# 59| 3: [AddEventAccessor] add_Event +#-----| 2: (Parameters) +# 59| 0: [Parameter] value +# 59| 4: [RemoveEventAccessor] remove_Event +#-----| 2: (Parameters) +# 59| 0: [Parameter] value +# 62| 5: [Interface] Interface2 +# 64| 4: [Method] Method +# 65| 5: [Indexer] Item +#-----| 1: (Parameters) +# 65| 0: [Parameter] i +# 65| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 65| 0: [Parameter] i +# 65| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 65| 0: [Parameter] i +# 65| 1: [Parameter] value +# 66| 6: [IndexerProperty] Prop +# 66| 3: [Getter] get_Prop +# 66| 4: [Setter] set_Prop +#-----| 2: (Parameters) +# 66| 0: [Parameter] value +# 67| 7: [Event] Event +# 67| 3: [AddEventAccessor] add_Event +#-----| 2: (Parameters) +# 67| 0: [Parameter] value +# 67| 4: [RemoveEventAccessor] remove_Event +#-----| 2: (Parameters) +# 67| 0: [Parameter] value +# 71| 6: [Enum] Enum +# 73| 7: [Enum] Enum2 +# 76| 8: [Struct] Struct +# 78| 9: [Struct] Struct2 diff --git a/csharp/ql/test/library-tests/members/PrintAst.qlref b/csharp/ql/test/library-tests/members/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/members/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/methods/PrintAst.expected b/csharp/ql/test/library-tests/methods/PrintAst.expected new file mode 100644 index 000000000000..f1fcd161bb39 --- /dev/null +++ b/csharp/ql/test/library-tests/methods/PrintAst.expected @@ -0,0 +1,367 @@ +methods.cs: +# 3| [NamespaceDeclaration] namespace ... { ... } +# 6| 1: [Class] TestRef +# 9| 5: [Method] Swap +#-----| 2: (Parameters) +# 9| 0: [Parameter] x +# 9| 1: [Parameter] y +# 10| 4: [BlockStmt] {...} +# 11| 0: [LocalVariableDeclStmt] ... ...; +# 11| 0: [LocalVariableDeclAndInitExpr] Int32 temp = ... +# 11| 0: [ParameterAccess] access to parameter x +# 11| 1: [LocalVariableAccess] access to local variable temp +# 12| 1: [ExprStmt] ...; +# 12| 0: [AssignExpr] ... = ... +# 12| 0: [ParameterAccess] access to parameter y +# 12| 1: [ParameterAccess] access to parameter x +# 13| 2: [ExprStmt] ...; +# 13| 0: [AssignExpr] ... = ... +# 13| 0: [LocalVariableAccess] access to local variable temp +# 13| 1: [ParameterAccess] access to parameter y +# 16| 6: [Method] Main +# 17| 4: [BlockStmt] {...} +# 18| 0: [LocalVariableDeclStmt] ... ...; +# 18| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 18| 0: [IntLiteral] 1 +# 18| 1: [LocalVariableAccess] access to local variable i +# 18| 1: [LocalVariableDeclAndInitExpr] Int32 j = ... +# 18| 0: [IntLiteral] 2 +# 18| 1: [LocalVariableAccess] access to local variable j +# 19| 1: [ExprStmt] ...; +# 19| 0: [MethodCall] call to method Swap +# 19| 0: [LocalVariableAccess] access to local variable i +# 19| 1: [LocalVariableAccess] access to local variable j +# 20| 2: [ExprStmt] ...; +# 20| 0: [MethodCall] call to method WriteLine +# 20| -1: [TypeAccess] access to type Console +# 20| 0: [StringLiteral] "{0} {1}" +# 20| 1: [CastExpr] (...) ... +# 20| 0: [LocalVariableAccess] access to local variable i +# 20| 2: [CastExpr] (...) ... +# 20| 0: [LocalVariableAccess] access to local variable j +# 21| 3: [ExprStmt] ...; +# 21| 0: [MethodCall] call to method WriteLine +# 21| -1: [TypeAccess] access to type Console +# 21| 0: [StringLiteral] "{0} {1}" +# 21| 1: [CastExpr] (...) ... +# 21| 0: [LocalVariableAccess] access to local variable i +# 21| 2: [CastExpr] (...) ... +# 21| 0: [LocalVariableAccess] access to local variable j +# 25| 2: [Class] TestOut +# 28| 5: [Method] Divide +#-----| 2: (Parameters) +# 28| 0: [Parameter] x +# 28| 1: [Parameter] y +# 28| 2: [Parameter] result +# 28| 3: [Parameter] remainder +# 29| 4: [BlockStmt] {...} +# 30| 0: [ExprStmt] ...; +# 30| 0: [AssignExpr] ... = ... +# 30| 0: [DivExpr] ... / ... +# 30| 0: [ParameterAccess] access to parameter x +# 30| 1: [ParameterAccess] access to parameter y +# 30| 1: [ParameterAccess] access to parameter result +# 31| 1: [ExprStmt] ...; +# 31| 0: [AssignExpr] ... = ... +# 31| 0: [RemExpr] ... % ... +# 31| 0: [ParameterAccess] access to parameter x +# 31| 1: [ParameterAccess] access to parameter y +# 31| 1: [ParameterAccess] access to parameter remainder +# 34| 6: [Method] Main +# 35| 4: [BlockStmt] {...} +# 36| 0: [LocalVariableDeclStmt] ... ...; +# 36| 0: [LocalVariableDeclExpr] Int32 res +# 36| 1: [LocalVariableDeclExpr] Int32 rem +# 37| 1: [ExprStmt] ...; +# 37| 0: [MethodCall] call to method Divide +# 37| 0: [IntLiteral] 10 +# 37| 1: [IntLiteral] 3 +# 37| 2: [LocalVariableAccess] access to local variable res +# 37| 3: [LocalVariableAccess] access to local variable rem +# 38| 2: [ExprStmt] ...; +# 38| 0: [MethodCall] call to method WriteLine +# 38| -1: [TypeAccess] access to type Console +# 38| 0: [StringLiteral] "{0} {1}" +# 38| 1: [CastExpr] (...) ... +# 38| 0: [LocalVariableAccess] access to local variable res +# 38| 2: [CastExpr] (...) ... +# 38| 0: [LocalVariableAccess] access to local variable rem +# 42| 3: [Class] Console +# 45| 5: [Method] Write +#-----| 2: (Parameters) +# 45| 0: [Parameter] fmt +# 45| 1: [Parameter] args +# 45| 4: [BlockStmt] {...} +# 46| 6: [Method] WriteLine +#-----| 2: (Parameters) +# 46| 0: [Parameter] fmt +# 46| 1: [Parameter] args +# 46| 4: [BlockStmt] {...} +# 49| 4: [Class] TestOverloading +# 52| 5: [Method] F +# 53| 4: [BlockStmt] {...} +# 54| 0: [ExprStmt] ...; +# 54| 0: [MethodCall] call to method WriteLine +# 54| -1: [TypeAccess] access to type Console +# 54| 0: [StringLiteral] "F()" +# 57| 6: [Method] F +#-----| 2: (Parameters) +# 57| 0: [Parameter] x +# 58| 4: [BlockStmt] {...} +# 59| 0: [ExprStmt] ...; +# 59| 0: [MethodCall] call to method WriteLine +# 59| -1: [TypeAccess] access to type Console +# 59| 0: [StringLiteral] "F(object)" +# 62| 7: [Method] F +#-----| 2: (Parameters) +# 62| 0: [Parameter] x +# 63| 4: [BlockStmt] {...} +# 64| 0: [ExprStmt] ...; +# 64| 0: [MethodCall] call to method WriteLine +# 64| -1: [TypeAccess] access to type Console +# 64| 0: [StringLiteral] "F(int)" +# 67| 8: [Method] F +#-----| 2: (Parameters) +# 67| 0: [Parameter] x +# 68| 4: [BlockStmt] {...} +# 69| 0: [ExprStmt] ...; +# 69| 0: [MethodCall] call to method WriteLine +# 69| -1: [TypeAccess] access to type Console +# 69| 0: [StringLiteral] "F(double)" +# 72| 9: [Method] F +#-----| 1: (Type parameters) +# 72| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 72| 0: [Parameter] x +# 73| 4: [BlockStmt] {...} +# 74| 0: [ExprStmt] ...; +# 74| 0: [MethodCall] call to method WriteLine +# 74| -1: [TypeAccess] access to type Console +# 74| 0: [StringLiteral] "F(T)" +# 77| 12: [Method] F +#-----| 2: (Parameters) +# 77| 0: [Parameter] x +# 77| 1: [Parameter] y +# 78| 4: [BlockStmt] {...} +# 79| 0: [ExprStmt] ...; +# 79| 0: [MethodCall] call to method WriteLine +# 79| -1: [TypeAccess] access to type Console +# 79| 0: [StringLiteral] "F(double, double)" +# 82| 13: [Method] Main +# 83| 4: [BlockStmt] {...} +# 84| 0: [ExprStmt] ...; +# 84| 0: [MethodCall] call to method F +# 85| 1: [ExprStmt] ...; +# 85| 0: [MethodCall] call to method F +# 85| 0: [IntLiteral] 1 +# 86| 2: [ExprStmt] ...; +# 86| 0: [MethodCall] call to method F +# 86| 0: [DoubleLiteral] 1 +# 87| 3: [ExprStmt] ...; +# 87| 0: [MethodCall] call to method F +# 87| 0: [StringLiteral] "abc" +# 88| 4: [ExprStmt] ...; +# 88| 0: [MethodCall] call to method F +# 88| 0: [CastExpr] (...) ... +# 88| 0: [IntLiteral] 1 +# 88| 1: [TypeAccess] access to type Double +# 89| 5: [ExprStmt] ...; +# 89| 0: [MethodCall] call to method F +# 89| 0: [CastExpr] (...) ... +# 89| 0: [IntLiteral] 1 +# 89| 1: [TypeAccess] access to type Object +# 90| 6: [ExprStmt] ...; +# 90| 0: [MethodCall] call to method F +# 90| 0: [IntLiteral] 1 +# 91| 7: [ExprStmt] ...; +# 91| 0: [MethodCall] call to method F +# 91| 0: [CastExpr] (...) ... +# 91| 0: [IntLiteral] 1 +# 91| 1: [CastExpr] (...) ... +# 91| 0: [IntLiteral] 1 +# 96| 5: [Class] Extensions +# 99| 4: [ExtensionMethod] ToInt32 +#-----| 2: (Parameters) +# 99| 0: [Parameter] s +# 100| 4: [BlockStmt] {...} +# 101| 0: [ReturnStmt] return ...; +# 101| 0: [MethodCall] call to method Parse +# 101| -1: [TypeAccess] access to type Int32 +# 101| 0: [ParameterAccess] access to parameter s +# 104| 5: [ExtensionMethod] ToBool +#-----| 2: (Parameters) +# 104| 0: [Parameter] s +# 104| 1: [Parameter] f +# 105| 4: [BlockStmt] {...} +# 106| 0: [ReturnStmt] return ...; +# 106| 0: [DelegateCall] delegate call +# 106| -1: [ParameterAccess] access to parameter f +# 106| 0: [ParameterAccess] access to parameter s +# 109| 6: [ExtensionMethod] Slice +#-----| 1: (Type parameters) +# 109| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 109| 0: [Parameter] source +# 109| 1: [Parameter] index +# 109| 2: [Parameter] count +# 110| 4: [BlockStmt] {...} +# 111| 0: [IfStmt] if (...) ... +# 111| 0: [LogicalOrExpr] ... || ... +# 111| 0: [LogicalOrExpr] ... || ... +# 111| 0: [LTExpr] ... < ... +# 111| 0: [ParameterAccess] access to parameter index +# 111| 1: [IntLiteral] 0 +# 111| 1: [LTExpr] ... < ... +# 111| 0: [ParameterAccess] access to parameter count +# 111| 1: [IntLiteral] 0 +# 111| 1: [LTExpr] ... < ... +# 111| 0: [SubExpr] ... - ... +# 111| 0: [PropertyCall] access to property Length +# 111| -1: [ParameterAccess] access to parameter source +# 111| 1: [ParameterAccess] access to parameter index +# 111| 1: [ParameterAccess] access to parameter count +# 112| 1: [ThrowStmt] throw ...; +# 112| 0: [ObjectCreation] object creation of type ArgumentException +# 113| 1: [LocalVariableDeclStmt] ... ...; +# 113| 0: [LocalVariableDeclAndInitExpr] T[] result = ... +# 113| 0: [ArrayCreation] array creation of type T[] +# 113| 0: [ParameterAccess] access to parameter count +# 113| 1: [LocalVariableAccess] access to local variable result +# 114| 2: [ExprStmt] ...; +# 114| 0: [MethodCall] call to method Copy +# 114| -1: [TypeAccess] access to type Array +# 114| 0: [ParameterAccess] access to parameter source +# 114| 1: [ParameterAccess] access to parameter index +# 114| 2: [LocalVariableAccess] access to local variable result +# 114| 3: [IntLiteral] 0 +# 114| 4: [ParameterAccess] access to parameter count +# 115| 3: [ReturnStmt] return ...; +# 115| 0: [LocalVariableAccess] access to local variable result +# 118| 8: [Method] CallToInt32 +# 118| 4: [MethodCall] call to method ToInt32 +# 118| 0: [StringLiteral] "0" +# 121| 6: [Class] TestExtensions +# 124| 4: [Method] Main +# 125| 4: [BlockStmt] {...} +# 126| 0: [LocalVariableDeclStmt] ... ...; +# 126| 0: [LocalVariableDeclAndInitExpr] String[] strings = ... +# 126| 0: [ArrayCreation] array creation of type String[] +# 126| -1: [ArrayInitializer] { ..., ... } +# 126| 0: [StringLiteral] "1" +# 126| 1: [StringLiteral] "22" +# 126| 2: [StringLiteral] "333" +# 126| 3: [StringLiteral] "4444" +# 126| 1: [LocalVariableAccess] access to local variable strings +# 127| 1: [ForeachStmt] foreach (... ... in ...) ... +# 127| 0: [LocalVariableDeclExpr] String s +# 127| 1: [MethodCall] call to method Slice +# 127| -1: [LocalVariableAccess] access to local variable strings +# 127| 0: [IntLiteral] 1 +# 127| 1: [IntLiteral] 2 +# 128| 2: [BlockStmt] {...} +# 129| 0: [ExprStmt] ...; +# 129| 0: [MethodCall] call to method WriteLine +# 129| -1: [TypeAccess] access to type Console +# 129| 0: [MethodCall] call to method ToInt32 +# 129| -1: [LocalVariableAccess] access to local variable s +# 132| 2: [ExprStmt] ...; +# 132| 0: [MethodCall] call to method ToInt32 +# 132| -1: [TypeAccess] access to type Extensions +# 132| 0: [StringLiteral] "" +# 134| 3: [ExprStmt] ...; +# 134| 0: [MethodCall] call to method ToBool +# 134| -1: [TypeAccess] access to type Extensions +# 134| 0: [StringLiteral] "true" +# 134| 1: [ImplicitDelegateCreation] delegate creation of type Func +# 134| 0: [MethodAccess] access to method Parse +# 134| -1: [TypeAccess] access to type Boolean +# 139| 7: [Class] TestDefaultParameters +# 141| 4: [Method] Method1 +#-----| 2: (Parameters) +# 141| 0: [Parameter] x +# 141| 1: [Parameter] y +# 142| 4: [BlockStmt] {...} +# 145| 5: [Method] Method2 +#-----| 2: (Parameters) +# 145| 0: [Parameter] a +# 145| 1: [Parameter] b +# 145| 2: [Parameter] c +# 145| 1: [IntLiteral] 1 +# 145| 3: [Parameter] d +# 145| 1: [IntLiteral] 2 +# 145| 4: [Parameter] e +# 145| 1: [AddExpr] ... + ... +# 145| 0: [StringLiteral] "a" +# 145| 1: [StringLiteral] "b" +# 146| 4: [BlockStmt] {...} +# 149| 6: [InstanceConstructor] TestDefaultParameters +#-----| 2: (Parameters) +# 149| 0: [Parameter] x +# 150| 4: [BlockStmt] {...} +# 153| 7: [InstanceConstructor] TestDefaultParameters +#-----| 2: (Parameters) +# 153| 0: [Parameter] x +# 153| 1: [StringLiteral] "abc" +# 153| 1: [Parameter] y +# 153| 1: [ObjectCreation] object creation of type Double +# 154| 4: [BlockStmt] {...} +# 157| 8: [DelegateType] Del +#-----| 2: (Parameters) +# 157| 0: [Parameter] a +# 157| 1: [Parameter] b +# 157| 1: [IntLiteral] 12 +# 157| 2: [Parameter] c +# 157| 1: [ObjectCreation] object creation of type Double +# 159| 9: [Indexer] Item +#-----| 1: (Parameters) +# 159| 0: [Parameter] x +# 159| 1: [Parameter] y +# 159| 1: [IntLiteral] 0 +# 161| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 159| 0: [Parameter] x +# 159| 1: [Parameter] y +# 159| 1: [IntLiteral] 0 +# 161| 4: [BlockStmt] {...} +# 161| 0: [ReturnStmt] return ...; +# 161| 0: [AddExpr] ... + ... +# 161| 0: [ParameterAccess] access to parameter x +# 161| 1: [ParameterAccess] access to parameter y +# 162| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 159| 0: [Parameter] x +# 159| 1: [Parameter] y +# 159| 1: [IntLiteral] 0 +# 162| 2: [Parameter] value +# 162| 4: [BlockStmt] {...} +# 166| 8: [Class] TestDefaultExtensionParameters +# 168| 4: [ExtensionMethod] Plus +#-----| 2: (Parameters) +# 168| 0: [Parameter] left +# 168| 1: [Parameter] right +# 168| 1: [IntLiteral] 0 +# 169| 4: [BlockStmt] {...} +# 170| 0: [ReturnStmt] return ...; +# 170| 0: [AddExpr] ... + ... +# 170| 0: [ParameterAccess] access to parameter left +# 170| 1: [ParameterAccess] access to parameter right +# 173| 5: [ExtensionMethod] SkipTwo +#-----| 1: (Type parameters) +# 173| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 173| 0: [Parameter] list +# 173| 1: [Parameter] i +# 173| 1: [IntLiteral] 1 +# 174| 4: [BlockStmt] {...} +# 175| 0: [ReturnStmt] return ...; +# 175| 0: [ParameterAccess] access to parameter list +# 178| 7: [ExtensionMethod] SkipTwoInt +#-----| 2: (Parameters) +# 178| 0: [Parameter] list +# 178| 1: [Parameter] i +# 178| 1: [IntLiteral] 1 +# 179| 4: [BlockStmt] {...} +# 180| 0: [ReturnStmt] return ...; +# 180| 0: [MethodCall] call to method SkipTwo +# 180| -1: [ParameterAccess] access to parameter list +# 180| 0: [ParameterAccess] access to parameter i diff --git a/csharp/ql/test/library-tests/methods/PrintAst.qlref b/csharp/ql/test/library-tests/methods/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/methods/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/namespaces/PrintAst.expected b/csharp/ql/test/library-tests/namespaces/PrintAst.expected new file mode 100644 index 000000000000..8025fdf4553b --- /dev/null +++ b/csharp/ql/test/library-tests/namespaces/PrintAst.expected @@ -0,0 +1,37 @@ +namespaces.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 8| 1: [Class] A +# 9| 2: [Class] B +# 13| [NamespaceDeclaration] namespace ... { ... } +# 16| 1: [NamespaceDeclaration] namespace ... { ... } +# 19| 1: [Class] A +# 20| 2: [Class] B +# 26| [NamespaceDeclaration] namespace ... { ... } +# 29| 1: [Class] A +# 33| [NamespaceDeclaration] namespace ... { ... } +# 36| 1: [Class] B +# 38| 2: [Struct] S +# 40| 3: [Interface] I +# 44| [NamespaceDeclaration] namespace ... { ... } +# 50| [NamespaceDeclaration] namespace ... { ... } +# 53| 1: [Class] A +# 57| [NamespaceDeclaration] namespace ... { ... } +# 64| 1: [Class] B +#-----| 3: (Base types) +# 64| 0: [Class] A +# 66| 2: [Class] C +#-----| 3: (Base types) +# 66| 0: [Class] A +# 70| [NamespaceDeclaration] namespace ... { ... } +# 73| 1: [Class] A<> +#-----| 1: (Type parameters) +# 73| 0: [TypeParameter] T +# 76| 5: [Class] B +# 80| 2: [Class] A +# 84| [NamespaceDeclaration] namespace ... { ... } +# 91| [NamespaceDeclaration] namespace ... { ... } +# 94| 1: [Class] A +# 97| [NamespaceDeclaration] namespace ... { ... } +# 102| 1: [Class] B +#-----| 3: (Base types) +# 102| 0: [Class] A diff --git a/csharp/ql/test/library-tests/namespaces/PrintAst.qlref b/csharp/ql/test/library-tests/namespaces/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/namespaces/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/nestedtypes/PrintAst.expected b/csharp/ql/test/library-tests/nestedtypes/PrintAst.expected new file mode 100644 index 000000000000..275afe9a2bc9 --- /dev/null +++ b/csharp/ql/test/library-tests/nestedtypes/PrintAst.expected @@ -0,0 +1,77 @@ +nestedtypes.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 8| 1: [Class] Base +# 11| 5: [Struct] S +# 13| 6: [Interface] I +# 15| 7: [DelegateType] MyDelegate +#-----| 2: (Parameters) +# 15| 0: [Parameter] s +# 17| 8: [Method] F +# 18| 4: [BlockStmt] {...} +# 19| 0: [ExprStmt] ...; +# 19| 0: [MethodCall] call to method WriteLine +# 19| -1: [TypeAccess] access to type Console +# 19| 0: [StringLiteral] "Base.F" +# 22| 9: [Class] C +# 26| 2: [Class] Derived +#-----| 3: (Base types) +# 26| 0: [Class] Base +# 29| 5: [Class] Nested +# 32| 5: [Method] G +# 33| 4: [BlockStmt] {...} +# 34| 0: [LocalVariableDeclStmt] ... ...; +# 34| 0: [LocalVariableDeclAndInitExpr] Derived d = ... +# 34| 0: [ObjectCreation] object creation of type Derived +# 34| 1: [LocalVariableAccess] access to local variable d +# 35| 1: [ExprStmt] ...; +# 35| 0: [MethodCall] call to method F +# 35| -1: [LocalVariableAccess] access to local variable d +# 42| 3: [Class] Test +# 45| 5: [Method] Main +# 46| 4: [BlockStmt] {...} +# 47| 0: [LocalVariableDeclStmt] ... ...; +# 47| 0: [LocalVariableDeclAndInitExpr] Nested n = ... +# 47| 0: [ObjectCreation] object creation of type Nested +# 47| 1: [LocalVariableAccess] access to local variable n +# 48| 1: [ExprStmt] ...; +# 48| 0: [MethodCall] call to method G +# 48| -1: [LocalVariableAccess] access to local variable n +# 53| 4: [Class] Outer<> +#-----| 1: (Type parameters) +# 53| 0: [TypeParameter] T +# 56| 5: [Class] Inner<> +#-----| 1: (Type parameters) +# 56| 0: [TypeParameter] U +# 59| 5: [Method] F +#-----| 2: (Parameters) +# 59| 0: [Parameter] t +# 59| 1: [Parameter] u +# 59| 4: [BlockStmt] {...} +# 63| 7: [Method] F +#-----| 2: (Parameters) +# 63| 0: [Parameter] t +# 64| 4: [BlockStmt] {...} +# 65| 0: [ExprStmt] ...; +# 65| 0: [MethodCall] call to method F +# 65| -1: [TypeAccess] access to type Inner +# 65| -1: [TypeAccess] access to type Outer<> +# 65| 0: [ParameterAccess] access to parameter t +# 65| 1: [StringLiteral] "abc" +# 68| 1: [ExprStmt] ...; +# 68| 0: [MethodCall] call to method F +# 68| -1: [TypeAccess] access to type Inner +# 68| -1: [TypeAccess] access to type Outer +# 68| 0: [IntLiteral] 3 +# 68| 1: [StringLiteral] "abc" +# 69| 2: [LocalVariableDeclStmt] ... ...; +# 69| 0: [LocalVariableDeclAndInitExpr] Type type = ... +# 69| 0: [TypeofExpr] typeof(...) +# 69| 0: [TypeAccess] access to type Inner<> +# 69| 1: [LocalVariableAccess] access to local variable type +# 74| 5: [Class] Outer2<> +#-----| 1: (Type parameters) +# 74| 0: [TypeParameter] T +# 77| 5: [Class] Inner2<> +#-----| 1: (Type parameters) +# 77| 0: [TypeParameter] T +# 80| 5: [Field] t diff --git a/csharp/ql/test/library-tests/nestedtypes/PrintAst.qlref b/csharp/ql/test/library-tests/nestedtypes/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/nestedtypes/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/operators/PrintAst.expected b/csharp/ql/test/library-tests/operators/PrintAst.expected new file mode 100644 index 000000000000..2aa62e022ca7 --- /dev/null +++ b/csharp/ql/test/library-tests/operators/PrintAst.expected @@ -0,0 +1,129 @@ +operators.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [Class] IntVector +# 10| 4: [InstanceConstructor] IntVector +#-----| 2: (Parameters) +# 10| 0: [Parameter] length +# 10| 4: [BlockStmt] {...} +# 12| 5: [Property] Length +# 12| 3: [Getter] get_Length +# 12| 4: [BlockStmt] {...} +# 12| 0: [ReturnStmt] return ...; +# 12| 0: [IntLiteral] 4 +# 14| 6: [Indexer] Item +#-----| 1: (Parameters) +# 14| 0: [Parameter] index +# 14| 3: [Getter] get_Item +#-----| 2: (Parameters) +# 14| 0: [Parameter] index +# 14| 4: [BlockStmt] {...} +# 14| 0: [ReturnStmt] return ...; +# 14| 0: [IntLiteral] 0 +# 14| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 14| 0: [Parameter] index +# 14| 1: [Parameter] value +# 14| 4: [BlockStmt] {...} +# 16| 7: [IncrementOperator] ++ +#-----| 2: (Parameters) +# 16| 0: [Parameter] iv +# 17| 4: [BlockStmt] {...} +# 18| 0: [LocalVariableDeclStmt] ... ...; +# 18| 0: [LocalVariableDeclAndInitExpr] IntVector temp = ... +# 18| 0: [ObjectCreation] object creation of type IntVector +# 18| 0: [PropertyCall] access to property Length +# 18| -1: [ParameterAccess] access to parameter iv +# 18| 1: [LocalVariableAccess] access to local variable temp +# 19| 1: [ForStmt] for (...;...;...) ... +# 19| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 19| 0: [IntLiteral] 0 +# 19| 1: [LocalVariableAccess] access to local variable i +# 19| 0: [LTExpr] ... < ... +# 19| 0: [LocalVariableAccess] access to local variable i +# 19| 1: [PropertyCall] access to property Length +# 19| -1: [ParameterAccess] access to parameter iv +# 19| 1: [PostIncrExpr] ...++ +# 19| 0: [LocalVariableAccess] access to local variable i +# 20| 2: [ExprStmt] ...; +# 20| 0: [AssignExpr] ... = ... +# 20| 0: [AddExpr] ... + ... +# 20| 0: [IndexerCall] access to indexer +# 20| -1: [ParameterAccess] access to parameter iv +# 20| 0: [LocalVariableAccess] access to local variable i +# 20| 1: [IntLiteral] 1 +# 20| 1: [IndexerCall] access to indexer +# 20| -1: [LocalVariableAccess] access to local variable temp +# 20| 0: [LocalVariableAccess] access to local variable i +# 21| 2: [ReturnStmt] return ...; +# 21| 0: [LocalVariableAccess] access to local variable temp +# 26| 2: [Class] TestUnaryOperator +# 29| 5: [Method] Main +# 30| 4: [BlockStmt] {...} +# 31| 0: [LocalVariableDeclStmt] ... ...; +# 31| 0: [LocalVariableDeclAndInitExpr] IntVector iv1 = ... +# 31| 0: [ObjectCreation] object creation of type IntVector +# 31| 0: [IntLiteral] 4 +# 31| 1: [LocalVariableAccess] access to local variable iv1 +# 32| 1: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclExpr] IntVector iv2 +# 33| 2: [ExprStmt] ...; +# 33| 0: [AssignExpr] ... = ... +# 33| 0: [OperatorCall] call to operator ++ +# 33| 0: [LocalVariableAccess] access to local variable iv1 +# 33| 1: [LocalVariableAccess] access to local variable iv2 +# 34| 3: [ExprStmt] ...; +# 34| 0: [AssignExpr] ... = ... +# 34| 0: [OperatorCall] call to operator ++ +# 34| 0: [LocalVariableAccess] access to local variable iv1 +# 34| 1: [LocalVariableAccess] access to local variable iv2 +# 39| 3: [Struct] Digit +# 42| 5: [Field] value +# 44| 6: [InstanceConstructor] Digit +#-----| 2: (Parameters) +# 44| 0: [Parameter] value +# 45| 4: [BlockStmt] {...} +# 46| 0: [IfStmt] if (...) ... +# 46| 0: [LogicalOrExpr] ... || ... +# 46| 0: [LTExpr] ... < ... +# 46| 0: [CastExpr] (...) ... +# 46| 0: [ParameterAccess] access to parameter value +# 46| 1: [IntLiteral] 0 +# 46| 1: [GTExpr] ... > ... +# 46| 0: [CastExpr] (...) ... +# 46| 0: [ParameterAccess] access to parameter value +# 46| 1: [IntLiteral] 9 +# 47| 1: [ThrowStmt] throw ...; +# 47| 0: [ObjectCreation] object creation of type ArgumentException +# 48| 1: [ExprStmt] ...; +# 48| 0: [AssignExpr] ... = ... +# 48| 0: [ParameterAccess] access to parameter value +# 48| 1: [FieldAccess] access to field value +# 48| -1: [ThisAccess] this access +# 51| 7: [ImplicitConversionOperator] implicit conversion +#-----| 2: (Parameters) +# 51| 0: [Parameter] d +# 52| 4: [BlockStmt] {...} +# 53| 0: [ReturnStmt] return ...; +# 53| 0: [FieldAccess] access to field value +# 53| -1: [ParameterAccess] access to parameter d +# 56| 8: [ExplicitConversionOperator] explicit conversion +#-----| 2: (Parameters) +# 56| 0: [Parameter] b +# 57| 4: [BlockStmt] {...} +# 58| 0: [ReturnStmt] return ...; +# 58| 0: [ObjectCreation] object creation of type Digit +# 58| 0: [ParameterAccess] access to parameter b +# 63| 4: [Class] TestConversionOperator +# 66| 5: [Method] Main +# 67| 4: [BlockStmt] {...} +# 68| 0: [LocalVariableDeclStmt] ... ...; +# 68| 0: [LocalVariableDeclAndInitExpr] Digit d = ... +# 68| 0: [OperatorCall] call to operator explicit conversion +# 68| 0: [CastExpr] (...) ... +# 68| 0: [IntLiteral] 8 +# 68| 1: [LocalVariableAccess] access to local variable d +# 69| 1: [LocalVariableDeclStmt] ... ...; +# 69| 0: [LocalVariableDeclAndInitExpr] Byte b = ... +# 69| 0: [OperatorCall] call to operator implicit conversion +# 69| 0: [LocalVariableAccess] access to local variable d +# 69| 1: [LocalVariableAccess] access to local variable b diff --git a/csharp/ql/test/library-tests/operators/PrintAst.qlref b/csharp/ql/test/library-tests/operators/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/operators/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/partial/MethodIsPartial.expected b/csharp/ql/test/library-tests/partial/MethodIsPartial.expected new file mode 100644 index 000000000000..8ee8f25b0c88 --- /dev/null +++ b/csharp/ql/test/library-tests/partial/MethodIsPartial.expected @@ -0,0 +1,7 @@ +| Partial.cs:3:18:3:39 | PartialMethodWithBody1 | true | +| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | true | +| Partial.cs:5:17:5:23 | Method2 | false | +| Partial.cs:11:17:11:23 | Method3 | false | +| Partial.cs:16:18:16:42 | PartialMethodWithoutBody2 | true | +| Partial.cs:17:17:17:23 | Method4 | false | +| Partial.cs:22:17:22:23 | Method5 | false | diff --git a/csharp/ql/test/library-tests/partial/MethodIsPartial.ql b/csharp/ql/test/library-tests/partial/MethodIsPartial.ql new file mode 100644 index 000000000000..b7e50bd5a443 --- /dev/null +++ b/csharp/ql/test/library-tests/partial/MethodIsPartial.ql @@ -0,0 +1,7 @@ +import csharp + +private boolean isPartial(Method m) { if m.isPartial() then result = true else result = false } + +from Method m +where m.fromSource() +select m, isPartial(m) diff --git a/csharp/ql/test/library-tests/partial/Partial.cs b/csharp/ql/test/library-tests/partial/Partial.cs index e8efae8c52ff..1d54614fc86d 100644 --- a/csharp/ql/test/library-tests/partial/Partial.cs +++ b/csharp/ql/test/library-tests/partial/Partial.cs @@ -1,11 +1,23 @@ -partial class A +partial class TwoPartClass { - partial void M(); - public void M2() { } + partial void PartialMethodWithBody1(); + partial void PartialMethodWithoutBody1(); + public void Method2() { } } -partial class A +partial class TwoPartClass { - partial void M() { } - public void M3() { } + partial void PartialMethodWithBody1() { } + public void Method3() { } } + +partial class OnePartPartialClass +{ + partial void PartialMethodWithoutBody2(); + public void Method4() { } +} + +class NonPartialClass +{ + public void Method5() { } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/partial/Partial1.expected b/csharp/ql/test/library-tests/partial/Partial1.expected index 3cc137e832fd..722482cac4f1 100644 --- a/csharp/ql/test/library-tests/partial/Partial1.expected +++ b/csharp/ql/test/library-tests/partial/Partial1.expected @@ -1,3 +1,6 @@ -| Partial.cs:1:15:1:15 | A | -| Partial.cs:3:18:3:18 | M | -| Partial.cs:7:15:7:15 | A | +| Partial.cs:1:15:1:26 | TwoPartClass | +| Partial.cs:3:18:3:39 | PartialMethodWithBody1 | +| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | +| Partial.cs:8:15:8:26 | TwoPartClass | +| Partial.cs:14:15:14:33 | OnePartPartialClass | +| Partial.cs:16:18:16:42 | PartialMethodWithoutBody2 | diff --git a/csharp/ql/test/library-tests/partial/Partial2.expected b/csharp/ql/test/library-tests/partial/Partial2.expected index 079601bb4971..6723b575fc7d 100644 --- a/csharp/ql/test/library-tests/partial/Partial2.expected +++ b/csharp/ql/test/library-tests/partial/Partial2.expected @@ -1,6 +1,10 @@ -| Partial.cs:1:15:1:15 | A | Partial.cs:3:18:3:18 | M | -| Partial.cs:1:15:1:15 | A | Partial.cs:4:17:4:18 | M2 | -| Partial.cs:1:15:1:15 | A | Partial.cs:10:17:10:18 | M3 | -| Partial.cs:7:15:7:15 | A | Partial.cs:3:18:3:18 | M | -| Partial.cs:7:15:7:15 | A | Partial.cs:4:17:4:18 | M2 | -| Partial.cs:7:15:7:15 | A | Partial.cs:10:17:10:18 | M3 | +| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:3:18:3:39 | PartialMethodWithBody1 | +| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | +| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:5:17:5:23 | Method2 | +| Partial.cs:1:15:1:26 | TwoPartClass | Partial.cs:11:17:11:23 | Method3 | +| Partial.cs:8:15:8:26 | TwoPartClass | Partial.cs:3:18:3:39 | PartialMethodWithBody1 | +| Partial.cs:8:15:8:26 | TwoPartClass | Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | +| Partial.cs:8:15:8:26 | TwoPartClass | Partial.cs:5:17:5:23 | Method2 | +| Partial.cs:8:15:8:26 | TwoPartClass | Partial.cs:11:17:11:23 | Method3 | +| Partial.cs:14:15:14:33 | OnePartPartialClass | Partial.cs:16:18:16:42 | PartialMethodWithoutBody2 | +| Partial.cs:14:15:14:33 | OnePartPartialClass | Partial.cs:17:17:17:23 | Method4 | diff --git a/csharp/ql/test/library-tests/partial/PartialMethodBody.expected b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected new file mode 100644 index 000000000000..e6592704f1ae --- /dev/null +++ b/csharp/ql/test/library-tests/partial/PartialMethodBody.expected @@ -0,0 +1,3 @@ +| Partial.cs:3:18:3:39 | PartialMethodWithBody1 | true | +| Partial.cs:4:18:4:42 | PartialMethodWithoutBody1 | false | +| Partial.cs:16:18:16:42 | PartialMethodWithoutBody2 | false | diff --git a/csharp/ql/test/library-tests/partial/PartialMethodBody.ql b/csharp/ql/test/library-tests/partial/PartialMethodBody.ql new file mode 100644 index 000000000000..53cb9be250a7 --- /dev/null +++ b/csharp/ql/test/library-tests/partial/PartialMethodBody.ql @@ -0,0 +1,7 @@ +import csharp + +private boolean hasBody(Method m) { if m.hasBody() then result = true else result = false } + +from Method m +where m.fromSource() and m.isPartial() +select m, hasBody(m) diff --git a/csharp/ql/test/library-tests/partial/PrintAst.expected b/csharp/ql/test/library-tests/partial/PrintAst.expected new file mode 100644 index 000000000000..c250c86c66dd --- /dev/null +++ b/csharp/ql/test/library-tests/partial/PrintAst.expected @@ -0,0 +1,16 @@ +Partial.cs: +# 1| [Class] TwoPartClass +# 3| 5: [Method] PartialMethodWithBody1 +# 10| 4: [BlockStmt] {...} +# 4| 6: [Method] PartialMethodWithoutBody1 +# 5| 7: [Method] Method2 +# 5| 4: [BlockStmt] {...} +# 11| 8: [Method] Method3 +# 11| 4: [BlockStmt] {...} +# 14| [Class] OnePartPartialClass +# 16| 5: [Method] PartialMethodWithoutBody2 +# 17| 6: [Method] Method4 +# 17| 4: [BlockStmt] {...} +# 20| [Class] NonPartialClass +# 22| 5: [Method] Method5 +# 22| 4: [BlockStmt] {...} diff --git a/csharp/ql/test/library-tests/partial/PrintAst.qlref b/csharp/ql/test/library-tests/partial/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/partial/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/properties/PrintAst.expected b/csharp/ql/test/library-tests/properties/PrintAst.expected new file mode 100644 index 000000000000..8d3daf118f52 --- /dev/null +++ b/csharp/ql/test/library-tests/properties/PrintAst.expected @@ -0,0 +1,194 @@ +properties.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [Class] Button +# 10| 5: [Field] caption +# 12| 6: [IndexerProperty] Caption +# 15| 3: [Getter] get_Caption +# 15| 4: [BlockStmt] {...} +# 15| 0: [ReturnStmt] return ...; +# 15| 0: [FieldAccess] access to field caption +# 17| 4: [Setter] set_Caption +#-----| 2: (Parameters) +# 17| 0: [Parameter] value +# 18| 4: [BlockStmt] {...} +# 19| 0: [IfStmt] if (...) ... +# 19| 0: [NEExpr] ... != ... +# 19| 0: [FieldAccess] access to field caption +# 19| 1: [ParameterAccess] access to parameter value +# 20| 1: [BlockStmt] {...} +# 21| 0: [ExprStmt] ...; +# 21| 0: [AssignExpr] ... = ... +# 21| 0: [ParameterAccess] access to parameter value +# 21| 1: [FieldAccess] access to field caption +# 26| 7: [Method] Paint +# 27| 4: [BlockStmt] {...} +# 28| 0: [LocalVariableDeclStmt] ... ...; +# 28| 0: [LocalVariableDeclAndInitExpr] Button okButton = ... +# 28| 0: [ObjectCreation] object creation of type Button +# 28| 1: [LocalVariableAccess] access to local variable okButton +# 29| 1: [ExprStmt] ...; +# 29| 0: [AssignExpr] ... = ... +# 29| 0: [StringLiteral] "OK" +# 29| 1: [PropertyCall] access to property Caption +# 29| -1: [LocalVariableAccess] access to local variable okButton +# 30| 2: [LocalVariableDeclStmt] ... ...; +# 30| 0: [LocalVariableDeclAndInitExpr] String s = ... +# 30| 0: [PropertyCall] access to property Caption +# 30| -1: [LocalVariableAccess] access to local variable okButton +# 30| 1: [LocalVariableAccess] access to local variable s +# 34| 2: [Class] Counter +# 37| 5: [Field] next +# 39| 6: [Property] Next +# 41| 3: [Getter] get_Next +# 41| 4: [BlockStmt] {...} +# 41| 0: [ReturnStmt] return ...; +# 41| 0: [PostIncrExpr] ...++ +# 41| 0: [FieldAccess] access to field next +# 46| 3: [Class] Point +# 49| 5: [Property] X +# 49| 3: [Getter] get_X +# 49| 4: [Setter] set_X +#-----| 2: (Parameters) +# 49| 0: [Parameter] value +# 50| 6: [Property] Y +# 50| 3: [Getter] get_Y +# 50| 4: [Setter] set_Y +#-----| 2: (Parameters) +# 50| 0: [Parameter] value +# 54| 4: [Class] ReadOnlyPoint +# 57| 4: [Property] X +# 57| 3: [Getter] get_X +# 57| 4: [Setter] set_X +#-----| 2: (Parameters) +# 57| 0: [Parameter] value +# 58| 5: [Property] Y +# 58| 3: [Getter] get_Y +# 58| 4: [Setter] set_Y +#-----| 2: (Parameters) +# 58| 0: [Parameter] value +# 59| 6: [InstanceConstructor] ReadOnlyPoint +#-----| 2: (Parameters) +# 59| 0: [Parameter] x +# 59| 1: [Parameter] y +# 60| 4: [BlockStmt] {...} +# 61| 0: [ExprStmt] ...; +# 61| 0: [AssignExpr] ... = ... +# 61| 0: [ParameterAccess] access to parameter x +# 61| 1: [PropertyCall] access to property X +# 62| 1: [ExprStmt] ...; +# 62| 0: [AssignExpr] ... = ... +# 62| 0: [ParameterAccess] access to parameter y +# 62| 1: [PropertyCall] access to property Y +# 67| 5: [Class] A +# 69| 5: [Field] y +# 70| 6: [Property] X +# 70| 3: [Getter] get_X +# 70| 4: [BlockStmt] {...} +# 70| 0: [ReturnStmt] return ...; +# 70| 0: [IntLiteral] 0 +# 71| 7: [Property] Y +# 73| 3: [Getter] get_Y +# 73| 4: [BlockStmt] {...} +# 73| 0: [ReturnStmt] return ...; +# 73| 0: [FieldAccess] access to field y +# 74| 4: [Setter] set_Y +#-----| 2: (Parameters) +# 74| 0: [Parameter] value +# 74| 4: [BlockStmt] {...} +# 74| 0: [ExprStmt] ...; +# 74| 0: [AssignExpr] ... = ... +# 74| 0: [ParameterAccess] access to parameter value +# 74| 1: [FieldAccess] access to field y +# 76| 8: [Property] Z +# 76| 3: [Getter] get_Z +# 76| 4: [Setter] set_Z +#-----| 2: (Parameters) +# 76| 0: [Parameter] value +# 79| 6: [Class] B +#-----| 3: (Base types) +# 79| 0: [Class] A +# 81| 5: [Field] z +# 82| 6: [Property] X +# 82| 3: [Getter] get_X +# 82| 4: [BlockStmt] {...} +# 82| 0: [ReturnStmt] return ...; +# 82| 0: [AddExpr] ... + ... +# 82| 0: [PropertyCall] access to property X +# 82| -1: [BaseAccess] base access +# 82| 1: [IntLiteral] 1 +# 83| 7: [Property] Y +# 83| 3: [Setter] set_Y +#-----| 2: (Parameters) +# 83| 0: [Parameter] value +# 83| 4: [BlockStmt] {...} +# 83| 0: [ExprStmt] ...; +# 83| 0: [AssignExpr] ... = ... +# 83| 0: [ConditionalExpr] ... ? ... : ... +# 83| 0: [LTExpr] ... < ... +# 83| 0: [ParameterAccess] access to parameter value +# 83| 1: [IntLiteral] 0 +# 83| 1: [IntLiteral] 0 +# 83| 2: [ParameterAccess] access to parameter value +# 83| 1: [PropertyCall] access to property Y +# 83| -1: [BaseAccess] base access +# 84| 8: [Property] Z +# 86| 3: [Getter] get_Z +# 86| 4: [BlockStmt] {...} +# 86| 0: [ReturnStmt] return ...; +# 86| 0: [FieldAccess] access to field z +# 87| 4: [Setter] set_Z +#-----| 2: (Parameters) +# 87| 0: [Parameter] value +# 87| 4: [BlockStmt] {...} +# 87| 0: [ExprStmt] ...; +# 87| 0: [AssignExpr] ... = ... +# 87| 0: [ParameterAccess] access to parameter value +# 87| 1: [FieldAccess] access to field z +# 91| 7: [Class] Test +# 93| 5: [Property] Init +# 93| 3: [Setter] set_Init +#-----| 2: (Parameters) +# 93| 0: [Parameter] value +# 93| 4: [BlockStmt] {...} +# 94| 6: [Method] Main +# 95| 4: [BlockStmt] {...} +# 96| 0: [LocalVariableDeclStmt] ... ...; +# 96| 0: [LocalVariableDeclAndInitExpr] List ds = ... +# 96| 0: [ObjectCreation] object creation of type List +# 96| 1: [LocalVariableAccess] access to local variable ds +# 97| 1: [LocalVariableDeclStmt] ... ...; +# 97| 0: [LocalVariableDeclAndInitExpr] List os = ... +# 97| 0: [ObjectCreation] object creation of type List +# 97| 1: [LocalVariableAccess] access to local variable os +# 98| 2: [IfStmt] if (...) ... +# 98| 0: [EQExpr] ... == ... +# 98| 0: [PropertyCall] access to property Count +# 98| -1: [LocalVariableAccess] access to local variable ds +# 98| 1: [PropertyCall] access to property Count +# 98| -1: [LocalVariableAccess] access to local variable os +# 99| 1: [BlockStmt] {...} +# 104| 8: [Interface] InterfaceWithProperties +# 106| 4: [Property] Prop1 +# 106| 3: [Getter] get_Prop1 +# 107| 5: [Property] Prop2 +# 107| 3: [Setter] set_Prop2 +#-----| 2: (Parameters) +# 107| 0: [Parameter] value +# 110| 9: [Class] ImplementsProperties +#-----| 3: (Base types) +# 110| 1: [Interface] InterfaceWithProperties +# 112| 5: [Property] Prop1 +# 114| 3: [Getter] get_Prop1 +# 114| 4: [BlockStmt] {...} +# 114| 0: [ReturnStmt] return ...; +# 114| 0: [IntLiteral] 0 +# 117| 6: [Property] Prop2 +# 119| 3: [Setter] set_Prop2 +#-----| 2: (Parameters) +# 119| 0: [Parameter] value +# 119| 4: [BlockStmt] {...} +# 122| 7: [Property] Prop2 +# 124| 3: [Setter] set_Prop2 +#-----| 2: (Parameters) +# 124| 0: [Parameter] value +# 124| 4: [BlockStmt] {...} diff --git a/csharp/ql/test/library-tests/properties/PrintAst.qlref b/csharp/ql/test/library-tests/properties/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/properties/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/regressions/Program.cs b/csharp/ql/test/library-tests/regressions/Program.cs index e167ef6d7d98..b1a68ae486f3 100644 --- a/csharp/ql/test/library-tests/regressions/Program.cs +++ b/csharp/ql/test/library-tests/regressions/Program.cs @@ -87,7 +87,7 @@ class WhileIs void Test() { object x = null; - while(x is string s) + while (x is string s) { var y = s; } @@ -116,7 +116,7 @@ struct Point void F() { - new Point { x=1, y=2 }; + new Point { x = 1, y = 2 }; } } @@ -130,20 +130,20 @@ void F() class LocalVariableTags { - Func F = x => { int y=x; return y; }; + Func F = x => { int y = x; return y; }; private static Func _getter => (o, n) => { - object x = o; - return x; + object x = o; + return x; }; } -partial class C1 where T: DynamicType +partial class C1 where T : DynamicType { } -partial class C1 where T: DynamicType +partial class C1 where T : DynamicType { } @@ -173,8 +173,8 @@ class UsingDiscard { void F() { - foreach(var _ in new IDisposable[] { }) - using(_) + foreach (var _ in new IDisposable[] { }) + using (_) { } } @@ -189,4 +189,12 @@ class TupleMatching } } +class C2 { } + +class C3 : C2> { } + +class C4 : C2> { } + +class C5 : C4 { } + // semmle-extractor-options: /r:System.Dynamic.Runtime.dll diff --git a/csharp/ql/test/library-tests/regressions/TypeMentions.expected b/csharp/ql/test/library-tests/regressions/TypeMentions.expected index a5628fb53dbe..78bc230e7484 100644 --- a/csharp/ql/test/library-tests/regressions/TypeMentions.expected +++ b/csharp/ql/test/library-tests/regressions/TypeMentions.expected @@ -41,7 +41,7 @@ | Program.cs:75:5:75:7 | Int32 | | Program.cs:87:5:87:8 | Void | | Program.cs:89:9:89:14 | Object | -| Program.cs:90:20:90:25 | String | +| Program.cs:90:21:90:26 | String | | Program.cs:92:13:92:15 | String | | Program.cs:101:16:101:21 | Object | | Program.cs:104:5:104:8 | Void | @@ -63,11 +63,11 @@ | Program.cs:135:25:135:30 | Object | | Program.cs:135:33:135:38 | String | | Program.cs:135:41:135:46 | Object | -| Program.cs:137:10:137:15 | Object | +| Program.cs:137:9:137:14 | Object | | Program.cs:142:27:142:27 | T | -| Program.cs:142:30:142:40 | DynamicType | +| Program.cs:142:31:142:41 | DynamicType | | Program.cs:146:27:146:27 | T | -| Program.cs:146:30:146:40 | DynamicType | +| Program.cs:146:31:146:41 | DynamicType | | Program.cs:156:15:156:35 | TEmbeddedTypesManager | | Program.cs:156:39:156:58 | EmbeddedTypesManager<,> | | Program.cs:156:60:156:80 | TEmbeddedTypesManager | @@ -81,7 +81,7 @@ | Program.cs:164:5:164:12 | Int32*[][] | | Program.cs:169:5:169:10 | String | | Program.cs:174:5:174:8 | Void | -| Program.cs:176:17:176:19 | IDisposable | +| Program.cs:176:18:176:20 | IDisposable | | Program.cs:185:5:185:17 | (Int32,Object) | | Program.cs:185:6:185:8 | Int32 | | Program.cs:185:11:185:16 | Object | @@ -91,3 +91,11 @@ | Program.cs:187:9:187:25 | Nullable<(Object,Object)> | | Program.cs:187:10:187:15 | Object | | Program.cs:187:18:187:23 | Object | +| Program.cs:194:15:194:16 | C2> | +| Program.cs:194:18:194:19 | C4 | +| Program.cs:194:21:194:21 | T | +| Program.cs:196:15:196:16 | C2> | +| Program.cs:196:18:196:19 | C3 | +| Program.cs:196:21:196:21 | T | +| Program.cs:198:12:198:13 | C4 | +| Program.cs:198:15:198:16 | C5 | diff --git a/csharp/ql/test/library-tests/statements/PrintAst.expected b/csharp/ql/test/library-tests/statements/PrintAst.expected new file mode 100644 index 000000000000..11eac4e2c280 --- /dev/null +++ b/csharp/ql/test/library-tests/statements/PrintAst.expected @@ -0,0 +1,579 @@ +fixed.cs: +# 3| [Class] Fixed +# 5| 5: [Method] fixed1 +# 6| 4: [BlockStmt] {...} +# 7| 0: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclAndInitExpr] Byte[] buffer = ... +# 7| 0: [ArrayCreation] array creation of type Byte[] +# 7| 0: [IntLiteral] 10 +# 7| 1: [LocalVariableAccess] access to local variable buffer +# 9| 1: [FixedStmt] fixed(...) { ... } +# 9| -1: [LocalVariableDeclAndInitExpr] Byte* pinned_buffer = ... +# 9| 0: [AddressOfExpr] &... +# 9| 0: [ArrayAccess] access to array element +# 9| -1: [LocalVariableAccess] access to local variable buffer +# 9| 0: [IntLiteral] 0 +# 9| 1: [LocalVariableAccess] access to local variable pinned_buffer +# 10| 0: [BlockStmt] {...} +# 11| 0: [LocalVariableDeclStmt] ... ...; +# 11| 0: [LocalVariableDeclAndInitExpr] Byte* t = ... +# 11| 0: [LocalVariableAccess] access to local variable pinned_buffer +# 11| 1: [LocalVariableAccess] access to local variable t +# 12| 1: [ExprStmt] ...; +# 12| 0: [MethodCall] call to method fixed1 +# 15| 2: [FixedStmt] fixed(...) { ... } +# 15| -1: [LocalVariableDeclAndInitExpr] Byte* pinned_buffer = ... +# 15| 0: [AddressOfExpr] &... +# 15| 0: [ArrayAccess] access to array element +# 15| -1: [LocalVariableAccess] access to local variable buffer +# 15| 0: [IntLiteral] 0 +# 15| 1: [LocalVariableAccess] access to local variable pinned_buffer +# 16| 0: [BlockStmt] {...} +# 19| 3: [FixedStmt] fixed(...) { ... } +# 19| -1: [LocalVariableDeclAndInitExpr] Byte* pinned_buffer = ... +# 19| 0: [AddressOfExpr] &... +# 19| 0: [ArrayAccess] access to array element +# 19| -1: [LocalVariableAccess] access to local variable buffer +# 19| 0: [IntLiteral] 0 +# 19| 1: [LocalVariableAccess] access to local variable pinned_buffer +# 19| 0: [EmptyStmt] ; +statements.cs: +# 5| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [Class] Class +# 10| 5: [Method] Main +# 11| 4: [BlockStmt] {...} +# 12| 0: [LabelStmt] block: +# 13| 1: [BlockStmt] {...} +# 14| 0: [BlockStmt] {...} +# 16| 1: [BlockStmt] {...} +# 17| 0: [BlockStmt] {...} +# 23| 6: [Method] MainEmpty +# 24| 4: [BlockStmt] {...} +# 25| 0: [LocalVariableDeclStmt] ... ...; +# 25| 0: [LocalVariableDeclAndInitExpr] Class c = ... +# 25| 0: [ObjectCreation] object creation of type Class +# 25| 1: [LocalVariableAccess] access to local variable c +# 26| 1: [EmptyStmt] ; +# 26| 2: [EmptyStmt] ; +# 26| 3: [EmptyStmt] ; +# 27| 4: [IfStmt] if (...) ... +# 27| 0: [BoolLiteral] true +# 27| 1: [EmptyStmt] ; +# 30| 7: [Method] MainLocalVarDecl +# 31| 4: [BlockStmt] {...} +# 32| 0: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclExpr] Int32 a +# 33| 1: [LocalVariableDeclStmt] ... ...; +# 33| 0: [LocalVariableDeclAndInitExpr] Int32 b = ... +# 33| 0: [IntLiteral] 2 +# 33| 1: [LocalVariableAccess] access to local variable b +# 33| 1: [LocalVariableDeclAndInitExpr] Int32 c = ... +# 33| 0: [IntLiteral] 3 +# 33| 1: [LocalVariableAccess] access to local variable c +# 34| 2: [ExprStmt] ...; +# 34| 0: [AssignExpr] ... = ... +# 34| 0: [IntLiteral] 1 +# 34| 1: [LocalVariableAccess] access to local variable a +# 35| 3: [ExprStmt] ...; +# 35| 0: [MethodCall] call to method WriteLine +# 35| -1: [TypeAccess] access to type Console +# 35| 0: [AddExpr] ... + ... +# 35| 0: [AddExpr] ... + ... +# 35| 0: [LocalVariableAccess] access to local variable a +# 35| 1: [LocalVariableAccess] access to local variable b +# 35| 1: [LocalVariableAccess] access to local variable c +# 36| 4: [LocalVariableDeclStmt] ... ...; +# 36| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 36| 0: [IntLiteral] 45 +# 36| 1: [LocalVariableAccess] access to local variable x +# 37| 5: [LocalVariableDeclStmt] ... ...; +# 37| 0: [LocalVariableDeclAndInitExpr] String y = ... +# 37| 0: [StringLiteral] "test" +# 37| 1: [LocalVariableAccess] access to local variable y +# 40| 8: [Method] MainLocalConstDecl +# 41| 4: [BlockStmt] {...} +# 42| 0: [LocalConstantDeclStmt] const ... ...; +# 42| 0: [LocalVariableDeclAndInitExpr] Single pi = ... +# 42| 0: [FloatLiteral] 3.1415927 +# 42| 1: [LocalVariableAccess] access to local variable pi +# 43| 1: [LocalConstantDeclStmt] const ... ...; +# 43| 0: [LocalVariableDeclAndInitExpr] Int32 r = ... +# 43| 0: [AddExpr] ... + ... +# 43| 0: [IntLiteral] 5 +# 43| 1: [IntLiteral] 20 +# 43| 1: [LocalVariableAccess] access to local variable r +# 44| 2: [ExprStmt] ...; +# 44| 0: [MethodCall] call to method WriteLine +# 44| -1: [TypeAccess] access to type Console +# 44| 0: [MulExpr] ... * ... +# 44| 0: [MulExpr] ... * ... +# 44| 0: [LocalVariableAccess] access to local variable pi +# 44| 1: [CastExpr] (...) ... +# 44| 0: [LocalVariableAccess] access to local variable r +# 44| 1: [CastExpr] (...) ... +# 44| 0: [LocalVariableAccess] access to local variable r +# 47| 9: [Method] MainExpr +# 48| 4: [BlockStmt] {...} +# 49| 0: [LocalVariableDeclStmt] ... ...; +# 49| 0: [LocalVariableDeclExpr] Int32 i +# 50| 1: [ExprStmt] ...; +# 50| 0: [AssignExpr] ... = ... +# 50| 0: [IntLiteral] 123 +# 50| 1: [LocalVariableAccess] access to local variable i +# 51| 2: [ExprStmt] ...; +# 51| 0: [MethodCall] call to method WriteLine +# 51| -1: [TypeAccess] access to type Console +# 51| 0: [LocalVariableAccess] access to local variable i +# 52| 3: [ExprStmt] ...; +# 52| 0: [PostIncrExpr] ...++ +# 52| 0: [LocalVariableAccess] access to local variable i +# 53| 4: [ExprStmt] ...; +# 53| 0: [MethodCall] call to method WriteLine +# 53| -1: [TypeAccess] access to type Console +# 53| 0: [LocalVariableAccess] access to local variable i +# 56| 10: [Method] MainIf +#-----| 2: (Parameters) +# 56| 0: [Parameter] args +# 57| 4: [BlockStmt] {...} +# 58| 0: [IfStmt] if (...) ... +# 58| 0: [EQExpr] ... == ... +# 58| 0: [PropertyCall] access to property Length +# 58| -1: [ParameterAccess] access to parameter args +# 58| 1: [IntLiteral] 0 +# 59| 1: [BlockStmt] {...} +# 60| 0: [ExprStmt] ...; +# 60| 0: [MethodCall] call to method WriteLine +# 60| -1: [TypeAccess] access to type Console +# 60| 0: [StringLiteral] "No arguments" +# 63| 2: [BlockStmt] {...} +# 64| 0: [ExprStmt] ...; +# 64| 0: [MethodCall] call to method WriteLine +# 64| -1: [TypeAccess] access to type Console +# 64| 0: [StringLiteral] "One or more arguments" +# 69| 11: [Method] MainSwitch +#-----| 2: (Parameters) +# 69| 0: [Parameter] args +# 70| 4: [BlockStmt] {...} +# 71| 0: [LocalVariableDeclStmt] ... ...; +# 71| 0: [LocalVariableDeclAndInitExpr] Int32 n = ... +# 71| 0: [PropertyCall] access to property Length +# 71| -1: [ParameterAccess] access to parameter args +# 71| 1: [LocalVariableAccess] access to local variable n +# 72| 1: [SwitchStmt] switch (...) {...} +# 72| 0: [LocalVariableAccess] access to local variable n +# 74| 0: [ConstCase] case ...: +# 74| 0: [ConstantPatternExpr,IntLiteral] 0 +# 75| 1: [ExprStmt] ...; +# 75| 0: [MethodCall] call to method WriteLine +# 75| -1: [TypeAccess] access to type Console +# 75| 0: [StringLiteral] "No arguments" +# 76| 2: [BreakStmt] break; +# 77| 3: [ConstCase] case ...: +# 77| 0: [ConstantPatternExpr,IntLiteral] 1 +# 78| 4: [ExprStmt] ...; +# 78| 0: [MethodCall] call to method WriteLine +# 78| -1: [TypeAccess] access to type Console +# 78| 0: [StringLiteral] "One argument" +# 79| 5: [BreakStmt] break; +# 80| 6: [DefaultCase] default: +# 81| 7: [ExprStmt] ...; +# 81| 0: [MethodCall] call to method WriteLine +# 81| -1: [TypeAccess] access to type Console +# 81| 0: [StringLiteral] "{0} arguments" +# 81| 1: [CastExpr] (...) ... +# 81| 0: [LocalVariableAccess] access to local variable n +# 82| 8: [BreakStmt] break; +# 86| 12: [Method] StringSwitch +#-----| 2: (Parameters) +# 86| 0: [Parameter] foo +# 87| 4: [BlockStmt] {...} +# 88| 0: [SwitchStmt] switch (...) {...} +# 88| 0: [ParameterAccess] access to parameter foo +# 90| 0: [ConstCase] case ...: +# 90| 0: [ConstantPatternExpr,StringLiteral] "black" +# 91| 1: [ReturnStmt] return ...; +# 91| 0: [IntLiteral] 0 +# 92| 2: [ConstCase] case ...: +# 92| 0: [ConstantPatternExpr,StringLiteral] "red" +# 93| 3: [ReturnStmt] return ...; +# 93| 0: [IntLiteral] 1 +# 94| 4: [ConstCase] case ...: +# 94| 0: [ConstantPatternExpr,StringLiteral] "green" +# 95| 5: [ReturnStmt] return ...; +# 95| 0: [IntLiteral] 2 +# 96| 6: [ConstCase] case ...: +# 96| 0: [ConstantPatternExpr,StringLiteral] "yellow" +# 97| 7: [ReturnStmt] return ...; +# 97| 0: [IntLiteral] 3 +# 98| 8: [ConstCase] case ...: +# 98| 0: [ConstantPatternExpr,StringLiteral] "blue" +# 99| 9: [ReturnStmt] return ...; +# 99| 0: [IntLiteral] 4 +# 100| 10: [ConstCase] case ...: +# 100| 0: [ConstantPatternExpr,StringLiteral] "magenta" +# 101| 11: [ReturnStmt] return ...; +# 101| 0: [IntLiteral] 5 +# 102| 12: [ConstCase] case ...: +# 102| 0: [ConstantPatternExpr,StringLiteral] "cyan" +# 103| 13: [ReturnStmt] return ...; +# 103| 0: [IntLiteral] 6 +# 104| 14: [ConstCase] case ...: +# 104| 0: [ConstantPatternExpr,StringLiteral] "grey" +# 105| 15: [ConstCase] case ...: +# 105| 0: [ConstantPatternExpr,StringLiteral] "white" +# 106| 16: [ReturnStmt] return ...; +# 106| 0: [IntLiteral] 7 +# 108| 1: [ReturnStmt] return ...; +# 108| 0: [IntLiteral] 7 +# 111| 13: [Method] MainWhile +#-----| 2: (Parameters) +# 111| 0: [Parameter] args +# 112| 4: [BlockStmt] {...} +# 113| 0: [LocalVariableDeclStmt] ... ...; +# 113| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 113| 0: [IntLiteral] 0 +# 113| 1: [LocalVariableAccess] access to local variable i +# 114| 1: [WhileStmt] while (...) ... +# 114| 0: [LTExpr] ... < ... +# 114| 0: [LocalVariableAccess] access to local variable i +# 114| 1: [PropertyCall] access to property Length +# 114| -1: [ParameterAccess] access to parameter args +# 115| 1: [BlockStmt] {...} +# 116| 0: [ExprStmt] ...; +# 116| 0: [MethodCall] call to method WriteLine +# 116| -1: [TypeAccess] access to type Console +# 116| 0: [ArrayAccess] access to array element +# 116| -1: [ParameterAccess] access to parameter args +# 116| 0: [LocalVariableAccess] access to local variable i +# 117| 1: [ExprStmt] ...; +# 117| 0: [PostIncrExpr] ...++ +# 117| 0: [LocalVariableAccess] access to local variable i +# 121| 14: [Method] MainDo +# 122| 4: [BlockStmt] {...} +# 123| 0: [LocalVariableDeclStmt] ... ...; +# 123| 0: [LocalVariableDeclExpr] String s +# 124| 1: [DoStmt] do ... while (...); +# 128| 0: [NEExpr] ... != ... +# 128| 0: [LocalVariableAccess] access to local variable s +# 128| 1: [NullLiteral] null +# 125| 1: [BlockStmt] {...} +# 126| 0: [ExprStmt] ...; +# 126| 0: [AssignExpr] ... = ... +# 126| 0: [MethodCall] call to method ReadLine +# 126| -1: [TypeAccess] access to type Console +# 126| 1: [LocalVariableAccess] access to local variable s +# 127| 1: [IfStmt] if (...) ... +# 127| 0: [NEExpr] ... != ... +# 127| 0: [LocalVariableAccess] access to local variable s +# 127| 1: [NullLiteral] null +# 127| 1: [ExprStmt] ...; +# 127| 0: [MethodCall] call to method WriteLine +# 127| -1: [TypeAccess] access to type Console +# 127| 0: [LocalVariableAccess] access to local variable s +# 131| 15: [Method] MainFor +#-----| 2: (Parameters) +# 131| 0: [Parameter] args +# 132| 4: [BlockStmt] {...} +# 133| 0: [ForStmt] for (...;...;...) ... +# 133| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 133| 0: [IntLiteral] 0 +# 133| 1: [LocalVariableAccess] access to local variable i +# 133| 0: [LTExpr] ... < ... +# 133| 0: [LocalVariableAccess] access to local variable i +# 133| 1: [PropertyCall] access to property Length +# 133| -1: [ParameterAccess] access to parameter args +# 133| 1: [PostIncrExpr] ...++ +# 133| 0: [LocalVariableAccess] access to local variable i +# 134| 2: [BlockStmt] {...} +# 135| 0: [ExprStmt] ...; +# 135| 0: [MethodCall] call to method WriteLine +# 135| -1: [TypeAccess] access to type Console +# 135| 0: [ArrayAccess] access to array element +# 135| -1: [ParameterAccess] access to parameter args +# 135| 0: [LocalVariableAccess] access to local variable i +# 140| 16: [Method] MainForeach +#-----| 2: (Parameters) +# 140| 0: [Parameter] args +# 141| 4: [BlockStmt] {...} +# 142| 0: [ForeachStmt] foreach (... ... in ...) ... +# 142| 0: [LocalVariableDeclExpr] String s +# 142| 1: [ParameterAccess] access to parameter args +# 143| 2: [BlockStmt] {...} +# 144| 0: [ExprStmt] ...; +# 144| 0: [MethodCall] call to method WriteLine +# 144| -1: [TypeAccess] access to type Console +# 144| 0: [LocalVariableAccess] access to local variable s +# 148| 17: [Method] MainBreak +# 149| 4: [BlockStmt] {...} +# 150| 0: [WhileStmt] while (...) ... +# 150| 0: [BoolLiteral] true +# 151| 1: [BlockStmt] {...} +# 152| 0: [LocalVariableDeclStmt] ... ...; +# 152| 0: [LocalVariableDeclAndInitExpr] String s = ... +# 152| 0: [MethodCall] call to method ReadLine +# 152| -1: [TypeAccess] access to type Console +# 152| 1: [LocalVariableAccess] access to local variable s +# 153| 1: [IfStmt] if (...) ... +# 153| 0: [EQExpr] ... == ... +# 153| 0: [LocalVariableAccess] access to local variable s +# 153| 1: [NullLiteral] null +# 153| 1: [BreakStmt] break; +# 154| 2: [ExprStmt] ...; +# 154| 0: [MethodCall] call to method WriteLine +# 154| -1: [TypeAccess] access to type Console +# 154| 0: [LocalVariableAccess] access to local variable s +# 158| 18: [Method] MainContinue +#-----| 2: (Parameters) +# 158| 0: [Parameter] args +# 159| 4: [BlockStmt] {...} +# 160| 0: [ForStmt] for (...;...;...) ... +# 160| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 160| 0: [IntLiteral] 0 +# 160| 1: [LocalVariableAccess] access to local variable i +# 160| 0: [LTExpr] ... < ... +# 160| 0: [LocalVariableAccess] access to local variable i +# 160| 1: [PropertyCall] access to property Length +# 160| -1: [ParameterAccess] access to parameter args +# 160| 1: [PostIncrExpr] ...++ +# 160| 0: [LocalVariableAccess] access to local variable i +# 161| 2: [BlockStmt] {...} +# 162| 0: [IfStmt] if (...) ... +# 162| 0: [MethodCall] call to method StartsWith +# 162| -1: [ArrayAccess] access to array element +# 162| -1: [ParameterAccess] access to parameter args +# 162| 0: [LocalVariableAccess] access to local variable i +# 162| 0: [StringLiteral] "/" +# 162| 1: [ContinueStmt] continue; +# 163| 1: [ExprStmt] ...; +# 163| 0: [MethodCall] call to method WriteLine +# 163| -1: [TypeAccess] access to type Console +# 163| 0: [ArrayAccess] access to array element +# 163| -1: [ParameterAccess] access to parameter args +# 163| 0: [LocalVariableAccess] access to local variable i +# 167| 19: [Method] MainGoto +#-----| 2: (Parameters) +# 167| 0: [Parameter] args +# 168| 4: [BlockStmt] {...} +# 169| 0: [LocalVariableDeclStmt] ... ...; +# 169| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 169| 0: [IntLiteral] 0 +# 169| 1: [LocalVariableAccess] access to local variable i +# 170| 1: [GotoLabelStmt] goto ...; +# 171| 2: [LabelStmt] loop: +# 171| 3: [ExprStmt] ...; +# 171| 0: [MethodCall] call to method WriteLine +# 171| -1: [TypeAccess] access to type Console +# 171| 0: [ArrayAccess] access to array element +# 171| -1: [ParameterAccess] access to parameter args +# 171| 0: [PostIncrExpr] ...++ +# 171| 0: [LocalVariableAccess] access to local variable i +# 172| 4: [LabelStmt] check: +# 172| 5: [IfStmt] if (...) ... +# 172| 0: [LTExpr] ... < ... +# 172| 0: [LocalVariableAccess] access to local variable i +# 172| 1: [PropertyCall] access to property Length +# 172| -1: [ParameterAccess] access to parameter args +# 172| 1: [GotoLabelStmt] goto ...; +# 175| 20: [Method] Add +#-----| 2: (Parameters) +# 175| 0: [Parameter] a +# 175| 1: [Parameter] b +# 176| 4: [BlockStmt] {...} +# 177| 0: [ReturnStmt] return ...; +# 177| 0: [AddExpr] ... + ... +# 177| 0: [ParameterAccess] access to parameter a +# 177| 1: [ParameterAccess] access to parameter b +# 179| 21: [Method] MainReturn +# 180| 4: [BlockStmt] {...} +# 181| 0: [ExprStmt] ...; +# 181| 0: [MethodCall] call to method WriteLine +# 181| -1: [TypeAccess] access to type Console +# 181| 0: [MethodCall] call to method Add +# 181| 0: [IntLiteral] 1 +# 181| 1: [IntLiteral] 2 +# 182| 1: [ReturnStmt] return ...; +# 185| 22: [Method] Range +#-----| 2: (Parameters) +# 185| 0: [Parameter] from +# 185| 1: [Parameter] to +# 186| 4: [BlockStmt] {...} +# 187| 0: [ForStmt] for (...;...;...) ... +# 187| -1: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 187| 0: [ParameterAccess] access to parameter from +# 187| 1: [LocalVariableAccess] access to local variable i +# 187| 0: [LTExpr] ... < ... +# 187| 0: [LocalVariableAccess] access to local variable i +# 187| 1: [ParameterAccess] access to parameter to +# 187| 1: [PostIncrExpr] ...++ +# 187| 0: [LocalVariableAccess] access to local variable i +# 188| 2: [BlockStmt] {...} +# 189| 0: [YieldReturnStmt] yield return ...; +# 189| 0: [LocalVariableAccess] access to local variable i +# 191| 1: [YieldBreakStmt] yield break; +# 193| 23: [Method] MainYield +# 194| 4: [BlockStmt] {...} +# 195| 0: [ForeachStmt] foreach (... ... in ...) ... +# 195| 0: [LocalVariableDeclExpr] Int32 x +# 195| 1: [MethodCall] call to method Range +# 195| 0: [UnaryMinusExpr] -... +# 195| 0: [IntLiteral] 10 +# 195| 1: [IntLiteral] 10 +# 196| 2: [BlockStmt] {...} +# 197| 0: [ExprStmt] ...; +# 197| 0: [MethodCall] call to method WriteLine +# 197| -1: [TypeAccess] access to type Console +# 197| 0: [LocalVariableAccess] access to local variable x +# 201| 24: [Method] Divide +#-----| 2: (Parameters) +# 201| 0: [Parameter] x +# 201| 1: [Parameter] y +# 202| 4: [BlockStmt] {...} +# 203| 0: [IfStmt] if (...) ... +# 203| 0: [EQExpr] ... == ... +# 203| 0: [ParameterAccess] access to parameter y +# 203| 1: [CastExpr] (...) ... +# 203| 0: [IntLiteral] 0 +# 203| 1: [ThrowStmt] throw ...; +# 203| 0: [ObjectCreation] object creation of type DivideByZeroException +# 204| 1: [ReturnStmt] return ...; +# 204| 0: [DivExpr] ... / ... +# 204| 0: [ParameterAccess] access to parameter x +# 204| 1: [ParameterAccess] access to parameter y +# 206| 25: [Method] MainTryThrow +#-----| 2: (Parameters) +# 206| 0: [Parameter] args +# 207| 4: [BlockStmt] {...} +# 208| 0: [TryStmt] try {...} ... +# 227| -1: [BlockStmt] {...} +# 228| 0: [ExprStmt] ...; +# 228| 0: [MethodCall] call to method WriteLine +# 228| -1: [TypeAccess] access to type Console +# 228| 0: [StringLiteral] "Good bye!" +# 209| 0: [BlockStmt] {...} +# 210| 0: [IfStmt] if (...) ... +# 210| 0: [NEExpr] ... != ... +# 210| 0: [PropertyCall] access to property Length +# 210| -1: [ParameterAccess] access to parameter args +# 210| 1: [IntLiteral] 2 +# 211| 1: [BlockStmt] {...} +# 212| 0: [ThrowStmt] throw ...; +# 212| 0: [ObjectCreation] object creation of type Exception +# 212| 0: [StringLiteral] "Two numbers required" +# 214| 1: [LocalVariableDeclStmt] ... ...; +# 214| 0: [LocalVariableDeclAndInitExpr] Double x = ... +# 214| 0: [MethodCall] call to method Parse +# 214| -1: [TypeAccess] access to type Double +# 214| 0: [ArrayAccess] access to array element +# 214| -1: [ParameterAccess] access to parameter args +# 214| 0: [IntLiteral] 0 +# 214| 1: [LocalVariableAccess] access to local variable x +# 215| 2: [LocalVariableDeclStmt] ... ...; +# 215| 0: [LocalVariableDeclAndInitExpr] Double y = ... +# 215| 0: [MethodCall] call to method Parse +# 215| -1: [TypeAccess] access to type Double +# 215| 0: [ArrayAccess] access to array element +# 215| -1: [ParameterAccess] access to parameter args +# 215| 0: [IntLiteral] 1 +# 215| 1: [LocalVariableAccess] access to local variable y +# 216| 3: [ExprStmt] ...; +# 216| 0: [MethodCall] call to method WriteLine +# 216| -1: [TypeAccess] access to type Console +# 216| 0: [MethodCall] call to method Divide +# 216| 0: [LocalVariableAccess] access to local variable x +# 216| 1: [LocalVariableAccess] access to local variable y +# 218| 1: [SpecificCatchClause] catch (...) {...} +# 218| 0: [LocalVariableDeclExpr] Exception e +# 219| 1: [BlockStmt] {...} +# 220| 0: [ExprStmt] ...; +# 220| 0: [MethodCall] call to method WriteLine +# 220| -1: [TypeAccess] access to type Console +# 220| 0: [PropertyCall] access to property Message +# 220| -1: [LocalVariableAccess] access to local variable e +# 222| 2: [GeneralCatchClause] catch {...} +# 223| 1: [BlockStmt] {...} +# 224| 0: [ExprStmt] ...; +# 224| 0: [MethodCall] call to method WriteLine +# 224| -1: [TypeAccess] access to type Console +# 224| 0: [StringLiteral] "Exception" +# 232| 26: [Method] MainCheckedUnchecked +# 233| 4: [BlockStmt] {...} +# 234| 0: [LocalVariableDeclStmt] ... ...; +# 234| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 234| 0: [MemberConstantAccess] access to constant MaxValue +# 234| -1: [TypeAccess] access to type Int32 +# 234| 1: [LocalVariableAccess] access to local variable i +# 235| 1: [CheckedStmt] checked {...} +# 236| 0: [BlockStmt] {...} +# 237| 0: [ExprStmt] ...; +# 237| 0: [MethodCall] call to method WriteLine +# 237| -1: [TypeAccess] access to type Console +# 237| 0: [AddExpr] ... + ... +# 237| 0: [LocalVariableAccess] access to local variable i +# 237| 1: [IntLiteral] 1 +# 239| 2: [UncheckedStmt] unchecked {...} +# 240| 0: [BlockStmt] {...} +# 241| 0: [ExprStmt] ...; +# 241| 0: [MethodCall] call to method WriteLine +# 241| -1: [TypeAccess] access to type Console +# 241| 0: [AddExpr] ... + ... +# 241| 0: [LocalVariableAccess] access to local variable i +# 241| 1: [IntLiteral] 1 +# 245| 27: [Class] AccountLock +# 247| 5: [Field] balance +# 248| 6: [Method] Withdraw +#-----| 2: (Parameters) +# 248| 0: [Parameter] amount +# 249| 4: [BlockStmt] {...} +# 250| 0: [LockStmt] lock (...) {...} +# 250| 0: [ThisAccess] this access +# 251| 1: [BlockStmt] {...} +# 252| 0: [IfStmt] if (...) ... +# 252| 0: [GTExpr] ... > ... +# 252| 0: [ParameterAccess] access to parameter amount +# 252| 1: [FieldAccess] access to field balance +# 253| 1: [BlockStmt] {...} +# 254| 0: [ThrowStmt] throw ...; +# 254| 0: [ObjectCreation] object creation of type Exception +# 254| 0: [StringLiteral] "Insufficient funds" +# 256| 1: [ExprStmt] ...; +# 256| 0: [AssignSubExpr] ... -= ... +# 256| 0: [ParameterAccess] access to parameter amount +# 256| 1: [FieldAccess] access to field balance +# 261| 28: [Method] MainUsing +# 262| 4: [BlockStmt] {...} +# 263| 0: [UsingBlockStmt] using (...) {...} +# 263| -1: [LocalVariableDeclAndInitExpr] TextWriter w = ... +# 263| 0: [MethodCall] call to method CreateText +# 263| -1: [TypeAccess] access to type File +# 263| 0: [StringLiteral] "test.txt" +# 263| 1: [LocalVariableAccess] access to local variable w +# 264| 1: [BlockStmt] {...} +# 265| 0: [ExprStmt] ...; +# 265| 0: [MethodCall] call to method WriteLine +# 265| -1: [LocalVariableAccess] access to local variable w +# 265| 0: [StringLiteral] "Line one" +# 266| 1: [ExprStmt] ...; +# 266| 0: [MethodCall] call to method WriteLine +# 266| -1: [LocalVariableAccess] access to local variable w +# 266| 0: [StringLiteral] "Line two" +# 267| 2: [ExprStmt] ...; +# 267| 0: [MethodCall] call to method WriteLine +# 267| -1: [LocalVariableAccess] access to local variable w +# 267| 0: [StringLiteral] "Line three" +# 269| 1: [UsingBlockStmt] using (...) {...} +# 269| 0: [MethodCall] call to method CreateText +# 269| -1: [TypeAccess] access to type File +# 269| 0: [StringLiteral] "test.txt" +# 270| 1: [BlockStmt] {...} +# 274| 29: [Method] MainLabeled +# 275| 4: [BlockStmt] {...} +# 276| 0: [GotoLabelStmt] goto ...; +# 277| 1: [LabelStmt] Label: +# 278| 2: [LocalVariableDeclStmt] ... ...; +# 278| 0: [LocalVariableDeclAndInitExpr] Int32 x = ... +# 278| 0: [IntLiteral] 23 +# 278| 1: [LocalVariableAccess] access to local variable x +# 279| 3: [ExprStmt] ...; +# 279| 0: [AssignExpr] ... = ... +# 279| 0: [IntLiteral] 9 +# 279| 1: [LocalVariableAccess] access to local variable x diff --git a/csharp/ql/test/library-tests/statements/PrintAst.qlref b/csharp/ql/test/library-tests/statements/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/statements/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/types/PrintAst.expected b/csharp/ql/test/library-tests/types/PrintAst.expected new file mode 100644 index 000000000000..db6915ac5559 --- /dev/null +++ b/csharp/ql/test/library-tests/types/PrintAst.expected @@ -0,0 +1,48 @@ +types.cs: +# 1| [NamespaceDeclaration] namespace ... { ... } +# 3| 1: [Class] Class +# 5| 5: [Method] BoolType +# 6| 6: [Method] CharType +# 7| 7: [Method] DecimalType +# 8| 8: [Method] SByteType +# 9| 9: [Method] ShortType +# 10| 10: [Method] IntType +# 11| 11: [Method] LongType +# 12| 12: [Method] ByteType +# 13| 13: [Method] UShortType +# 14| 14: [Method] UIntType +# 15| 15: [Method] ULongType +# 16| 16: [Method] FloatType +# 17| 17: [Method] DoubleType +# 18| 18: [Method] NullableType +# 19| 19: [Method] VoidType +# 20| 20: [Method] ArrayType +# 21| 21: [Method] ArrayArrayType +# 22| 22: [Method] ConstructedClassType +# 23| 23: [Method] ConstructedInterfaceType +# 24| 24: [Method] ConstructedStructType +# 25| 25: [Method] DelegateType +# 26| 26: [Method] PointerType +# 27| 27: [Method] PointerPointerType +# 28| 28: [Method] Map +# 29| 29: [Method] Null +# 30| 4: [BlockStmt] {...} +# 31| 0: [ReturnStmt] return ...; +# 31| 0: [NullLiteral] null +# 34| 2: [Enum] Enum +# 37| 3: [Struct] Struct +# 40| 4: [Interface] Interface +# 43| 5: [DelegateType] Delegate +# 44| 6: [Class] GenericClass<> +#-----| 1: (Type parameters) +# 44| 0: [TypeParameter] T +# 45| 7: [Interface] GenericInterface<> +#-----| 1: (Type parameters) +# 45| 0: [TypeParameter] T +# 46| 8: [Struct] GenericStruct<> +#-----| 1: (Type parameters) +# 46| 0: [TypeParameter] T +# 47| 9: [Class] Map<,> +#-----| 1: (Type parameters) +# 47| 0: [TypeParameter] U +# 47| 1: [TypeParameter] V diff --git a/csharp/ql/test/library-tests/types/PrintAst.qlref b/csharp/ql/test/library-tests/types/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/types/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/unsafe/PrintAst.expected b/csharp/ql/test/library-tests/unsafe/PrintAst.expected new file mode 100644 index 000000000000..079719907c78 --- /dev/null +++ b/csharp/ql/test/library-tests/unsafe/PrintAst.expected @@ -0,0 +1,116 @@ +unsafe.cs: +# 1| [NamespaceDeclaration] namespace ... { ... } +# 3| 1: [Class] Test +# 5| 5: [Method] Main +#-----| 2: (Parameters) +# 5| 0: [Parameter] args +# 6| 4: [BlockStmt] {...} +# 7| 0: [LocalVariableDeclStmt] ... ...; +# 7| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 7| 0: [IntLiteral] 42 +# 7| 1: [LocalVariableAccess] access to local variable i +# 8| 1: [LocalVariableDeclStmt] ... ...; +# 8| 0: [LocalVariableDeclAndInitExpr] Int32[] ia = ... +# 8| 0: [ArrayCreation] array creation of type Int32[] +# 8| 0: [IntLiteral] 2 +# 8| 1: [LocalVariableAccess] access to local variable ia +# 9| 2: [ExprStmt] ...; +# 9| 0: [AssignExpr] ... = ... +# 9| 0: [IntLiteral] 0 +# 9| 1: [ArrayAccess] access to array element +# 9| -1: [LocalVariableAccess] access to local variable ia +# 9| 0: [IntLiteral] 0 +# 10| 3: [ExprStmt] ...; +# 10| 0: [AssignExpr] ... = ... +# 10| 0: [IntLiteral] 1 +# 10| 1: [ArrayAccess] access to array element +# 10| -1: [LocalVariableAccess] access to local variable ia +# 10| 0: [IntLiteral] 1 +# 11| 4: [LocalVariableDeclStmt] ... ...; +# 11| 0: [LocalVariableDeclAndInitExpr] Int32* ip = ... +# 11| 0: [AddressOfExpr] &... +# 11| 0: [LocalVariableAccess] access to local variable i +# 11| 1: [LocalVariableAccess] access to local variable ip +# 12| 5: [ExprStmt] ...; +# 12| 0: [AssignExpr] ... = ... +# 12| 0: [AddExpr] ... + ... +# 12| 0: [LocalVariableAccess] access to local variable ip +# 12| 1: [IntLiteral] 1 +# 12| 1: [LocalVariableAccess] access to local variable ip +# 13| 6: [ExprStmt] ...; +# 13| 0: [AssignExpr] ... = ... +# 13| 0: [AddExpr] ... + ... +# 13| 0: [PointerIndirectionExpr] *... +# 13| 0: [LocalVariableAccess] access to local variable ip +# 13| 1: [LocalVariableAccess] access to local variable ip +# 13| 1: [LocalVariableAccess] access to local variable ip +# 14| 7: [ExprStmt] ...; +# 14| 0: [AssignExpr] ... = ... +# 14| 0: [AddExpr] ... + ... +# 14| 0: [PointerIndirectionExpr] *... +# 14| 0: [LocalVariableAccess] access to local variable ip +# 14| 1: [AddressOfExpr] &... +# 14| 0: [LocalVariableAccess] access to local variable i +# 14| 1: [LocalVariableAccess] access to local variable ip +# 15| 8: [LocalVariableDeclStmt] ... ...; +# 15| 0: [LocalVariableDeclAndInitExpr] Int32* ip42 = ... +# 15| 0: [AddressOfExpr] &... +# 15| 0: [LocalVariableAccess] access to local variable i +# 15| 1: [LocalVariableAccess] access to local variable ip42 +# 16| 9: [ExprStmt] ...; +# 16| 0: [PostIncrExpr] ...++ +# 16| 0: [LocalVariableAccess] access to local variable ip +# 17| 10: [ExprStmt] ...; +# 17| 0: [AssignExpr] ... = ... +# 17| 0: [SubExpr] ... - ... +# 17| 0: [LocalVariableAccess] access to local variable ip +# 17| 1: [IntLiteral] 1 +# 17| 1: [LocalVariableAccess] access to local variable ip +# 18| 11: [ExprStmt] ...; +# 18| 0: [AssignExpr] ... = ... +# 18| 0: [SizeofExpr] sizeof(..) +# 18| 0: [TypeAccess] access to type Char* +# 18| 1: [PointerIndirectionExpr] *... +# 18| 0: [LocalVariableAccess] access to local variable ip42 +# 19| 12: [LocalVariableDeclStmt] ... ...; +# 19| 0: [LocalVariableDeclAndInitExpr] Int64 distance = ... +# 19| 0: [SubExpr] ... - ... +# 19| 0: [LocalVariableAccess] access to local variable ip +# 19| 1: [LocalVariableAccess] access to local variable ip42 +# 19| 1: [LocalVariableAccess] access to local variable distance +# 22| 6: [Method] f +#-----| 2: (Parameters) +# 22| 0: [Parameter] p +# 23| 4: [BlockStmt] {...} +# 24| 0: [ExprStmt] ...; +# 24| 0: [MethodCall] call to method ToString +# 24| -1: [PointerIndirectionExpr] *... +# 24| 0: [ParameterAccess] access to parameter p +# 25| 1: [ExprStmt] ...; +# 25| 0: [MethodCall] call to method ToString +# 25| -1: [PointerIndirectionExpr] *... +# 25| 0: [ParameterAccess] access to parameter p +# 26| 2: [ExprStmt] ...; +# 26| 0: [MethodCall] call to method ToString +# 26| -1: [PointerIndirectionExpr] *... +# 26| 0: [AddExpr] ... + ... +# 26| 0: [ParameterAccess] access to parameter p +# 26| 1: [IntLiteral] 0 +# 30| 7: [Method] g +# 30| 4: [BlockStmt] {...} +# 32| 8: [Method] h +# 33| 4: [BlockStmt] {...} +# 34| 0: [UnsafeStmt] unsafe {...} +# 35| 0: [BlockStmt] {...} +# 36| 0: [LocalVariableDeclStmt] ... ...; +# 36| 0: [LocalVariableDeclAndInitExpr] Int32[] data = ... +# 36| 0: [ArrayCreation] array creation of type Int32[] +# 36| 0: [IntLiteral] 10 +# 36| 1: [LocalVariableAccess] access to local variable data +# 37| 1: [FixedStmt] fixed(...) { ... } +# 37| -1: [LocalVariableDeclAndInitExpr] Int32* p = ... +# 37| 0: [CastExpr] (...) ... +# 37| 0: [LocalVariableAccess] access to local variable data +# 37| 1: [LocalVariableAccess] access to local variable p +# 38| 0: [BlockStmt] {...} +# 44| 2: [Class] SafeClass diff --git a/csharp/ql/test/library-tests/unsafe/PrintAst.qlref b/csharp/ql/test/library-tests/unsafe/PrintAst.qlref new file mode 100644 index 000000000000..15af8b109dda --- /dev/null +++ b/csharp/ql/test/library-tests/unsafe/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/csharp/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/API Abuse/FormatInvalid/FormatInvalid.expected b/csharp/ql/test/query-tests/API Abuse/FormatInvalid/FormatInvalid.expected index 31ef1b638e64..18340a5d5c87 100644 --- a/csharp/ql/test/query-tests/API Abuse/FormatInvalid/FormatInvalid.expected +++ b/csharp/ql/test/query-tests/API Abuse/FormatInvalid/FormatInvalid.expected @@ -48,7 +48,7 @@ nodes | FormatInvalid.cs:108:23:108:25 | "}" | semmle.label | "}" | | FormatInvalid.cs:109:23:109:25 | "}" | semmle.label | "}" | | FormatInvalid.cs:110:23:110:25 | "}" | semmle.label | "}" | -| FormatInvalid.cs:115:56:115:58 | "}" | semmle.label | "}" | +| FormatInvalid.cs:115:56:115:58 | [assertion success] "}" | semmle.label | [assertion success] "}" | | FormatInvalid.cs:116:18:116:20 | "}" | semmle.label | "}" | | FormatInvalid.cs:117:40:117:42 | "}" | semmle.label | "}" | | FormatInvalidBad.cs:7:30:7:44 | "class {0} { }" | semmle.label | "class {0} { }" | @@ -93,7 +93,7 @@ edges | FormatInvalid.cs:108:24:108:25 | "}" | FormatInvalid.cs:108:23:108:25 | "}" | FormatInvalid.cs:108:23:108:25 | "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:108:9:108:32 | call to method Write | this | | FormatInvalid.cs:109:24:109:25 | "}" | FormatInvalid.cs:109:23:109:25 | "}" | FormatInvalid.cs:109:23:109:25 | "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:109:9:109:35 | call to method Write | this | | FormatInvalid.cs:110:24:110:25 | "}" | FormatInvalid.cs:110:23:110:25 | "}" | FormatInvalid.cs:110:23:110:25 | "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:110:9:110:38 | call to method Write | this | -| FormatInvalid.cs:115:57:115:58 | "}" | FormatInvalid.cs:115:56:115:58 | "}" | FormatInvalid.cs:115:56:115:58 | "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:115:9:115:63 | call to method Assert | this | +| FormatInvalid.cs:115:57:115:58 | "}" | FormatInvalid.cs:115:56:115:58 | [assertion success] "}" | FormatInvalid.cs:115:56:115:58 | [assertion success] "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:115:9:115:63 | call to method Assert | this | | FormatInvalid.cs:116:19:116:20 | "}" | FormatInvalid.cs:116:18:116:20 | "}" | FormatInvalid.cs:116:18:116:20 | "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:116:9:116:24 | call to method Write | this | | FormatInvalid.cs:117:41:117:42 | "}" | FormatInvalid.cs:117:40:117:42 | "}" | FormatInvalid.cs:117:40:117:42 | "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:117:9:117:47 | call to method Print | this | | FormatInvalidBad.cs:7:41:7:44 | "class {0} { }" | FormatInvalidBad.cs:7:30:7:44 | "class {0} { }" | FormatInvalidBad.cs:7:30:7:44 | "class {0} { }" | Invalid format string used in $@ formatting call. | FormatInvalidBad.cs:7:16:7:45 | call to method Format | this | diff --git a/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.cs b/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.cs index 47df3468b4a1..e36143d6b402 100644 --- a/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.cs +++ b/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.cs @@ -18,4 +18,13 @@ void f() var good6 = (Action)(delegate (int x) { }); var good7 = (Action)((int x) => { }); } + + enum Enum + { + A = 2, + B = 1 | A, + C = 1 | (int)A, // BAD + D = 9 | (32 << A), + E = 9 | (32 << (int)A) // BAD + } } diff --git a/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.expected b/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.expected index 28dcbbb9f6e0..f5c4b708253a 100644 --- a/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.expected +++ b/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.expected @@ -1,3 +1,5 @@ | UselessCastToSelf.cs:8:20:8:25 | (...) ... | This cast is redundant because the expression already has type Int32. | | UselessCastToSelf.cs:9:20:9:29 | (...) ... | This cast is redundant because the expression already has type Test. | | UselessCastToSelf.cs:10:20:10:31 | ... as ... | This cast is redundant because the expression already has type Test. | +| UselessCastToSelf.cs:26:17:26:22 | (...) ... | This cast is redundant because the expression already has type Int32. | +| UselessCastToSelf.cs:28:24:28:29 | (...) ... | This cast is redundant because the expression already has type Int32. | diff --git a/csharp/ql/test/query-tests/Likely Bugs/SelfAssignment/selfassigns.cs b/csharp/ql/test/query-tests/Likely Bugs/SelfAssignment/selfassigns.cs index 3553606275f9..6d1387a5e1db 100644 --- a/csharp/ql/test/query-tests/Likely Bugs/SelfAssignment/selfassigns.cs +++ b/csharp/ql/test/query-tests/Likely Bugs/SelfAssignment/selfassigns.cs @@ -82,4 +82,11 @@ public void NotOK(SelfAssigns obj, int y) this.Self.Self.Self.StringProp = Self.Self.Self.StringProp; intArray[1] = this.intArray[1 + 0]; } + + enum Enum + { + X = 42, + Y = 100, + Z + } } diff --git a/csharp/ql/test/query-tests/Metrics/Dependencies/ExternalDependencies/ExternalDependencies.expected b/csharp/ql/test/query-tests/Metrics/Dependencies/ExternalDependencies/ExternalDependencies.expected index 096443a411df..ca75d5f716ad 100644 --- a/csharp/ql/test/query-tests/Metrics/Dependencies/ExternalDependencies/ExternalDependencies.expected +++ b/csharp/ql/test/query-tests/Metrics/Dependencies/ExternalDependencies/ExternalDependencies.expected @@ -1,5 +1,5 @@ -| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Net.Http<\|>4.2.1.0 | 11 | -| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Private.DataContractSerialization<\|>4.1.4.0 | 2 | -| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Private.Xml<\|>4.0.1.0 | 2 | -| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Data.Common<\|>4.2.1.0 | 1 | -| /query-tests/Metrics/Dependencies/ExternalDependencies/File2.cs<\|>System.Net.Http<\|>4.2.1.0 | 1 | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Net.Http<\|>4.2.2.0 | 11 | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Private.DataContractSerialization<\|>4.1.5.0 | 2 | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Private.Xml<\|>4.0.2.0 | 2 | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Data.Common<\|>4.2.2.0 | 1 | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File2.cs<\|>System.Net.Http<\|>4.2.2.0 | 1 | diff --git a/csharp/ql/test/query-tests/Metrics/Dependencies/ExternalDependencies/ExternalDependenciesSourceLinks.expected b/csharp/ql/test/query-tests/Metrics/Dependencies/ExternalDependencies/ExternalDependenciesSourceLinks.expected index 656ec8431144..e1fe8dd8d037 100644 --- a/csharp/ql/test/query-tests/Metrics/Dependencies/ExternalDependencies/ExternalDependenciesSourceLinks.expected +++ b/csharp/ql/test/query-tests/Metrics/Dependencies/ExternalDependencies/ExternalDependenciesSourceLinks.expected @@ -1,5 +1,5 @@ -| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Data.Common<\|>4.2.1.0 | File1.cs:0:0:0:0 | File1.cs | -| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Net.Http<\|>4.2.1.0 | File1.cs:0:0:0:0 | File1.cs | -| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Private.DataContractSerialization<\|>4.1.4.0 | File1.cs:0:0:0:0 | File1.cs | -| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Private.Xml<\|>4.0.1.0 | File1.cs:0:0:0:0 | File1.cs | -| /query-tests/Metrics/Dependencies/ExternalDependencies/File2.cs<\|>System.Net.Http<\|>4.2.1.0 | File2.cs:0:0:0:0 | File2.cs | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Data.Common<\|>4.2.2.0 | File1.cs:0:0:0:0 | File1.cs | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Net.Http<\|>4.2.2.0 | File1.cs:0:0:0:0 | File1.cs | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Private.DataContractSerialization<\|>4.1.5.0 | File1.cs:0:0:0:0 | File1.cs | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File1.cs<\|>System.Private.Xml<\|>4.0.2.0 | File1.cs:0:0:0:0 | File1.cs | +| /query-tests/Metrics/Dependencies/ExternalDependencies/File2.cs<\|>System.Net.Http<\|>4.2.2.0 | File2.cs:0:0:0:0 | File2.cs | diff --git a/csharp/ql/test/query-tests/Nullness/Assert.cs b/csharp/ql/test/query-tests/Nullness/Assert.cs index c483d1f36d3c..a57ba59a861e 100644 --- a/csharp/ql/test/query-tests/Nullness/Assert.cs +++ b/csharp/ql/test/query-tests/Nullness/Assert.cs @@ -39,7 +39,7 @@ void Fn(bool b) Console.WriteLine(s.Length); // GOOD s = b ? null : ""; - Assert.IsFalse(s == null || b); + Assert.IsFalse(s == null || !b); Console.WriteLine(s.Length); // GOOD s = b ? null : ""; @@ -47,7 +47,7 @@ void Fn(bool b) Console.WriteLine(s.Length); // BAD (always) s = b ? null : ""; - Assert.IsFalse(s != null || b); + Assert.IsFalse(s != null || !b); Console.WriteLine(s.Length); // BAD (always) } } diff --git a/csharp/ql/test/query-tests/Nullness/E.cs b/csharp/ql/test/query-tests/Nullness/E.cs index 67b5267ffbec..511955e2b44b 100644 --- a/csharp/ql/test/query-tests/Nullness/E.cs +++ b/csharp/ql/test/query-tests/Nullness/E.cs @@ -385,6 +385,32 @@ static bool Ex37(E e1, E e2) return true; return e1.Long == e2.Long; // GOOD (false positive) } + + int Ex38(int? i) + { + i ??= 0; + return i.Value; // GOOD + } + + System.Drawing.Color Ex39(System.Drawing.Color? color) + { + color ??= System.Drawing.Color.White; + return color.Value; // GOOD + } + + int Ex40() + { + int? i = null; + i ??= null; + return i.Value; // BAD (always) + } + + int Ex41() + { + int? i = 1; + i ??= null; + return i.Value; // GOOD + } } public static class Extensions @@ -393,4 +419,4 @@ public static void M1(this string s) { } public static int M2(this string s) => s.Length; } -// semmle-extractor-options: /r:System.Linq.dll +// semmle-extractor-options: /r:System.Linq.dll /r:System.Drawing.Primitives.dll diff --git a/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected b/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected index 5f739e93c2b1..dc70346eea54 100644 --- a/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected +++ b/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected @@ -191,8 +191,11 @@ | E.cs:85:18:85:29 | ... != ... | false | E.cs:85:18:85:21 | access to parameter vals | E.cs:85:26:85:29 | null | | E.cs:85:18:85:29 | ... != ... | false | E.cs:85:26:85:29 | null | E.cs:85:18:85:21 | access to parameter vals | | E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:92:18:92:27 | access to constant MY_CONST_A | +| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_A | E.cs:92:18:92:27 | access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard | | E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:97:18:97:27 | access to constant MY_CONST_B | +| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_B | E.cs:97:18:97:27 | access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard | | E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:95:18:95:27 | access to constant MY_CONST_C | +| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_C | E.cs:95:18:95:27 | access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard | | E.cs:126:21:126:29 | ... == ... | true | E.cs:126:21:126:24 | access to local variable step | E.cs:126:29:126:29 | 0 | | E.cs:126:21:126:29 | ... == ... | true | E.cs:126:29:126:29 | 0 | E.cs:126:21:126:24 | access to local variable step | | E.cs:153:13:153:24 | ... != ... | false | E.cs:153:13:153:16 | access to local variable obj2 | E.cs:153:21:153:24 | null | @@ -216,6 +219,7 @@ | E.cs:293:13:293:24 | ... == ... | true | E.cs:293:15:293:19 | call to method M2 | E.cs:293:24:293:24 | (...) ... | | E.cs:293:13:293:24 | ... == ... | true | E.cs:293:24:293:24 | (...) ... | E.cs:293:15:293:19 | call to method M2 | | E.cs:321:13:321:30 | ... is ... | true | E.cs:321:14:321:21 | ... ?? ... | E.cs:321:27:321:30 | null | +| E.cs:321:13:321:30 | ... is ... | true | E.cs:321:27:321:30 | null | E.cs:321:14:321:21 | ... ?? ... | | E.cs:355:13:355:21 | dynamic call to operator != | false | E.cs:355:13:355:13 | access to local variable x | E.cs:355:18:355:21 | null | | E.cs:355:13:355:21 | dynamic call to operator != | false | E.cs:355:18:355:21 | null | E.cs:355:13:355:13 | access to local variable x | | E.cs:362:13:362:29 | ... != ... | false | E.cs:362:13:362:13 | access to local variable x | E.cs:362:18:362:29 | (...) ... | diff --git a/csharp/ql/test/query-tests/Nullness/Implications.expected b/csharp/ql/test/query-tests/Nullness/Implications.expected index d7a06795fc21..8ac85c3fe789 100644 --- a/csharp/ql/test/query-tests/Nullness/Implications.expected +++ b/csharp/ql/test/query-tests/Nullness/Implications.expected @@ -66,7 +66,9 @@ | Assert.cs:13:13:13:25 | ... ? ... : ... | null | Assert.cs:13:17:13:20 | null | null | | Assert.cs:14:23:14:23 | access to local variable s | empty | Assert.cs:13:13:13:25 | ... ? ... : ... | empty | | Assert.cs:14:23:14:23 | access to local variable s | non-empty | Assert.cs:13:13:13:25 | ... ? ... : ... | non-empty | +| Assert.cs:14:23:14:23 | access to local variable s | non-null | Assert.cs:13:13:13:13 | access to parameter b | false | | Assert.cs:14:23:14:23 | access to local variable s | non-null | Assert.cs:13:13:13:25 | ... ? ... : ... | non-null | +| Assert.cs:14:23:14:23 | access to local variable s | null | Assert.cs:13:13:13:13 | access to parameter b | true | | Assert.cs:14:23:14:23 | access to local variable s | null | Assert.cs:13:13:13:25 | ... ? ... : ... | null | | Assert.cs:15:27:15:27 | access to local variable s | non-null | Assert.cs:13:13:13:25 | ... ? ... : ... | non-null | | Assert.cs:15:27:15:27 | access to local variable s | null | Assert.cs:13:13:13:25 | ... ? ... : ... | null | @@ -76,7 +78,9 @@ | Assert.cs:17:13:17:25 | ... ? ... : ... | null | Assert.cs:17:17:17:20 | null | null | | Assert.cs:18:26:18:26 | access to local variable s | empty | Assert.cs:17:13:17:25 | ... ? ... : ... | empty | | Assert.cs:18:26:18:26 | access to local variable s | non-empty | Assert.cs:17:13:17:25 | ... ? ... : ... | non-empty | +| Assert.cs:18:26:18:26 | access to local variable s | non-null | Assert.cs:17:13:17:13 | access to parameter b | false | | Assert.cs:18:26:18:26 | access to local variable s | non-null | Assert.cs:17:13:17:25 | ... ? ... : ... | non-null | +| Assert.cs:18:26:18:26 | access to local variable s | null | Assert.cs:17:13:17:13 | access to parameter b | true | | Assert.cs:18:26:18:26 | access to local variable s | null | Assert.cs:17:13:17:25 | ... ? ... : ... | null | | Assert.cs:19:27:19:27 | access to local variable s | non-null | Assert.cs:17:13:17:25 | ... ? ... : ... | non-null | | Assert.cs:19:27:19:27 | access to local variable s | null | Assert.cs:17:13:17:25 | ... ? ... : ... | null | @@ -164,8 +168,10 @@ | Assert.cs:42:24:42:32 | ... == ... | false | Assert.cs:42:24:42:24 | access to local variable s | non-null | | Assert.cs:42:24:42:32 | ... == ... | true | Assert.cs:41:13:41:13 | access to parameter b | true | | Assert.cs:42:24:42:32 | ... == ... | true | Assert.cs:42:24:42:24 | access to local variable s | null | -| Assert.cs:42:24:42:37 | ... \|\| ... | false | Assert.cs:42:24:42:32 | ... == ... | false | -| Assert.cs:42:24:42:37 | ... \|\| ... | false | Assert.cs:42:37:42:37 | access to parameter b | false | +| Assert.cs:42:24:42:38 | ... \|\| ... | false | Assert.cs:42:24:42:32 | ... == ... | false | +| Assert.cs:42:24:42:38 | ... \|\| ... | false | Assert.cs:42:37:42:38 | !... | false | +| Assert.cs:42:37:42:38 | !... | false | Assert.cs:42:38:42:38 | access to parameter b | true | +| Assert.cs:42:37:42:38 | !... | true | Assert.cs:42:38:42:38 | access to parameter b | false | | Assert.cs:43:27:43:27 | access to local variable s | non-null | Assert.cs:41:13:41:25 | ... ? ... : ... | non-null | | Assert.cs:43:27:43:27 | access to local variable s | null | Assert.cs:41:13:41:25 | ... ? ... : ... | null | | Assert.cs:45:13:45:25 | ... ? ... : ... | non-null | Assert.cs:45:13:45:13 | access to parameter b | false | @@ -196,8 +202,10 @@ | Assert.cs:50:24:50:32 | ... != ... | false | Assert.cs:50:24:50:24 | access to local variable s | null | | Assert.cs:50:24:50:32 | ... != ... | true | Assert.cs:49:13:49:13 | access to parameter b | false | | Assert.cs:50:24:50:32 | ... != ... | true | Assert.cs:50:24:50:24 | access to local variable s | non-null | -| Assert.cs:50:24:50:37 | ... \|\| ... | false | Assert.cs:50:24:50:32 | ... != ... | false | -| Assert.cs:50:24:50:37 | ... \|\| ... | false | Assert.cs:50:37:50:37 | access to parameter b | false | +| Assert.cs:50:24:50:38 | ... \|\| ... | false | Assert.cs:50:24:50:32 | ... != ... | false | +| Assert.cs:50:24:50:38 | ... \|\| ... | false | Assert.cs:50:37:50:38 | !... | false | +| Assert.cs:50:37:50:38 | !... | false | Assert.cs:50:38:50:38 | access to parameter b | true | +| Assert.cs:50:37:50:38 | !... | true | Assert.cs:50:38:50:38 | access to parameter b | false | | Assert.cs:51:27:51:27 | access to local variable s | non-null | Assert.cs:49:13:49:25 | ... ? ... : ... | non-null | | Assert.cs:51:27:51:27 | access to local variable s | null | Assert.cs:49:13:49:25 | ... ? ... : ... | null | | B.cs:12:13:12:24 | access to local variable eqCallAlways | non-null | B.cs:7:26:7:29 | null | non-null | @@ -320,7 +328,9 @@ | C.cs:55:18:55:36 | ... ? ... : ... | null | C.cs:55:28:55:31 | null | null | | C.cs:56:23:56:24 | access to local variable o2 | empty | C.cs:55:18:55:36 | ... ? ... : ... | empty | | C.cs:56:23:56:24 | access to local variable o2 | non-empty | C.cs:55:18:55:36 | ... ? ... : ... | non-empty | +| C.cs:56:23:56:24 | access to local variable o2 | non-null | C.cs:55:18:55:24 | call to method Maybe | false | | C.cs:56:23:56:24 | access to local variable o2 | non-null | C.cs:55:18:55:36 | ... ? ... : ... | non-null | +| C.cs:56:23:56:24 | access to local variable o2 | null | C.cs:55:18:55:24 | call to method Maybe | true | | C.cs:56:23:56:24 | access to local variable o2 | null | C.cs:55:18:55:36 | ... ? ... : ... | null | | C.cs:57:9:57:10 | access to local variable o2 | non-null | C.cs:55:18:55:36 | ... ? ... : ... | non-null | | C.cs:57:9:57:10 | access to local variable o2 | null | C.cs:55:18:55:36 | ... ? ... : ... | null | @@ -344,7 +354,9 @@ | C.cs:70:18:70:46 | ... ? ... : ... | non-null | C.cs:70:35:70:46 | object creation of type Object | non-null | | C.cs:70:18:70:46 | ... ? ... : ... | null | C.cs:70:18:70:24 | call to method Maybe | true | | C.cs:70:18:70:46 | ... ? ... : ... | null | C.cs:70:28:70:31 | null | null | +| C.cs:71:26:71:27 | access to local variable o3 | non-null | C.cs:70:18:70:24 | call to method Maybe | false | | C.cs:71:26:71:27 | access to local variable o3 | non-null | C.cs:70:18:70:46 | ... ? ... : ... | non-null | +| C.cs:71:26:71:27 | access to local variable o3 | null | C.cs:70:18:70:24 | call to method Maybe | true | | C.cs:71:26:71:27 | access to local variable o3 | null | C.cs:70:18:70:46 | ... ? ... : ... | null | | C.cs:72:9:72:10 | access to local variable o3 | non-null | C.cs:70:18:70:46 | ... ? ... : ... | non-null | | C.cs:72:9:72:10 | access to local variable o3 | null | C.cs:70:18:70:46 | ... ? ... : ... | null | @@ -1115,12 +1127,14 @@ | E.cs:176:13:176:14 | access to local variable b2 | true | E.cs:175:19:175:42 | ... ? ... : ... | true | | E.cs:176:13:176:22 | ... == ... | false | E.cs:176:13:176:14 | (...) ... | non-null | | E.cs:176:13:176:22 | ... == ... | true | E.cs:176:13:176:14 | (...) ... | null | +| E.cs:176:13:176:22 | ... == ... | true | E.cs:176:19:176:22 | null | non-null | | E.cs:180:13:180:23 | ... == ... | false | E.cs:180:13:180:15 | access to parameter obj | non-null | | E.cs:180:13:180:23 | ... == ... | true | E.cs:180:13:180:15 | access to parameter obj | null | | E.cs:184:13:184:14 | (...) ... | non-null | E.cs:184:13:184:14 | access to parameter b1 | non-null | | E.cs:184:13:184:14 | (...) ... | null | E.cs:184:13:184:14 | access to parameter b1 | null | | E.cs:184:13:184:22 | ... == ... | false | E.cs:184:13:184:14 | (...) ... | non-null | | E.cs:184:13:184:22 | ... == ... | true | E.cs:184:13:184:14 | (...) ... | null | +| E.cs:184:13:184:22 | ... == ... | true | E.cs:184:19:184:22 | null | non-null | | E.cs:193:19:193:29 | call to method ToString | non-null | E.cs:193:17:193:17 | access to parameter o | non-null | | E.cs:198:17:198:29 | ... ? ... : ... | non-null | E.cs:198:17:198:17 | access to parameter b | false | | E.cs:198:17:198:29 | ... ? ... : ... | non-null | E.cs:198:28:198:29 | "" | non-null | @@ -1256,6 +1270,26 @@ | E.cs:384:13:384:36 | ... && ... | true | E.cs:384:27:384:36 | ... == ... | true | | E.cs:384:27:384:36 | ... == ... | false | E.cs:384:27:384:28 | access to parameter e2 | non-null | | E.cs:384:27:384:36 | ... == ... | true | E.cs:384:27:384:28 | access to parameter e2 | null | +| E.cs:404:9:404:9 | access to local variable i | non-null | E.cs:403:18:403:21 | null | non-null | +| E.cs:404:9:404:9 | access to local variable i | null | E.cs:403:18:403:21 | null | null | +| E.cs:404:9:404:18 | ... = ... | non-null | E.cs:404:9:404:9 | access to local variable i | non-null | +| E.cs:404:9:404:18 | ... = ... | non-null | E.cs:404:9:404:18 | ... ?? ... | non-null | +| E.cs:404:9:404:18 | ... = ... | null | E.cs:404:9:404:9 | access to local variable i | null | +| E.cs:404:9:404:18 | ... = ... | null | E.cs:404:9:404:18 | ... ?? ... | null | +| E.cs:404:9:404:18 | ... ?? ... | null | E.cs:404:9:404:9 | access to local variable i | null | +| E.cs:404:9:404:18 | ... ?? ... | null | E.cs:404:15:404:18 | null | null | +| E.cs:405:16:405:16 | access to local variable i | non-null | E.cs:404:9:404:18 | ... ?? ... | non-null | +| E.cs:405:16:405:16 | access to local variable i | null | E.cs:404:9:404:18 | ... ?? ... | null | +| E.cs:411:9:411:9 | access to local variable i | non-null | E.cs:410:18:410:18 | (...) ... | non-null | +| E.cs:411:9:411:9 | access to local variable i | null | E.cs:410:18:410:18 | (...) ... | null | +| E.cs:411:9:411:18 | ... = ... | non-null | E.cs:411:9:411:9 | access to local variable i | non-null | +| E.cs:411:9:411:18 | ... = ... | non-null | E.cs:411:9:411:18 | ... ?? ... | non-null | +| E.cs:411:9:411:18 | ... = ... | null | E.cs:411:9:411:9 | access to local variable i | null | +| E.cs:411:9:411:18 | ... = ... | null | E.cs:411:9:411:18 | ... ?? ... | null | +| E.cs:411:9:411:18 | ... ?? ... | null | E.cs:411:9:411:9 | access to local variable i | null | +| E.cs:411:9:411:18 | ... ?? ... | null | E.cs:411:15:411:18 | null | null | +| E.cs:412:16:412:16 | access to local variable i | non-null | E.cs:411:9:411:18 | ... ?? ... | non-null | +| E.cs:412:16:412:16 | access to local variable i | null | E.cs:411:9:411:18 | ... ?? ... | null | | Forwarding.cs:9:13:9:30 | !... | false | Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | true | | Forwarding.cs:9:13:9:30 | !... | true | Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | false | | Forwarding.cs:9:14:9:14 | access to local variable s | empty | Forwarding.cs:7:20:7:23 | null | empty | diff --git a/csharp/ql/test/query-tests/Nullness/NullAlways.expected b/csharp/ql/test/query-tests/Nullness/NullAlways.expected index 01c054b9379e..2aaaeb87c56a 100644 --- a/csharp/ql/test/query-tests/Nullness/NullAlways.expected +++ b/csharp/ql/test/query-tests/Nullness/NullAlways.expected @@ -36,6 +36,7 @@ | E.cs:323:13:323:14 | access to parameter s1 | Variable $@ is always null here. | E.cs:319:29:319:30 | s1 | s1 | | E.cs:324:13:324:14 | access to parameter s2 | Variable $@ is always null here. | E.cs:319:40:319:41 | s2 | s2 | | E.cs:331:9:331:9 | access to local variable x | Variable $@ is always null here. | E.cs:330:13:330:13 | x | x | +| E.cs:405:16:405:16 | access to local variable i | Variable $@ is always null here. | E.cs:403:14:403:14 | i | i | | Forwarding.cs:36:31:36:31 | access to local variable s | Variable $@ is always null here. | Forwarding.cs:7:16:7:16 | s | s | | Forwarding.cs:40:27:40:27 | access to local variable s | Variable $@ is always null here. | Forwarding.cs:7:16:7:16 | s | s | | NullAlwaysBad.cs:9:30:9:30 | access to parameter s | Variable $@ is always null here. | NullAlwaysBad.cs:7:29:7:29 | s | s | diff --git a/csharp/ql/test/query-tests/Nullness/NullCheck.expected b/csharp/ql/test/query-tests/Nullness/NullCheck.expected index 1c2b22ca6b56..bf1d6480e643 100644 --- a/csharp/ql/test/query-tests/Nullness/NullCheck.expected +++ b/csharp/ql/test/query-tests/Nullness/NullCheck.expected @@ -1,5 +1,9 @@ | Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:22:10:22 | access to local variable s | false | true | | Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:22:10:22 | access to local variable s | true | false | +| Assert.cs:14:23:14:23 | access to local variable s | Assert.cs:14:23:14:23 | access to local variable s | non-null | false | +| Assert.cs:14:23:14:23 | access to local variable s | Assert.cs:14:23:14:23 | access to local variable s | null | true | +| Assert.cs:18:26:18:26 | access to local variable s | Assert.cs:18:26:18:26 | access to local variable s | non-null | false | +| Assert.cs:18:26:18:26 | access to local variable s | Assert.cs:18:26:18:26 | access to local variable s | null | true | | Assert.cs:22:23:22:31 | ... == ... | Assert.cs:22:23:22:23 | access to local variable s | false | false | | Assert.cs:22:23:22:31 | ... == ... | Assert.cs:22:23:22:23 | access to local variable s | true | true | | Assert.cs:26:23:26:31 | ... != ... | Assert.cs:26:23:26:23 | access to local variable s | false | true | @@ -60,6 +64,10 @@ | C.cs:41:22:41:30 | ... == ... | C.cs:41:22:41:22 | access to local variable s | true | true | | C.cs:45:22:45:30 | ... != ... | C.cs:45:22:45:22 | access to local variable s | false | true | | C.cs:45:22:45:30 | ... != ... | C.cs:45:22:45:22 | access to local variable s | true | false | +| C.cs:56:23:56:24 | access to local variable o2 | C.cs:56:23:56:24 | access to local variable o2 | non-null | false | +| C.cs:56:23:56:24 | access to local variable o2 | C.cs:56:23:56:24 | access to local variable o2 | null | true | +| C.cs:71:26:71:27 | access to local variable o3 | C.cs:71:26:71:27 | access to local variable o3 | non-null | false | +| C.cs:71:26:71:27 | access to local variable o3 | C.cs:71:26:71:27 | access to local variable o3 | null | true | | C.cs:78:13:78:24 | call to method IsNotNull | C.cs:78:23:78:23 | access to local variable o | false | true | | C.cs:78:13:78:24 | call to method IsNotNull | C.cs:78:23:78:23 | access to local variable o | true | false | | C.cs:82:14:82:22 | call to method IsNull | C.cs:82:21:82:21 | access to local variable o | false | false | @@ -219,10 +227,12 @@ | E.cs:175:19:175:29 | ... == ... | E.cs:175:19:175:21 | access to parameter obj | true | true | | E.cs:176:13:176:22 | ... == ... | E.cs:176:13:176:14 | (...) ... | false | false | | E.cs:176:13:176:22 | ... == ... | E.cs:176:13:176:14 | (...) ... | true | true | +| E.cs:176:13:176:22 | ... == ... | E.cs:176:19:176:22 | null | true | false | | E.cs:180:13:180:23 | ... == ... | E.cs:180:13:180:15 | access to parameter obj | false | false | | E.cs:180:13:180:23 | ... == ... | E.cs:180:13:180:15 | access to parameter obj | true | true | | E.cs:184:13:184:22 | ... == ... | E.cs:184:13:184:14 | (...) ... | false | false | | E.cs:184:13:184:22 | ... == ... | E.cs:184:13:184:14 | (...) ... | true | true | +| E.cs:184:13:184:22 | ... == ... | E.cs:184:19:184:22 | null | true | false | | E.cs:193:17:193:17 | access to parameter o | E.cs:193:17:193:17 | access to parameter o | non-null | false | | E.cs:193:17:193:17 | access to parameter o | E.cs:193:17:193:17 | access to parameter o | null | true | | E.cs:208:13:208:23 | ... is ... | E.cs:208:13:208:13 | access to parameter s | false | true | @@ -276,6 +286,14 @@ | E.cs:384:13:384:22 | ... == ... | E.cs:384:13:384:14 | access to parameter e1 | true | true | | E.cs:384:27:384:36 | ... == ... | E.cs:384:27:384:28 | access to parameter e2 | false | false | | E.cs:384:27:384:36 | ... == ... | E.cs:384:27:384:28 | access to parameter e2 | true | true | +| E.cs:391:9:391:9 | access to parameter i | E.cs:391:9:391:9 | access to parameter i | non-null | false | +| E.cs:391:9:391:9 | access to parameter i | E.cs:391:9:391:9 | access to parameter i | null | true | +| E.cs:397:9:397:13 | access to parameter color | E.cs:397:9:397:13 | access to parameter color | non-null | false | +| E.cs:397:9:397:13 | access to parameter color | E.cs:397:9:397:13 | access to parameter color | null | true | +| E.cs:404:9:404:9 | access to local variable i | E.cs:404:9:404:9 | access to local variable i | non-null | false | +| E.cs:404:9:404:9 | access to local variable i | E.cs:404:9:404:9 | access to local variable i | null | true | +| E.cs:411:9:411:9 | access to local variable i | E.cs:411:9:411:9 | access to local variable i | non-null | false | +| E.cs:411:9:411:9 | access to local variable i | E.cs:411:9:411:9 | access to local variable i | null | true | | Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | Forwarding.cs:9:14:9:14 | access to local variable s | false | false | | Forwarding.cs:14:13:14:32 | call to method IsNotNullOrEmpty | Forwarding.cs:14:13:14:13 | access to local variable s | true | false | | Forwarding.cs:19:14:19:23 | call to method IsNull | Forwarding.cs:19:14:19:14 | access to local variable s | false | false | diff --git a/csharp/ql/test/query-tests/Nullness/NullMaybe.expected b/csharp/ql/test/query-tests/Nullness/NullMaybe.expected index 4d4f9344986f..6433b844ee02 100644 --- a/csharp/ql/test/query-tests/Nullness/NullMaybe.expected +++ b/csharp/ql/test/query-tests/Nullness/NullMaybe.expected @@ -30,14 +30,9 @@ nodes | Assert.cs:29:9:29:25 | [b (line 7): true] SSA def(s) | | Assert.cs:31:27:31:27 | access to local variable s | | Assert.cs:31:27:31:27 | access to local variable s | -| Assert.cs:45:9:45:25 | [b (line 7): false] SSA def(s) | | Assert.cs:45:9:45:25 | [b (line 7): true] SSA def(s) | -| Assert.cs:46:36:46:36 | [b (line 7): false] access to parameter b | -| Assert.cs:46:36:46:36 | [b (line 7): true] access to parameter b | | Assert.cs:47:27:47:27 | access to local variable s | -| Assert.cs:47:27:47:27 | access to local variable s | -| Assert.cs:49:9:49:25 | SSA def(s) | -| Assert.cs:50:37:50:37 | access to parameter b | +| Assert.cs:49:9:49:25 | [b (line 7): true] SSA def(s) | | Assert.cs:51:27:51:27 | access to local variable s | | B.cs:7:11:7:29 | SSA def(eqCallAlways) | | B.cs:10:11:10:30 | SSA def(neqCallAlways) | @@ -370,6 +365,9 @@ nodes | E.cs:384:27:384:28 | access to parameter e2 | | E.cs:386:16:386:17 | access to parameter e1 | | E.cs:386:27:386:28 | access to parameter e2 | +| E.cs:404:9:404:18 | SSA def(i) | +| E.cs:404:9:404:18 | SSA def(i) | +| E.cs:405:16:405:16 | access to local variable i | | Forwarding.cs:7:16:7:23 | SSA def(s) | | Forwarding.cs:14:9:17:9 | if (...) ... | | Forwarding.cs:19:9:22:9 | if (...) ... | @@ -420,12 +418,8 @@ edges | Assert.cs:21:9:21:25 | [b (line 7): true] SSA def(s) | Assert.cs:23:27:23:27 | access to local variable s | | Assert.cs:29:9:29:25 | [b (line 7): false] SSA def(s) | Assert.cs:31:27:31:27 | access to local variable s | | Assert.cs:29:9:29:25 | [b (line 7): true] SSA def(s) | Assert.cs:31:27:31:27 | access to local variable s | -| Assert.cs:45:9:45:25 | [b (line 7): false] SSA def(s) | Assert.cs:46:36:46:36 | [b (line 7): false] access to parameter b | -| Assert.cs:45:9:45:25 | [b (line 7): true] SSA def(s) | Assert.cs:46:36:46:36 | [b (line 7): true] access to parameter b | -| Assert.cs:46:36:46:36 | [b (line 7): false] access to parameter b | Assert.cs:47:27:47:27 | access to local variable s | -| Assert.cs:46:36:46:36 | [b (line 7): true] access to parameter b | Assert.cs:47:27:47:27 | access to local variable s | -| Assert.cs:49:9:49:25 | SSA def(s) | Assert.cs:50:37:50:37 | access to parameter b | -| Assert.cs:50:37:50:37 | access to parameter b | Assert.cs:51:27:51:27 | access to local variable s | +| Assert.cs:45:9:45:25 | [b (line 7): true] SSA def(s) | Assert.cs:47:27:47:27 | access to local variable s | +| Assert.cs:49:9:49:25 | [b (line 7): true] SSA def(s) | Assert.cs:51:27:51:27 | access to local variable s | | B.cs:7:11:7:29 | SSA def(eqCallAlways) | B.cs:13:13:13:24 | access to local variable eqCallAlways | | B.cs:10:11:10:30 | SSA def(neqCallAlways) | B.cs:13:13:13:36 | ...; | | B.cs:10:11:10:30 | SSA def(neqCallAlways) | B.cs:15:9:16:26 | if (...) ... | @@ -719,6 +713,8 @@ edges | E.cs:384:9:385:24 | if (...) ... | E.cs:384:27:384:28 | access to parameter e2 | | E.cs:384:9:385:24 | if (...) ... | E.cs:386:27:386:28 | access to parameter e2 | | E.cs:384:27:384:28 | access to parameter e2 | E.cs:386:16:386:17 | access to parameter e1 | +| E.cs:404:9:404:18 | SSA def(i) | E.cs:405:16:405:16 | access to local variable i | +| E.cs:404:9:404:18 | SSA def(i) | E.cs:405:16:405:16 | access to local variable i | | Forwarding.cs:7:16:7:23 | SSA def(s) | Forwarding.cs:14:9:17:9 | if (...) ... | | Forwarding.cs:14:9:17:9 | if (...) ... | Forwarding.cs:19:9:22:9 | if (...) ... | | Forwarding.cs:19:9:22:9 | if (...) ... | Forwarding.cs:24:9:27:9 | if (...) ... | diff --git a/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.cs b/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.cs index 0befa88d0198..1159e50cae63 100755 --- a/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.cs +++ b/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.cs @@ -152,6 +152,21 @@ void f8(object arguments) break; } } + + void f9() + { + var l1 = new MyList(); // BAD + var x1 = l1[0]; + + var l2 = new MyList(); // GOOD + var x2 = l2[0]; + l2.Prop = 42; + } + + class MyList : List + { + public int Prop { get { return 0; } set { Add(value); } } + } } // semmle-extractor-options: /r:System.Collections.dll diff --git a/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.expected b/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.expected index 39e5f5e96284..a8e778c2b80c 100755 --- a/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.expected +++ b/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.expected @@ -8,4 +8,5 @@ | ReadOnlyContainer.cs:91:13:91:14 | v8 | The contents of this container are never initialized. | | ReadOnlyContainer.cs:96:13:96:14 | v9 | The contents of this container are never initialized. | | ReadOnlyContainer.cs:99:13:99:15 | v10 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:121:13:121:15 | v11 | The contents of this container are never initialized. | \ No newline at end of file +| ReadOnlyContainer.cs:121:13:121:15 | v11 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:158:13:158:14 | l1 | The contents of this container are never initialized. | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.expected b/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.expected index 223fcf616c20..ccfaf8853694 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-022/ZipSlip/ZipSlip.expected @@ -1,25 +1,45 @@ edges -| ZipSlip.cs:16:52:16:65 | access to property FullName : String | ZipSlip.cs:32:41:32:52 | access to local variable destFilePath | -| ZipSlip.cs:16:52:16:65 | access to property FullName : String | ZipSlip.cs:36:45:36:56 | access to local variable destFilePath | -| ZipSlip.cs:16:52:16:65 | access to property FullName : String | ZipSlip.cs:40:41:40:52 | access to local variable destFilePath | -| ZipSlip.cs:19:31:19:44 | access to property FullName : String | ZipSlip.cs:24:41:24:52 | access to local variable destFileName | -| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:69:74:69:85 | access to local variable destFilePath | -| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:76:71:76:82 | access to local variable destFilePath | -| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:83:57:83:68 | access to local variable destFilePath | -| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:91:58:91:69 | access to local variable destFilePath | -| ZipSlipBad.cs:9:59:9:72 | access to property FullName : String | ZipSlipBad.cs:10:29:10:40 | access to local variable destFileName | +| ZipSlip.cs:16:35:16:66 | call to method GetFullPath : String | ZipSlip.cs:31:71:31:78 | access to local variable fullPath : String | +| ZipSlip.cs:16:35:16:66 | call to method GetFullPath : String | ZipSlip.cs:39:81:39:88 | access to local variable fullPath : String | +| ZipSlip.cs:16:52:16:65 | access to property FullName : String | ZipSlip.cs:16:35:16:66 | call to method GetFullPath : String | +| ZipSlip.cs:19:31:19:44 | access to property FullName : String | ZipSlip.cs:23:71:23:74 | access to local variable file : String | +| ZipSlip.cs:23:43:23:75 | call to method Combine : String | ZipSlip.cs:24:41:24:52 | access to local variable destFileName | +| ZipSlip.cs:23:71:23:74 | access to local variable file : String | ZipSlip.cs:23:43:23:75 | call to method Combine : String | +| ZipSlip.cs:31:43:31:79 | call to method Combine : String | ZipSlip.cs:32:41:32:52 | access to local variable destFilePath | +| ZipSlip.cs:31:43:31:79 | call to method Combine : String | ZipSlip.cs:36:45:36:56 | access to local variable destFilePath | +| ZipSlip.cs:31:71:31:78 | access to local variable fullPath : String | ZipSlip.cs:31:43:31:79 | call to method Combine : String | +| ZipSlip.cs:39:36:39:90 | call to method GetFullPath : String | ZipSlip.cs:40:41:40:52 | access to local variable destFilePath | +| ZipSlip.cs:39:53:39:89 | call to method Combine : String | ZipSlip.cs:39:36:39:90 | call to method GetFullPath : String | +| ZipSlip.cs:39:81:39:88 | access to local variable fullPath : String | ZipSlip.cs:39:53:39:89 | call to method Combine : String | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | ZipSlip.cs:69:74:69:85 | access to local variable destFilePath | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | ZipSlip.cs:76:71:76:82 | access to local variable destFilePath | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | ZipSlip.cs:83:57:83:68 | access to local variable destFilePath | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | ZipSlip.cs:91:58:91:69 | access to local variable destFilePath | +| ZipSlip.cs:62:72:62:85 | access to property FullName : String | ZipSlip.cs:62:47:62:86 | call to method Combine : String | +| ZipSlipBad.cs:9:31:9:73 | call to method Combine : String | ZipSlipBad.cs:10:29:10:40 | access to local variable destFileName | +| ZipSlipBad.cs:9:59:9:72 | access to property FullName : String | ZipSlipBad.cs:9:31:9:73 | call to method Combine : String | nodes +| ZipSlip.cs:16:35:16:66 | call to method GetFullPath : String | semmle.label | call to method GetFullPath : String | | ZipSlip.cs:16:52:16:65 | access to property FullName : String | semmle.label | access to property FullName : String | | ZipSlip.cs:19:31:19:44 | access to property FullName : String | semmle.label | access to property FullName : String | +| ZipSlip.cs:23:43:23:75 | call to method Combine : String | semmle.label | call to method Combine : String | +| ZipSlip.cs:23:71:23:74 | access to local variable file : String | semmle.label | access to local variable file : String | | ZipSlip.cs:24:41:24:52 | access to local variable destFileName | semmle.label | access to local variable destFileName | +| ZipSlip.cs:31:43:31:79 | call to method Combine : String | semmle.label | call to method Combine : String | +| ZipSlip.cs:31:71:31:78 | access to local variable fullPath : String | semmle.label | access to local variable fullPath : String | | ZipSlip.cs:32:41:32:52 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | | ZipSlip.cs:36:45:36:56 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | +| ZipSlip.cs:39:36:39:90 | call to method GetFullPath : String | semmle.label | call to method GetFullPath : String | +| ZipSlip.cs:39:53:39:89 | call to method Combine : String | semmle.label | call to method Combine : String | +| ZipSlip.cs:39:81:39:88 | access to local variable fullPath : String | semmle.label | access to local variable fullPath : String | | ZipSlip.cs:40:41:40:52 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | +| ZipSlip.cs:62:47:62:86 | call to method Combine : String | semmle.label | call to method Combine : String | | ZipSlip.cs:62:72:62:85 | access to property FullName : String | semmle.label | access to property FullName : String | | ZipSlip.cs:69:74:69:85 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | | ZipSlip.cs:76:71:76:82 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | | ZipSlip.cs:83:57:83:68 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | | ZipSlip.cs:91:58:91:69 | access to local variable destFilePath | semmle.label | access to local variable destFilePath | +| ZipSlipBad.cs:9:31:9:73 | call to method Combine : String | semmle.label | call to method Combine : String | | ZipSlipBad.cs:9:59:9:72 | access to property FullName : String | semmle.label | access to property FullName : String | | ZipSlipBad.cs:10:29:10:40 | access to local variable destFileName | semmle.label | access to local variable destFileName | #select diff --git a/csharp/ql/test/query-tests/Security Features/CWE-079/StoredXSS/XSS.expected b/csharp/ql/test/query-tests/Security Features/CWE-079/StoredXSS/XSS.expected index 8f04dc6f81ae..a661a86c71cc 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-079/StoredXSS/XSS.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-079/StoredXSS/XSS.expected @@ -1,10 +1,14 @@ edges -| XSS.cs:25:48:25:62 | access to field categoryTextBox : TextBox | XSS.cs:26:32:26:51 | call to method ToString | -| XSS.cs:25:48:25:62 | access to field categoryTextBox : TextBox | XSS.cs:27:29:27:48 | call to method ToString | -| XSS.cs:25:48:25:62 | access to field categoryTextBox : TextBox | XSS.cs:28:26:28:45 | call to method ToString | +| XSS.cs:25:13:25:21 | [post] access to local variable userInput [[]] : String | XSS.cs:26:32:26:40 | access to local variable userInput [[]] : String | +| XSS.cs:25:13:25:21 | [post] access to local variable userInput [[]] : String | XSS.cs:27:29:27:37 | access to local variable userInput [[]] : String | +| XSS.cs:25:13:25:21 | [post] access to local variable userInput [[]] : String | XSS.cs:28:26:28:34 | access to local variable userInput [[]] : String | +| XSS.cs:25:48:25:62 | access to field categoryTextBox : TextBox | XSS.cs:25:48:25:67 | access to property Text : String | +| XSS.cs:25:48:25:67 | access to property Text : String | XSS.cs:25:13:25:21 | [post] access to local variable userInput [[]] : String | +| XSS.cs:26:32:26:40 | access to local variable userInput [[]] : String | XSS.cs:26:32:26:51 | call to method ToString | +| XSS.cs:27:29:27:37 | access to local variable userInput [[]] : String | XSS.cs:27:29:27:48 | call to method ToString | +| XSS.cs:28:26:28:34 | access to local variable userInput [[]] : String | XSS.cs:28:26:28:45 | call to method ToString | | XSS.cs:37:27:37:53 | access to property QueryString : NameValueCollection | XSS.cs:38:36:38:39 | access to local variable name | | XSS.cs:57:27:57:65 | access to property QueryString : NameValueCollection | XSS.cs:59:22:59:25 | access to local variable name | -| XSS.cs:65:27:65:65 | access to property QueryString : NameValueCollection | XSS.cs:69:13:69:49 | access to property OutputStream | | XSS.cs:75:27:75:53 | access to property QueryString : NameValueCollection | XSS.cs:76:36:76:39 | access to local variable name | | XSS.cs:78:28:78:42 | access to property Request : HttpRequestBase | XSS.cs:79:36:79:40 | access to local variable name2 | | XSS.cs:85:27:85:53 | access to property QueryString : NameValueCollection | XSS.cs:86:28:86:31 | access to local variable name | @@ -19,7 +23,6 @@ edges | XSS.cs:28:26:28:45 | call to method ToString | XSS.cs:25:48:25:62 | access to field categoryTextBox : TextBox | XSS.cs:28:26:28:45 | call to method ToString | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:25:48:25:62 | access to field categoryTextBox : TextBox | User-provided value | | XSS.cs:38:36:38:39 | access to local variable name | XSS.cs:37:27:37:53 | access to property QueryString : NameValueCollection | XSS.cs:38:36:38:39 | access to local variable name | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:37:27:37:53 | access to property QueryString : NameValueCollection | User-provided value | | XSS.cs:59:22:59:25 | access to local variable name | XSS.cs:57:27:57:65 | access to property QueryString : NameValueCollection | XSS.cs:59:22:59:25 | access to local variable name | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:57:27:57:65 | access to property QueryString : NameValueCollection | User-provided value | -| XSS.cs:69:13:69:49 | access to property OutputStream | XSS.cs:65:27:65:65 | access to property QueryString : NameValueCollection | XSS.cs:69:13:69:49 | access to property OutputStream | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:65:27:65:65 | access to property QueryString : NameValueCollection | User-provided value | | XSS.cs:76:36:76:39 | access to local variable name | XSS.cs:75:27:75:53 | access to property QueryString : NameValueCollection | XSS.cs:76:36:76:39 | access to local variable name | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:75:27:75:53 | access to property QueryString : NameValueCollection | User-provided value | | XSS.cs:79:36:79:40 | access to local variable name2 | XSS.cs:78:28:78:42 | access to property Request : HttpRequestBase | XSS.cs:79:36:79:40 | access to local variable name2 | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:78:28:78:42 | access to property Request : HttpRequestBase | User-provided value | | XSS.cs:86:28:86:31 | access to local variable name | XSS.cs:85:27:85:53 | access to property QueryString : NameValueCollection | XSS.cs:86:28:86:31 | access to local variable name | $@ flows to here and is written to HTML or JavaScript. | XSS.cs:85:27:85:53 | access to property QueryString : NameValueCollection | User-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-079/XSS/XSS.expected b/csharp/ql/test/query-tests/Security Features/CWE-079/XSS/XSS.expected index 81946f1054ed..36d8ecfb339c 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-079/XSS/XSS.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-079/XSS/XSS.expected @@ -2,11 +2,16 @@ edges | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | XSSAspNet.cs:27:30:27:34 | access to local variable sayHi | | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | XSSAspNet.cs:37:40:37:44 | access to local variable sayHi | | XSSAspNet.cs:44:28:44:46 | access to property QueryString : NameValueCollection | XSSAspNet.cs:44:28:44:55 | access to indexer | -| XSSAspNetCore.cs:21:52:21:64 | access to property Query : IQueryCollection | XSSAspNetCore.cs:21:52:21:76 | call to operator implicit conversion | +| XSSAspNetCore.cs:21:52:21:64 | access to property Query : IQueryCollection | XSSAspNetCore.cs:21:52:21:76 | access to indexer : StringValues | +| XSSAspNetCore.cs:21:52:21:76 | access to indexer : StringValues | XSSAspNetCore.cs:21:52:21:76 | call to operator implicit conversion | | XSSAspNetCore.cs:40:56:40:58 | foo : String | XSSAspNetCore.cs:44:51:44:53 | access to parameter foo | -| XSSAspNetCore.cs:58:43:58:55 | access to property Query : IQueryCollection | XSSAspNetCore.cs:58:43:58:73 | call to method ToString | +| XSSAspNetCore.cs:58:43:58:55 | access to property Query : IQueryCollection | XSSAspNetCore.cs:58:43:58:62 | access to indexer : StringValues | +| XSSAspNetCore.cs:58:43:58:62 | access to indexer : StringValues | XSSAspNetCore.cs:58:43:58:73 | call to method ToString | +| XSSAspNetCore.cs:61:44:61:56 | access to property Query : IQueryCollection | XSSAspNetCore.cs:61:44:61:63 | access to indexer : StringValues | | XSSAspNetCore.cs:61:44:61:56 | access to property Query : IQueryCollection | XSSAspNetCore.cs:61:44:61:66 | access to indexer | -| XSSAspNetCore.cs:72:51:72:65 | access to property Headers : IHeaderDictionary | XSSAspNetCore.cs:72:51:72:72 | call to operator implicit conversion | +| XSSAspNetCore.cs:61:44:61:63 | access to indexer : StringValues | XSSAspNetCore.cs:61:44:61:66 | access to indexer | +| XSSAspNetCore.cs:72:51:72:65 | access to property Headers : IHeaderDictionary | XSSAspNetCore.cs:72:51:72:72 | access to indexer : StringValues | +| XSSAspNetCore.cs:72:51:72:72 | access to indexer : StringValues | XSSAspNetCore.cs:72:51:72:72 | call to operator implicit conversion | #select | XSSAspNet.cs:27:30:27:34 | access to local variable sayHi | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | XSSAspNet.cs:27:30:27:34 | access to local variable sayHi | $@ flows to here and is written to HTML or JavaScript: System.Web.WebPages.WebPage.WriteLiteral() method. | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | User-provided value | | XSSAspNet.cs:37:40:37:44 | access to local variable sayHi | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | XSSAspNet.cs:37:40:37:44 | access to local variable sayHi | $@ flows to here and is written to HTML or JavaScript: System.Web.WebPages.WebPage.WriteLiteralTo() method. | XSSAspNet.cs:20:25:20:43 | access to property QueryString : NameValueCollection | User-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-112/MissingXMLValidation.expected b/csharp/ql/test/query-tests/Security Features/CWE-112/MissingXMLValidation.expected index b9abafda2f46..38c31c17825f 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-112/MissingXMLValidation.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-112/MissingXMLValidation.expected @@ -1,16 +1,26 @@ edges -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:29:26:29:58 | object creation of type StringReader | -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:37:26:37:58 | object creation of type StringReader | -| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:47:26:47:58 | object creation of type StringReader | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:18:43:18:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:23:43:23:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:29:43:29:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:37:43:37:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:47:43:47:57 | access to local variable userProvidedXml : String | +| MissingXMLValidation.cs:18:43:18:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | +| MissingXMLValidation.cs:23:43:23:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | +| MissingXMLValidation.cs:29:43:29:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:29:26:29:58 | object creation of type StringReader | +| MissingXMLValidation.cs:37:43:37:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:37:26:37:58 | object creation of type StringReader | +| MissingXMLValidation.cs:47:43:47:57 | access to local variable userProvidedXml : String | MissingXMLValidation.cs:47:26:47:58 | object creation of type StringReader | nodes | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:18:43:18:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:23:43:23:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | | MissingXMLValidation.cs:29:26:29:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:29:43:29:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | | MissingXMLValidation.cs:37:26:37:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:37:43:37:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | | MissingXMLValidation.cs:47:26:47:58 | object creation of type StringReader | semmle.label | object creation of type StringReader | +| MissingXMLValidation.cs:47:43:47:57 | access to local variable userProvidedXml : String | semmle.label | access to local variable userProvidedXml : String | #select | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:18:26:18:58 | object creation of type StringReader | $@ flows to here and is processed as XML without validation because there is no 'XmlReaderSettings' instance specifying schema validation. | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString | User-provided value | | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString : NameValueCollection | MissingXMLValidation.cs:23:26:23:58 | object creation of type StringReader | $@ flows to here and is processed as XML without validation because the 'XmlReaderSettings' instance does not specify the 'ValidationType' as 'Schema'. | MissingXMLValidation.cs:14:34:14:56 | access to property QueryString | User-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs index 4ec825ddd63f..90be31d0a07f 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs @@ -13,18 +13,18 @@ public void CryptoMethod() // GOOD: Key size is greater than 128 new RC2CryptoServiceProvider().EffectiveKeySize = 256; - // BAD: Key size is less than 1024. + // BAD: Key size is less than 2048. DSACryptoServiceProvider dsaBad = new DSACryptoServiceProvider(512); - // GOOD: Key size defaults to 1024. + // GOOD: Key size defaults to 2048. DSACryptoServiceProvider dsaGood1 = new DSACryptoServiceProvider(); - // GOOD: Key size is greater than 1024. + // GOOD: Key size is greater than 2048. DSACryptoServiceProvider dsaGood2 = new DSACryptoServiceProvider(2048); - // BAD: Key size is less than 1024. + // BAD: Key size is less than 2048. RSACryptoServiceProvider rsaBad = new RSACryptoServiceProvider(512); - // GOOD: Key size defaults to 1024. + // GOOD: Key size defaults to 2048. RSACryptoServiceProvider rsaGood1 = new RSACryptoServiceProvider(); - // GOOD: Key size is greater than 1024. + // GOOD: Key size is greater than 2048. RSACryptoServiceProvider rsaGood2 = new RSACryptoServiceProvider(2048); } } diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected index dc03302c7f35..feb87da77d28 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected @@ -1,3 +1,3 @@ | InsufficientKeySize.cs:10:9:10:60 | ... = ... | Key size should be at least 128 bits for RC2 encryption. | -| InsufficientKeySize.cs:17:43:17:75 | object creation of type DSACryptoServiceProvider | Key size should be at least 1024 bits for DSA encryption. | -| InsufficientKeySize.cs:24:43:24:75 | object creation of type RSACryptoServiceProvider | Key size should be at least 1024 bits for RSA encryption. | +| InsufficientKeySize.cs:17:43:17:75 | object creation of type DSACryptoServiceProvider | Key size should be at least 2048 bits for DSA encryption. | +| InsufficientKeySize.cs:24:43:24:75 | object creation of type RSACryptoServiceProvider | Key size should be at least 2048 bits for RSA encryption. | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-338/InsecureRandomness.expected b/csharp/ql/test/query-tests/Security Features/CWE-338/InsecureRandomness.expected index b6aff7ad8aa4..138451c31dcb 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-338/InsecureRandomness.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-338/InsecureRandomness.expected @@ -1,8 +1,14 @@ edges -| InsecureRandomness.cs:28:23:28:43 | (...) ... : Int32 | InsecureRandomness.cs:31:16:31:32 | call to method ToString : String | +| InsecureRandomness.cs:28:13:28:16 | [post] access to local variable data [[]] : Int32 | InsecureRandomness.cs:29:57:29:60 | access to local variable data [[]] : Int32 | +| InsecureRandomness.cs:28:23:28:43 | (...) ... : Int32 | InsecureRandomness.cs:28:13:28:16 | [post] access to local variable data [[]] : Int32 | | InsecureRandomness.cs:28:29:28:43 | call to method Next : Int32 | InsecureRandomness.cs:28:23:28:43 | (...) ... : Int32 | +| InsecureRandomness.cs:29:13:29:18 | [post] access to local variable result [[]] : String | InsecureRandomness.cs:31:16:31:21 | access to local variable result [[]] : String | +| InsecureRandomness.cs:29:27:29:61 | call to method GetString : String | InsecureRandomness.cs:29:13:29:18 | [post] access to local variable result [[]] : String | +| InsecureRandomness.cs:29:57:29:60 | access to local variable data [[]] : Int32 | InsecureRandomness.cs:29:27:29:61 | call to method GetString : String | +| InsecureRandomness.cs:31:16:31:21 | access to local variable result [[]] : String | InsecureRandomness.cs:31:16:31:32 | call to method ToString : String | | InsecureRandomness.cs:31:16:31:32 | call to method ToString : String | InsecureRandomness.cs:12:27:12:50 | call to method InsecureRandomString | -| InsecureRandomness.cs:60:31:60:39 | call to method Next : Int32 | InsecureRandomness.cs:62:16:62:32 | call to method ToString : String | +| InsecureRandomness.cs:60:31:60:39 | call to method Next : Int32 | InsecureRandomness.cs:62:16:62:21 | access to local variable result : String | +| InsecureRandomness.cs:62:16:62:21 | access to local variable result : String | InsecureRandomness.cs:62:16:62:32 | call to method ToString : String | | InsecureRandomness.cs:62:16:62:32 | call to method ToString : String | InsecureRandomness.cs:13:20:13:56 | call to method InsecureRandomStringFromSelection | | InsecureRandomness.cs:72:31:72:39 | call to method Next : Int32 | InsecureRandomness.cs:74:16:74:21 | access to local variable result : String | | InsecureRandomness.cs:74:16:74:21 | access to local variable result : String | InsecureRandomness.cs:14:20:14:54 | call to method InsecureRandomStringFromIndexer | @@ -10,10 +16,16 @@ nodes | InsecureRandomness.cs:12:27:12:50 | call to method InsecureRandomString | semmle.label | call to method InsecureRandomString | | InsecureRandomness.cs:13:20:13:56 | call to method InsecureRandomStringFromSelection | semmle.label | call to method InsecureRandomStringFromSelection | | InsecureRandomness.cs:14:20:14:54 | call to method InsecureRandomStringFromIndexer | semmle.label | call to method InsecureRandomStringFromIndexer | +| InsecureRandomness.cs:28:13:28:16 | [post] access to local variable data [[]] : Int32 | semmle.label | [post] access to local variable data [[]] : Int32 | | InsecureRandomness.cs:28:23:28:43 | (...) ... : Int32 | semmle.label | (...) ... : Int32 | | InsecureRandomness.cs:28:29:28:43 | call to method Next : Int32 | semmle.label | call to method Next : Int32 | +| InsecureRandomness.cs:29:13:29:18 | [post] access to local variable result [[]] : String | semmle.label | [post] access to local variable result [[]] : String | +| InsecureRandomness.cs:29:27:29:61 | call to method GetString : String | semmle.label | call to method GetString : String | +| InsecureRandomness.cs:29:57:29:60 | access to local variable data [[]] : Int32 | semmle.label | access to local variable data [[]] : Int32 | +| InsecureRandomness.cs:31:16:31:21 | access to local variable result [[]] : String | semmle.label | access to local variable result [[]] : String | | InsecureRandomness.cs:31:16:31:32 | call to method ToString : String | semmle.label | call to method ToString : String | | InsecureRandomness.cs:60:31:60:39 | call to method Next : Int32 | semmle.label | call to method Next : Int32 | +| InsecureRandomness.cs:62:16:62:21 | access to local variable result : String | semmle.label | access to local variable result : String | | InsecureRandomness.cs:62:16:62:32 | call to method ToString : String | semmle.label | call to method ToString : String | | InsecureRandomness.cs:72:31:72:39 | call to method Next : Int32 | semmle.label | call to method Next : Int32 | | InsecureRandomness.cs:74:16:74:21 | access to local variable result : String | semmle.label | access to local variable result : String | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected b/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected index afcc216fc020..2ed9af3f8f20 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected @@ -4,15 +4,19 @@ edges | UrlRedirect.cs:39:44:39:66 | access to property QueryString : NameValueCollection | UrlRedirect.cs:39:44:39:74 | access to indexer | | UrlRedirect.cs:40:47:40:69 | access to property QueryString : NameValueCollection | UrlRedirect.cs:40:47:40:77 | access to indexer | | UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:18:22:18:26 | access to parameter value | -| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:21:44:21:48 | call to operator implicit conversion | -| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:27:46:27:50 | call to operator implicit conversion | +| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:21:44:21:48 | access to parameter value : String | +| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:27:46:27:50 | access to parameter value : String | | UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:33:66:33:70 | access to parameter value | -| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:36:49:36:53 | call to operator implicit conversion | +| UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:36:49:36:53 | access to parameter value : String | | UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:39:69:39:73 | access to parameter value | | UrlRedirectCore.cs:15:44:15:48 | value : String | UrlRedirectCore.cs:42:39:42:53 | ... + ... | +| UrlRedirectCore.cs:21:44:21:48 | access to parameter value : String | UrlRedirectCore.cs:21:44:21:48 | call to operator implicit conversion | +| UrlRedirectCore.cs:27:46:27:50 | access to parameter value : String | UrlRedirectCore.cs:27:46:27:50 | call to operator implicit conversion | +| UrlRedirectCore.cs:36:49:36:53 | access to parameter value : String | UrlRedirectCore.cs:36:49:36:53 | call to operator implicit conversion | | UrlRedirectCore.cs:47:51:47:55 | value : String | UrlRedirectCore.cs:50:28:50:32 | access to parameter value | -| UrlRedirectCore.cs:47:51:47:55 | value : String | UrlRedirectCore.cs:55:32:55:45 | object creation of type Uri | +| UrlRedirectCore.cs:47:51:47:55 | value : String | UrlRedirectCore.cs:55:40:55:44 | access to parameter value : String | | UrlRedirectCore.cs:47:51:47:55 | value : String | UrlRedirectCore.cs:58:31:58:35 | access to parameter value | +| UrlRedirectCore.cs:55:40:55:44 | access to parameter value : String | UrlRedirectCore.cs:55:32:55:45 | object creation of type Uri | nodes | UrlRedirect.cs:14:31:14:53 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | UrlRedirect.cs:14:31:14:61 | access to indexer | semmle.label | access to indexer | @@ -24,15 +28,19 @@ nodes | UrlRedirect.cs:49:29:49:31 | access to local variable url | semmle.label | access to local variable url | | UrlRedirectCore.cs:15:44:15:48 | value : String | semmle.label | value : String | | UrlRedirectCore.cs:18:22:18:26 | access to parameter value | semmle.label | access to parameter value | +| UrlRedirectCore.cs:21:44:21:48 | access to parameter value : String | semmle.label | access to parameter value : String | | UrlRedirectCore.cs:21:44:21:48 | call to operator implicit conversion | semmle.label | call to operator implicit conversion | +| UrlRedirectCore.cs:27:46:27:50 | access to parameter value : String | semmle.label | access to parameter value : String | | UrlRedirectCore.cs:27:46:27:50 | call to operator implicit conversion | semmle.label | call to operator implicit conversion | | UrlRedirectCore.cs:33:66:33:70 | access to parameter value | semmle.label | access to parameter value | +| UrlRedirectCore.cs:36:49:36:53 | access to parameter value : String | semmle.label | access to parameter value : String | | UrlRedirectCore.cs:36:49:36:53 | call to operator implicit conversion | semmle.label | call to operator implicit conversion | | UrlRedirectCore.cs:39:69:39:73 | access to parameter value | semmle.label | access to parameter value | | UrlRedirectCore.cs:42:39:42:53 | ... + ... | semmle.label | ... + ... | | UrlRedirectCore.cs:47:51:47:55 | value : String | semmle.label | value : String | | UrlRedirectCore.cs:50:28:50:32 | access to parameter value | semmle.label | access to parameter value | | UrlRedirectCore.cs:55:32:55:45 | object creation of type Uri | semmle.label | object creation of type Uri | +| UrlRedirectCore.cs:55:40:55:44 | access to parameter value : String | semmle.label | access to parameter value : String | | UrlRedirectCore.cs:58:31:58:35 | access to parameter value | semmle.label | access to parameter value | #select | UrlRedirect.cs:14:31:14:61 | access to indexer | UrlRedirect.cs:14:31:14:53 | access to property QueryString : NameValueCollection | UrlRedirect.cs:14:31:14:61 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:14:31:14:53 | access to property QueryString | user-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-807/ConditionalBypass.expected b/csharp/ql/test/query-tests/Security Features/CWE-807/ConditionalBypass.expected index c969d8a9befb..72c7464ba38a 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-807/ConditionalBypass.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-807/ConditionalBypass.expected @@ -1,32 +1,44 @@ edges | ConditionalBypass.cs:14:26:14:48 | access to property QueryString : NameValueCollection | ConditionalBypass.cs:18:13:18:30 | ... == ... | -| ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:24:13:24:29 | access to property Value : String | -| ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:29:13:29:29 | access to property Value : String | +| ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:24:13:24:23 | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:29:13:29:23 | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:24:13:24:23 | access to local variable adminCookie : HttpCookie | ConditionalBypass.cs:24:13:24:29 | access to property Value : String | | ConditionalBypass.cs:24:13:24:29 | access to property Value : String | ConditionalBypass.cs:24:13:24:45 | call to method Equals | +| ConditionalBypass.cs:29:13:29:23 | access to local variable adminCookie : HttpCookie | ConditionalBypass.cs:29:13:29:29 | access to property Value : String | | ConditionalBypass.cs:29:13:29:29 | access to property Value : String | ConditionalBypass.cs:29:13:29:40 | ... == ... | -| ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | ConditionalBypass.cs:46:13:46:29 | access to property HostName : String | -| ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | ConditionalBypass.cs:51:13:51:29 | access to property HostName | +| ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | ConditionalBypass.cs:46:13:46:20 | access to local variable hostInfo : IPHostEntry | +| ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | ConditionalBypass.cs:51:13:51:20 | access to local variable hostInfo : IPHostEntry | +| ConditionalBypass.cs:46:13:46:20 | access to local variable hostInfo : IPHostEntry | ConditionalBypass.cs:46:13:46:29 | access to property HostName : String | | ConditionalBypass.cs:46:13:46:29 | access to property HostName : String | ConditionalBypass.cs:46:13:46:46 | ... == ... | -| ConditionalBypass.cs:72:34:72:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:74:13:74:29 | access to property Value : String | +| ConditionalBypass.cs:51:13:51:20 | access to local variable hostInfo : IPHostEntry | ConditionalBypass.cs:51:13:51:29 | access to property HostName | +| ConditionalBypass.cs:72:34:72:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:74:13:74:23 | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:74:13:74:23 | access to local variable adminCookie : HttpCookie | ConditionalBypass.cs:74:13:74:29 | access to property Value : String | | ConditionalBypass.cs:74:13:74:29 | access to property Value : String | ConditionalBypass.cs:74:13:74:40 | ... == ... | -| ConditionalBypass.cs:85:34:85:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:86:13:86:29 | access to property Value : String | +| ConditionalBypass.cs:85:34:85:52 | access to property Cookies : HttpCookieCollection | ConditionalBypass.cs:86:13:86:23 | access to local variable adminCookie : HttpCookie | +| ConditionalBypass.cs:86:13:86:23 | access to local variable adminCookie : HttpCookie | ConditionalBypass.cs:86:13:86:29 | access to property Value : String | | ConditionalBypass.cs:86:13:86:29 | access to property Value : String | ConditionalBypass.cs:86:13:86:40 | ... == ... | nodes | ConditionalBypass.cs:14:26:14:48 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | ConditionalBypass.cs:18:13:18:30 | ... == ... | semmle.label | ... == ... | | ConditionalBypass.cs:21:34:21:52 | access to property Cookies : HttpCookieCollection | semmle.label | access to property Cookies : HttpCookieCollection | +| ConditionalBypass.cs:24:13:24:23 | access to local variable adminCookie : HttpCookie | semmle.label | access to local variable adminCookie : HttpCookie | | ConditionalBypass.cs:24:13:24:29 | access to property Value : String | semmle.label | access to property Value : String | | ConditionalBypass.cs:24:13:24:45 | call to method Equals | semmle.label | call to method Equals | +| ConditionalBypass.cs:29:13:29:23 | access to local variable adminCookie : HttpCookie | semmle.label | access to local variable adminCookie : HttpCookie | | ConditionalBypass.cs:29:13:29:29 | access to property Value : String | semmle.label | access to property Value : String | | ConditionalBypass.cs:29:13:29:40 | ... == ... | semmle.label | ... == ... | | ConditionalBypass.cs:44:32:44:66 | call to method GetHostByAddress : IPHostEntry | semmle.label | call to method GetHostByAddress : IPHostEntry | +| ConditionalBypass.cs:46:13:46:20 | access to local variable hostInfo : IPHostEntry | semmle.label | access to local variable hostInfo : IPHostEntry | | ConditionalBypass.cs:46:13:46:29 | access to property HostName : String | semmle.label | access to property HostName : String | | ConditionalBypass.cs:46:13:46:46 | ... == ... | semmle.label | ... == ... | +| ConditionalBypass.cs:51:13:51:20 | access to local variable hostInfo : IPHostEntry | semmle.label | access to local variable hostInfo : IPHostEntry | | ConditionalBypass.cs:51:13:51:29 | access to property HostName | semmle.label | access to property HostName | | ConditionalBypass.cs:72:34:72:52 | access to property Cookies : HttpCookieCollection | semmle.label | access to property Cookies : HttpCookieCollection | +| ConditionalBypass.cs:74:13:74:23 | access to local variable adminCookie : HttpCookie | semmle.label | access to local variable adminCookie : HttpCookie | | ConditionalBypass.cs:74:13:74:29 | access to property Value : String | semmle.label | access to property Value : String | | ConditionalBypass.cs:74:13:74:40 | ... == ... | semmle.label | ... == ... | | ConditionalBypass.cs:85:34:85:52 | access to property Cookies : HttpCookieCollection | semmle.label | access to property Cookies : HttpCookieCollection | +| ConditionalBypass.cs:86:13:86:23 | access to local variable adminCookie : HttpCookie | semmle.label | access to local variable adminCookie : HttpCookie | | ConditionalBypass.cs:86:13:86:29 | access to property Value : String | semmle.label | access to property Value : String | | ConditionalBypass.cs:86:13:86:40 | ... == ... | semmle.label | ... == ... | #select diff --git a/csharp/ql/test/query-tests/Security Features/CWE-838/InappropriateEncoding.expected b/csharp/ql/test/query-tests/Security Features/CWE-838/InappropriateEncoding.expected index 3bdac85df068..0dd5f0c1e2cb 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-838/InappropriateEncoding.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-838/InappropriateEncoding.expected @@ -3,7 +3,8 @@ edges | InappropriateEncoding.cs:15:28:15:40 | call to method Encode : String | InappropriateEncoding.cs:20:46:20:51 | access to local variable query1 | | InappropriateEncoding.cs:36:28:36:55 | call to method UrlEncode : String | InappropriateEncoding.cs:37:32:37:43 | access to local variable encodedValue | | InappropriateEncoding.cs:36:28:36:55 | call to method UrlEncode : String | InappropriateEncoding.cs:38:22:38:59 | ... + ... | -| InappropriateEncoding.cs:36:28:36:55 | call to method UrlEncode : String | InappropriateEncoding.cs:39:22:39:71 | call to method Format | +| InappropriateEncoding.cs:36:28:36:55 | call to method UrlEncode : String | InappropriateEncoding.cs:39:59:39:70 | access to local variable encodedValue : String | +| InappropriateEncoding.cs:39:59:39:70 | access to local variable encodedValue : String | InappropriateEncoding.cs:39:22:39:71 | call to method Format | | InappropriateEncoding.cs:57:28:57:56 | call to method HtmlEncode : String | InappropriateEncoding.cs:58:31:58:42 | access to local variable encodedValue | | InappropriateEncoding.cs:68:16:68:42 | call to method Replace : String | InappropriateEncoding.cs:15:28:15:40 | call to method Encode : String | | SqlEncode.cs:16:62:16:87 | call to method Replace : String | SqlEncode.cs:17:46:17:50 | access to local variable query | @@ -20,6 +21,7 @@ nodes | InappropriateEncoding.cs:37:32:37:43 | access to local variable encodedValue | semmle.label | access to local variable encodedValue | | InappropriateEncoding.cs:38:22:38:59 | ... + ... | semmle.label | ... + ... | | InappropriateEncoding.cs:39:22:39:71 | call to method Format | semmle.label | call to method Format | +| InappropriateEncoding.cs:39:59:39:70 | access to local variable encodedValue : String | semmle.label | access to local variable encodedValue : String | | InappropriateEncoding.cs:57:28:57:56 | call to method HtmlEncode : String | semmle.label | call to method HtmlEncode : String | | InappropriateEncoding.cs:58:31:58:42 | access to local variable encodedValue | semmle.label | access to local variable encodedValue | | InappropriateEncoding.cs:68:16:68:42 | call to method Replace : String | semmle.label | call to method Replace : String | diff --git a/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected b/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected index 5c7911617f30..967642a861fc 100644 --- a/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected +++ b/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected @@ -1 +1 @@ -| // This file contains auto-generated code.\n// original-extractor-options: /r:System.Text.RegularExpressions.dll /r:System.Collections.Specialized.dll /r:System.Net.dll /r:System.Web.dll /r:System.Net.HttpListener.dll /r:System.Collections.Specialized.dll /r:System.Private.Uri.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Parallel.dll /r:System.Collections.Concurrent.dll /r:System.Linq.Expressions.dll /r:System.Collections.dll /r:System.Linq.Queryable.dll /r:System.Linq.dll /r:System.Collections.NonGeneric.dll /r:System.ObjectModel.dll /r:System.ComponentModel.TypeConverter.dll /r:System.IO.Compression.dll /r:System.IO.Pipes.dll /r:System.Net.Primitives.dll /r:System.Net.Security.dll /r:System.Security.Cryptography.Primitives.dll /r:System.Text.RegularExpressions.dll ${testdir}/../../resources/stubs/System.Web.cs /r:System.Runtime.Serialization.Primitives.dll\n\nnamespace System\n{\n// Generated from `System.Uri` in `System.Private.Uri, Version=4.0.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Uri : System.Runtime.Serialization.ISerializable\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;\n}\n\nnamespace Collections\n{\n// Generated from `System.Collections.SortedList` in `System.Collections.NonGeneric, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedList : System.ICloneable, System.Collections.IEnumerable, System.Collections.IDictionary, System.Collections.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public virtual System.Collections.ICollection Keys { get => throw null; }\n public virtual System.Collections.ICollection Values { get => throw null; }\n public virtual System.Collections.IDictionaryEnumerator GetEnumerator() => throw null;\n public virtual bool Contains(object key) => throw null;\n public virtual bool IsFixedSize { get => throw null; }\n public virtual bool IsReadOnly { get => throw null; }\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object GetByIndex(int index) => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual object this[object key] { get => throw null; set => throw null; }\n public virtual void Add(object key, object value) => throw null;\n public virtual void Clear() => throw null;\n public virtual void CopyTo(System.Array array, int arrayIndex) => throw null;\n public virtual void Remove(object key) => throw null;\n}\n\nnamespace Generic\n{\n// Generated from `System.Collections.Generic.Stack<>` in `System.Collections, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public T Peek() => throw null;\n public int Count { get => throw null; }\n void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) => throw null;\n}\n\n}\nnamespace Specialized\n{\n// Generated from `System.Collections.Specialized.NameObjectCollectionBase` in `System.Collections.Specialized, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class NameObjectCollectionBase : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection\n{\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual int Count { get => throw null; }\n public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n public virtual void OnDeserialization(object sender) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Specialized.NameValueCollection` in `System.Collections.Specialized, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n public string this[string name] { get => throw null; set => throw null; }\n}\n\n}\n}\nnamespace IO\n{\n// Generated from `System.IO.StringReader` in `System.Runtime.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class StringReader : System.IO.TextReader\n{\n protected override void Dispose(bool disposing) => throw null;\n public StringReader(string s) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Char[] buffer, int index, int count) => throw null;\n public override System.Threading.Tasks.Task ReadBlockAsync(System.Char[] buffer, int index, int count) => throw null;\n public override System.Threading.Tasks.Task ReadLineAsync() => throw null;\n public override System.Threading.Tasks.Task ReadToEndAsync() => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadBlockAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override int Peek() => throw null;\n public override int Read() => throw null;\n public override int Read(System.Char[] buffer, int index, int count) => throw null;\n public override int Read(System.Span buffer) => throw null;\n public override int ReadBlock(System.Span buffer) => throw null;\n public override string ReadLine() => throw null;\n public override string ReadToEnd() => throw null;\n public override void Close() => throw null;\n}\n\n}\nnamespace Linq\n{\n// Generated from `System.Linq.Enumerable` in `System.Linq, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Enumerable\n{\n public static System.Collections.Generic.IEnumerable Select(this System.Collections.Generic.IEnumerable source, System.Func selector) => throw null;\n}\n\n// Generated from `System.Linq.IQueryable` in `System.Linq.Expressions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IQueryable : System.Collections.IEnumerable\n{\n}\n\n// Generated from `System.Linq.ParallelEnumerable` in `System.Linq.Parallel, Version=4.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class ParallelEnumerable\n{\n public static System.Linq.ParallelQuery AsParallel(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery` in `System.Linq.Parallel, Version=4.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Collections.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Queryable` in `System.Linq.Queryable, Version=4.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Queryable\n{\n public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) => throw null;\n}\n\n}\nnamespace Runtime\n{\nnamespace Serialization\n{\n// Generated from `System.Runtime.Serialization.DataContractAttribute` in `System.Runtime.Serialization.Primitives, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataContractAttribute : System.Attribute\n{\n public DataContractAttribute() => throw null;\n}\n\n// Generated from `System.Runtime.Serialization.DataMemberAttribute` in `System.Runtime.Serialization.Primitives, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataMemberAttribute : System.Attribute\n{\n public DataMemberAttribute() => throw null;\n}\n\n}\n}\nnamespace Text\n{\nnamespace RegularExpressions\n{\n// Generated from `System.Text.RegularExpressions.Capture` in `System.Text.RegularExpressions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Capture\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.Group` in `System.Text.RegularExpressions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Group : System.Text.RegularExpressions.Capture\n{\n}\n\n// Generated from `System.Text.RegularExpressions.Match` in `System.Text.RegularExpressions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Match : System.Text.RegularExpressions.Group\n{\n}\n\n// Generated from `System.Text.RegularExpressions.RegexOptions` in `System.Text.RegularExpressions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic enum RegexOptions\n{\n IgnoreCase,\n}\n\n// Generated from `System.Text.RegularExpressions.Regex` in `System.Text.RegularExpressions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Regex : System.Runtime.Serialization.ISerializable\n{\n public Regex(string pattern) => throw null;\n public Regex(string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public System.Text.RegularExpressions.Match Match(string input) => throw null;\n public override string ToString() => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public string Replace(string input, string replacement) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n}\n}\n}\n | +| // This file contains auto-generated code.\n// original-extractor-options: /r:System.Text.RegularExpressions.dll /r:System.Collections.Specialized.dll /r:System.Net.dll /r:System.Web.dll /r:System.Net.HttpListener.dll /r:System.Collections.Specialized.dll /r:System.Private.Uri.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Parallel.dll /r:System.Collections.Concurrent.dll /r:System.Linq.Expressions.dll /r:System.Collections.dll /r:System.Linq.Queryable.dll /r:System.Linq.dll /r:System.Collections.NonGeneric.dll /r:System.ObjectModel.dll /r:System.ComponentModel.TypeConverter.dll /r:System.IO.Compression.dll /r:System.IO.Pipes.dll /r:System.Net.Primitives.dll /r:System.Net.Security.dll /r:System.Security.Cryptography.Primitives.dll /r:System.Text.RegularExpressions.dll ${testdir}/../../resources/stubs/System.Web.cs /r:System.Runtime.Serialization.Primitives.dll\n\nnamespace System\n{\n// Generated from `System.Uri` in `System.Private.Uri, Version=4.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Uri : System.Runtime.Serialization.ISerializable\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;\n}\n\nnamespace Collections\n{\n// Generated from `System.Collections.SortedList` in `System.Collections.NonGeneric, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedList : System.ICloneable, System.Collections.IEnumerable, System.Collections.IDictionary, System.Collections.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public virtual System.Collections.ICollection Keys { get => throw null; }\n public virtual System.Collections.ICollection Values { get => throw null; }\n public virtual System.Collections.IDictionaryEnumerator GetEnumerator() => throw null;\n public virtual bool Contains(object key) => throw null;\n public virtual bool IsFixedSize { get => throw null; }\n public virtual bool IsReadOnly { get => throw null; }\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object GetByIndex(int index) => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual object this[object key] { get => throw null; set => throw null; }\n public virtual void Add(object key, object value) => throw null;\n public virtual void Clear() => throw null;\n public virtual void CopyTo(System.Array array, int arrayIndex) => throw null;\n public virtual void Remove(object key) => throw null;\n}\n\nnamespace Generic\n{\n// Generated from `System.Collections.Generic.Stack<>` in `System.Collections, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public T Peek() => throw null;\n public int Count { get => throw null; }\n void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) => throw null;\n}\n\n}\nnamespace Specialized\n{\n// Generated from `System.Collections.Specialized.NameObjectCollectionBase` in `System.Collections.Specialized, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class NameObjectCollectionBase : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection\n{\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual int Count { get => throw null; }\n public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n public virtual void OnDeserialization(object sender) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Specialized.NameValueCollection` in `System.Collections.Specialized, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n public string this[string name] { get => throw null; set => throw null; }\n}\n\n}\n}\nnamespace IO\n{\n// Generated from `System.IO.StringReader` in `System.Runtime.Extensions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class StringReader : System.IO.TextReader\n{\n protected override void Dispose(bool disposing) => throw null;\n public StringReader(string s) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Char[] buffer, int index, int count) => throw null;\n public override System.Threading.Tasks.Task ReadBlockAsync(System.Char[] buffer, int index, int count) => throw null;\n public override System.Threading.Tasks.Task ReadLineAsync() => throw null;\n public override System.Threading.Tasks.Task ReadToEndAsync() => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadBlockAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override int Peek() => throw null;\n public override int Read() => throw null;\n public override int Read(System.Char[] buffer, int index, int count) => throw null;\n public override int Read(System.Span buffer) => throw null;\n public override int ReadBlock(System.Span buffer) => throw null;\n public override string ReadLine() => throw null;\n public override string ReadToEnd() => throw null;\n public override void Close() => throw null;\n}\n\n}\nnamespace Linq\n{\n// Generated from `System.Linq.Enumerable` in `System.Linq, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Enumerable\n{\n public static System.Collections.Generic.IEnumerable Select(this System.Collections.Generic.IEnumerable source, System.Func selector) => throw null;\n}\n\n// Generated from `System.Linq.IQueryable` in `System.Linq.Expressions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IQueryable : System.Collections.IEnumerable\n{\n}\n\n// Generated from `System.Linq.ParallelEnumerable` in `System.Linq.Parallel, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class ParallelEnumerable\n{\n public static System.Linq.ParallelQuery AsParallel(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery` in `System.Linq.Parallel, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Collections.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Queryable` in `System.Linq.Queryable, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Queryable\n{\n public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) => throw null;\n}\n\n}\nnamespace Runtime\n{\nnamespace Serialization\n{\n// Generated from `System.Runtime.Serialization.DataContractAttribute` in `System.Runtime.Serialization.Primitives, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataContractAttribute : System.Attribute\n{\n public DataContractAttribute() => throw null;\n}\n\n// Generated from `System.Runtime.Serialization.DataMemberAttribute` in `System.Runtime.Serialization.Primitives, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataMemberAttribute : System.Attribute\n{\n public DataMemberAttribute() => throw null;\n}\n\n}\n}\nnamespace Text\n{\nnamespace RegularExpressions\n{\n// Generated from `System.Text.RegularExpressions.Capture` in `System.Text.RegularExpressions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Capture\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.Group` in `System.Text.RegularExpressions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Group : System.Text.RegularExpressions.Capture\n{\n}\n\n// Generated from `System.Text.RegularExpressions.Match` in `System.Text.RegularExpressions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Match : System.Text.RegularExpressions.Group\n{\n}\n\n// Generated from `System.Text.RegularExpressions.RegexOptions` in `System.Text.RegularExpressions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic enum RegexOptions\n{\n IgnoreCase,\n}\n\n// Generated from `System.Text.RegularExpressions.Regex` in `System.Text.RegularExpressions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Regex : System.Runtime.Serialization.ISerializable\n{\n public Regex(string pattern) => throw null;\n public Regex(string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public System.Text.RegularExpressions.Match Match(string input) => throw null;\n public override string ToString() => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public string Replace(string input, string replacement) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n}\n}\n}\n | diff --git a/csharp/tools/autobuild.cmd b/csharp/tools/autobuild.cmd new file mode 100644 index 000000000000..7d24060ff46b --- /dev/null +++ b/csharp/tools/autobuild.cmd @@ -0,0 +1,4 @@ +@echo off + +type NUL && "%CODEQL_EXTRACTOR_CSHARP_ROOT%/tools/%CODEQL_PLATFORM%/Semmle.Autobuild.CSharp.exe" +exit /b %ERRORLEVEL% diff --git a/csharp/tools/autobuild.sh b/csharp/tools/autobuild.sh new file mode 100755 index 000000000000..e8e007a10d74 --- /dev/null +++ b/csharp/tools/autobuild.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -eu + +if [ "$CODEQL_PLATFORM" != "linux64" ] && [ "$CODEQL_PLATFORM" != "osx64" ] ; then + echo "Automatic build detection for $CODEQL_PLATFORM is not implemented." + exit 1 +fi + +"$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/$CODEQL_PLATFORM/Semmle.Autobuild.CSharp" || exit $? diff --git a/csharp/tools/linux64/compiler-tracing.spec b/csharp/tools/linux64/compiler-tracing.spec new file mode 100644 index 000000000000..a4d46ad6e294 --- /dev/null +++ b/csharp/tools/linux64/compiler-tracing.spec @@ -0,0 +1,9 @@ +**/mcs.exe: +**/csc.exe: + invoke ${config_dir}/Semmle.Extraction.CSharp.Driver + prepend --compiler + prepend "${compiler}" + prepend --cil +**/mono*: +**/dotnet: + invoke ${config_dir}/extract-csharp.sh diff --git a/csharp/tools/linux64/extract-csharp.sh b/csharp/tools/linux64/extract-csharp.sh new file mode 100755 index 000000000000..ec7013bf03eb --- /dev/null +++ b/csharp/tools/linux64/extract-csharp.sh @@ -0,0 +1,16 @@ +#!/bin/bash +echo extract-csharp.sh: Called with arguments: "$@" + +extractor="$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/$CODEQL_PLATFORM/Semmle.Extraction.CSharp.Driver" + +for i in "$@" +do + shift + if [[ `basename -- "$i"` =~ csc.exe|mcs.exe|csc.dll ]] + then + echo extract-csharp.sh: exec $extractor --cil $@ + exec "$extractor" --compiler $i --cil $@ + fi +done + +echo extract-csharp.sh: Not a compiler invocation diff --git a/csharp/tools/osx64/compiler-tracing.spec b/csharp/tools/osx64/compiler-tracing.spec new file mode 100644 index 000000000000..e68f2f5ed984 --- /dev/null +++ b/csharp/tools/osx64/compiler-tracing.spec @@ -0,0 +1,24 @@ +**/mcs.exe: +**/csc.exe: + invoke ${config_dir}/Semmle.Extraction.CSharp.Driver + prepend --compiler + prepend "${compiler}" + prepend --cil +**/mono*: +**/dotnet: + invoke ${config_dir}/extract-csharp.sh +/usr/bin/codesign: + replace yes + invoke /usr/bin/env + prepend /usr/bin/codesign + trace no +/usr/bin/pkill: + replace yes + invoke /usr/bin/env + prepend /usr/bin/pkill + trace no +/usr/bin/pgrep: + replace yes + invoke /usr/bin/env + prepend /usr/bin/pgrep + trace no diff --git a/csharp/tools/osx64/extract-csharp.sh b/csharp/tools/osx64/extract-csharp.sh new file mode 100755 index 000000000000..ec7013bf03eb --- /dev/null +++ b/csharp/tools/osx64/extract-csharp.sh @@ -0,0 +1,16 @@ +#!/bin/bash +echo extract-csharp.sh: Called with arguments: "$@" + +extractor="$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/$CODEQL_PLATFORM/Semmle.Extraction.CSharp.Driver" + +for i in "$@" +do + shift + if [[ `basename -- "$i"` =~ csc.exe|mcs.exe|csc.dll ]] + then + echo extract-csharp.sh: exec $extractor --cil $@ + exec "$extractor" --compiler $i --cil $@ + fi +done + +echo extract-csharp.sh: Not a compiler invocation diff --git a/csharp/tools/pre-finalize.cmd b/csharp/tools/pre-finalize.cmd new file mode 100644 index 000000000000..b6e7abab41b9 --- /dev/null +++ b/csharp/tools/pre-finalize.cmd @@ -0,0 +1,15 @@ +@echo off + +type NUL && "%CODEQL_DIST%\codeql" database index-files ^ + --include-extension=.config ^ + --include-extension=.csproj ^ + --include-extension=.props ^ + --include-extension=.xml ^ + --size-limit 10m ^ + --language xml ^ + -- ^ + "%CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE%" +IF %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% + +type NUL && "%CODEQL_JAVA_HOME%\bin\java.exe" -jar "%CODEQL_EXTRACTOR_CSHARP_ROOT%\tools\extractor-asp.jar" . +exit /b %ERRORLEVEL% diff --git a/csharp/tools/pre-finalize.sh b/csharp/tools/pre-finalize.sh new file mode 100755 index 000000000000..0e8ab78c9fd5 --- /dev/null +++ b/csharp/tools/pre-finalize.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -eu + +"$CODEQL_DIST/codeql" database index-files \ + --include-extension=.config \ + --include-extension=.csproj \ + --include-extension=.props \ + --include-extension=.xml \ + --size-limit 10m \ + --language xml \ + -- \ + "$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE" + +"$CODEQL_JAVA_HOME/bin/java" -jar "$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/extractor-asp.jar" . diff --git a/csharp/tools/win64/compiler-tracing.spec b/csharp/tools/win64/compiler-tracing.spec new file mode 100644 index 000000000000..72ef54a8cabb --- /dev/null +++ b/csharp/tools/win64/compiler-tracing.spec @@ -0,0 +1,9 @@ +**\fakes*.exe: +**\moles*.exe: + order compiler + trace no +**\csc*.exe: + invoke ${config_dir}\Semmle.Extraction.CSharp.Driver.exe + prepend --compiler + prepend "${compiler}" + prepend --cil diff --git a/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/old.dbscheme b/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/old.dbscheme new file mode 100644 index 000000000000..f2aa2d4ac313 --- /dev/null +++ b/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/old.dbscheme @@ -0,0 +1,1889 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | --cil + * 3 | f1.cs + * 4 | f2.cs + * 5 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location_default ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/* + * External artifacts + */ + +externalDefects( + unique int id: @externalDefect, + string queryPath: string ref, + int location: @location ref, + string message: string ref, + float severity: float ref); + +externalMetrics( + unique int id: @externalMetric, + string queryPath: string ref, + int location: @location ref, + float value: float ref); + +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +snapshotDate( + unique date snapshotDate: date ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id: @duplication, + string relativePath: string ref, + int equivClass: int ref); + +similarCode( + unique int id: @similarity, + string relativePath: string ref, + int equivClass: int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id: @duplication_or_similarity ref, + int offset: int ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @external_element + | @xmllocatable | @asp_element | @namespace; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@external_element = @externalMetric | @externalDefect | @externalDataElement; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +extend( + unique int sub: @type ref, + int super: @type_or_ref ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +compiler_generated(unique int id: @modifiable_direct ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + unique int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, array = 3, this = 4 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; + +@throw_element = @throw_expr | @throw_stmt; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_compiler_generated( + unique int id: @expr ref); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); + +/* Common Intermediate Language - CIL */ + +case @cil_instruction.opcode of + 0 = @cil_nop +| 1 = @cil_break +| 2 = @cil_ldarg_0 +| 3 = @cil_ldarg_1 +| 4 = @cil_ldarg_2 +| 5 = @cil_ldarg_3 +| 6 = @cil_ldloc_0 +| 7 = @cil_ldloc_1 +| 8 = @cil_ldloc_2 +| 9 = @cil_ldloc_3 +| 10 = @cil_stloc_0 +| 11 = @cil_stloc_1 +| 12 = @cil_stloc_2 +| 13 = @cil_stloc_3 +| 14 = @cil_ldarg_s +| 15 = @cil_ldarga_s +| 16 = @cil_starg_s +| 17 = @cil_ldloc_s +| 18 = @cil_ldloca_s +| 19 = @cil_stloc_s +| 20 = @cil_ldnull +| 21 = @cil_ldc_i4_m1 +| 22 = @cil_ldc_i4_0 +| 23 = @cil_ldc_i4_1 +| 24 = @cil_ldc_i4_2 +| 25 = @cil_ldc_i4_3 +| 26 = @cil_ldc_i4_4 +| 27 = @cil_ldc_i4_5 +| 28 = @cil_ldc_i4_6 +| 29 = @cil_ldc_i4_7 +| 30 = @cil_ldc_i4_8 +| 31 = @cil_ldc_i4_s +| 32 = @cil_ldc_i4 +| 33 = @cil_ldc_i8 +| 34 = @cil_ldc_r4 +| 35 = @cil_ldc_r8 +| 37 = @cil_dup +| 38 = @cil_pop +| 39 = @cil_jmp +| 40 = @cil_call +| 41 = @cil_calli +| 42 = @cil_ret +| 43 = @cil_br_s +| 44 = @cil_brfalse_s +| 45 = @cil_brtrue_s +| 46 = @cil_beq_s +| 47 = @cil_bge_s +| 48 = @cil_bgt_s +| 49 = @cil_ble_s +| 50 = @cil_blt_s +| 51 = @cil_bne_un_s +| 52 = @cil_bge_un_s +| 53 = @cil_bgt_un_s +| 54 = @cil_ble_un_s +| 55 = @cil_blt_un_s +| 56 = @cil_br +| 57 = @cil_brfalse +| 58 = @cil_brtrue +| 59 = @cil_beq +| 60 = @cil_bge +| 61 = @cil_bgt +| 62 = @cil_ble +| 63 = @cil_blt +| 64 = @cil_bne_un +| 65 = @cil_bge_un +| 66 = @cil_bgt_un +| 67 = @cil_ble_un +| 68 = @cil_blt_un +| 69 = @cil_switch +| 70 = @cil_ldind_i1 +| 71 = @cil_ldind_u1 +| 72 = @cil_ldind_i2 +| 73 = @cil_ldind_u2 +| 74 = @cil_ldind_i4 +| 75 = @cil_ldind_u4 +| 76 = @cil_ldind_i8 +| 77 = @cil_ldind_i +| 78 = @cil_ldind_r4 +| 79 = @cil_ldind_r8 +| 80 = @cil_ldind_ref +| 81 = @cil_stind_ref +| 82 = @cil_stind_i1 +| 83 = @cil_stind_i2 +| 84 = @cil_stind_i4 +| 85 = @cil_stind_i8 +| 86 = @cil_stind_r4 +| 87 = @cil_stind_r8 +| 88 = @cil_add +| 89 = @cil_sub +| 90 = @cil_mul +| 91 = @cil_div +| 92 = @cil_div_un +| 93 = @cil_rem +| 94 = @cil_rem_un +| 95 = @cil_and +| 96 = @cil_or +| 97 = @cil_xor +| 98 = @cil_shl +| 99 = @cil_shr +| 100 = @cil_shr_un +| 101 = @cil_neg +| 102 = @cil_not +| 103 = @cil_conv_i1 +| 104 = @cil_conv_i2 +| 105 = @cil_conv_i4 +| 106 = @cil_conv_i8 +| 107 = @cil_conv_r4 +| 108 = @cil_conv_r8 +| 109 = @cil_conv_u4 +| 110 = @cil_conv_u8 +| 111 = @cil_callvirt +| 112 = @cil_cpobj +| 113 = @cil_ldobj +| 114 = @cil_ldstr +| 115 = @cil_newobj +| 116 = @cil_castclass +| 117 = @cil_isinst +| 118 = @cil_conv_r_un +| 121 = @cil_unbox +| 122 = @cil_throw +| 123 = @cil_ldfld +| 124 = @cil_ldflda +| 125 = @cil_stfld +| 126 = @cil_ldsfld +| 127 = @cil_ldsflda +| 128 = @cil_stsfld +| 129 = @cil_stobj +| 130 = @cil_conv_ovf_i1_un +| 131 = @cil_conv_ovf_i2_un +| 132 = @cil_conv_ovf_i4_un +| 133 = @cil_conv_ovf_i8_un +| 134 = @cil_conv_ovf_u1_un +| 135 = @cil_conv_ovf_u2_un +| 136 = @cil_conv_ovf_u4_un +| 137 = @cil_conv_ovf_u8_un +| 138 = @cil_conv_ovf_i_un +| 139 = @cil_conv_ovf_u_un +| 140 = @cil_box +| 141 = @cil_newarr +| 142 = @cil_ldlen +| 143 = @cil_ldelema +| 144 = @cil_ldelem_i1 +| 145 = @cil_ldelem_u1 +| 146 = @cil_ldelem_i2 +| 147 = @cil_ldelem_u2 +| 148 = @cil_ldelem_i4 +| 149 = @cil_ldelem_u4 +| 150 = @cil_ldelem_i8 +| 151 = @cil_ldelem_i +| 152 = @cil_ldelem_r4 +| 153 = @cil_ldelem_r8 +| 154 = @cil_ldelem_ref +| 155 = @cil_stelem_i +| 156 = @cil_stelem_i1 +| 157 = @cil_stelem_i2 +| 158 = @cil_stelem_i4 +| 159 = @cil_stelem_i8 +| 160 = @cil_stelem_r4 +| 161 = @cil_stelem_r8 +| 162 = @cil_stelem_ref +| 163 = @cil_ldelem +| 164 = @cil_stelem +| 165 = @cil_unbox_any +| 179 = @cil_conv_ovf_i1 +| 180 = @cil_conv_ovf_u1 +| 181 = @cil_conv_ovf_i2 +| 182 = @cil_conv_ovf_u2 +| 183 = @cil_conv_ovf_i4 +| 184 = @cil_conv_ovf_u4 +| 185 = @cil_conv_ovf_i8 +| 186 = @cil_conv_ovf_u8 +| 194 = @cil_refanyval +| 195 = @cil_ckinfinite +| 198 = @cil_mkrefany +| 208 = @cil_ldtoken +| 209 = @cil_conv_u2 +| 210 = @cil_conv_u1 +| 211 = @cil_conv_i +| 212 = @cil_conv_ovf_i +| 213 = @cil_conv_ovf_u +| 214 = @cil_add_ovf +| 215 = @cil_add_ovf_un +| 216 = @cil_mul_ovf +| 217 = @cil_mul_ovf_un +| 218 = @cil_sub_ovf +| 219 = @cil_sub_ovf_un +| 220 = @cil_endfinally +| 221 = @cil_leave +| 222 = @cil_leave_s +| 223 = @cil_stind_i +| 224 = @cil_conv_u +| 65024 = @cil_arglist +| 65025 = @cil_ceq +| 65026 = @cil_cgt +| 65027 = @cil_cgt_un +| 65028 = @cil_clt +| 65029 = @cil_clt_un +| 65030 = @cil_ldftn +| 65031 = @cil_ldvirtftn +| 65033 = @cil_ldarg +| 65034 = @cil_ldarga +| 65035 = @cil_starg +| 65036 = @cil_ldloc +| 65037 = @cil_ldloca +| 65038 = @cil_stloc +| 65039 = @cil_localloc +| 65041 = @cil_endfilter +| 65042 = @cil_unaligned +| 65043 = @cil_volatile +| 65044 = @cil_tail +| 65045 = @cil_initobj +| 65046 = @cil_constrained +| 65047 = @cil_cpblk +| 65048 = @cil_initblk +| 65050 = @cil_rethrow +| 65052 = @cil_sizeof +| 65053 = @cil_refanytype +| 65054 = @cil_readonly +; + +// CIL ignored instructions + +@cil_ignore = @cil_nop | @cil_break | @cil_volatile | @cil_unaligned; + +// CIL local/parameter/field access + +@cil_ldarg_any = @cil_ldarg_0 | @cil_ldarg_1 | @cil_ldarg_2 | @cil_ldarg_3 | @cil_ldarg_s | @cil_ldarga_s | @cil_ldarg | @cil_ldarga; +@cil_starg_any = @cil_starg | @cil_starg_s; + +@cil_ldloc_any = @cil_ldloc_0 | @cil_ldloc_1 | @cil_ldloc_2 | @cil_ldloc_3 | @cil_ldloc_s | @cil_ldloca_s | @cil_ldloc | @cil_ldloca; +@cil_stloc_any = @cil_stloc_0 | @cil_stloc_1 | @cil_stloc_2 | @cil_stloc_3 | @cil_stloc_s | @cil_stloc; + +@cil_ldfld_any = @cil_ldfld | @cil_ldsfld | @cil_ldsflda | @cil_ldflda; +@cil_stfld_any = @cil_stfld | @cil_stsfld; + +@cil_local_access = @cil_stloc_any | @cil_ldloc_any; +@cil_arg_access = @cil_starg_any | @cil_ldarg_any; +@cil_read_access = @cil_ldloc_any | @cil_ldarg_any | @cil_ldfld_any; +@cil_write_access = @cil_stloc_any | @cil_starg_any | @cil_stfld_any; + +@cil_stack_access = @cil_local_access | @cil_arg_access; +@cil_field_access = @cil_ldfld_any | @cil_stfld_any; + +@cil_access = @cil_read_access | @cil_write_access; + +// CIL constant/literal instructions + +@cil_ldc_i = @cil_ldc_i4_any | @cil_ldc_i8; + +@cil_ldc_i4_any = @cil_ldc_i4_m1 | @cil_ldc_i4_0 | @cil_ldc_i4_1 | @cil_ldc_i4_2 | @cil_ldc_i4_3 | + @cil_ldc_i4_4 | @cil_ldc_i4_5 | @cil_ldc_i4_6 | @cil_ldc_i4_7 | @cil_ldc_i4_8 | @cil_ldc_i4_s | @cil_ldc_i4; + +@cil_ldc_r = @cil_ldc_r4 | @cil_ldc_r8; + +@cil_literal = @cil_ldnull | @cil_ldc_i | @cil_ldc_r | @cil_ldstr; + +// Control flow + +@cil_conditional_jump = @cil_binary_jump | @cil_unary_jump; +@cil_binary_jump = @cil_beq_s | @cil_bge_s | @cil_bgt_s | @cil_ble_s | @cil_blt_s | + @cil_bne_un_s | @cil_bge_un_s | @cil_bgt_un_s | @cil_ble_un_s | @cil_blt_un_s | + @cil_beq | @cil_bge | @cil_bgt | @cil_ble | @cil_blt | + @cil_bne_un | @cil_bge_un | @cil_bgt_un | @cil_ble_un | @cil_blt_un; +@cil_unary_jump = @cil_brfalse_s | @cil_brtrue_s | @cil_brfalse | @cil_brtrue | @cil_switch; +@cil_unconditional_jump = @cil_br | @cil_br_s | @cil_leave_any; +@cil_leave_any = @cil_leave | @cil_leave_s; +@cil_jump = @cil_unconditional_jump | @cil_conditional_jump; + +// CIL call instructions + +@cil_call_any = @cil_jmp | @cil_call | @cil_calli | @cil_tail | @cil_callvirt | @cil_newobj; + +// CIL expression instructions + +@cil_expr = @cil_literal | @cil_binary_expr | @cil_unary_expr | @cil_call_any | @cil_read_access | + @cil_newarr | @cil_ldtoken | @cil_sizeof | + @cil_ldftn | @cil_ldvirtftn | @cil_localloc | @cil_mkrefany | @cil_refanytype | @cil_arglist | @cil_dup; + +@cil_unary_expr = + @cil_conversion_operation | @cil_unary_arithmetic_operation | @cil_unary_bitwise_operation| + @cil_ldlen | @cil_isinst | @cil_box | @cil_ldobj | @cil_castclass | @cil_unbox_any | + @cil_ldind | @cil_unbox; + +@cil_conversion_operation = + @cil_conv_i1 | @cil_conv_i2 | @cil_conv_i4 | @cil_conv_i8 | + @cil_conv_u1 | @cil_conv_u2 | @cil_conv_u4 | @cil_conv_u8 | + @cil_conv_ovf_i | @cil_conv_ovf_i_un | @cil_conv_ovf_i1 | @cil_conv_ovf_i1_un | + @cil_conv_ovf_i2 | @cil_conv_ovf_i2_un | @cil_conv_ovf_i4 | @cil_conv_ovf_i4_un | + @cil_conv_ovf_i8 | @cil_conv_ovf_i8_un | @cil_conv_ovf_u | @cil_conv_ovf_u_un | + @cil_conv_ovf_u1 | @cil_conv_ovf_u1_un | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_ovf_u4 | @cil_conv_ovf_u4_un | @cil_conv_ovf_u8 | @cil_conv_ovf_u8_un | + @cil_conv_r4 | @cil_conv_r8 | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_i | @cil_conv_u | @cil_conv_r_un; + +@cil_ldind = @cil_ldind_i | @cil_ldind_i1 | @cil_ldind_i2 | @cil_ldind_i4 | @cil_ldind_i8 | + @cil_ldind_r4 | @cil_ldind_r8 | @cil_ldind_ref | @cil_ldind_u1 | @cil_ldind_u2 | @cil_ldind_u4; + +@cil_stind = @cil_stind_i | @cil_stind_i1 | @cil_stind_i2 | @cil_stind_i4 | @cil_stind_i8 | + @cil_stind_r4 | @cil_stind_r8 | @cil_stind_ref; + +@cil_bitwise_operation = @cil_binary_bitwise_operation | @cil_unary_bitwise_operation; + +@cil_binary_bitwise_operation = @cil_and | @cil_or | @cil_xor | @cil_shr | @cil_shr | @cil_shr_un | @cil_shl; + +@cil_binary_arithmetic_operation = @cil_add | @cil_sub | @cil_mul | @cil_div | @cil_div_un | + @cil_rem | @cil_rem_un | @cil_add_ovf | @cil_add_ovf_un | @cil_mul_ovf | @cil_mul_ovf_un | + @cil_sub_ovf | @cil_sub_ovf_un; + +@cil_unary_bitwise_operation = @cil_not; + +@cil_binary_expr = @cil_binary_arithmetic_operation | @cil_binary_bitwise_operation | @cil_read_array | @cil_comparison_operation; + +@cil_unary_arithmetic_operation = @cil_neg; + +@cil_comparison_operation = @cil_cgt_un | @cil_ceq | @cil_cgt | @cil_clt | @cil_clt_un; + +// Elements that retrieve an address of something +@cil_read_ref = @cil_ldloca_s | @cil_ldarga_s | @cil_ldflda | @cil_ldsflda | @cil_ldelema; + +// CIL array instructions + +@cil_read_array = + @cil_ldelem | @cil_ldelema | @cil_ldelem_i1 | @cil_ldelem_ref | @cil_ldelem_i | + @cil_ldelem_i1 | @cil_ldelem_i2 | @cil_ldelem_i4 | @cil_ldelem_i8 | @cil_ldelem_r4 | + @cil_ldelem_r8 | @cil_ldelem_u1 | @cil_ldelem_u2 | @cil_ldelem_u4; + +@cil_write_array = @cil_stelem | @cil_stelem_ref | + @cil_stelem_i | @cil_stelem_i1 | @cil_stelem_i2 | @cil_stelem_i4 | @cil_stelem_i8 | + @cil_stelem_r4 | @cil_stelem_r8; + +@cil_throw_any = @cil_throw | @cil_rethrow; + +#keyset[impl, index] +cil_instruction( + unique int id: @cil_instruction, + int opcode: int ref, + int index: int ref, + int impl: @cil_method_implementation ref); + +cil_jump( + unique int instruction: @cil_jump ref, + int target: @cil_instruction ref); + +cil_access( + unique int instruction: @cil_instruction ref, + int target: @cil_accessible ref); + +cil_value( + unique int instruction: @cil_literal ref, + string value: string ref); + +#keyset[instruction, index] +cil_switch( + int instruction: @cil_switch ref, + int index: int ref, + int target: @cil_instruction ref); + +cil_instruction_location( + unique int id: @cil_instruction ref, + int loc: @location ref); + +cil_type_location( + int id: @cil_type ref, + int loc: @location ref); + +cil_method_location( + int id: @cil_method ref, + int loc: @location ref); + +@cil_namespace = @namespace; + +@cil_type_container = @cil_type | @cil_namespace | @cil_method; + +case @cil_type.kind of + 0 = @cil_valueorreftype +| 1 = @cil_typeparameter +| 2 = @cil_array_type +| 3 = @cil_pointer_type +; + +cil_type( + unique int id: @cil_type, + string name: string ref, + int kind: int ref, + int parent: @cil_type_container ref, + int sourceDecl: @cil_type ref); + +cil_pointer_type( + unique int id: @cil_pointer_type ref, + int pointee: @cil_type ref); + +cil_array_type( + unique int id: @cil_array_type ref, + int element_type: @cil_type ref, + int rank: int ref); + +cil_method( + unique int id: @cil_method, + string name: string ref, + int parent: @cil_type ref, + int return_type: @cil_type ref); + +cil_method_source_declaration( + unique int method: @cil_method ref, + int source: @cil_method ref); + +cil_method_implementation( + unique int id: @cil_method_implementation, + int method: @cil_method ref, + int location: @assembly ref); + +cil_implements( + int id: @cil_method ref, + int decl: @cil_method ref); + +#keyset[parent, name] +cil_field( + unique int id: @cil_field, + int parent: @cil_type ref, + string name: string ref, + int field_type: @cil_type ref); + +@cil_element = @cil_instruction | @cil_declaration | @cil_handler | @cil_attribute | @cil_namespace; +@cil_named_element = @cil_declaration | @cil_namespace; +@cil_declaration = @cil_variable | @cil_method | @cil_type | @cil_member; +@cil_accessible = @cil_declaration; +@cil_variable = @cil_field | @cil_stack_variable; +@cil_stack_variable = @cil_local_variable | @cil_parameter; +@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event; + +#keyset[method, index] +cil_parameter( + unique int id: @cil_parameter, + int method: @cil_method ref, + int index: int ref, + int param_type: @cil_type ref); + +cil_parameter_in(unique int id: @cil_parameter ref); +cil_parameter_out(unique int id: @cil_parameter ref); + +cil_setter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_getter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_adder(unique int event: @cil_event ref, + int method: @cil_method ref); + +cil_remover(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_raiser(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_property( + unique int id: @cil_property, + int parent: @cil_type ref, + string name: string ref, + int property_type: @cil_type ref); + +#keyset[parent, name] +cil_event(unique int id: @cil_event, + int parent: @cil_type ref, + string name: string ref, + int event_type: @cil_type ref); + +#keyset[impl, index] +cil_local_variable( + unique int id: @cil_local_variable, + int impl: @cil_method_implementation ref, + int index: int ref, + int var_type: @cil_type ref); + +// CIL handlers (exception handlers etc). + +case @cil_handler.kind of + 0 = @cil_catch_handler +| 1 = @cil_filter_handler +| 2 = @cil_finally_handler +| 4 = @cil_fault_handler +; + +#keyset[impl, index] +cil_handler( + unique int id: @cil_handler, + int impl: @cil_method_implementation ref, + int index: int ref, + int kind: int ref, + int try_start: @cil_instruction ref, + int try_end: @cil_instruction ref, + int handler_start: @cil_instruction ref); + +cil_handler_filter( + unique int id: @cil_handler ref, + int filter_start: @cil_instruction ref); + +cil_handler_type( + unique int id: @cil_handler ref, + int catch_type: @cil_type ref); + +@cil_controlflow_node = @cil_entry_point | @cil_instruction; + +@cil_entry_point = @cil_method_implementation | @cil_handler; + +@cil_dataflow_node = @cil_instruction | @cil_variable | @cil_method; + +cil_method_stack_size( + unique int method: @cil_method_implementation ref, + int size: int ref); + +// CIL modifiers + +cil_public(int id: @cil_member ref); +cil_private(int id: @cil_member ref); +cil_protected(int id: @cil_member ref); +cil_internal(int id: @cil_member ref); +cil_static(int id: @cil_member ref); +cil_sealed(int id: @cil_member ref); +cil_virtual(int id: @cil_method ref); +cil_abstract(int id: @cil_member ref); +cil_class(int id: @cil_type ref); +cil_interface(int id: @cil_type ref); +cil_security(int id: @cil_member ref); +cil_requiresecobject(int id: @cil_method ref); +cil_specialname(int id: @cil_method ref); +cil_newslot(int id: @cil_method ref); + +cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref); +cil_base_interface(int id: @cil_type ref, int base: @cil_type ref); + +#keyset[unbound, index] +cil_type_parameter( + int unbound: @cil_member ref, + int index: int ref, + int param: @cil_typeparameter ref); + +#keyset[bound, index] +cil_type_argument( + int bound: @cil_member ref, + int index: int ref, + int t: @cil_type ref); + +// CIL type parameter constraints + +cil_typeparam_covariant(int tp: @cil_typeparameter ref); +cil_typeparam_contravariant(int tp: @cil_typeparameter ref); +cil_typeparam_class(int tp: @cil_typeparameter ref); +cil_typeparam_struct(int tp: @cil_typeparameter ref); +cil_typeparam_new(int tp: @cil_typeparameter ref); +cil_typeparam_constraint(int tp: @cil_typeparameter ref, int supertype: @cil_type ref); + +// CIL attributes + +cil_attribute( + unique int attributeid: @cil_attribute, + int element: @cil_declaration ref, + int constructor: @cil_method ref); + +#keyset[attribute_id, param] +cil_attribute_named_argument( + int attribute_id: @cil_attribute ref, + string param: string ref, + string value: string ref); + +#keyset[attribute_id, index] +cil_attribute_positional_argument( + int attribute_id: @cil_attribute ref, + int index: int ref, + string value: string ref); + + +// Common .Net data model covering both C# and CIL + +// Common elements +@dotnet_element = @element | @cil_element; +@dotnet_named_element = @named_element | @cil_named_element; +@dotnet_callable = @callable | @cil_method; +@dotnet_variable = @variable | @cil_variable; +@dotnet_field = @field | @cil_field; +@dotnet_parameter = @parameter | @cil_parameter; +@dotnet_declaration = @declaration | @cil_declaration; +@dotnet_member = @member | @cil_member; +@dotnet_event = @event | @cil_event; +@dotnet_property = @property | @cil_property | @indexer; + +// Common types +@dotnet_type = @type | @cil_type; +@dotnet_call = @call | @cil_call_any; +@dotnet_throw = @throw_element | @cil_throw_any; +@dotnet_valueorreftype = @cil_valueorreftype | @value_or_ref_type | @cil_array_type | @void_type; +@dotnet_typeparameter = @type_parameter | @cil_typeparameter; +@dotnet_array_type = @array_type | @cil_array_type; +@dotnet_pointer_type = @pointer_type | @cil_pointer_type; +@dotnet_type_parameter = @type_parameter | @cil_typeparameter; +@dotnet_generic = @dotnet_valueorreftype | @dotnet_callable; + +// Attributes +@dotnet_attribute = @attribute | @cil_attribute; + +// Expressions +@dotnet_expr = @expr | @cil_expr; + +// Literals +@dotnet_literal = @literal_expr | @cil_literal; +@dotnet_string_literal = @string_literal_expr | @cil_ldstr; +@dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; +@dotnet_float_literal = @float_literal_expr | @cil_ldc_r; +@dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/semmlecode.csharp.dbscheme b/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/semmlecode.csharp.dbscheme new file mode 100644 index 000000000000..ddd39829bb71 --- /dev/null +++ b/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/semmlecode.csharp.dbscheme @@ -0,0 +1,1889 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | --cil + * 3 | f1.cs + * 4 | f2.cs + * 5 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location_default ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/* + * External artifacts + */ + +externalDefects( + unique int id: @externalDefect, + string queryPath: string ref, + int location: @location ref, + string message: string ref, + float severity: float ref); + +externalMetrics( + unique int id: @externalMetric, + string queryPath: string ref, + int location: @location ref, + float value: float ref); + +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +snapshotDate( + unique date snapshotDate: date ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id: @duplication, + string relativePath: string ref, + int equivClass: int ref); + +similarCode( + unique int id: @similarity, + string relativePath: string ref, + int equivClass: int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id: @duplication_or_similarity ref, + int offset: int ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @external_element + | @xmllocatable | @asp_element | @namespace; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@external_element = @externalMetric | @externalDefect | @externalDataElement; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +extend( + unique int sub: @type ref, + int super: @type_or_ref ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +compiler_generated(unique int id: @modifiable_direct ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, array = 3, this = 4 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; + +@throw_element = @throw_expr | @throw_stmt; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_compiler_generated( + unique int id: @expr ref); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); + +/* Common Intermediate Language - CIL */ + +case @cil_instruction.opcode of + 0 = @cil_nop +| 1 = @cil_break +| 2 = @cil_ldarg_0 +| 3 = @cil_ldarg_1 +| 4 = @cil_ldarg_2 +| 5 = @cil_ldarg_3 +| 6 = @cil_ldloc_0 +| 7 = @cil_ldloc_1 +| 8 = @cil_ldloc_2 +| 9 = @cil_ldloc_3 +| 10 = @cil_stloc_0 +| 11 = @cil_stloc_1 +| 12 = @cil_stloc_2 +| 13 = @cil_stloc_3 +| 14 = @cil_ldarg_s +| 15 = @cil_ldarga_s +| 16 = @cil_starg_s +| 17 = @cil_ldloc_s +| 18 = @cil_ldloca_s +| 19 = @cil_stloc_s +| 20 = @cil_ldnull +| 21 = @cil_ldc_i4_m1 +| 22 = @cil_ldc_i4_0 +| 23 = @cil_ldc_i4_1 +| 24 = @cil_ldc_i4_2 +| 25 = @cil_ldc_i4_3 +| 26 = @cil_ldc_i4_4 +| 27 = @cil_ldc_i4_5 +| 28 = @cil_ldc_i4_6 +| 29 = @cil_ldc_i4_7 +| 30 = @cil_ldc_i4_8 +| 31 = @cil_ldc_i4_s +| 32 = @cil_ldc_i4 +| 33 = @cil_ldc_i8 +| 34 = @cil_ldc_r4 +| 35 = @cil_ldc_r8 +| 37 = @cil_dup +| 38 = @cil_pop +| 39 = @cil_jmp +| 40 = @cil_call +| 41 = @cil_calli +| 42 = @cil_ret +| 43 = @cil_br_s +| 44 = @cil_brfalse_s +| 45 = @cil_brtrue_s +| 46 = @cil_beq_s +| 47 = @cil_bge_s +| 48 = @cil_bgt_s +| 49 = @cil_ble_s +| 50 = @cil_blt_s +| 51 = @cil_bne_un_s +| 52 = @cil_bge_un_s +| 53 = @cil_bgt_un_s +| 54 = @cil_ble_un_s +| 55 = @cil_blt_un_s +| 56 = @cil_br +| 57 = @cil_brfalse +| 58 = @cil_brtrue +| 59 = @cil_beq +| 60 = @cil_bge +| 61 = @cil_bgt +| 62 = @cil_ble +| 63 = @cil_blt +| 64 = @cil_bne_un +| 65 = @cil_bge_un +| 66 = @cil_bgt_un +| 67 = @cil_ble_un +| 68 = @cil_blt_un +| 69 = @cil_switch +| 70 = @cil_ldind_i1 +| 71 = @cil_ldind_u1 +| 72 = @cil_ldind_i2 +| 73 = @cil_ldind_u2 +| 74 = @cil_ldind_i4 +| 75 = @cil_ldind_u4 +| 76 = @cil_ldind_i8 +| 77 = @cil_ldind_i +| 78 = @cil_ldind_r4 +| 79 = @cil_ldind_r8 +| 80 = @cil_ldind_ref +| 81 = @cil_stind_ref +| 82 = @cil_stind_i1 +| 83 = @cil_stind_i2 +| 84 = @cil_stind_i4 +| 85 = @cil_stind_i8 +| 86 = @cil_stind_r4 +| 87 = @cil_stind_r8 +| 88 = @cil_add +| 89 = @cil_sub +| 90 = @cil_mul +| 91 = @cil_div +| 92 = @cil_div_un +| 93 = @cil_rem +| 94 = @cil_rem_un +| 95 = @cil_and +| 96 = @cil_or +| 97 = @cil_xor +| 98 = @cil_shl +| 99 = @cil_shr +| 100 = @cil_shr_un +| 101 = @cil_neg +| 102 = @cil_not +| 103 = @cil_conv_i1 +| 104 = @cil_conv_i2 +| 105 = @cil_conv_i4 +| 106 = @cil_conv_i8 +| 107 = @cil_conv_r4 +| 108 = @cil_conv_r8 +| 109 = @cil_conv_u4 +| 110 = @cil_conv_u8 +| 111 = @cil_callvirt +| 112 = @cil_cpobj +| 113 = @cil_ldobj +| 114 = @cil_ldstr +| 115 = @cil_newobj +| 116 = @cil_castclass +| 117 = @cil_isinst +| 118 = @cil_conv_r_un +| 121 = @cil_unbox +| 122 = @cil_throw +| 123 = @cil_ldfld +| 124 = @cil_ldflda +| 125 = @cil_stfld +| 126 = @cil_ldsfld +| 127 = @cil_ldsflda +| 128 = @cil_stsfld +| 129 = @cil_stobj +| 130 = @cil_conv_ovf_i1_un +| 131 = @cil_conv_ovf_i2_un +| 132 = @cil_conv_ovf_i4_un +| 133 = @cil_conv_ovf_i8_un +| 134 = @cil_conv_ovf_u1_un +| 135 = @cil_conv_ovf_u2_un +| 136 = @cil_conv_ovf_u4_un +| 137 = @cil_conv_ovf_u8_un +| 138 = @cil_conv_ovf_i_un +| 139 = @cil_conv_ovf_u_un +| 140 = @cil_box +| 141 = @cil_newarr +| 142 = @cil_ldlen +| 143 = @cil_ldelema +| 144 = @cil_ldelem_i1 +| 145 = @cil_ldelem_u1 +| 146 = @cil_ldelem_i2 +| 147 = @cil_ldelem_u2 +| 148 = @cil_ldelem_i4 +| 149 = @cil_ldelem_u4 +| 150 = @cil_ldelem_i8 +| 151 = @cil_ldelem_i +| 152 = @cil_ldelem_r4 +| 153 = @cil_ldelem_r8 +| 154 = @cil_ldelem_ref +| 155 = @cil_stelem_i +| 156 = @cil_stelem_i1 +| 157 = @cil_stelem_i2 +| 158 = @cil_stelem_i4 +| 159 = @cil_stelem_i8 +| 160 = @cil_stelem_r4 +| 161 = @cil_stelem_r8 +| 162 = @cil_stelem_ref +| 163 = @cil_ldelem +| 164 = @cil_stelem +| 165 = @cil_unbox_any +| 179 = @cil_conv_ovf_i1 +| 180 = @cil_conv_ovf_u1 +| 181 = @cil_conv_ovf_i2 +| 182 = @cil_conv_ovf_u2 +| 183 = @cil_conv_ovf_i4 +| 184 = @cil_conv_ovf_u4 +| 185 = @cil_conv_ovf_i8 +| 186 = @cil_conv_ovf_u8 +| 194 = @cil_refanyval +| 195 = @cil_ckinfinite +| 198 = @cil_mkrefany +| 208 = @cil_ldtoken +| 209 = @cil_conv_u2 +| 210 = @cil_conv_u1 +| 211 = @cil_conv_i +| 212 = @cil_conv_ovf_i +| 213 = @cil_conv_ovf_u +| 214 = @cil_add_ovf +| 215 = @cil_add_ovf_un +| 216 = @cil_mul_ovf +| 217 = @cil_mul_ovf_un +| 218 = @cil_sub_ovf +| 219 = @cil_sub_ovf_un +| 220 = @cil_endfinally +| 221 = @cil_leave +| 222 = @cil_leave_s +| 223 = @cil_stind_i +| 224 = @cil_conv_u +| 65024 = @cil_arglist +| 65025 = @cil_ceq +| 65026 = @cil_cgt +| 65027 = @cil_cgt_un +| 65028 = @cil_clt +| 65029 = @cil_clt_un +| 65030 = @cil_ldftn +| 65031 = @cil_ldvirtftn +| 65033 = @cil_ldarg +| 65034 = @cil_ldarga +| 65035 = @cil_starg +| 65036 = @cil_ldloc +| 65037 = @cil_ldloca +| 65038 = @cil_stloc +| 65039 = @cil_localloc +| 65041 = @cil_endfilter +| 65042 = @cil_unaligned +| 65043 = @cil_volatile +| 65044 = @cil_tail +| 65045 = @cil_initobj +| 65046 = @cil_constrained +| 65047 = @cil_cpblk +| 65048 = @cil_initblk +| 65050 = @cil_rethrow +| 65052 = @cil_sizeof +| 65053 = @cil_refanytype +| 65054 = @cil_readonly +; + +// CIL ignored instructions + +@cil_ignore = @cil_nop | @cil_break | @cil_volatile | @cil_unaligned; + +// CIL local/parameter/field access + +@cil_ldarg_any = @cil_ldarg_0 | @cil_ldarg_1 | @cil_ldarg_2 | @cil_ldarg_3 | @cil_ldarg_s | @cil_ldarga_s | @cil_ldarg | @cil_ldarga; +@cil_starg_any = @cil_starg | @cil_starg_s; + +@cil_ldloc_any = @cil_ldloc_0 | @cil_ldloc_1 | @cil_ldloc_2 | @cil_ldloc_3 | @cil_ldloc_s | @cil_ldloca_s | @cil_ldloc | @cil_ldloca; +@cil_stloc_any = @cil_stloc_0 | @cil_stloc_1 | @cil_stloc_2 | @cil_stloc_3 | @cil_stloc_s | @cil_stloc; + +@cil_ldfld_any = @cil_ldfld | @cil_ldsfld | @cil_ldsflda | @cil_ldflda; +@cil_stfld_any = @cil_stfld | @cil_stsfld; + +@cil_local_access = @cil_stloc_any | @cil_ldloc_any; +@cil_arg_access = @cil_starg_any | @cil_ldarg_any; +@cil_read_access = @cil_ldloc_any | @cil_ldarg_any | @cil_ldfld_any; +@cil_write_access = @cil_stloc_any | @cil_starg_any | @cil_stfld_any; + +@cil_stack_access = @cil_local_access | @cil_arg_access; +@cil_field_access = @cil_ldfld_any | @cil_stfld_any; + +@cil_access = @cil_read_access | @cil_write_access; + +// CIL constant/literal instructions + +@cil_ldc_i = @cil_ldc_i4_any | @cil_ldc_i8; + +@cil_ldc_i4_any = @cil_ldc_i4_m1 | @cil_ldc_i4_0 | @cil_ldc_i4_1 | @cil_ldc_i4_2 | @cil_ldc_i4_3 | + @cil_ldc_i4_4 | @cil_ldc_i4_5 | @cil_ldc_i4_6 | @cil_ldc_i4_7 | @cil_ldc_i4_8 | @cil_ldc_i4_s | @cil_ldc_i4; + +@cil_ldc_r = @cil_ldc_r4 | @cil_ldc_r8; + +@cil_literal = @cil_ldnull | @cil_ldc_i | @cil_ldc_r | @cil_ldstr; + +// Control flow + +@cil_conditional_jump = @cil_binary_jump | @cil_unary_jump; +@cil_binary_jump = @cil_beq_s | @cil_bge_s | @cil_bgt_s | @cil_ble_s | @cil_blt_s | + @cil_bne_un_s | @cil_bge_un_s | @cil_bgt_un_s | @cil_ble_un_s | @cil_blt_un_s | + @cil_beq | @cil_bge | @cil_bgt | @cil_ble | @cil_blt | + @cil_bne_un | @cil_bge_un | @cil_bgt_un | @cil_ble_un | @cil_blt_un; +@cil_unary_jump = @cil_brfalse_s | @cil_brtrue_s | @cil_brfalse | @cil_brtrue | @cil_switch; +@cil_unconditional_jump = @cil_br | @cil_br_s | @cil_leave_any; +@cil_leave_any = @cil_leave | @cil_leave_s; +@cil_jump = @cil_unconditional_jump | @cil_conditional_jump; + +// CIL call instructions + +@cil_call_any = @cil_jmp | @cil_call | @cil_calli | @cil_tail | @cil_callvirt | @cil_newobj; + +// CIL expression instructions + +@cil_expr = @cil_literal | @cil_binary_expr | @cil_unary_expr | @cil_call_any | @cil_read_access | + @cil_newarr | @cil_ldtoken | @cil_sizeof | + @cil_ldftn | @cil_ldvirtftn | @cil_localloc | @cil_mkrefany | @cil_refanytype | @cil_arglist | @cil_dup; + +@cil_unary_expr = + @cil_conversion_operation | @cil_unary_arithmetic_operation | @cil_unary_bitwise_operation| + @cil_ldlen | @cil_isinst | @cil_box | @cil_ldobj | @cil_castclass | @cil_unbox_any | + @cil_ldind | @cil_unbox; + +@cil_conversion_operation = + @cil_conv_i1 | @cil_conv_i2 | @cil_conv_i4 | @cil_conv_i8 | + @cil_conv_u1 | @cil_conv_u2 | @cil_conv_u4 | @cil_conv_u8 | + @cil_conv_ovf_i | @cil_conv_ovf_i_un | @cil_conv_ovf_i1 | @cil_conv_ovf_i1_un | + @cil_conv_ovf_i2 | @cil_conv_ovf_i2_un | @cil_conv_ovf_i4 | @cil_conv_ovf_i4_un | + @cil_conv_ovf_i8 | @cil_conv_ovf_i8_un | @cil_conv_ovf_u | @cil_conv_ovf_u_un | + @cil_conv_ovf_u1 | @cil_conv_ovf_u1_un | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_ovf_u4 | @cil_conv_ovf_u4_un | @cil_conv_ovf_u8 | @cil_conv_ovf_u8_un | + @cil_conv_r4 | @cil_conv_r8 | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_i | @cil_conv_u | @cil_conv_r_un; + +@cil_ldind = @cil_ldind_i | @cil_ldind_i1 | @cil_ldind_i2 | @cil_ldind_i4 | @cil_ldind_i8 | + @cil_ldind_r4 | @cil_ldind_r8 | @cil_ldind_ref | @cil_ldind_u1 | @cil_ldind_u2 | @cil_ldind_u4; + +@cil_stind = @cil_stind_i | @cil_stind_i1 | @cil_stind_i2 | @cil_stind_i4 | @cil_stind_i8 | + @cil_stind_r4 | @cil_stind_r8 | @cil_stind_ref; + +@cil_bitwise_operation = @cil_binary_bitwise_operation | @cil_unary_bitwise_operation; + +@cil_binary_bitwise_operation = @cil_and | @cil_or | @cil_xor | @cil_shr | @cil_shr | @cil_shr_un | @cil_shl; + +@cil_binary_arithmetic_operation = @cil_add | @cil_sub | @cil_mul | @cil_div | @cil_div_un | + @cil_rem | @cil_rem_un | @cil_add_ovf | @cil_add_ovf_un | @cil_mul_ovf | @cil_mul_ovf_un | + @cil_sub_ovf | @cil_sub_ovf_un; + +@cil_unary_bitwise_operation = @cil_not; + +@cil_binary_expr = @cil_binary_arithmetic_operation | @cil_binary_bitwise_operation | @cil_read_array | @cil_comparison_operation; + +@cil_unary_arithmetic_operation = @cil_neg; + +@cil_comparison_operation = @cil_cgt_un | @cil_ceq | @cil_cgt | @cil_clt | @cil_clt_un; + +// Elements that retrieve an address of something +@cil_read_ref = @cil_ldloca_s | @cil_ldarga_s | @cil_ldflda | @cil_ldsflda | @cil_ldelema; + +// CIL array instructions + +@cil_read_array = + @cil_ldelem | @cil_ldelema | @cil_ldelem_i1 | @cil_ldelem_ref | @cil_ldelem_i | + @cil_ldelem_i1 | @cil_ldelem_i2 | @cil_ldelem_i4 | @cil_ldelem_i8 | @cil_ldelem_r4 | + @cil_ldelem_r8 | @cil_ldelem_u1 | @cil_ldelem_u2 | @cil_ldelem_u4; + +@cil_write_array = @cil_stelem | @cil_stelem_ref | + @cil_stelem_i | @cil_stelem_i1 | @cil_stelem_i2 | @cil_stelem_i4 | @cil_stelem_i8 | + @cil_stelem_r4 | @cil_stelem_r8; + +@cil_throw_any = @cil_throw | @cil_rethrow; + +#keyset[impl, index] +cil_instruction( + unique int id: @cil_instruction, + int opcode: int ref, + int index: int ref, + int impl: @cil_method_implementation ref); + +cil_jump( + unique int instruction: @cil_jump ref, + int target: @cil_instruction ref); + +cil_access( + unique int instruction: @cil_instruction ref, + int target: @cil_accessible ref); + +cil_value( + unique int instruction: @cil_literal ref, + string value: string ref); + +#keyset[instruction, index] +cil_switch( + int instruction: @cil_switch ref, + int index: int ref, + int target: @cil_instruction ref); + +cil_instruction_location( + unique int id: @cil_instruction ref, + int loc: @location ref); + +cil_type_location( + int id: @cil_type ref, + int loc: @location ref); + +cil_method_location( + int id: @cil_method ref, + int loc: @location ref); + +@cil_namespace = @namespace; + +@cil_type_container = @cil_type | @cil_namespace | @cil_method; + +case @cil_type.kind of + 0 = @cil_valueorreftype +| 1 = @cil_typeparameter +| 2 = @cil_array_type +| 3 = @cil_pointer_type +; + +cil_type( + unique int id: @cil_type, + string name: string ref, + int kind: int ref, + int parent: @cil_type_container ref, + int sourceDecl: @cil_type ref); + +cil_pointer_type( + unique int id: @cil_pointer_type ref, + int pointee: @cil_type ref); + +cil_array_type( + unique int id: @cil_array_type ref, + int element_type: @cil_type ref, + int rank: int ref); + +cil_method( + unique int id: @cil_method, + string name: string ref, + int parent: @cil_type ref, + int return_type: @cil_type ref); + +cil_method_source_declaration( + unique int method: @cil_method ref, + int source: @cil_method ref); + +cil_method_implementation( + unique int id: @cil_method_implementation, + int method: @cil_method ref, + int location: @assembly ref); + +cil_implements( + int id: @cil_method ref, + int decl: @cil_method ref); + +#keyset[parent, name] +cil_field( + unique int id: @cil_field, + int parent: @cil_type ref, + string name: string ref, + int field_type: @cil_type ref); + +@cil_element = @cil_instruction | @cil_declaration | @cil_handler | @cil_attribute | @cil_namespace; +@cil_named_element = @cil_declaration | @cil_namespace; +@cil_declaration = @cil_variable | @cil_method | @cil_type | @cil_member; +@cil_accessible = @cil_declaration; +@cil_variable = @cil_field | @cil_stack_variable; +@cil_stack_variable = @cil_local_variable | @cil_parameter; +@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event; + +#keyset[method, index] +cil_parameter( + unique int id: @cil_parameter, + int method: @cil_method ref, + int index: int ref, + int param_type: @cil_type ref); + +cil_parameter_in(unique int id: @cil_parameter ref); +cil_parameter_out(unique int id: @cil_parameter ref); + +cil_setter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_getter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_adder(unique int event: @cil_event ref, + int method: @cil_method ref); + +cil_remover(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_raiser(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_property( + unique int id: @cil_property, + int parent: @cil_type ref, + string name: string ref, + int property_type: @cil_type ref); + +#keyset[parent, name] +cil_event(unique int id: @cil_event, + int parent: @cil_type ref, + string name: string ref, + int event_type: @cil_type ref); + +#keyset[impl, index] +cil_local_variable( + unique int id: @cil_local_variable, + int impl: @cil_method_implementation ref, + int index: int ref, + int var_type: @cil_type ref); + +// CIL handlers (exception handlers etc). + +case @cil_handler.kind of + 0 = @cil_catch_handler +| 1 = @cil_filter_handler +| 2 = @cil_finally_handler +| 4 = @cil_fault_handler +; + +#keyset[impl, index] +cil_handler( + unique int id: @cil_handler, + int impl: @cil_method_implementation ref, + int index: int ref, + int kind: int ref, + int try_start: @cil_instruction ref, + int try_end: @cil_instruction ref, + int handler_start: @cil_instruction ref); + +cil_handler_filter( + unique int id: @cil_handler ref, + int filter_start: @cil_instruction ref); + +cil_handler_type( + unique int id: @cil_handler ref, + int catch_type: @cil_type ref); + +@cil_controlflow_node = @cil_entry_point | @cil_instruction; + +@cil_entry_point = @cil_method_implementation | @cil_handler; + +@cil_dataflow_node = @cil_instruction | @cil_variable | @cil_method; + +cil_method_stack_size( + unique int method: @cil_method_implementation ref, + int size: int ref); + +// CIL modifiers + +cil_public(int id: @cil_member ref); +cil_private(int id: @cil_member ref); +cil_protected(int id: @cil_member ref); +cil_internal(int id: @cil_member ref); +cil_static(int id: @cil_member ref); +cil_sealed(int id: @cil_member ref); +cil_virtual(int id: @cil_method ref); +cil_abstract(int id: @cil_member ref); +cil_class(int id: @cil_type ref); +cil_interface(int id: @cil_type ref); +cil_security(int id: @cil_member ref); +cil_requiresecobject(int id: @cil_method ref); +cil_specialname(int id: @cil_method ref); +cil_newslot(int id: @cil_method ref); + +cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref); +cil_base_interface(int id: @cil_type ref, int base: @cil_type ref); + +#keyset[unbound, index] +cil_type_parameter( + int unbound: @cil_member ref, + int index: int ref, + int param: @cil_typeparameter ref); + +#keyset[bound, index] +cil_type_argument( + int bound: @cil_member ref, + int index: int ref, + int t: @cil_type ref); + +// CIL type parameter constraints + +cil_typeparam_covariant(int tp: @cil_typeparameter ref); +cil_typeparam_contravariant(int tp: @cil_typeparameter ref); +cil_typeparam_class(int tp: @cil_typeparameter ref); +cil_typeparam_struct(int tp: @cil_typeparameter ref); +cil_typeparam_new(int tp: @cil_typeparameter ref); +cil_typeparam_constraint(int tp: @cil_typeparameter ref, int supertype: @cil_type ref); + +// CIL attributes + +cil_attribute( + unique int attributeid: @cil_attribute, + int element: @cil_declaration ref, + int constructor: @cil_method ref); + +#keyset[attribute_id, param] +cil_attribute_named_argument( + int attribute_id: @cil_attribute ref, + string param: string ref, + string value: string ref); + +#keyset[attribute_id, index] +cil_attribute_positional_argument( + int attribute_id: @cil_attribute ref, + int index: int ref, + string value: string ref); + + +// Common .Net data model covering both C# and CIL + +// Common elements +@dotnet_element = @element | @cil_element; +@dotnet_named_element = @named_element | @cil_named_element; +@dotnet_callable = @callable | @cil_method; +@dotnet_variable = @variable | @cil_variable; +@dotnet_field = @field | @cil_field; +@dotnet_parameter = @parameter | @cil_parameter; +@dotnet_declaration = @declaration | @cil_declaration; +@dotnet_member = @member | @cil_member; +@dotnet_event = @event | @cil_event; +@dotnet_property = @property | @cil_property | @indexer; + +// Common types +@dotnet_type = @type | @cil_type; +@dotnet_call = @call | @cil_call_any; +@dotnet_throw = @throw_element | @cil_throw_any; +@dotnet_valueorreftype = @cil_valueorreftype | @value_or_ref_type | @cil_array_type | @void_type; +@dotnet_typeparameter = @type_parameter | @cil_typeparameter; +@dotnet_array_type = @array_type | @cil_array_type; +@dotnet_pointer_type = @pointer_type | @cil_pointer_type; +@dotnet_type_parameter = @type_parameter | @cil_typeparameter; +@dotnet_generic = @dotnet_valueorreftype | @dotnet_callable; + +// Attributes +@dotnet_attribute = @attribute | @cil_attribute; + +// Expressions +@dotnet_expr = @expr | @cil_expr; + +// Literals +@dotnet_literal = @literal_expr | @cil_literal; +@dotnet_string_literal = @string_literal_expr | @cil_ldstr; +@dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; +@dotnet_float_literal = @float_literal_expr | @cil_ldc_r; +@dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/upgrade.properties b/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/upgrade.properties new file mode 100644 index 000000000000..75d52d62e13a --- /dev/null +++ b/csharp/upgrades/f2aa2d4ac31309bd83ab633d0f40e8a442767bd1/upgrade.properties @@ -0,0 +1,2 @@ +description: Removed uniqueness constraint from 'explicitly_implements' relation +compatibility: full diff --git a/docs/language/README.rst b/docs/language/README.rst index 552323611994..d47dc641b4da 100644 --- a/docs/language/README.rst +++ b/docs/language/README.rst @@ -103,7 +103,7 @@ generates html slide shows in the ```` directory when run from the ``ql-training`` source directory. For more information about creating slides for QL training and variant analysis -examples, see the `template slide deck `__. +examples, see the `template slide deck `__. Viewing the current version of the CodeQL documentation ******************************************************* diff --git a/docs/language/global-sphinx-files/_static/custom.css_t b/docs/language/global-sphinx-files/_static/custom.css_t index 482cefea5e98..927660034fe1 100644 --- a/docs/language/global-sphinx-files/_static/custom.css_t +++ b/docs/language/global-sphinx-files/_static/custom.css_t @@ -1,305 +1,85 @@ /* - * Sphinx stylesheet to add some customizations to the default Alabaster theme. + * This Sphinx stylesheet adds some customizations to the default Alabaster theme. * * The source for the default stylesheet can be found at * https://github.com/bitprophet/alabaster/blob/master/alabaster/static/alabaster.css_t * + * For the classes provided by the primer, see https://unpkg.com/@primer/css/dist/primer.css */ -div.body h1 { - margin-top: 1em; -} - -div.wrapper { - padding-top: 78px; - margin-bottom: 0px; - height: 637px; -} - -/* -- HEADER ------------------------------------------------------------------------------- */ - -div.header { - background-color: #140f46; - top:0; - position: fixed; - width: 100%; - padding: 0 30px 30px 30px; -} -.textContainer { - margin: auto; -} - -/*div.logocontainer { - width: 50%; -}*/ -#siteBanner { - position: fixed; - width: 100%; - height: 4.5rem; - margin: 0; - padding: 0 20px 0 30px; - background-color: #140f46; - opacity: 0.95; - transform: translate3d(0px, 0px, 0px); - z-index: 1; -} -#siteBanner input { - border-width: 0; - /*color: rgb(244, 244, 244); - background-color: #140f46; - font-size: 0.9rem;*/ - letter-spacing: 0.1rem; - /*width: 12rem;*/ -} -#siteBanner input:focus, -#siteBanner textarea:focus, -#siteBanner select:focus{ - outline: none; -} -svg.Header-logo-white { - display: inline; -} -svg.Header-logo-purple { - display: none; -} +/* -- CODE SNIPPETS ----------------------------------------------------------------------- */ -div.linkcontainer { - /*width: 50%;*/ - margin-left: auto; - margin-top: -1.3rem; - padding-right: 50px; +code { + font-size: 0.9em !important; /* makes code snippets in headings the correct size */ } -div.linkbar { - float:right; -} -.linkbar a { - color: White; - font-size: 18px; - margin-left: 20px; - text-decoration: none; -} - - -.linkbar a:hover { - text-decoration: underline; -} -.small-bar { - float:right; - display:none; -} - -.small-bar a { - color: White; - font-size: 18px; - margin-left: 20px; - text-decoration: none; -} -.small-bar a:hover { - text-decoration: underline; -} - -#Header-logo { - text-decoration: none; - position: relative; - width: 98px; - height: 20px; -} - -#Header-logo > * { - margin-top: 1.7rem; -} +/* -- MAIN BODY ---------------------------------------------------------------------------- */ -svg.Header-logo-white { - display: inline; +main { + min-height: calc(100vh - 68px); } - -/* -- DOCUMENT ------------------------------------------------------------------------------- */ - -div.document { - width: 100%; +div.body { + max-width: 100%; + min-width: unset; padding: 0; - margin: 0 0 30px 0; } -div.documentwrapper { - width: 90%; - box-shadow: 0.1rem 0.4rem 1.5rem #d0cee4; - margin: 20px 20px 20px 20px; - padding-bottom: 50px; -} -div.mainBox { - margin-left: calc(30% + 25px); /* Allow 25px for the width of the ToC scrollbar */ +div.body li { + margin: 0 0 0.5em 0; /* Increase spacing between list items */ } -div.privacy { - text-align: right; - padding-right: 5%; - padding-bottom: 20px; +article { + min-height: calc(100vh - 145px); /* Makes sure GitHub footer stays at bottom of viewport */ } /* -- SIDEBAR ------------------------------------------------------------------------------- */ -div.navBox { - overflow-y:auto; - width:29%; - padding-left: 25px; - position: fixed; - height: calc(100% - 108px); - z-index: 100; - background-color: #FFFFFF; - padding-top: 30px; +.SideNav { + max-height: 100vh; /* Makes sure sidebar doesn't cover GitHub footer */ } -.navBox li { +.SideNav li { margin: 10px 0 10px 0px; } -.navBox ul, .navBox ol, .navBox li { +.SideNav ul, .SideNav ol, .SideNav li { list-style-type: none; } -.navBox ul li a { +.SideNav ul li a { border-bottom: none; + color: black; } -.navBox ul li a:hover { +.SideNav ul li a:hover { font-weight: bold; border-bottom: none; } -.navBox a.current { +.SideNav a.current { font-weight: bold; text-decoration: underline; } -/* -- SMALL SCREEN ------------------------------------------------------------------------------- */ - -@media screen and (max-width: 875px){ - - #siteBanner { - position: relative; - } - div.wrapper { - /*margin-top:-100px;*/ - flex-direction: column; - height: 100%; - width: 100%; - padding-top: 0px; - } - div.mainBox { - margin: 0 10px 0 10px; - position: relative; - height: 100%; - width: 100%; - } - div.documentwrapper { - padding-left: 20px; - width:100%; - box-shadow: none; - margin: 0; - padding-left:0; - } - - div.navBox { - position: relative; - width: 100%; - border-color: #2f1695; - } - body { - margin: 0; - padding: 0; - overflow: inherit; - width:95%; - } - - div.body { - min-width: unset; - } - div.linkbar { - display: none; - - } - .small-bar { - display: inherit; - } - - ul { - margin: 10px 20px 10px 20px; - } - - div.privacy { - padding-right: 0%; - } +/* -- BREADCRUMBS --------------------------------------------------------------------------------*/ +div.related ul { + padding: 0; } - -/* -- PRINT VIEW ----------------------------------------------------------------------------*/ -@media print { - div.navBox { - display: none; - } +/* -- LINKS --------------------------------------------------------------------------------------*/ - #siteBanner { - display: none; - } - - div.wrapper { - /*margin-top:-100px;*/ - flex-direction: column; - height: 100%; - width: 100%; - padding-top: 0px; - } - div.mainBox { - margin: 0 10px 0 10px; - position: relative; - height: 100%; - width: 100%; - } - div.documentwrapper { - padding-left: 20px; - width:100%; - box-shadow: none; - margin: 0; - padding-left:0; - } - - body { - margin: 0; - padding: 0; - overflow: inherit; - width:95%; - } - - div.body { - min-width: unset; - } - div.linkbar { - display: none; - - } - .small-bar { - display: inherit; - } - - ul { - margin: 10px 20px 10px 20px; - } - - div.privacy { - display: none; - } +a.reference { + border-bottom: none; } -/* -- MAIN BODY ---------------------------------------------------------------------------- */ +a.reference:hover { + text-decoration: none; +} -div.body { - max-width: 100%; /* overrule 800px in basic.css */ -} +/* -- ADMONITIONS ---------------------------------------------------------------------------- */ /* * Override default styling for "admonitions". @@ -337,8 +117,11 @@ pre { border: 1px solid #BBB; background: #F6F6F6; border-radius: 5px; + white-space: pre-wrap; } +/* -- QL SYNTAX HIGHLIGHTING -------------------------------------------------- */ + /* * Use more appropriate colors for syntax highlighting * @@ -354,35 +137,25 @@ pre { .highlight .kt { color: #7a65cd; font-weight: bold } /* built-in type keywords */ .highlight .kr { color: #333; font-style: italic } /* annotations */ -/* -- INDEX -------------------------------------------------------------------------------- */ + +/* -- SEARCH PAGE ---------------------------------------------------------------------------- */ + +div.context { /* hide rst search snippets */ + display: none; +} + +/* -- INDEX ---------------------------------------------------------------------------------------- */ table.indextable li > a { text-decoration: unset; } +/* -- ANCHOR LINKS ------------------------------------------------------------------------------- */ -/* -- FOOTER ------------------------------------------------------------------------------- */ -div.footer { - display: none; - /* background-color: #2F1695; */ - /* color: #FFFFFF; */ - /* padding: 30px; */ - /* margin: 0; */ - /* width: calc(100% - 60px); */ /* Allow for 2 x 30px of padding (at left & right side) */ - /* position: fixed; */ - /* bottom: 0; */ -} +/* make anchor highlighting more sensible */ -/* Adjust anchor targets to allow for the fixed banner */ -div.documentwrapper a[name], -div.documentwrapper :target::before, -div.footnote-group a[name], -div.footnote-group :target::before - { - content: ""; - display: block; - height: 78px; /* Height of banner */ - margin-top: -78px; /* Prevent this showing as a gap */ +dt:target::before { + background-color: white; } /* -- CSV TABLE STYLING ------------------------------------------------------------------------------- */ @@ -444,4 +217,39 @@ blockquote.pull-quote > :last-child { .toggle .name.open:after { content: " â–ŧ"; +} + +/* -- PRINT VIEW ----------------------------------------------------------------------------*/ + +@media print { + + div.SideNav { + display: none; + } + + body { + margin: 0; + padding: 0; + overflow: inherit; + width:95%; + } + + div.privacy { + display: none; + } +} + +/* -- SMALL SCREEN ------------------------------------------------------------------------------- */ + +@media screen and (max-width: 875px) { + + /* Overrides strange behaviour caused by default styles */ + + body { + padding: 0; + } + + div.footer { + display: block; + } } \ No newline at end of file diff --git a/docs/language/global-sphinx-files/_static/primer.css b/docs/language/global-sphinx-files/_static/primer.css new file mode 100644 index 000000000000..47c60f18fd01 --- /dev/null +++ b/docs/language/global-sphinx-files/_static/primer.css @@ -0,0 +1,14785 @@ +/*! + * Primer CSS + * https://primer.style + * + * Released under MIT license. + */ + +/*! + * @primer/css/core + * http://primer.style/css + * + * Released under MIT license. Copyright (c) 2019 GitHub Inc. + */ + +.octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor +} + +/*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */ + +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0 +} + +article, aside, details, figcaption, figure, footer, header, main, menu, nav, section { + display: block +} + +summary { + display: list-item +} + +audio, canvas, progress, video { + display: inline-block +} + +audio:not([controls]) { + display: none; + height: 0 +} + +progress { + vertical-align: baseline +} + +template, [hidden] { + display: none !important +} + +a { + background-color: transparent +} + +a:active, a:hover { + outline-width: 0 +} + +abbr[title] { + border-bottom: none; + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted +} + +b, strong { + font-weight: inherit +} + +b, strong { + font-weight: bolder +} + +dfn { + font-style: italic +} + +h1 { + font-size: 2em; + margin: 0.67em 0 +} + +mark { + background-color: #ff0; + color: #1b1f23 +} + +small { + font-size: 80% +} + +sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sub { + bottom: -0.25em +} + +sup { + top: -0.5em +} + +img { + border-style: none +} + +svg:not(:root) { + overflow: hidden +} + +code, kbd, pre, samp { + font-family: monospace, monospace; + font-size: 1em +} + +figure { + margin: 1em 40px +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible +} + +button, input, select, textarea { + font: inherit; + margin: 0 +} + +optgroup { + font-weight: 600 +} + +button, input { + overflow: visible +} + +button, select { + text-transform: none +} + +button, html [type="button"], [type="reset"], [type="submit"] { + -webkit-appearance: button +} + +button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0 +} + +button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText +} + +fieldset { + border: 1px solid silver; + margin: 0 2px; + padding: 0.35em 0.625em .75em +} + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal +} + +textarea { + overflow: auto +} + +[type="checkbox"], [type="radio"] { + box-sizing: border-box; + padding: 0 +} + +[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { + height: auto +} + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px +} + +[type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-input-placeholder { + color: inherit; + opacity: 0.54 +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit +} + +* { + box-sizing: border-box +} + +input, select, textarea, button { + font-family: inherit; + font-size: inherit; + line-height: inherit +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + font-size: 14px; + line-height: 1.5; + color: #24292e; + background-color: #fff +} + +a { + color: #0366d6; + text-decoration: none +} + +a:hover { + text-decoration: underline +} + +b, strong { + font-weight: 600 +} + +hr, .rule { + height: 0; + margin: 15px 0; + overflow: hidden; + background: transparent; + border: 0; + border-bottom: 1px solid #dfe2e5 +} + +hr::before, .rule::before { + display: table; + content: "" +} + +hr::after, .rule::after { + display: table; + clear: both; + content: "" +} + +table { + border-spacing: 0; + border-collapse: collapse +} + +td, th { + padding: 0 +} + +button { + cursor: pointer; + border-radius: 0 +} + +[hidden][hidden] { + display: none !important +} + +details summary { + cursor: pointer +} + +details:not([open])>*:not(summary) { + display: none !important +} + +kbd { + display: inline-block; + padding: 3px 5px; + font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + line-height: 10px; + color: #444d56; + vertical-align: middle; + background-color: #fafbfc; + border: solid 1px #d1d5da; + border-bottom-color: #d1d5da; + border-radius: 6px; + box-shadow: inset 0 -1px 0 #d1d5da +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0 +} + +h1 { + font-size: 32px; + font-weight: 600 +} + +h2 { + font-size: 24px; + font-weight: 600 +} + +h3 { + font-size: 20px; + font-weight: 600 +} + +h4 { + font-size: 16px; + font-weight: 600 +} + +h5 { + font-size: 14px; + font-weight: 600 +} + +h6 { + font-size: 12px; + font-weight: 600 +} + +p { + margin-top: 0; + margin-bottom: 10px +} + +small { + font-size: 90% +} + +blockquote { + margin: 0 +} + +ul, ol { + padding-left: 0; + margin-top: 0; + margin-bottom: 0 +} + +ol ol, ul ol { + list-style-type: lower-roman +} + +ul ul ol, ul ol ol, ol ul ol, ol ol ol { + list-style-type: lower-alpha +} + +dd { + margin-left: 0 +} + +tt, code { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + font-size: 12px +} + +pre { + margin-top: 0; + margin-bottom: 0; + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + font-size: 12px +} + +.octicon { + vertical-align: text-bottom +} + +.Box { + background-color: #fff; + border: 1px #e1e4e8 solid; + border-radius: 6px +} + +.Box--condensed { + line-height: 1.25 +} + +.Box--condensed .Box-header { + padding: 8px 16px +} + +.Box--condensed .Box-body { + padding: 8px 16px +} + +.Box--condensed .Box-footer { + padding: 8px 16px +} + +.Box--condensed .Box-btn-octicon.btn-octicon { + padding: 8px 16px; + margin: -8px -16px; + line-height: 1.25 +} + +.Box--condensed .Box-row { + padding: 8px 16px +} + +.Box--spacious .Box-header { + padding: 24px; + line-height: 1.25 +} + +.Box--spacious .Box-title { + font-size: 20px +} + +.Box--spacious .Box-body { + padding: 24px +} + +.Box--spacious .Box-footer { + padding: 24px +} + +.Box--spacious .Box-btn-octicon.btn-octicon { + padding: 24px; + margin: -24px -24px +} + +.Box--spacious .Box-row { + padding: 24px +} + +.Box-header { + padding: 16px; + margin: -1px -1px 0; + background-color: #f6f8fa; + border-color: #e1e4e8; + border-style: solid; + border-width: 1px; + border-top-left-radius: 6px; + border-top-right-radius: 6px +} + +.Box-title { + font-size: 14px; + font-weight: 600 +} + +.Box-body { + padding: 16px; + border-bottom: 1px solid #e1e4e8 +} + +.Box-body:last-of-type { + margin-bottom: -1px; + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px +} + +.Box-row { + padding: 16px; + margin-top: -1px; + list-style-type: none; + border-top: 1px #e1e4e8 solid +} + +.Box-row:first-of-type { + border-top-left-radius: 6px; + border-top-right-radius: 6px +} + +.Box-row:last-of-type { + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px +} + +.Box-row.Box-row--unread, .Box-row.unread { + box-shadow: 2px 0 0 #0366d6 inset +} + +.Box-row.navigation-focus .Box-row--drag-button { + color: #0366d6; + cursor: grab; + opacity: 100 +} + +.Box-row.navigation-focus.is-dragging .Box-row--drag-button { + cursor: grabbing +} + +.Box-row.navigation-focus.sortable-chosen { + background-color: #fafbfc +} + +.Box-row.navigation-focus.sortable-ghost { + background-color: #f6f8fa +} + +.Box-row.navigation-focus.sortable-ghost .Box-row--drag-hide { + opacity: 0 +} + +.Box-row--focus-gray.navigation-focus { + background-color: #f6f8fa +} + +.Box-row--focus-blue.navigation-focus { + background-color: #f1f8ff +} + +.Box-row--hover-gray:hover { + background-color: #f6f8fa +} + +.Box-row--hover-blue:hover { + background-color: #f1f8ff +} + +@media (min-width: 768px) { + .Box-row-link { + color: #24292e; + text-decoration: none + } + .Box-row-link:hover { + color: #0366d6; + text-decoration: none + } +} + +.Box-row--drag-button { + opacity: 0 +} + +.Box-footer { + padding: 16px; + margin-top: -1px; + border-top: 1px #e1e4e8 solid +} + +.Box--scrollable { + max-height: 324px; + overflow: scroll +} + +.Box--blue { + border-color: #c8e1ff +} + +.Box--blue .Box-header { + background-color: #f1f8ff; + border-color: #c8e1ff +} + +.Box--blue .Box-body { + border-color: #c8e1ff +} + +.Box--blue .Box-row { + border-color: #c8e1ff +} + +.Box--blue .Box-footer { + border-color: #c8e1ff +} + +.Box--danger { + border-color: #d73a49 +} + +.Box--danger .Box-row:first-of-type { + border-color: #d73a49 +} + +.Box--danger .Box-body:last-of-type { + border-color: #d73a49 +} + +.Box-header--blue { + background-color: #f1f8ff; + border-color: #c8e1ff +} + +.Box-row--yellow { + background-color: #fffbdd +} + +.Box-row--blue { + background-color: #f1f8ff +} + +.Box-row--gray { + background-color: #f6f8fa +} + +.Box-btn-octicon.btn-octicon { + padding: 16px 16px; + margin: -16px -16px; + line-height: 1.5 +} + +.breadcrumb-item { + display: inline-block; + margin-left: -0.35em; + white-space: nowrap; + list-style: none +} + +.breadcrumb-item::after { + padding-right: .5em; + padding-left: .5em; + color: #e1e4e8; + content: "/" +} + +.breadcrumb-item:first-child { + margin-left: 0 +} + +.breadcrumb-item-selected, .breadcrumb-item[aria-current]:not([aria-current=false]) { + color: #586069 +} + +.breadcrumb-item-selected::after, .breadcrumb-item[aria-current]:not([aria-current=false])::after { + content: none +} + +.btn { + position: relative; + display: inline-block; + padding: 5px 16px; + font-size: 14px; + font-weight: 500; + line-height: 20px; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border: 1px solid; + border-radius: 6px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.btn:hover { + text-decoration: none +} + +.btn:disabled, .btn.disabled, .btn[aria-disabled=true] { + cursor: default +} + +.btn:disabled .octicon, .btn.disabled .octicon, .btn[aria-disabled=true] .octicon { + color: inherit +} + +.btn i { + font-style: normal; + font-weight: 500; + opacity: 0.75 +} + +.btn .octicon { + margin-right: 4px; + color: #6a737d; + vertical-align: text-bottom +} + +.btn .octicon:only-child { + margin-right: 0 +} + +.btn .Counter { + margin-left: 2px; + color: inherit; + text-shadow: none; + vertical-align: top; + background-color: rgba(27, 31, 35, 0.08) +} + +.btn .dropdown-caret { + margin-left: 4px; + opacity: 0.8 +} + +.btn { + color: #24292e; + background-color: #fafbfc; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.25); + transition: background-color 0.2s cubic-bezier(0.3, 0, 0.5, 1) +} + +.btn:hover, .btn.hover, [open]>.btn { + background-color: #f3f4f6; + transition-duration: 0.1s +} + +.btn:active, .btn.selected, .btn[aria-selected=true] { + background-color: #edeff2; + box-shadow: inset 0 1px 0 rgba(225, 228, 232, 0.2); + transition: none +} + +.btn:disabled, .btn.disabled, .btn[aria-disabled=true] { + color: #959da5; + background-color: #fafbfc; + border-color: rgba(27, 31, 35, 0.15) +} + +.btn:focus, .btn.focus { + outline: 1px dotted transparent; + outline-offset: 2px; + box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.3) +} + +.btn-primary { + color: #fff; + background-color: #2ea44f; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.03) +} + +.btn-primary:hover, .btn-primary.hover, [open]>.btn-primary { + background-color: #2c974b +} + +.btn-primary:active, .btn-primary.selected, .btn-primary[aria-selected=true] { + background-color: #2a8f47; + box-shadow: inset 0 1px 0 rgba(20, 70, 32, 0.2) +} + +.btn-primary:disabled, .btn-primary.disabled, .btn-primary[aria-disabled=true] { + color: rgba(255, 255, 255, 0.8); + background-color: #94d3a2; + border-color: rgba(27, 31, 35, 0.1); + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.03) +} + +.btn-primary:focus, .btn-primary.focus { + box-shadow: 0 0 0 3px rgba(46, 164, 79, 0.4) +} + +.btn-primary .Counter { + color: inherit; + background-color: rgba(255, 255, 255, 0.2) +} + +.btn-primary .octicon { + color: rgba(255, 255, 255, 0.8) +} + +.btn-danger { + color: #cb2431; + transition: none +} + +.btn-danger:hover, [open]>.btn-danger { + color: #fff; + background-color: #cb2431; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.03) +} + +.btn-danger:hover .Counter, [open]>.btn-danger .Counter { + background-color: rgba(255, 255, 255, 0.2) +} + +.btn-danger:hover .octicon, [open]>.btn-danger .octicon { + color: inherit +} + +.btn-danger:active, .btn-danger.selected, .btn-danger[aria-selected=true] { + color: #fff; + background-color: #be222e; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: inset 0 1px 0 rgba(134, 24, 29, 0.2) +} + +.btn-danger:disabled, .btn-danger.disabled, .btn-danger[aria-disabled=true] { + color: rgba(203, 36, 49, 0.5); + background-color: #fafbfc; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.25) +} + +.btn-danger:disabled .Counter, .btn-danger.disabled .Counter, .btn-danger[aria-disabled=true] .Counter { + background-color: rgba(203, 36, 49, 0.05) +} + +.btn-danger:focus { + box-shadow: 0 0 0 3px rgba(203, 36, 49, 0.4) +} + +.btn-danger .Counter { + color: inherit; + background-color: rgba(203, 36, 49, 0.1) +} + +.btn-outline { + color: #0366d6; + transition: none +} + +.btn-outline:hover, [open]>.btn-outline { + color: #fff; + background-color: #0366d6; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.03) +} + +.btn-outline:hover .Counter, [open]>.btn-outline .Counter { + background-color: rgba(255, 255, 255, 0.2) +} + +.btn-outline:hover .octicon, [open]>.btn-outline .octicon { + color: inherit +} + +.btn-outline:active, .btn-outline.selected, .btn-outline[aria-selected=true] { + color: #fff; + background-color: #035fc7; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: inset 0 1px 0 rgba(5, 38, 76, 0.2) +} + +.btn-outline:disabled, .btn-outline.disabled, .btn-outline[aria-disabled=true] { + color: rgba(3, 102, 214, 0.5); + background-color: #fafbfc; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.25) +} + +.btn-outline:disabled .Counter, .btn-outline.disabled .Counter, .btn-outline[aria-disabled=true] .Counter { + background-color: rgba(3, 102, 214, 0.05) +} + +.btn-outline:focus { + box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.4) +} + +.btn-outline .Counter { + color: inherit; + background-color: rgba(3, 102, 214, 0.1) +} + +.btn-blue { + color: #fff; + background-color: #0361cc; + background-image: linear-gradient(-180deg, #0679fc 0%, #0361cc 90%) +} + +.btn-blue:focus, .btn-blue.focus { + box-shadow: 0 0 0 0.2em rgba(6, 121, 252, 0.4) +} + +.btn-blue:hover, .btn-blue.hover { + background-color: #035cc2; + background-image: linear-gradient(-180deg, #0374f4 0%, #035cc2 90%); + background-position: -.5em; + border-color: rgba(27, 31, 35, 0.5) +} + +.btn-blue:active, .btn-blue.selected, .btn-blue[aria-selected=true], [open]>.btn-blue { + background-color: #045cc1; + background-image: none; + border-color: rgba(27, 31, 35, 0.5); + box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15) +} + +.btn-blue:disabled, .btn-blue.disabled, .btn-blue[aria-disabled=true] { + color: rgba(255, 255, 255, 0.75); + background-color: #81b0e5; + background-image: none; + border-color: rgba(27, 31, 35, 0.15); + box-shadow: none +} + +.btn-blue .Counter { + color: #0366d6; + background-color: #fff +} + +.btn-sm { + padding: 3px 12px; + font-size: 12px; + line-height: 20px +} + +.btn-sm .octicon { + vertical-align: text-top +} + +.btn-large { + padding: .75em 1.5em; + font-size: inherit; + line-height: 1.5; + border-radius: 0.5em +} + +.btn-block { + display: block; + width: 100%; + text-align: center +} + +.BtnGroup { + display: inline-block; + vertical-align: middle +} + +.BtnGroup::before { + display: table; + content: "" +} + +.BtnGroup::after { + display: table; + clear: both; + content: "" +} + +.BtnGroup+.BtnGroup, .BtnGroup+.btn { + margin-left: 4px +} + +.BtnGroup-item { + position: relative; + float: left; + border-right-width: 0; + border-radius: 0 +} + +.BtnGroup-item:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px +} + +.BtnGroup-item:last-child { + border-right-width: 1px; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px +} + +.BtnGroup-item.selected, .BtnGroup-item[aria-selected=true], .BtnGroup-item:focus, .BtnGroup-item:active, .BtnGroup-item:hover { + border-right-width: 1px +} + +.BtnGroup-item.selected+.BtnGroup-item, .BtnGroup-item.selected+.BtnGroup-parent .BtnGroup-item, .BtnGroup-item[aria-selected=true]+.BtnGroup-item, .BtnGroup-item[aria-selected=true]+.BtnGroup-parent .BtnGroup-item, .BtnGroup-item:focus+.BtnGroup-item, .BtnGroup-item:focus+.BtnGroup-parent .BtnGroup-item, .BtnGroup-item:active+.BtnGroup-item, .BtnGroup-item:active+.BtnGroup-parent .BtnGroup-item, .BtnGroup-item:hover+.BtnGroup-item, .BtnGroup-item:hover+.BtnGroup-parent .BtnGroup-item { + border-left-width: 0 +} + +.BtnGroup-parent { + float: left +} + +.BtnGroup-parent:first-child .BtnGroup-item { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px +} + +.BtnGroup-parent:last-child .BtnGroup-item { + border-right-width: 1px; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px +} + +.BtnGroup-parent .BtnGroup-item { + border-right-width: 0; + border-radius: 0 +} + +.BtnGroup-parent.selected .BtnGroup-item, .BtnGroup-parent[aria-selected=true] .BtnGroup-item, .BtnGroup-parent:focus .BtnGroup-item, .BtnGroup-parent:active .BtnGroup-item, .BtnGroup-parent:hover .BtnGroup-item { + border-right-width: 1px +} + +.BtnGroup-parent.selected+.BtnGroup-item, .BtnGroup-parent.selected+.BtnGroup-parent .BtnGroup-item, .BtnGroup-parent[aria-selected=true]+.BtnGroup-item, .BtnGroup-parent[aria-selected=true]+.BtnGroup-parent .BtnGroup-item, .BtnGroup-parent:focus+.BtnGroup-item, .BtnGroup-parent:focus+.BtnGroup-parent .BtnGroup-item, .BtnGroup-parent:active+.BtnGroup-item, .BtnGroup-parent:active+.BtnGroup-parent .BtnGroup-item, .BtnGroup-parent:hover+.BtnGroup-item, .BtnGroup-parent:hover+.BtnGroup-parent .BtnGroup-item { + border-left-width: 0 +} + +.BtnGroup-item:focus, .BtnGroup-item:active, .BtnGroup-parent:focus, .BtnGroup-parent:active { + z-index: 1 +} + +.btn-link { + display: inline-block; + padding: 0; + font-size: inherit; + color: #0366d6; + text-decoration: none; + white-space: nowrap; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: transparent; + border: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.btn-link:hover { + text-decoration: underline +} + +.btn-link:disabled, .btn-link:disabled:hover, .btn-link[aria-disabled=true], .btn-link[aria-disabled=true]:hover { + color: rgba(88, 96, 105, 0.5); + cursor: default +} + +.btn-invisible { + color: #0366d6; + background-color: transparent; + border: 0; + border-radius: 0; + box-shadow: none +} + +.btn-invisible:hover, .btn-invisible:active, .btn-invisible:focus, .btn-invisible.selected, .btn-invisible[aria-selected=true], .btn-invisible.zeroclipboard-is-hover, .btn-invisible.zeroclipboard-is-active { + color: #0366d6; + background: none; + outline: none; + box-shadow: none +} + +.btn-octicon { + display: inline-block; + padding: 5px; + margin-left: 5px; + line-height: 1; + color: #586069; + vertical-align: middle; + background: transparent; + border: 0 +} + +.btn-octicon:hover { + color: #0366d6 +} + +.btn-octicon.disabled, .btn-octicon[aria-disabled=true] { + color: #959da5; + cursor: default +} + +.btn-octicon.disabled:hover, .btn-octicon[aria-disabled=true]:hover { + color: #959da5 +} + +.btn-octicon-danger:hover { + color: #cb2431 +} + +.close-button { + padding: 0; + background: transparent; + border: 0; + outline: none +} + +.hidden-text-expander { + display: block +} + +.hidden-text-expander.inline { + position: relative; + top: -1px; + display: inline-block; + margin-left: 5px; + line-height: 0 +} + +.hidden-text-expander a, .ellipsis-expander { + display: inline-block; + height: 12px; + padding: 0 5px 5px; + font-size: 12px; + font-weight: 600; + line-height: 6px; + color: #444d56; + text-decoration: none; + vertical-align: middle; + background: #dfe2e5; + border: 0; + border-radius: 1px +} + +.hidden-text-expander a:hover, .ellipsis-expander:hover { + text-decoration: none; + background-color: #c6cbd1 +} + +.hidden-text-expander a:active, .ellipsis-expander:active { + color: #fff; + background-color: #2188ff +} + +.btn-with-count { + float: left; + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.btn-with-count:focus { + z-index: 1 +} + +.social-count { + position: relative; + float: left; + padding: 3px 12px; + font-size: 12px; + font-weight: 600; + line-height: 20px; + color: #24292e; + vertical-align: middle; + background-color: #fff; + border: 1px solid rgba(27, 31, 35, 0.15); + border-left: 0; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.25) +} + +.social-count:hover, .social-count:active { + text-decoration: none +} + +.social-count:hover { + color: #0366d6; + cursor: pointer +} + +.social-count:focus { + z-index: 1; + outline: 0; + box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.3) +} + +.TableObject { + display: table +} + +.TableObject-item { + display: table-cell; + width: 1%; + white-space: nowrap; + vertical-align: middle +} + +.TableObject-item--primary { + width: 99% +} + +fieldset { + padding: 0; + margin: 0; + border: 0 +} + +label { + font-weight: 600 +} + +.form-control, .form-select { + padding: 5px 12px; + font-size: 14px; + line-height: 20px; + color: #24292e; + vertical-align: middle; + background-color: #fff; + background-repeat: no-repeat; + background-position: right 8px center; + border: 1px solid #e1e4e8; + border-radius: 6px; + outline: none; + box-shadow: inset 0 1px 0 rgba(225, 228, 232, 0.2) +} + +.form-control.focus, .form-control:focus, .form-select.focus, .form-select:focus { + border-color: #0366d6; + outline: none; + box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.3) +} + +.form-control[disabled], .form-select[disabled] { + color: #959da5; + background-color: #f3f4f6 +} + +@supports (-webkit-touch-callout: none) { + .form-control, .form-select { + font-size: 16px + } + @media (min-width: 768px) { + .form-control, .form-select { + font-size: 14px + } + } +} + +textarea.form-control { + padding-top: 8px; + padding-bottom: 8px; + line-height: 1.5 +} + +.input-contrast { + background-color: #fafbfc +} + +.input-contrast:focus { + background-color: #fff +} + +.input-dark { + color: #fff; + background-color: rgba(255, 255, 255, 0.15); + border-color: transparent; + box-shadow: none +} + +.input-dark:-ms-input-placeholder { + color: inherit; + opacity: 0.6 +} + +.input-dark::-ms-input-placeholder { + color: inherit; + opacity: 0.6 +} + +.input-dark::placeholder { + color: inherit; + opacity: 0.6 +} + +.input-dark.focus, .input-dark:focus { + border-color: rgba(27, 31, 35, 0.3); + box-shadow: 0 0 0 0.2em rgba(121, 184, 255, 0.4) +} + +:-ms-input-placeholder { + color: #6a737d; + opacity: 1 +} + +::-ms-input-placeholder { + color: #6a737d; + opacity: 1 +} + +::placeholder { + color: #6a737d; + opacity: 1 +} + +.input-sm { + padding-top: 3px; + padding-bottom: 3px; + font-size: 12px; + line-height: 20px +} + +.input-lg { + font-size: 16px +} + +.input-block { + display: block; + width: 100% +} + +.input-monospace { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace +} + +.input-hide-webkit-autofill::-webkit-contacts-auto-fill-button { + position: absolute; + right: 0; + display: none !important; + pointer-events: none; + visibility: hidden +} + +.form-checkbox { + padding-left: 20px; + margin: 15px 0; + vertical-align: middle +} + +.form-checkbox label em.highlight { + position: relative; + left: -4px; + padding: 2px 4px; + font-style: normal; + background: #fffbdd; + border-radius: 6px +} + +.form-checkbox input[type=checkbox], .form-checkbox input[type=radio] { + float: left; + margin: 5px 0 0 -20px; + vertical-align: middle +} + +.form-checkbox .note { + display: block; + margin: 0; + font-size: 12px; + font-weight: 400; + color: #586069 +} + +.form-checkbox-details { + display: none +} + +.form-checkbox-details-trigger:checked~* .form-checkbox-details, .form-checkbox-details-trigger:checked~.form-checkbox-details { + display: block +} + +.hfields { + margin: 15px 0 +} + +.hfields::before { + display: table; + content: "" +} + +.hfields::after { + display: table; + clear: both; + content: "" +} + +.hfields .form-group { + float: left; + margin: 0 30px 0 0 +} + +.hfields .form-group dt label, .hfields .form-group .form-group-header label { + display: inline-block; + margin: 5px 0 0; + color: #586069 +} + +.hfields .form-group dt img, .hfields .form-group .form-group-header img { + position: relative; + top: -2px +} + +.hfields .btn { + float: left; + margin: 28px 25px 0 -20px +} + +.hfields .form-select { + margin-top: 5px +} + +input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { + margin: 0; + -webkit-appearance: none; + appearance: none +} + +.form-actions::before { + display: table; + content: "" +} + +.form-actions::after { + display: table; + clear: both; + content: "" +} + +.form-actions .btn { + float: right +} + +.form-actions .btn+.btn { + margin-right: 5px +} + +.form-warning { + padding: 8px 10px; + margin: 10px 0; + font-size: 14px; + color: #735c0f; + background: #fffbdd; + border: 1px solid #f9c513; + border-radius: 6px +} + +.form-warning p { + margin: 0; + line-height: 1.5 +} + +.form-warning a { + font-weight: 600 +} + +.form-select { + display: inline-block; + max-width: 100%; + height: 32px; + padding-right: 24px; + background-color: #fff; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAMAAACzvE1FAAAADFBMVEUzMzMzMzMzMzMzMzMKAG/3AAAAA3RSTlMAf4C/aSLHAAAAPElEQVR42q3NMQ4AIAgEQTn//2cLdRKppSGzBYwzVXvznNWs8C58CiussPJj8h6NwgorrKRdTvuV9v16Afn0AYFOB7aYAAAAAElFTkSuQmCC"); + background-repeat: no-repeat; + background-position: right 8px center; + background-size: 8px 10px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.form-select::-ms-expand { + opacity: 0 +} + +.form-select[multiple] { + height: auto +} + +.select-sm { + height: 28px; + padding-top: 3px; + padding-bottom: 3px; + font-size: 12px +} + +.select-sm[multiple] { + height: auto; + min-height: 0 +} + +.form-group { + margin: 15px 0 +} + +.form-group .form-control { + width: 440px; + max-width: 100%; + margin-right: 5px; + background-color: #fafbfc +} + +.form-group .form-control:focus { + background-color: #fff +} + +.form-group .form-control.shorter { + width: 130px +} + +.form-group .form-control.short { + width: 250px +} + +.form-group .form-control.long { + width: 100% +} + +.form-group textarea.form-control { + width: 100%; + height: 200px; + min-height: 200px +} + +.form-group textarea.form-control.short { + height: 50px; + min-height: 50px +} + +.form-group dt, .form-group .form-group-header { + margin: 0 0 6px +} + +.form-group label { + position: relative +} + +.form-group.flattened dt, .form-group.flattened .form-group-header { + float: left; + margin: 0; + line-height: 32px +} + +.form-group.flattened dd, .form-group.flattened .form-group-body { + line-height: 32px +} + +.form-group dd h4, .form-group .form-group-body h4 { + margin: 4px 0 0 +} + +.form-group dd h4.is-error, .form-group .form-group-body h4.is-error { + color: #cb2431 +} + +.form-group dd h4.is-success, .form-group .form-group-body h4.is-success { + color: #22863a +} + +.form-group dd h4+.note, .form-group .form-group-body h4+.note { + margin-top: 0 +} + +.form-group.required dt label::after, .form-group.required .form-group-header label::after { + padding-left: 5px; + color: #cb2431; + content: "*" +} + +.form-group .success, .form-group .error, .form-group .indicator { + display: none; + font-size: 12px; + font-weight: 600 +} + +.form-group.loading { + opacity: 0.5 +} + +.form-group.loading .indicator { + display: inline +} + +.form-group.loading .spinner { + display: inline-block; + vertical-align: middle +} + +.form-group.successful .success { + display: inline; + color: #22863a +} + +.form-group.successed .success, .form-group.successed .warning, .form-group.successed .error, .form-group.warn .success, .form-group.warn .warning, .form-group.warn .error, .form-group.errored .success, .form-group.errored .warning, .form-group.errored .error { + position: absolute; + z-index: 10; + display: block; + max-width: 450px; + padding: 4px 8px; + margin: 8px 0 0; + font-size: 12px; + font-weight: 400; + border-style: solid; + border-width: 1px; + border-radius: 6px +} + +.form-group.successed .success::after, .form-group.successed .success::before, .form-group.successed .warning::after, .form-group.successed .warning::before, .form-group.successed .error::after, .form-group.successed .error::before, .form-group.warn .success::after, .form-group.warn .success::before, .form-group.warn .warning::after, .form-group.warn .warning::before, .form-group.warn .error::after, .form-group.warn .error::before, .form-group.errored .success::after, .form-group.errored .success::before, .form-group.errored .warning::after, .form-group.errored .warning::before, .form-group.errored .error::after, .form-group.errored .error::before { + position: absolute; + bottom: 100%; + left: 10px; + z-index: 15; + width: 0; + height: 0; + pointer-events: none; + content: " "; + border: solid transparent +} + +.form-group.successed .success::after, .form-group.successed .warning::after, .form-group.successed .error::after, .form-group.warn .success::after, .form-group.warn .warning::after, .form-group.warn .error::after, .form-group.errored .success::after, .form-group.errored .warning::after, .form-group.errored .error::after { + border-width: 5px +} + +.form-group.successed .success::before, .form-group.successed .warning::before, .form-group.successed .error::before, .form-group.warn .success::before, .form-group.warn .warning::before, .form-group.warn .error::before, .form-group.errored .success::before, .form-group.errored .warning::before, .form-group.errored .error::before { + margin-left: -1px; + border-width: 6px +} + +.form-group.successed .success { + color: #144620; + background-color: #dcffe4; + border-color: #34d058 +} + +.form-group.successed .success::after { + border-bottom-color: #dcffe4 +} + +.form-group.successed .success::before { + border-bottom-color: #34d058 +} + +.form-group.warn .form-control { + border-color: #f9c513 +} + +.form-group.warn .warning { + background-color: #fff5b1; + border-color: #f9c513 +} + +.form-group.warn .warning::after { + border-bottom-color: #fff5b1 +} + +.form-group.warn .warning::before { + border-bottom-color: #f9c513 +} + +.form-group.errored .form-control { + border-color: #cb2431 +} + +.form-group.errored label { + color: #cb2431 +} + +.form-group.errored .error { + background-color: #ffeef0; + border-color: #f97583 +} + +.form-group.errored .error::after { + border-bottom-color: #ffeef0 +} + +.form-group.errored .error::before { + border-bottom-color: #f97583 +} + +.note { + min-height: 17px; + margin: 4px 0 2px; + font-size: 12px; + color: #586069 +} + +.note .spinner { + margin-right: 3px; + vertical-align: middle +} + +dl.form-group>dd .form-control.is-autocheck-loading, dl.form-group>dd .form-control.is-autocheck-successful, dl.form-group>dd .form-control.is-autocheck-errored, .form-group>.form-group-body .form-control.is-autocheck-loading, .form-group>.form-group-body .form-control.is-autocheck-successful, .form-group>.form-group-body .form-control.is-autocheck-errored { + padding-right: 30px +} + +dl.form-group>dd .form-control.is-autocheck-loading, .form-group>.form-group-body .form-control.is-autocheck-loading { + background-image: url("/images/spinners/octocat-spinner-16px.gif") +} + +dl.form-group>dd .form-control.is-autocheck-successful, .form-group>.form-group-body .form-control.is-autocheck-successful { + background-image: url("/images/modules/ajax/success.png") +} + +dl.form-group>dd .form-control.is-autocheck-errored, .form-group>.form-group-body .form-control.is-autocheck-errored { + background-image: url("/images/modules/ajax/error.png") +} + +@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-moz-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { + dl.form-group>dd .form-control.is-autocheck-loading, dl.form-group>dd .form-control.is-autocheck-successful, dl.form-group>dd .form-control.is-autocheck-errored, .form-group>.form-group-body .form-control.is-autocheck-loading, .form-group>.form-group-body .form-control.is-autocheck-successful, .form-group>.form-group-body .form-control.is-autocheck-errored { + background-size: 16px 16px + } + dl.form-group>dd .form-control.is-autocheck-loading, .form-group>.form-group-body .form-control.is-autocheck-loading { + background-image: url("/images/spinners/octocat-spinner-32.gif") + } + dl.form-group>dd .form-control.is-autocheck-successful, .form-group>.form-group-body .form-control.is-autocheck-successful { + background-image: url("/images/modules/ajax/success@2x.png") + } + dl.form-group>dd .form-control.is-autocheck-errored, .form-group>.form-group-body .form-control.is-autocheck-errored { + background-image: url("/images/modules/ajax/error@2x.png") + } +} + +.status-indicator { + display: inline-block; + width: 16px; + height: 16px; + margin-left: 5px +} + +.status-indicator .octicon { + display: none +} + +.status-indicator-success::before { + content: "" +} + +.status-indicator-success .octicon-check { + display: inline-block; + color: #28a745; + fill: #28a745 +} + +.status-indicator-success .octicon-x { + display: none +} + +.status-indicator-failed::before { + content: "" +} + +.status-indicator-failed .octicon-check { + display: none +} + +.status-indicator-failed .octicon-x { + display: inline-block; + color: #cb2431; + fill: #d73a49 +} + +.status-indicator-loading { + width: 16px; + background-image: url("/images/spinners/octocat-spinner-32-EAF2F5.gif"); + background-repeat: no-repeat; + background-position: 0 0; + background-size: 16px +} + +.inline-form { + display: inline-block +} + +.inline-form .btn-plain { + background-color: transparent; + border: 0 +} + +.drag-and-drop { + padding: 7px 10px; + margin: 0; + font-size: 13px; + line-height: 16px; + color: #586069; + background-color: #fafbfc; + border: 1px solid #c3c8cf; + border-top: 0; + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px +} + +.drag-and-drop .default, .drag-and-drop .loading, .drag-and-drop .error { + display: none +} + +.drag-and-drop .error { + color: #cb2431 +} + +.drag-and-drop img { + vertical-align: top +} + +.is-default .drag-and-drop .default { + display: inline-block +} + +.is-uploading .drag-and-drop .loading { + display: inline-block +} + +.is-bad-file .drag-and-drop .bad-file { + display: inline-block +} + +.is-duplicate-filename .drag-and-drop .duplicate-filename { + display: inline-block +} + +.is-too-big .drag-and-drop .too-big { + display: inline-block +} + +.is-hidden-file .drag-and-drop .hidden-file { + display: inline-block +} + +.is-empty .drag-and-drop .empty { + display: inline-block +} + +.is-bad-permissions .drag-and-drop .bad-permissions { + display: inline-block +} + +.is-repository-required .drag-and-drop .repository-required { + display: inline-block +} + +.drag-and-drop-error-info { + font-weight: 400; + color: #586069 +} + +.drag-and-drop-error-info a { + color: #0366d6 +} + +.is-failed .drag-and-drop .failed-request { + display: inline-block +} + +.manual-file-chooser { + position: absolute; + width: 240px; + padding: 5px; + margin-left: -80px; + cursor: pointer; + opacity: 0.0001 +} + +.manual-file-chooser:hover+.manual-file-chooser-text { + text-decoration: underline +} + +.btn .manual-file-chooser { + top: 0; + padding: 0; + line-height: 34px +} + +.upload-enabled textarea { + display: block; + border-bottom: 1px dashed #dfe2e5; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.upload-enabled.focused { + border-radius: 6px; + box-shadow: inset 0 1px 2px rgba(27, 31, 35, 0.075), 0 0 0 0.2em rgba(3, 102, 214, 0.3) +} + +.upload-enabled.focused .form-control { + box-shadow: none +} + +.upload-enabled.focused .drag-and-drop { + border-color: #4a9eff +} + +.dragover textarea, .dragover .drag-and-drop { + box-shadow: #c9ff00 0 0 3px +} + +.write-content { + position: relative +} + +.previewable-comment-form { + position: relative +} + +.previewable-comment-form .tabnav { + position: relative; + padding: 8px 8px 0 +} + +.previewable-comment-form .comment { + border: 1px solid #c3c8cf +} + +.previewable-comment-form .comment-form-error { + margin-bottom: 8px +} + +.previewable-comment-form .write-content, .previewable-comment-form .preview-content { + display: none; + margin: 0 8px 8px +} + +.previewable-comment-form.write-selected .write-content, .previewable-comment-form.preview-selected .preview-content { + display: block +} + +.previewable-comment-form textarea { + display: block; + width: 100%; + min-height: 100px; + max-height: 500px; + padding: 8px; + resize: vertical +} + +.form-action-spacious { + margin-top: 10px +} + +div.composer { + margin-top: 0; + border: 0 +} + +.composer .comment-form-textarea { + height: 200px; + min-height: 200px +} + +.composer .tabnav { + margin: 0 0 10px +} + +h2.account { + margin: 15px 0 0; + font-size: 18px; + font-weight: 400; + color: #586069 +} + +p.explain { + position: relative; + font-size: 12px; + color: #586069 +} + +p.explain strong { + color: #24292e +} + +p.explain .octicon { + margin-right: 5px; + color: #959da5 +} + +p.explain .minibutton { + top: -4px; + float: right +} + +.form-group label { + position: static +} + +.input-group { + display: table +} + +.input-group .form-control { + position: relative; + width: 100% +} + +.input-group .form-control:focus { + z-index: 2 +} + +.input-group .form-control+.btn { + margin-left: 0 +} + +.input-group.inline { + display: inline-table +} + +.input-group .form-control, .input-group-button { + display: table-cell +} + +.input-group-button { + width: 1%; + vertical-align: middle +} + +.input-group .form-control:first-child, .input-group-button:first-child .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group-button:first-child .btn { + margin-right: -1px +} + +.input-group .form-control:last-child, .input-group-button:last-child .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.input-group-button:last-child .btn { + margin-left: -1px +} + +.radio-group::before { + display: table; + content: "" +} + +.radio-group::after { + display: table; + clear: both; + content: "" +} + +.radio-label { + float: left; + padding: 6px 16px 6px 36px; + margin-left: -1px; + font-size: 14px; + line-height: 20px; + color: #24292e; + cursor: pointer; + border: 1px solid #e1e4e8 +} + +:checked+.radio-label { + position: relative; + z-index: 1; + border-color: #0366d6 +} + +.radio-label:first-of-type { + margin-left: 0; + border-top-left-radius: 6px; + border-bottom-left-radius: 6px +} + +.radio-label:last-of-type { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px +} + +.radio-input { + z-index: 3; + float: left; + margin: 10px -32px 0 16px +} + +.container-sm { + max-width: 544px; + margin-right: auto; + margin-left: auto +} + +.container-md { + max-width: 768px; + margin-right: auto; + margin-left: auto +} + +.container-lg { + max-width: 1012px; + margin-right: auto; + margin-left: auto +} + +.container-xl { + max-width: 1280px; + margin-right: auto; + margin-left: auto +} + +.col-1 { + width: 8.33333% +} + +.col-2 { + width: 16.66667% +} + +.col-3 { + width: 25% +} + +.col-4 { + width: 33.33333% +} + +.col-5 { + width: 41.66667% +} + +.col-6 { + width: 50% +} + +.col-7 { + width: 58.33333% +} + +.col-8 { + width: 66.66667% +} + +.col-9 { + width: 75% +} + +.col-10 { + width: 83.33333% +} + +.col-11 { + width: 91.66667% +} + +.col-12 { + width: 100% +} + +@media (min-width: 544px) { + .col-sm-1 { + width: 8.33333% + } + .col-sm-2 { + width: 16.66667% + } + .col-sm-3 { + width: 25% + } + .col-sm-4 { + width: 33.33333% + } + .col-sm-5 { + width: 41.66667% + } + .col-sm-6 { + width: 50% + } + .col-sm-7 { + width: 58.33333% + } + .col-sm-8 { + width: 66.66667% + } + .col-sm-9 { + width: 75% + } + .col-sm-10 { + width: 83.33333% + } + .col-sm-11 { + width: 91.66667% + } + .col-sm-12 { + width: 100% + } +} + +@media (min-width: 768px) { + .col-md-1 { + width: 8.33333% + } + .col-md-2 { + width: 16.66667% + } + .col-md-3 { + width: 25% + } + .col-md-4 { + width: 33.33333% + } + .col-md-5 { + width: 41.66667% + } + .col-md-6 { + width: 50% + } + .col-md-7 { + width: 58.33333% + } + .col-md-8 { + width: 66.66667% + } + .col-md-9 { + width: 75% + } + .col-md-10 { + width: 83.33333% + } + .col-md-11 { + width: 91.66667% + } + .col-md-12 { + width: 100% + } +} + +@media (min-width: 1012px) { + .col-lg-1 { + width: 8.33333% + } + .col-lg-2 { + width: 16.66667% + } + .col-lg-3 { + width: 25% + } + .col-lg-4 { + width: 33.33333% + } + .col-lg-5 { + width: 41.66667% + } + .col-lg-6 { + width: 50% + } + .col-lg-7 { + width: 58.33333% + } + .col-lg-8 { + width: 66.66667% + } + .col-lg-9 { + width: 75% + } + .col-lg-10 { + width: 83.33333% + } + .col-lg-11 { + width: 91.66667% + } + .col-lg-12 { + width: 100% + } +} + +@media (min-width: 1280px) { + .col-xl-1 { + width: 8.33333% + } + .col-xl-2 { + width: 16.66667% + } + .col-xl-3 { + width: 25% + } + .col-xl-4 { + width: 33.33333% + } + .col-xl-5 { + width: 41.66667% + } + .col-xl-6 { + width: 50% + } + .col-xl-7 { + width: 58.33333% + } + .col-xl-8 { + width: 66.66667% + } + .col-xl-9 { + width: 75% + } + .col-xl-10 { + width: 83.33333% + } + .col-xl-11 { + width: 91.66667% + } + .col-xl-12 { + width: 100% + } +} + +.gutter { + margin-right: -16px; + margin-left: -16px +} + +.gutter>[class*="col-"] { + padding-right: 16px !important; + padding-left: 16px !important +} + +.gutter-condensed { + margin-right: -8px; + margin-left: -8px +} + +.gutter-condensed>[class*="col-"] { + padding-right: 8px !important; + padding-left: 8px !important +} + +.gutter-spacious { + margin-right: -24px; + margin-left: -24px +} + +.gutter-spacious>[class*="col-"] { + padding-right: 24px !important; + padding-left: 24px !important +} + +@media (min-width: 544px) { + .gutter-sm { + margin-right: -16px; + margin-left: -16px + } + .gutter-sm>[class*="col-"] { + padding-right: 16px !important; + padding-left: 16px !important + } + .gutter-sm-condensed { + margin-right: -8px; + margin-left: -8px + } + .gutter-sm-condensed>[class*="col-"] { + padding-right: 8px !important; + padding-left: 8px !important + } + .gutter-sm-spacious { + margin-right: -24px; + margin-left: -24px + } + .gutter-sm-spacious>[class*="col-"] { + padding-right: 24px !important; + padding-left: 24px !important + } +} + +@media (min-width: 768px) { + .gutter-md { + margin-right: -16px; + margin-left: -16px + } + .gutter-md>[class*="col-"] { + padding-right: 16px !important; + padding-left: 16px !important + } + .gutter-md-condensed { + margin-right: -8px; + margin-left: -8px + } + .gutter-md-condensed>[class*="col-"] { + padding-right: 8px !important; + padding-left: 8px !important + } + .gutter-md-spacious { + margin-right: -24px; + margin-left: -24px + } + .gutter-md-spacious>[class*="col-"] { + padding-right: 24px !important; + padding-left: 24px !important + } +} + +@media (min-width: 1012px) { + .gutter-lg { + margin-right: -16px; + margin-left: -16px + } + .gutter-lg>[class*="col-"] { + padding-right: 16px !important; + padding-left: 16px !important + } + .gutter-lg-condensed { + margin-right: -8px; + margin-left: -8px + } + .gutter-lg-condensed>[class*="col-"] { + padding-right: 8px !important; + padding-left: 8px !important + } + .gutter-lg-spacious { + margin-right: -24px; + margin-left: -24px + } + .gutter-lg-spacious>[class*="col-"] { + padding-right: 24px !important; + padding-left: 24px !important + } +} + +@media (min-width: 1280px) { + .gutter-xl { + margin-right: -16px; + margin-left: -16px + } + .gutter-xl>[class*="col-"] { + padding-right: 16px !important; + padding-left: 16px !important + } + .gutter-xl-condensed { + margin-right: -8px; + margin-left: -8px + } + .gutter-xl-condensed>[class*="col-"] { + padding-right: 8px !important; + padding-left: 8px !important + } + .gutter-xl-spacious { + margin-right: -24px; + margin-left: -24px + } + .gutter-xl-spacious>[class*="col-"] { + padding-right: 24px !important; + padding-left: 24px !important + } +} + +.offset-1 { + margin-left: 8.33333% !important +} + +.offset-2 { + margin-left: 16.66667% !important +} + +.offset-3 { + margin-left: 25% !important +} + +.offset-4 { + margin-left: 33.33333% !important +} + +.offset-5 { + margin-left: 41.66667% !important +} + +.offset-6 { + margin-left: 50% !important +} + +.offset-7 { + margin-left: 58.33333% !important +} + +.offset-8 { + margin-left: 66.66667% !important +} + +.offset-9 { + margin-left: 75% !important +} + +.offset-10 { + margin-left: 83.33333% !important +} + +.offset-11 { + margin-left: 91.66667% !important +} + +@media (min-width: 544px) { + .offset-sm-1 { + margin-left: 8.33333% !important + } + .offset-sm-2 { + margin-left: 16.66667% !important + } + .offset-sm-3 { + margin-left: 25% !important + } + .offset-sm-4 { + margin-left: 33.33333% !important + } + .offset-sm-5 { + margin-left: 41.66667% !important + } + .offset-sm-6 { + margin-left: 50% !important + } + .offset-sm-7 { + margin-left: 58.33333% !important + } + .offset-sm-8 { + margin-left: 66.66667% !important + } + .offset-sm-9 { + margin-left: 75% !important + } + .offset-sm-10 { + margin-left: 83.33333% !important + } + .offset-sm-11 { + margin-left: 91.66667% !important + } +} + +@media (min-width: 768px) { + .offset-md-1 { + margin-left: 8.33333% !important + } + .offset-md-2 { + margin-left: 16.66667% !important + } + .offset-md-3 { + margin-left: 25% !important + } + .offset-md-4 { + margin-left: 33.33333% !important + } + .offset-md-5 { + margin-left: 41.66667% !important + } + .offset-md-6 { + margin-left: 50% !important + } + .offset-md-7 { + margin-left: 58.33333% !important + } + .offset-md-8 { + margin-left: 66.66667% !important + } + .offset-md-9 { + margin-left: 75% !important + } + .offset-md-10 { + margin-left: 83.33333% !important + } + .offset-md-11 { + margin-left: 91.66667% !important + } +} + +@media (min-width: 1012px) { + .offset-lg-1 { + margin-left: 8.33333% !important + } + .offset-lg-2 { + margin-left: 16.66667% !important + } + .offset-lg-3 { + margin-left: 25% !important + } + .offset-lg-4 { + margin-left: 33.33333% !important + } + .offset-lg-5 { + margin-left: 41.66667% !important + } + .offset-lg-6 { + margin-left: 50% !important + } + .offset-lg-7 { + margin-left: 58.33333% !important + } + .offset-lg-8 { + margin-left: 66.66667% !important + } + .offset-lg-9 { + margin-left: 75% !important + } + .offset-lg-10 { + margin-left: 83.33333% !important + } + .offset-lg-11 { + margin-left: 91.66667% !important + } +} + +@media (min-width: 1280px) { + .offset-xl-1 { + margin-left: 8.33333% !important + } + .offset-xl-2 { + margin-left: 16.66667% !important + } + .offset-xl-3 { + margin-left: 25% !important + } + .offset-xl-4 { + margin-left: 33.33333% !important + } + .offset-xl-5 { + margin-left: 41.66667% !important + } + .offset-xl-6 { + margin-left: 50% !important + } + .offset-xl-7 { + margin-left: 58.33333% !important + } + .offset-xl-8 { + margin-left: 66.66667% !important + } + .offset-xl-9 { + margin-left: 75% !important + } + .offset-xl-10 { + margin-left: 83.33333% !important + } + .offset-xl-11 { + margin-left: 91.66667% !important + } +} + +.menu { + margin-bottom: 16px; + list-style: none; + background-color: #fff; + border: 1px #e1e4e8 solid; + border-radius: 6px +} + +.menu-item { + position: relative; + display: block; + padding: 8px 16px; + color: #1b1f23; + border-bottom: 1px solid #eaecef +} + +.menu-item:first-child { + border-top: 0; + border-top-left-radius: 6px; + border-top-right-radius: 6px +} + +.menu-item:first-child::before { + border-top-left-radius: 6px +} + +.menu-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px +} + +.menu-item:last-child::before { + border-bottom-left-radius: 6px +} + +.menu-item:focus, .menu-item:hover { + text-decoration: none; + background-color: #f6f8fa; + outline: none +} + +.menu-item:active { + background-color: #fafbfc +} + +.menu-item.selected, .menu-item[aria-selected=true], .menu-item[aria-current]:not([aria-current=false]) { + cursor: default +} + +.menu-item.selected::before, .menu-item[aria-selected=true]::before, .menu-item[aria-current]:not([aria-current=false])::before { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 2px; + content: ""; + background-color: #f9826c +} + +.menu-item .octicon { + width: 16px; + margin-right: 8px; + color: #959da5; + text-align: center +} + +.menu-item .Counter { + float: right; + margin-left: 4px +} + +.menu-item .menu-warning { + float: right; + color: #86181d +} + +.menu-item .avatar { + float: left; + margin-right: 4px +} + +.menu-item.alert .Counter { + color: #cb2431 +} + +.menu-heading { + display: block; + padding: 8px 16px; + margin-top: 0; + margin-bottom: 0; + font-size: inherit; + font-weight: 600; + color: #1b1f23; + border-bottom: 1px solid #eaecef +} + +.menu-heading:hover { + text-decoration: none +} + +.menu-heading:first-child { + border-top-left-radius: 6px; + border-top-right-radius: 6px +} + +.menu-heading:last-child { + border-bottom: 0; + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px +} + +.tabnav { + margin-top: 0; + margin-bottom: 16px; + border-bottom: 1px #e1e4e8 solid +} + +.tabnav-tabs { + display: flex; + margin-bottom: -1px; + overflow: auto +} + +.tabnav-tab { + display: inline-block; + flex-shrink: 0; + padding: 8px 16px; + font-size: 14px; + line-height: 23px; + color: #24292e; + text-decoration: none; + background-color: transparent; + border: 1px solid transparent; + border-bottom: 0 +} + +.tabnav-tab.selected, .tabnav-tab[aria-selected=true], .tabnav-tab[aria-current]:not([aria-current=false]) { + background-color: #fff; + border-color: #e1e4e8; + border-radius: 6px 6px 0 0 +} + +.tabnav-tab:hover, .tabnav-tab:focus { + color: #586069; + text-decoration: none +} + +.tabnav-tab:active { + color: #6a737d +} + +.tabnav-tab .octicon { + margin-right: 4px; + color: #959da5 +} + +.tabnav-tab .Counter { + margin-left: 4px +} + +.tabnav-extra { + display: inline-block; + padding-top: 10px; + margin-left: 10px; + font-size: 12px; + color: #586069 +} + +.tabnav-extra>.octicon { + margin-right: 2px +} + +a.tabnav-extra:hover { + color: #0366d6; + text-decoration: none +} + +.tabnav-btn { + margin-left: 8px +} + +.filter-list { + list-style-type: none +} + +.filter-list.small .filter-item { + padding: 6px 12px; + font-size: 12px +} + +.filter-list.pjax-active .filter-item { + color: #586069; + background-color: transparent +} + +.filter-list.pjax-active .filter-item.pjax-active { + color: #fff; + background-color: #0366d6 +} + +.filter-item { + position: relative; + display: block; + padding: 8px 16px; + margin-bottom: 4px; + overflow: hidden; + font-size: 14px; + color: #586069; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + cursor: pointer; + border-radius: 6px +} + +.filter-item:hover { + text-decoration: none; + background-color: #f6f8fa +} + +.filter-item.selected, .filter-item[aria-selected=true], .filter-item[aria-current]:not([aria-current=false]) { + color: #fff; + background-color: #0366d6 +} + +.filter-item .count { + float: right; + font-weight: 600 +} + +.filter-item .bar { + position: absolute; + top: 2px; + right: 0; + bottom: 2px; + z-index: -1; + display: inline-block; + background-color: #eff3f6 +} + +.SideNav { + background-color: #fafbfc +} + +.SideNav-item { + position: relative; + display: block; + width: 100%; + padding: 12px 16px; + color: #1b1f23; + text-align: left; + background-color: transparent; + border: 0; + border-top: 1px solid #eaecef +} + +.SideNav-item:first-child { + border-top: 0 +} + +.SideNav-item:last-child { + box-shadow: 0 1px 0 #e1e4e8 +} + +.SideNav-item::before { + position: absolute; + top: 0; + bottom: 0; + left: 0; + z-index: 1; + width: 2px; + pointer-events: none; + content: "" +} + +.SideNav-item:hover, .SideNav-item:focus { + text-decoration: none; + background-color: #f6f8fa; + outline: none +} + +.SideNav-item:active { + background-color: #fafbfc +} + +.SideNav-item[aria-current]:not([aria-current=false]), .SideNav-item[aria-selected="true"] { + background-color: #fff +} + +.SideNav-item[aria-current]:not([aria-current=false])::before, .SideNav-item[aria-selected="true"]::before { + background-color: #f9826c +} + +.SideNav-icon { + width: 16px; + margin-right: 8px; + color: #6a737d +} + +.SideNav-subItem { + position: relative; + display: block; + width: 100%; + padding: 4px 0; + color: #0366d6; + text-align: left; + background-color: transparent; + border: 0 +} + +.SideNav-subItem:hover, .SideNav-subItem:focus { + color: #24292e; + text-decoration: none; + outline: none +} + +.SideNav-subItem[aria-current]:not([aria-current=false]), .SideNav-subItem[aria-selected="true"] { + font-weight: 500; + color: #24292e +} + +.subnav { + margin-bottom: 20px +} + +.subnav::before { + display: table; + content: "" +} + +.subnav::after { + display: table; + clear: both; + content: "" +} + +.subnav-bordered { + padding-bottom: 20px; + border-bottom: 1px solid #eaecef +} + +.subnav-flush { + margin-bottom: 0 +} + +.subnav-item { + position: relative; + float: left; + padding: 5px 16px; + font-weight: 500; + line-height: 20px; + color: #24292e; + border: 1px #e1e4e8 solid +} + +.subnav-item+.subnav-item { + margin-left: -1px +} + +.subnav-item:hover, .subnav-item:focus { + text-decoration: none; + background-color: #f6f8fa +} + +.subnav-item.selected, .subnav-item[aria-selected=true], .subnav-item[aria-current]:not([aria-current=false]) { + z-index: 2; + color: #fff; + background-color: #0366d6; + border-color: #005cc5 +} + +.subnav-item:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px +} + +.subnav-item:last-child { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px +} + +.subnav-search { + position: relative; + margin-left: 12px +} + +.subnav-search-input { + width: 320px; + padding-left: 32px; + color: #586069 +} + +.subnav-search-input-wide { + width: 500px +} + +.subnav-search-icon { + position: absolute; + top: 9px; + left: 8px; + display: block; + color: #959da5; + text-align: center; + pointer-events: none +} + +.subnav-search-context .btn { + color: #444d56; + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.subnav-search-context .btn:hover, .subnav-search-context .btn:focus, .subnav-search-context .btn:active, .subnav-search-context .btn.selected { + z-index: 2 +} + +.subnav-search-context+.subnav-search { + margin-left: -1px +} + +.subnav-search-context+.subnav-search .subnav-search-input { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.subnav-search-context .select-menu-modal-holder { + z-index: 30 +} + +.subnav-search-context .select-menu-modal { + width: 220px +} + +.subnav-search-context .select-menu-item-icon { + color: inherit +} + +.subnav-spacer-right { + padding-right: 12px +} + +.UnderlineNav { + display: flex; + overflow-x: auto; + overflow-y: hidden; + box-shadow: inset 0 -1px 0 #e1e4e8; + justify-content: space-between +} + +.UnderlineNav-body { + display: flex +} + +.UnderlineNav-item { + padding: 8px 16px; + font-size: 14px; + line-height: 30px; + color: #1b1f23; + text-align: center; + white-space: nowrap; + background-color: transparent; + border: 0; + border-bottom: 2px solid rgba(209, 213, 218, 0); + transition: border-bottom-color 0.36s ease-in +} + +.UnderlineNav-item:hover, .UnderlineNav-item:focus { + text-decoration: none; + border-bottom-color: #d1d5da; + outline: 1px dotted transparent; + outline-offset: -1px; + transition-timing-function: ease-out; + transition-duration: 0.12s +} + +.UnderlineNav-item.selected, .UnderlineNav-item[role=tab][aria-selected=true], .UnderlineNav-item[aria-current]:not([aria-current=false]) { + font-weight: 600; + border-bottom-color: #f9826c; + outline: 1px dotted transparent; + outline-offset: -1px +} + +.UnderlineNav-item.selected .UnderlineNav-octicon, .UnderlineNav-item[role=tab][aria-selected=true] .UnderlineNav-octicon, .UnderlineNav-item[aria-current]:not([aria-current=false]) .UnderlineNav-octicon { + color: #586069 +} + +.UnderlineNav--right { + justify-content: flex-end +} + +.UnderlineNav--right .UnderlineNav-actions { + flex: 1 1 auto +} + +.UnderlineNav-actions { + align-self: center +} + +.UnderlineNav--full { + display: block +} + +.UnderlineNav-octicon { + margin-right: 4px; + color: #959da5 +} + +.UnderlineNav .Counter { + margin-left: 4px +} + +.UnderlineNav-container { + display: flex; + justify-content: space-between +} + +.pagination a, .pagination span, .pagination em { + display: inline-block; + min-width: 32px; + padding: 5px 10px; + font-style: normal; + line-height: 20px; + color: #24292e; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border: 1px solid transparent; + border-radius: 6px; + transition: border-color 0.2s cubic-bezier(0.3, 0, 0.5, 1) +} + +.pagination a:hover, .pagination a:focus, .pagination span:hover, .pagination span:focus, .pagination em:hover, .pagination em:focus { + text-decoration: none; + border-color: #e1e4e8; + outline: 0; + transition-duration: 0.1s +} + +.pagination a:active, .pagination span:active, .pagination em:active { + border-color: #eaecef; + transition: none +} + +.pagination .previous_page, .pagination .next_page { + color: #0366d6 +} + +.pagination .current, .pagination .current:hover, .pagination [aria-current]:not([aria-current=false]) { + color: #fff; + background-color: #0366d6; + border-color: transparent +} + +.pagination .gap, .pagination .disabled, .pagination [aria-disabled=true], .pagination .gap:hover, .pagination .disabled:hover, .pagination [aria-disabled=true]:hover { + color: #6a737d; + cursor: default; + border-color: transparent +} + +@supports ((-webkit-clip-path: polygon(50% 0, 100% 50%, 50% 100%)) or (clip-path: polygon(50% 0, 100% 50%, 50% 100%))) { + .pagination .previous_page::before, .pagination .next_page::after { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: text-bottom; + content: ""; + background-color: currentColor + } + .pagination .previous_page::before { + margin-right: 4px; + -webkit-clip-path: polygon(9.8px 12.8px, 8.7px 12.8px, 4.5px 8.5px, 4.5px 7.5px, 8.7px 3.2px, 9.8px 4.3px, 6.1px 8px, 9.8px 11.7px, 9.8px 12.8px); + clip-path: polygon(9.8px 12.8px, 8.7px 12.8px, 4.5px 8.5px, 4.5px 7.5px, 8.7px 3.2px, 9.8px 4.3px, 6.1px 8px, 9.8px 11.7px, 9.8px 12.8px) + } + .pagination .next_page::after { + margin-left: 4px; + -webkit-clip-path: polygon(6.2px 3.2px, 7.3px 3.2px, 11.5px 7.5px, 11.5px 8.5px, 7.3px 12.8px, 6.2px 11.7px, 9.9px 8px, 6.2px 4.3px, 6.2px 3.2px); + clip-path: polygon(6.2px 3.2px, 7.3px 3.2px, 11.5px 7.5px, 11.5px 8.5px, 7.3px 12.8px, 6.2px 11.7px, 9.9px 8px, 6.2px 4.3px, 6.2px 3.2px) + } +} + +.paginate-container { + margin-top: 16px; + margin-bottom: 16px; + text-align: center +} + +.paginate-container .pagination { + display: inline-block +} + +.tooltipped { + position: relative +} + +.tooltipped::after { + position: absolute; + z-index: 1000000; + display: none; + padding: .5em .75em; + font: normal normal 11px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + -webkit-font-smoothing: subpixel-antialiased; + color: #fff; + text-align: center; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-wrap: break-word; + white-space: pre; + pointer-events: none; + content: attr(aria-label); + background: #1b1f23; + border-radius: 6px; + opacity: 0 +} + +.tooltipped::before { + position: absolute; + z-index: 1000001; + display: none; + width: 0; + height: 0; + color: #1b1f23; + pointer-events: none; + content: ""; + border: 6px solid transparent; + opacity: 0 +} + +@keyframes tooltip-appear { + from { + opacity: 0 + } + to { + opacity: 1 + } +} + +.tooltipped:hover::before, .tooltipped:hover::after, .tooltipped:active::before, .tooltipped:active::after, .tooltipped:focus::before, .tooltipped:focus::after { + display: inline-block; + text-decoration: none; + animation-name: tooltip-appear; + animation-duration: .1s; + animation-fill-mode: forwards; + animation-timing-function: ease-in; + animation-delay: .4s +} + +.tooltipped-no-delay:hover::before, .tooltipped-no-delay:hover::after, .tooltipped-no-delay:active::before, .tooltipped-no-delay:active::after, .tooltipped-no-delay:focus::before, .tooltipped-no-delay:focus::after { + animation-delay: 0s +} + +.tooltipped-multiline:hover::after, .tooltipped-multiline:active::after, .tooltipped-multiline:focus::after { + display: table-cell +} + +.tooltipped-s::after, .tooltipped-se::after, .tooltipped-sw::after { + top: 100%; + right: 50%; + margin-top: 6px +} + +.tooltipped-s::before, .tooltipped-se::before, .tooltipped-sw::before { + top: auto; + right: 50%; + bottom: -7px; + margin-right: -6px; + border-bottom-color: #1b1f23 +} + +.tooltipped-se::after { + right: auto; + left: 50%; + margin-left: -16px +} + +.tooltipped-sw::after { + margin-right: -16px +} + +.tooltipped-n::after, .tooltipped-ne::after, .tooltipped-nw::after { + right: 50%; + bottom: 100%; + margin-bottom: 6px +} + +.tooltipped-n::before, .tooltipped-ne::before, .tooltipped-nw::before { + top: -7px; + right: 50%; + bottom: auto; + margin-right: -6px; + border-top-color: #1b1f23 +} + +.tooltipped-ne::after { + right: auto; + left: 50%; + margin-left: -16px +} + +.tooltipped-nw::after { + margin-right: -16px +} + +.tooltipped-s::after, .tooltipped-n::after { + transform: translateX(50%) +} + +.tooltipped-w::after { + right: 100%; + bottom: 50%; + margin-right: 6px; + transform: translateY(50%) +} + +.tooltipped-w::before { + top: 50%; + bottom: 50%; + left: -7px; + margin-top: -6px; + border-left-color: #1b1f23 +} + +.tooltipped-e::after { + bottom: 50%; + left: 100%; + margin-left: 6px; + transform: translateY(50%) +} + +.tooltipped-e::before { + top: 50%; + right: -7px; + bottom: 50%; + margin-top: -6px; + border-right-color: #1b1f23 +} + +.tooltipped-align-right-1::after, .tooltipped-align-right-2::after { + right: 0; + margin-right: 0 +} + +.tooltipped-align-right-1::before { + right: 10px +} + +.tooltipped-align-right-2::before { + right: 15px +} + +.tooltipped-align-left-1::after, .tooltipped-align-left-2::after { + left: 0; + margin-left: 0 +} + +.tooltipped-align-left-1::before { + left: 5px +} + +.tooltipped-align-left-2::before { + left: 10px +} + +.tooltipped-multiline::after { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + max-width: 250px; + word-wrap: break-word; + white-space: pre-line; + border-collapse: separate +} + +.tooltipped-multiline.tooltipped-s::after, .tooltipped-multiline.tooltipped-n::after { + right: auto; + left: 50%; + transform: translateX(-50%) +} + +.tooltipped-multiline.tooltipped-w::after, .tooltipped-multiline.tooltipped-e::after { + right: 100% +} + +@media screen and (min-width: 0\0) { + .tooltipped-multiline::after { + width: 250px + } +} + +.tooltipped-sticky::before, .tooltipped-sticky::after { + display: inline-block +} + +.tooltipped-sticky.tooltipped-multiline::after { + display: table-cell +} + +.css-truncate.css-truncate-overflow, .css-truncate .css-truncate-overflow, .css-truncate.css-truncate-target, .css-truncate .css-truncate-target { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap +} + +.css-truncate.css-truncate-target, .css-truncate .css-truncate-target { + display: inline-block; + max-width: 125px; + vertical-align: top +} + +.css-truncate.expandable.zeroclipboard-is-hover .css-truncate-target, .css-truncate.expandable.zeroclipboard-is-hover.css-truncate-target, .css-truncate.expandable:hover .css-truncate-target, .css-truncate.expandable:hover.css-truncate-target { + max-width: 10000px !important +} + +.anim-fade-in { + animation-name: fade-in; + animation-duration: 1s; + animation-timing-function: ease-in-out +} + +.anim-fade-in.fast { + animation-duration: 300ms +} + +@keyframes fade-in { + 0% { + opacity: 0 + } + 100% { + opacity: 1 + } +} + +.anim-fade-out { + animation-name: fade-out; + animation-duration: 1s; + animation-timing-function: ease-out +} + +.anim-fade-out.fast { + animation-duration: 0.3s +} + +@keyframes fade-out { + 0% { + opacity: 1 + } + 100% { + opacity: 0 + } +} + +.anim-fade-up { + opacity: 0; + animation-name: fade-up; + animation-duration: 0.3s; + animation-fill-mode: forwards; + animation-timing-function: ease-out; + animation-delay: 1s +} + +@keyframes fade-up { + 0% { + opacity: 0.8; + transform: translateY(100%) + } + 100% { + opacity: 1; + transform: translateY(0) + } +} + +.anim-fade-down { + animation-name: fade-down; + animation-duration: 0.3s; + animation-fill-mode: forwards; + animation-timing-function: ease-in +} + +@keyframes fade-down { + 0% { + opacity: 1; + transform: translateY(0) + } + 100% { + opacity: 0.5; + transform: translateY(100%) + } +} + +.anim-grow-x { + width: 0%; + animation-name: grow-x; + animation-duration: 0.3s; + animation-fill-mode: forwards; + animation-timing-function: ease; + animation-delay: 0.5s +} + +@keyframes grow-x { + to { + width: 100% + } +} + +.anim-shrink-x { + animation-name: shrink-x; + animation-duration: 0.3s; + animation-fill-mode: forwards; + animation-timing-function: ease-in-out; + animation-delay: 0.5s +} + +@keyframes shrink-x { + to { + width: 0% + } +} + +.anim-scale-in { + animation-name: scale-in; + animation-duration: 0.15s; + animation-timing-function: cubic-bezier(0.2, 0, 0.13, 1.5) +} + +@keyframes scale-in { + 0% { + opacity: 0; + transform: scale(0.5) + } + 100% { + opacity: 1; + transform: scale(1) + } +} + +.anim-pulse { + animation-name: pulse; + animation-duration: 2s; + animation-timing-function: linear; + animation-iteration-count: infinite +} + +@keyframes pulse { + 0% { + opacity: 0.3 + } + 10% { + opacity: 1 + } + 100% { + opacity: 0.3 + } +} + +.anim-pulse-in { + animation-name: pulse-in; + animation-duration: 0.5s +} + +@keyframes pulse-in { + 0% { + transform: scale3d(1, 1, 1) + } + 50% { + transform: scale3d(1.1, 1.1, 1.1) + } + 100% { + transform: scale3d(1, 1, 1) + } +} + +.hover-grow { + transition: transform 0.3s; + -webkit-backface-visibility: hidden; + backface-visibility: hidden +} + +.hover-grow:hover { + transform: scale(1.025) +} + +.border-x { + border-right: 1px #e1e4e8 solid !important; + border-left: 1px #e1e4e8 solid !important +} + +.border-y { + border-top: 1px #e1e4e8 solid !important; + border-bottom: 1px #e1e4e8 solid !important +} + +.border { + border: 1px #e1e4e8 solid !important +} + +.border-0 { + border: 0 !important +} + +.border-top { + border-top: 1px #e1e4e8 solid !important +} + +.border-right { + border-right: 1px #e1e4e8 solid !important +} + +.border-bottom { + border-bottom: 1px #e1e4e8 solid !important +} + +.border-left { + border-left: 1px #e1e4e8 solid !important +} + +.border-top-0 { + border-top: 0 !important +} + +.border-right-0 { + border-right: 0 !important +} + +.border-bottom-0 { + border-bottom: 0 !important +} + +.border-left-0 { + border-left: 0 !important +} + +.rounded { + border-radius: 6px !important +} + +.rounded-0 { + border-radius: 0 !important +} + +.rounded-1 { + border-radius: 4px !important +} + +.rounded-2 { + border-radius: 6px !important +} + +.rounded-3 { + border-radius: 8px !important +} + +.rounded-top-0 { + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important +} + +.rounded-top-1 { + border-top-left-radius: 4px !important; + border-top-right-radius: 4px !important +} + +.rounded-top-2 { + border-top-left-radius: 6px !important; + border-top-right-radius: 6px !important +} + +.rounded-top-3 { + border-top-left-radius: 8px !important; + border-top-right-radius: 8px !important +} + +.rounded-right-0 { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important +} + +.rounded-right-1 { + border-top-right-radius: 4px !important; + border-bottom-right-radius: 4px !important +} + +.rounded-right-2 { + border-top-right-radius: 6px !important; + border-bottom-right-radius: 6px !important +} + +.rounded-right-3 { + border-top-right-radius: 8px !important; + border-bottom-right-radius: 8px !important +} + +.rounded-bottom-0 { + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important +} + +.rounded-bottom-1 { + border-bottom-right-radius: 4px !important; + border-bottom-left-radius: 4px !important +} + +.rounded-bottom-2 { + border-bottom-right-radius: 6px !important; + border-bottom-left-radius: 6px !important +} + +.rounded-bottom-3 { + border-bottom-right-radius: 8px !important; + border-bottom-left-radius: 8px !important +} + +.rounded-left-0 { + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important +} + +.rounded-left-1 { + border-bottom-left-radius: 4px !important; + border-top-left-radius: 4px !important +} + +.rounded-left-2 { + border-bottom-left-radius: 6px !important; + border-top-left-radius: 6px !important +} + +.rounded-left-3 { + border-bottom-left-radius: 8px !important; + border-top-left-radius: 8px !important +} + +@media (min-width: 544px) { + .border-sm { + border: 1px #e1e4e8 solid !important + } + .border-sm-0 { + border: 0 !important + } + .border-sm-top { + border-top: 1px #e1e4e8 solid !important + } + .border-sm-right { + border-right: 1px #e1e4e8 solid !important + } + .border-sm-bottom { + border-bottom: 1px #e1e4e8 solid !important + } + .border-sm-left { + border-left: 1px #e1e4e8 solid !important + } + .border-sm-top-0 { + border-top: 0 !important + } + .border-sm-right-0 { + border-right: 0 !important + } + .border-sm-bottom-0 { + border-bottom: 0 !important + } + .border-sm-left-0 { + border-left: 0 !important + } + .rounded-sm { + border-radius: 6px !important + } + .rounded-sm-0 { + border-radius: 0 !important + } + .rounded-sm-1 { + border-radius: 4px !important + } + .rounded-sm-2 { + border-radius: 6px !important + } + .rounded-sm-3 { + border-radius: 8px !important + } + .rounded-sm-top-0 { + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important + } + .rounded-sm-top-1 { + border-top-left-radius: 4px !important; + border-top-right-radius: 4px !important + } + .rounded-sm-top-2 { + border-top-left-radius: 6px !important; + border-top-right-radius: 6px !important + } + .rounded-sm-top-3 { + border-top-left-radius: 8px !important; + border-top-right-radius: 8px !important + } + .rounded-sm-right-0 { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important + } + .rounded-sm-right-1 { + border-top-right-radius: 4px !important; + border-bottom-right-radius: 4px !important + } + .rounded-sm-right-2 { + border-top-right-radius: 6px !important; + border-bottom-right-radius: 6px !important + } + .rounded-sm-right-3 { + border-top-right-radius: 8px !important; + border-bottom-right-radius: 8px !important + } + .rounded-sm-bottom-0 { + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important + } + .rounded-sm-bottom-1 { + border-bottom-right-radius: 4px !important; + border-bottom-left-radius: 4px !important + } + .rounded-sm-bottom-2 { + border-bottom-right-radius: 6px !important; + border-bottom-left-radius: 6px !important + } + .rounded-sm-bottom-3 { + border-bottom-right-radius: 8px !important; + border-bottom-left-radius: 8px !important + } + .rounded-sm-left-0 { + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important + } + .rounded-sm-left-1 { + border-bottom-left-radius: 4px !important; + border-top-left-radius: 4px !important + } + .rounded-sm-left-2 { + border-bottom-left-radius: 6px !important; + border-top-left-radius: 6px !important + } + .rounded-sm-left-3 { + border-bottom-left-radius: 8px !important; + border-top-left-radius: 8px !important + } +} + +@media (min-width: 768px) { + .border-md { + border: 1px #e1e4e8 solid !important + } + .border-md-0 { + border: 0 !important + } + .border-md-top { + border-top: 1px #e1e4e8 solid !important + } + .border-md-right { + border-right: 1px #e1e4e8 solid !important + } + .border-md-bottom { + border-bottom: 1px #e1e4e8 solid !important + } + .border-md-left { + border-left: 1px #e1e4e8 solid !important + } + .border-md-top-0 { + border-top: 0 !important + } + .border-md-right-0 { + border-right: 0 !important + } + .border-md-bottom-0 { + border-bottom: 0 !important + } + .border-md-left-0 { + border-left: 0 !important + } + .rounded-md { + border-radius: 6px !important + } + .rounded-md-0 { + border-radius: 0 !important + } + .rounded-md-1 { + border-radius: 4px !important + } + .rounded-md-2 { + border-radius: 6px !important + } + .rounded-md-3 { + border-radius: 8px !important + } + .rounded-md-top-0 { + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important + } + .rounded-md-top-1 { + border-top-left-radius: 4px !important; + border-top-right-radius: 4px !important + } + .rounded-md-top-2 { + border-top-left-radius: 6px !important; + border-top-right-radius: 6px !important + } + .rounded-md-top-3 { + border-top-left-radius: 8px !important; + border-top-right-radius: 8px !important + } + .rounded-md-right-0 { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important + } + .rounded-md-right-1 { + border-top-right-radius: 4px !important; + border-bottom-right-radius: 4px !important + } + .rounded-md-right-2 { + border-top-right-radius: 6px !important; + border-bottom-right-radius: 6px !important + } + .rounded-md-right-3 { + border-top-right-radius: 8px !important; + border-bottom-right-radius: 8px !important + } + .rounded-md-bottom-0 { + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important + } + .rounded-md-bottom-1 { + border-bottom-right-radius: 4px !important; + border-bottom-left-radius: 4px !important + } + .rounded-md-bottom-2 { + border-bottom-right-radius: 6px !important; + border-bottom-left-radius: 6px !important + } + .rounded-md-bottom-3 { + border-bottom-right-radius: 8px !important; + border-bottom-left-radius: 8px !important + } + .rounded-md-left-0 { + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important + } + .rounded-md-left-1 { + border-bottom-left-radius: 4px !important; + border-top-left-radius: 4px !important + } + .rounded-md-left-2 { + border-bottom-left-radius: 6px !important; + border-top-left-radius: 6px !important + } + .rounded-md-left-3 { + border-bottom-left-radius: 8px !important; + border-top-left-radius: 8px !important + } +} + +@media (min-width: 1012px) { + .border-lg { + border: 1px #e1e4e8 solid !important + } + .border-lg-0 { + border: 0 !important + } + .border-lg-top { + border-top: 1px #e1e4e8 solid !important + } + .border-lg-right { + border-right: 1px #e1e4e8 solid !important + } + .border-lg-bottom { + border-bottom: 1px #e1e4e8 solid !important + } + .border-lg-left { + border-left: 1px #e1e4e8 solid !important + } + .border-lg-top-0 { + border-top: 0 !important + } + .border-lg-right-0 { + border-right: 0 !important + } + .border-lg-bottom-0 { + border-bottom: 0 !important + } + .border-lg-left-0 { + border-left: 0 !important + } + .rounded-lg { + border-radius: 6px !important + } + .rounded-lg-0 { + border-radius: 0 !important + } + .rounded-lg-1 { + border-radius: 4px !important + } + .rounded-lg-2 { + border-radius: 6px !important + } + .rounded-lg-3 { + border-radius: 8px !important + } + .rounded-lg-top-0 { + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important + } + .rounded-lg-top-1 { + border-top-left-radius: 4px !important; + border-top-right-radius: 4px !important + } + .rounded-lg-top-2 { + border-top-left-radius: 6px !important; + border-top-right-radius: 6px !important + } + .rounded-lg-top-3 { + border-top-left-radius: 8px !important; + border-top-right-radius: 8px !important + } + .rounded-lg-right-0 { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important + } + .rounded-lg-right-1 { + border-top-right-radius: 4px !important; + border-bottom-right-radius: 4px !important + } + .rounded-lg-right-2 { + border-top-right-radius: 6px !important; + border-bottom-right-radius: 6px !important + } + .rounded-lg-right-3 { + border-top-right-radius: 8px !important; + border-bottom-right-radius: 8px !important + } + .rounded-lg-bottom-0 { + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important + } + .rounded-lg-bottom-1 { + border-bottom-right-radius: 4px !important; + border-bottom-left-radius: 4px !important + } + .rounded-lg-bottom-2 { + border-bottom-right-radius: 6px !important; + border-bottom-left-radius: 6px !important + } + .rounded-lg-bottom-3 { + border-bottom-right-radius: 8px !important; + border-bottom-left-radius: 8px !important + } + .rounded-lg-left-0 { + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important + } + .rounded-lg-left-1 { + border-bottom-left-radius: 4px !important; + border-top-left-radius: 4px !important + } + .rounded-lg-left-2 { + border-bottom-left-radius: 6px !important; + border-top-left-radius: 6px !important + } + .rounded-lg-left-3 { + border-bottom-left-radius: 8px !important; + border-top-left-radius: 8px !important + } +} + +@media (min-width: 1280px) { + .border-xl { + border: 1px #e1e4e8 solid !important + } + .border-xl-0 { + border: 0 !important + } + .border-xl-top { + border-top: 1px #e1e4e8 solid !important + } + .border-xl-right { + border-right: 1px #e1e4e8 solid !important + } + .border-xl-bottom { + border-bottom: 1px #e1e4e8 solid !important + } + .border-xl-left { + border-left: 1px #e1e4e8 solid !important + } + .border-xl-top-0 { + border-top: 0 !important + } + .border-xl-right-0 { + border-right: 0 !important + } + .border-xl-bottom-0 { + border-bottom: 0 !important + } + .border-xl-left-0 { + border-left: 0 !important + } + .rounded-xl { + border-radius: 6px !important + } + .rounded-xl-0 { + border-radius: 0 !important + } + .rounded-xl-1 { + border-radius: 4px !important + } + .rounded-xl-2 { + border-radius: 6px !important + } + .rounded-xl-3 { + border-radius: 8px !important + } + .rounded-xl-top-0 { + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important + } + .rounded-xl-top-1 { + border-top-left-radius: 4px !important; + border-top-right-radius: 4px !important + } + .rounded-xl-top-2 { + border-top-left-radius: 6px !important; + border-top-right-radius: 6px !important + } + .rounded-xl-top-3 { + border-top-left-radius: 8px !important; + border-top-right-radius: 8px !important + } + .rounded-xl-right-0 { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important + } + .rounded-xl-right-1 { + border-top-right-radius: 4px !important; + border-bottom-right-radius: 4px !important + } + .rounded-xl-right-2 { + border-top-right-radius: 6px !important; + border-bottom-right-radius: 6px !important + } + .rounded-xl-right-3 { + border-top-right-radius: 8px !important; + border-bottom-right-radius: 8px !important + } + .rounded-xl-bottom-0 { + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important + } + .rounded-xl-bottom-1 { + border-bottom-right-radius: 4px !important; + border-bottom-left-radius: 4px !important + } + .rounded-xl-bottom-2 { + border-bottom-right-radius: 6px !important; + border-bottom-left-radius: 6px !important + } + .rounded-xl-bottom-3 { + border-bottom-right-radius: 8px !important; + border-bottom-left-radius: 8px !important + } + .rounded-xl-left-0 { + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important + } + .rounded-xl-left-1 { + border-bottom-left-radius: 4px !important; + border-top-left-radius: 4px !important + } + .rounded-xl-left-2 { + border-bottom-left-radius: 6px !important; + border-top-left-radius: 6px !important + } + .rounded-xl-left-3 { + border-bottom-left-radius: 8px !important; + border-top-left-radius: 8px !important + } +} + +.circle { + border-radius: 50% !important +} + +.border-dashed { + border-style: dashed !important +} + +.border-blue { + border-color: #0366d6 !important +} + +.border-blue-light { + border-color: #c8e1ff !important +} + +.border-green { + border-color: #34d058 !important +} + +.border-green-light { + border-color: #a2cbac !important +} + +.border-red { + border-color: #d73a49 !important +} + +.border-red-light { + border-color: #f97583 !important +} + +.border-purple { + border-color: #6f42c1 !important +} + +.border-yellow { + border-color: #f9c513 !important +} + +.border-gray-light { + border-color: #eaecef !important +} + +.border-gray-dark { + border-color: #d1d5da !important +} + +.border-black-fade { + border-color: rgba(27, 31, 35, 0.15) !important +} + +.border-white-fade { + border-color: rgba(255, 255, 255, 0.15) !important +} + +.border-white-fade-15 { + border-color: rgba(255, 255, 255, 0.15) !important +} + +.border-white-fade-30 { + border-color: rgba(255, 255, 255, 0.3) !important +} + +.border-white-fade-50 { + border-color: rgba(255, 255, 255, 0.5) !important +} + +.border-white-fade-70 { + border-color: rgba(255, 255, 255, 0.7) !important +} + +.border-white-fade-85 { + border-color: rgba(255, 255, 255, 0.85) !important +} + +.box-shadow { + box-shadow: 0 1px 0 rgba(27, 31, 35, 0.04) !important +} + +.box-shadow-medium { + box-shadow: 0 3px 6px rgba(149, 157, 165, 0.15) !important +} + +.box-shadow-large { + box-shadow: 0 8px 24px rgba(149, 157, 165, 0.2) !important +} + +.box-shadow-extra-large { + box-shadow: 0 12px 48px rgba(149, 157, 165, 0.3) !important +} + +.box-shadow-none { + box-shadow: none !important +} + +.bg-white { + background-color: #fff !important +} + +.bg-blue { + background-color: #0366d6 !important +} + +.bg-blue-light { + background-color: #f1f8ff !important +} + +.bg-gray-dark { + background-color: #24292e !important +} + +.bg-gray { + background-color: #f6f8fa !important +} + +.bg-gray-light { + background-color: #fafbfc !important +} + +.bg-green { + background-color: #28a745 !important +} + +.bg-green-light { + background-color: #dcffe4 !important +} + +.bg-red { + background-color: #d73a49 !important +} + +.bg-red-light { + background-color: #ffeef0 !important +} + +.bg-yellow { + background-color: #ffd33d !important +} + +.bg-yellow-light { + background-color: #fff5b1 !important +} + +.bg-yellow-dark { + background-color: #dbab09 !important +} + +.bg-purple { + background-color: #6f42c1 !important +} + +.bg-pink { + background-color: #ea4aaa !important +} + +.bg-purple-light { + background-color: #f5f0ff !important +} + +.bg-orange { + background-color: #d15704 !important +} + +.color-gray-0 { + color: #fafbfc !important +} + +.bg-gray-0 { + background-color: #fafbfc !important +} + +.color-gray-1 { + color: #f6f8fa !important +} + +.bg-gray-1 { + background-color: #f6f8fa !important +} + +.color-gray-2 { + color: #e1e4e8 !important +} + +.bg-gray-2 { + background-color: #e1e4e8 !important +} + +.color-gray-3 { + color: #d1d5da !important +} + +.bg-gray-3 { + background-color: #d1d5da !important +} + +.color-gray-4 { + color: #959da5 !important +} + +.bg-gray-4 { + background-color: #959da5 !important +} + +.color-gray-5 { + color: #6a737d !important +} + +.bg-gray-5 { + background-color: #6a737d !important +} + +.color-gray-6 { + color: #586069 !important +} + +.bg-gray-6 { + background-color: #586069 !important +} + +.color-gray-7 { + color: #444d56 !important +} + +.bg-gray-7 { + background-color: #444d56 !important +} + +.color-gray-8 { + color: #2f363d !important +} + +.bg-gray-8 { + background-color: #2f363d !important +} + +.color-gray-9 { + color: #24292e !important +} + +.bg-gray-9 { + background-color: #24292e !important +} + +.color-blue-0 { + color: #f1f8ff !important +} + +.bg-blue-0 { + background-color: #f1f8ff !important +} + +.color-blue-1 { + color: #dbedff !important +} + +.bg-blue-1 { + background-color: #dbedff !important +} + +.color-blue-2 { + color: #c8e1ff !important +} + +.bg-blue-2 { + background-color: #c8e1ff !important +} + +.color-blue-3 { + color: #79b8ff !important +} + +.bg-blue-3 { + background-color: #79b8ff !important +} + +.color-blue-4 { + color: #2188ff !important +} + +.bg-blue-4 { + background-color: #2188ff !important +} + +.color-blue-5 { + color: #0366d6 !important +} + +.bg-blue-5 { + background-color: #0366d6 !important +} + +.color-blue-6 { + color: #005cc5 !important +} + +.bg-blue-6 { + background-color: #005cc5 !important +} + +.color-blue-7 { + color: #044289 !important +} + +.bg-blue-7 { + background-color: #044289 !important +} + +.color-blue-8 { + color: #032f62 !important +} + +.bg-blue-8 { + background-color: #032f62 !important +} + +.color-blue-9 { + color: #05264c !important +} + +.bg-blue-9 { + background-color: #05264c !important +} + +.color-green-0 { + color: #f0fff4 !important +} + +.bg-green-0 { + background-color: #f0fff4 !important +} + +.color-green-1 { + color: #dcffe4 !important +} + +.bg-green-1 { + background-color: #dcffe4 !important +} + +.color-green-2 { + color: #bef5cb !important +} + +.bg-green-2 { + background-color: #bef5cb !important +} + +.color-green-3 { + color: #85e89d !important +} + +.bg-green-3 { + background-color: #85e89d !important +} + +.color-green-4 { + color: #34d058 !important +} + +.bg-green-4 { + background-color: #34d058 !important +} + +.color-green-5 { + color: #28a745 !important +} + +.bg-green-5 { + background-color: #28a745 !important +} + +.color-green-6 { + color: #22863a !important +} + +.bg-green-6 { + background-color: #22863a !important +} + +.color-green-7 { + color: #176f2c !important +} + +.bg-green-7 { + background-color: #176f2c !important +} + +.color-green-8 { + color: #165c26 !important +} + +.bg-green-8 { + background-color: #165c26 !important +} + +.color-green-9 { + color: #144620 !important +} + +.bg-green-9 { + background-color: #144620 !important +} + +.color-yellow-0 { + color: #fffdef !important +} + +.bg-yellow-0 { + background-color: #fffdef !important +} + +.color-yellow-1 { + color: #fffbdd !important +} + +.bg-yellow-1 { + background-color: #fffbdd !important +} + +.color-yellow-2 { + color: #fff5b1 !important +} + +.bg-yellow-2 { + background-color: #fff5b1 !important +} + +.color-yellow-3 { + color: #ffea7f !important +} + +.bg-yellow-3 { + background-color: #ffea7f !important +} + +.color-yellow-4 { + color: #ffdf5d !important +} + +.bg-yellow-4 { + background-color: #ffdf5d !important +} + +.color-yellow-5 { + color: #ffd33d !important +} + +.bg-yellow-5 { + background-color: #ffd33d !important +} + +.color-yellow-6 { + color: #f9c513 !important +} + +.bg-yellow-6 { + background-color: #f9c513 !important +} + +.color-yellow-7 { + color: #dbab09 !important +} + +.bg-yellow-7 { + background-color: #dbab09 !important +} + +.color-yellow-8 { + color: #b08800 !important +} + +.bg-yellow-8 { + background-color: #b08800 !important +} + +.color-yellow-9 { + color: #735c0f !important +} + +.bg-yellow-9 { + background-color: #735c0f !important +} + +.color-orange-0 { + color: #fff8f2 !important +} + +.bg-orange-0 { + background-color: #fff8f2 !important +} + +.color-orange-1 { + color: #ffebda !important +} + +.bg-orange-1 { + background-color: #ffebda !important +} + +.color-orange-2 { + color: #ffd1ac !important +} + +.bg-orange-2 { + background-color: #ffd1ac !important +} + +.color-orange-3 { + color: #ffab70 !important +} + +.bg-orange-3 { + background-color: #ffab70 !important +} + +.color-orange-4 { + color: #fb8532 !important +} + +.bg-orange-4 { + background-color: #fb8532 !important +} + +.color-orange-5 { + color: #f66a0a !important +} + +.bg-orange-5 { + background-color: #f66a0a !important +} + +.color-orange-6 { + color: #e36209 !important +} + +.bg-orange-6 { + background-color: #e36209 !important +} + +.color-orange-7 { + color: #d15704 !important +} + +.bg-orange-7 { + background-color: #d15704 !important +} + +.color-orange-8 { + color: #c24e00 !important +} + +.bg-orange-8 { + background-color: #c24e00 !important +} + +.color-orange-9 { + color: #a04100 !important +} + +.bg-orange-9 { + background-color: #a04100 !important +} + +.color-red-0 { + color: #ffeef0 !important +} + +.bg-red-0 { + background-color: #ffeef0 !important +} + +.color-red-1 { + color: #ffdce0 !important +} + +.bg-red-1 { + background-color: #ffdce0 !important +} + +.color-red-2 { + color: #fdaeb7 !important +} + +.bg-red-2 { + background-color: #fdaeb7 !important +} + +.color-red-3 { + color: #f97583 !important +} + +.bg-red-3 { + background-color: #f97583 !important +} + +.color-red-4 { + color: #ea4a5a !important +} + +.bg-red-4 { + background-color: #ea4a5a !important +} + +.color-red-5 { + color: #d73a49 !important +} + +.bg-red-5 { + background-color: #d73a49 !important +} + +.color-red-6 { + color: #cb2431 !important +} + +.bg-red-6 { + background-color: #cb2431 !important +} + +.color-red-7 { + color: #b31d28 !important +} + +.bg-red-7 { + background-color: #b31d28 !important +} + +.color-red-8 { + color: #9e1c23 !important +} + +.bg-red-8 { + background-color: #9e1c23 !important +} + +.color-red-9 { + color: #86181d !important +} + +.bg-red-9 { + background-color: #86181d !important +} + +.color-purple-0 { + color: #f5f0ff !important +} + +.bg-purple-0 { + background-color: #f5f0ff !important +} + +.color-purple-1 { + color: #e6dcfd !important +} + +.bg-purple-1 { + background-color: #e6dcfd !important +} + +.color-purple-2 { + color: #d1bcf9 !important +} + +.bg-purple-2 { + background-color: #d1bcf9 !important +} + +.color-purple-3 { + color: #b392f0 !important +} + +.bg-purple-3 { + background-color: #b392f0 !important +} + +.color-purple-4 { + color: #8a63d2 !important +} + +.bg-purple-4 { + background-color: #8a63d2 !important +} + +.color-purple-5 { + color: #6f42c1 !important +} + +.bg-purple-5 { + background-color: #6f42c1 !important +} + +.color-purple-6 { + color: #5a32a3 !important +} + +.bg-purple-6 { + background-color: #5a32a3 !important +} + +.color-purple-7 { + color: #4c2889 !important +} + +.bg-purple-7 { + background-color: #4c2889 !important +} + +.color-purple-8 { + color: #3a1d6e !important +} + +.bg-purple-8 { + background-color: #3a1d6e !important +} + +.color-purple-9 { + color: #29134e !important +} + +.bg-purple-9 { + background-color: #29134e !important +} + +.color-pink-0 { + color: #ffeef8 !important +} + +.bg-pink-0 { + background-color: #ffeef8 !important +} + +.color-pink-1 { + color: #fedbf0 !important +} + +.bg-pink-1 { + background-color: #fedbf0 !important +} + +.color-pink-2 { + color: #f9b3dd !important +} + +.bg-pink-2 { + background-color: #f9b3dd !important +} + +.color-pink-3 { + color: #f692ce !important +} + +.bg-pink-3 { + background-color: #f692ce !important +} + +.color-pink-4 { + color: #ec6cb9 !important +} + +.bg-pink-4 { + background-color: #ec6cb9 !important +} + +.color-pink-5 { + color: #ea4aaa !important +} + +.bg-pink-5 { + background-color: #ea4aaa !important +} + +.color-pink-6 { + color: #d03592 !important +} + +.bg-pink-6 { + background-color: #d03592 !important +} + +.color-pink-7 { + color: #b93a86 !important +} + +.bg-pink-7 { + background-color: #b93a86 !important +} + +.color-pink-8 { + color: #99306f !important +} + +.bg-pink-8 { + background-color: #99306f !important +} + +.color-pink-9 { + color: #6d224f !important +} + +.bg-pink-9 { + background-color: #6d224f !important +} + +.bg-shade-gradient { + background-image: linear-gradient(180deg, rgba(27, 31, 35, 0.065), rgba(27, 31, 35, 0)) !important; + background-repeat: no-repeat !important; + background-size: 100% 200px !important +} + +.text-blue { + color: #0366d6 !important +} + +.text-red { + color: #cb2431 !important +} + +.text-gray-light { + color: #6a737d !important +} + +.text-gray { + color: #586069 !important +} + +.text-gray-dark { + color: #24292e !important +} + +.text-green { + color: #22863a !important +} + +.text-yellow { + color: #b08800 !important +} + +.text-orange { + color: #a04100 !important +} + +.text-orange-light { + color: #e36209 !important +} + +.text-purple { + color: #6f42c1 !important +} + +.text-pink { + color: #ea4aaa !important +} + +.text-white { + color: #fff !important +} + +.text-inherit { + color: inherit !important +} + +.link-gray { + color: #586069 !important +} + +.link-gray:hover { + color: #0366d6 !important +} + +.link-gray-dark { + color: #24292e !important +} + +.link-gray-dark:hover { + color: #0366d6 !important +} + +.link-hover-blue:hover { + color: #0366d6 !important +} + +.muted-link { + color: #586069 !important +} + +.muted-link:hover { + color: #0366d6 !important; + text-decoration: none +} + +.details-overlay[open]>summary::before { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 80; + display: block; + cursor: default; + content: " "; + background: transparent +} + +.details-overlay-dark[open]>summary::before { + z-index: 99; + background: rgba(27, 31, 35, 0.5) +} + +.details-reset>summary { + list-style: none +} + +.details-reset>summary::before { + display: none +} + +.details-reset>summary::-webkit-details-marker { + display: none +} + +.flex-row { + flex-direction: row !important +} + +.flex-row-reverse { + flex-direction: row-reverse !important +} + +.flex-column { + flex-direction: column !important +} + +.flex-column-reverse { + flex-direction: column-reverse !important +} + +.flex-wrap { + flex-wrap: wrap !important +} + +.flex-nowrap { + flex-wrap: nowrap !important +} + +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important +} + +.flex-justify-start { + justify-content: flex-start !important +} + +.flex-justify-end { + justify-content: flex-end !important +} + +.flex-justify-center { + justify-content: center !important +} + +.flex-justify-between { + justify-content: space-between !important +} + +.flex-justify-around { + justify-content: space-around !important +} + +.flex-items-start { + align-items: flex-start !important +} + +.flex-items-end { + align-items: flex-end !important +} + +.flex-items-center { + align-items: center !important +} + +.flex-items-baseline { + align-items: baseline !important +} + +.flex-items-stretch { + align-items: stretch !important +} + +.flex-content-start { + align-content: flex-start !important +} + +.flex-content-end { + align-content: flex-end !important +} + +.flex-content-center { + align-content: center !important +} + +.flex-content-between { + align-content: space-between !important +} + +.flex-content-around { + align-content: space-around !important +} + +.flex-content-stretch { + align-content: stretch !important +} + +.flex-1 { + flex: 1 !important +} + +.flex-auto { + flex: auto !important +} + +.flex-grow-0 { + flex-grow: 0 !important +} + +.flex-shrink-0 { + flex-shrink: 0 !important +} + +.flex-self-auto { + align-self: auto !important +} + +.flex-self-start { + align-self: flex-start !important +} + +.flex-self-end { + align-self: flex-end !important +} + +.flex-self-center { + align-self: center !important +} + +.flex-self-baseline { + align-self: baseline !important +} + +.flex-self-stretch { + align-self: stretch !important +} + +.flex-order-1 { + order: 1 !important +} + +.flex-order-2 { + order: 2 !important +} + +.flex-order-none { + order: inherit !important +} + +@media (min-width: 544px) { + .flex-sm-row { + flex-direction: row !important + } + .flex-sm-row-reverse { + flex-direction: row-reverse !important + } + .flex-sm-column { + flex-direction: column !important + } + .flex-sm-column-reverse { + flex-direction: column-reverse !important + } + .flex-sm-wrap { + flex-wrap: wrap !important + } + .flex-sm-nowrap { + flex-wrap: nowrap !important + } + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important + } + .flex-sm-justify-start { + justify-content: flex-start !important + } + .flex-sm-justify-end { + justify-content: flex-end !important + } + .flex-sm-justify-center { + justify-content: center !important + } + .flex-sm-justify-between { + justify-content: space-between !important + } + .flex-sm-justify-around { + justify-content: space-around !important + } + .flex-sm-items-start { + align-items: flex-start !important + } + .flex-sm-items-end { + align-items: flex-end !important + } + .flex-sm-items-center { + align-items: center !important + } + .flex-sm-items-baseline { + align-items: baseline !important + } + .flex-sm-items-stretch { + align-items: stretch !important + } + .flex-sm-content-start { + align-content: flex-start !important + } + .flex-sm-content-end { + align-content: flex-end !important + } + .flex-sm-content-center { + align-content: center !important + } + .flex-sm-content-between { + align-content: space-between !important + } + .flex-sm-content-around { + align-content: space-around !important + } + .flex-sm-content-stretch { + align-content: stretch !important + } + .flex-sm-1 { + flex: 1 !important + } + .flex-sm-auto { + flex: auto !important + } + .flex-sm-grow-0 { + flex-grow: 0 !important + } + .flex-sm-shrink-0 { + flex-shrink: 0 !important + } + .flex-sm-self-auto { + align-self: auto !important + } + .flex-sm-self-start { + align-self: flex-start !important + } + .flex-sm-self-end { + align-self: flex-end !important + } + .flex-sm-self-center { + align-self: center !important + } + .flex-sm-self-baseline { + align-self: baseline !important + } + .flex-sm-self-stretch { + align-self: stretch !important + } + .flex-sm-order-1 { + order: 1 !important + } + .flex-sm-order-2 { + order: 2 !important + } + .flex-sm-order-none { + order: inherit !important + } +} + +@media (min-width: 768px) { + .flex-md-row { + flex-direction: row !important + } + .flex-md-row-reverse { + flex-direction: row-reverse !important + } + .flex-md-column { + flex-direction: column !important + } + .flex-md-column-reverse { + flex-direction: column-reverse !important + } + .flex-md-wrap { + flex-wrap: wrap !important + } + .flex-md-nowrap { + flex-wrap: nowrap !important + } + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important + } + .flex-md-justify-start { + justify-content: flex-start !important + } + .flex-md-justify-end { + justify-content: flex-end !important + } + .flex-md-justify-center { + justify-content: center !important + } + .flex-md-justify-between { + justify-content: space-between !important + } + .flex-md-justify-around { + justify-content: space-around !important + } + .flex-md-items-start { + align-items: flex-start !important + } + .flex-md-items-end { + align-items: flex-end !important + } + .flex-md-items-center { + align-items: center !important + } + .flex-md-items-baseline { + align-items: baseline !important + } + .flex-md-items-stretch { + align-items: stretch !important + } + .flex-md-content-start { + align-content: flex-start !important + } + .flex-md-content-end { + align-content: flex-end !important + } + .flex-md-content-center { + align-content: center !important + } + .flex-md-content-between { + align-content: space-between !important + } + .flex-md-content-around { + align-content: space-around !important + } + .flex-md-content-stretch { + align-content: stretch !important + } + .flex-md-1 { + flex: 1 !important + } + .flex-md-auto { + flex: auto !important + } + .flex-md-grow-0 { + flex-grow: 0 !important + } + .flex-md-shrink-0 { + flex-shrink: 0 !important + } + .flex-md-self-auto { + align-self: auto !important + } + .flex-md-self-start { + align-self: flex-start !important + } + .flex-md-self-end { + align-self: flex-end !important + } + .flex-md-self-center { + align-self: center !important + } + .flex-md-self-baseline { + align-self: baseline !important + } + .flex-md-self-stretch { + align-self: stretch !important + } + .flex-md-order-1 { + order: 1 !important + } + .flex-md-order-2 { + order: 2 !important + } + .flex-md-order-none { + order: inherit !important + } +} + +@media (min-width: 1012px) { + .flex-lg-row { + flex-direction: row !important + } + .flex-lg-row-reverse { + flex-direction: row-reverse !important + } + .flex-lg-column { + flex-direction: column !important + } + .flex-lg-column-reverse { + flex-direction: column-reverse !important + } + .flex-lg-wrap { + flex-wrap: wrap !important + } + .flex-lg-nowrap { + flex-wrap: nowrap !important + } + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important + } + .flex-lg-justify-start { + justify-content: flex-start !important + } + .flex-lg-justify-end { + justify-content: flex-end !important + } + .flex-lg-justify-center { + justify-content: center !important + } + .flex-lg-justify-between { + justify-content: space-between !important + } + .flex-lg-justify-around { + justify-content: space-around !important + } + .flex-lg-items-start { + align-items: flex-start !important + } + .flex-lg-items-end { + align-items: flex-end !important + } + .flex-lg-items-center { + align-items: center !important + } + .flex-lg-items-baseline { + align-items: baseline !important + } + .flex-lg-items-stretch { + align-items: stretch !important + } + .flex-lg-content-start { + align-content: flex-start !important + } + .flex-lg-content-end { + align-content: flex-end !important + } + .flex-lg-content-center { + align-content: center !important + } + .flex-lg-content-between { + align-content: space-between !important + } + .flex-lg-content-around { + align-content: space-around !important + } + .flex-lg-content-stretch { + align-content: stretch !important + } + .flex-lg-1 { + flex: 1 !important + } + .flex-lg-auto { + flex: auto !important + } + .flex-lg-grow-0 { + flex-grow: 0 !important + } + .flex-lg-shrink-0 { + flex-shrink: 0 !important + } + .flex-lg-self-auto { + align-self: auto !important + } + .flex-lg-self-start { + align-self: flex-start !important + } + .flex-lg-self-end { + align-self: flex-end !important + } + .flex-lg-self-center { + align-self: center !important + } + .flex-lg-self-baseline { + align-self: baseline !important + } + .flex-lg-self-stretch { + align-self: stretch !important + } + .flex-lg-order-1 { + order: 1 !important + } + .flex-lg-order-2 { + order: 2 !important + } + .flex-lg-order-none { + order: inherit !important + } +} + +@media (min-width: 1280px) { + .flex-xl-row { + flex-direction: row !important + } + .flex-xl-row-reverse { + flex-direction: row-reverse !important + } + .flex-xl-column { + flex-direction: column !important + } + .flex-xl-column-reverse { + flex-direction: column-reverse !important + } + .flex-xl-wrap { + flex-wrap: wrap !important + } + .flex-xl-nowrap { + flex-wrap: nowrap !important + } + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important + } + .flex-xl-justify-start { + justify-content: flex-start !important + } + .flex-xl-justify-end { + justify-content: flex-end !important + } + .flex-xl-justify-center { + justify-content: center !important + } + .flex-xl-justify-between { + justify-content: space-between !important + } + .flex-xl-justify-around { + justify-content: space-around !important + } + .flex-xl-items-start { + align-items: flex-start !important + } + .flex-xl-items-end { + align-items: flex-end !important + } + .flex-xl-items-center { + align-items: center !important + } + .flex-xl-items-baseline { + align-items: baseline !important + } + .flex-xl-items-stretch { + align-items: stretch !important + } + .flex-xl-content-start { + align-content: flex-start !important + } + .flex-xl-content-end { + align-content: flex-end !important + } + .flex-xl-content-center { + align-content: center !important + } + .flex-xl-content-between { + align-content: space-between !important + } + .flex-xl-content-around { + align-content: space-around !important + } + .flex-xl-content-stretch { + align-content: stretch !important + } + .flex-xl-1 { + flex: 1 !important + } + .flex-xl-auto { + flex: auto !important + } + .flex-xl-grow-0 { + flex-grow: 0 !important + } + .flex-xl-shrink-0 { + flex-shrink: 0 !important + } + .flex-xl-self-auto { + align-self: auto !important + } + .flex-xl-self-start { + align-self: flex-start !important + } + .flex-xl-self-end { + align-self: flex-end !important + } + .flex-xl-self-center { + align-self: center !important + } + .flex-xl-self-baseline { + align-self: baseline !important + } + .flex-xl-self-stretch { + align-self: stretch !important + } + .flex-xl-order-1 { + order: 1 !important + } + .flex-xl-order-2 { + order: 2 !important + } + .flex-xl-order-none { + order: inherit !important + } +} + +.position-static { + position: static !important +} + +.position-relative { + position: relative !important +} + +.position-absolute { + position: absolute !important +} + +.position-fixed { + position: fixed !important +} + +.position-sticky { + position: -webkit-sticky !important; + position: sticky !important +} + +@media (min-width: 544px) { + .position-sm-static { + position: static !important + } + .position-sm-relative { + position: relative !important + } + .position-sm-absolute { + position: absolute !important + } + .position-sm-fixed { + position: fixed !important + } + .position-sm-sticky { + position: -webkit-sticky !important; + position: sticky !important + } +} + +@media (min-width: 768px) { + .position-md-static { + position: static !important + } + .position-md-relative { + position: relative !important + } + .position-md-absolute { + position: absolute !important + } + .position-md-fixed { + position: fixed !important + } + .position-md-sticky { + position: -webkit-sticky !important; + position: sticky !important + } +} + +@media (min-width: 1012px) { + .position-lg-static { + position: static !important + } + .position-lg-relative { + position: relative !important + } + .position-lg-absolute { + position: absolute !important + } + .position-lg-fixed { + position: fixed !important + } + .position-lg-sticky { + position: -webkit-sticky !important; + position: sticky !important + } +} + +@media (min-width: 1280px) { + .position-xl-static { + position: static !important + } + .position-xl-relative { + position: relative !important + } + .position-xl-absolute { + position: absolute !important + } + .position-xl-fixed { + position: fixed !important + } + .position-xl-sticky { + position: -webkit-sticky !important; + position: sticky !important + } +} + +.top-0 { + top: 0 !important +} + +.right-0 { + right: 0 !important +} + +.bottom-0 { + bottom: 0 !important +} + +.left-0 { + left: 0 !important +} + +.top-auto { + top: auto !important +} + +.right-auto { + right: auto !important +} + +.bottom-auto { + bottom: auto !important +} + +.left-auto { + left: auto !important +} + +@media (min-width: 544px) { + .top-sm-0 { + top: 0 !important + } + .right-sm-0 { + right: 0 !important + } + .bottom-sm-0 { + bottom: 0 !important + } + .left-sm-0 { + left: 0 !important + } + .top-sm-auto { + top: auto !important + } + .right-sm-auto { + right: auto !important + } + .bottom-sm-auto { + bottom: auto !important + } + .left-sm-auto { + left: auto !important + } +} + +@media (min-width: 768px) { + .top-md-0 { + top: 0 !important + } + .right-md-0 { + right: 0 !important + } + .bottom-md-0 { + bottom: 0 !important + } + .left-md-0 { + left: 0 !important + } + .top-md-auto { + top: auto !important + } + .right-md-auto { + right: auto !important + } + .bottom-md-auto { + bottom: auto !important + } + .left-md-auto { + left: auto !important + } +} + +@media (min-width: 1012px) { + .top-lg-0 { + top: 0 !important + } + .right-lg-0 { + right: 0 !important + } + .bottom-lg-0 { + bottom: 0 !important + } + .left-lg-0 { + left: 0 !important + } + .top-lg-auto { + top: auto !important + } + .right-lg-auto { + right: auto !important + } + .bottom-lg-auto { + bottom: auto !important + } + .left-lg-auto { + left: auto !important + } +} + +@media (min-width: 1280px) { + .top-xl-0 { + top: 0 !important + } + .right-xl-0 { + right: 0 !important + } + .bottom-xl-0 { + bottom: 0 !important + } + .left-xl-0 { + left: 0 !important + } + .top-xl-auto { + top: auto !important + } + .right-xl-auto { + right: auto !important + } + .bottom-xl-auto { + bottom: auto !important + } + .left-xl-auto { + left: auto !important + } +} + +.v-align-middle { + vertical-align: middle !important +} + +.v-align-top { + vertical-align: top !important +} + +.v-align-bottom { + vertical-align: bottom !important +} + +.v-align-text-top { + vertical-align: text-top !important +} + +.v-align-text-bottom { + vertical-align: text-bottom !important +} + +.v-align-baseline { + vertical-align: baseline !important +} + +.overflow-visible { + overflow: visible !important +} + +.overflow-x-visible { + overflow-x: visible !important +} + +.overflow-y-visible { + overflow-y: visible !important +} + +.overflow-hidden { + overflow: hidden !important +} + +.overflow-x-hidden { + overflow-x: hidden !important +} + +.overflow-y-hidden { + overflow-y: hidden !important +} + +.overflow-auto { + overflow: auto !important +} + +.overflow-x-auto { + overflow-x: auto !important +} + +.overflow-y-auto { + overflow-y: auto !important +} + +.overflow-scroll { + overflow: scroll !important +} + +.overflow-x-scroll { + overflow-x: scroll !important +} + +.overflow-y-scroll { + overflow-y: scroll !important +} + +@media (min-width: 544px) { + .overflow-sm-visible { + overflow: visible !important + } + .overflow-sm-x-visible { + overflow-x: visible !important + } + .overflow-sm-y-visible { + overflow-y: visible !important + } + .overflow-sm-hidden { + overflow: hidden !important + } + .overflow-sm-x-hidden { + overflow-x: hidden !important + } + .overflow-sm-y-hidden { + overflow-y: hidden !important + } + .overflow-sm-auto { + overflow: auto !important + } + .overflow-sm-x-auto { + overflow-x: auto !important + } + .overflow-sm-y-auto { + overflow-y: auto !important + } + .overflow-sm-scroll { + overflow: scroll !important + } + .overflow-sm-x-scroll { + overflow-x: scroll !important + } + .overflow-sm-y-scroll { + overflow-y: scroll !important + } +} + +@media (min-width: 768px) { + .overflow-md-visible { + overflow: visible !important + } + .overflow-md-x-visible { + overflow-x: visible !important + } + .overflow-md-y-visible { + overflow-y: visible !important + } + .overflow-md-hidden { + overflow: hidden !important + } + .overflow-md-x-hidden { + overflow-x: hidden !important + } + .overflow-md-y-hidden { + overflow-y: hidden !important + } + .overflow-md-auto { + overflow: auto !important + } + .overflow-md-x-auto { + overflow-x: auto !important + } + .overflow-md-y-auto { + overflow-y: auto !important + } + .overflow-md-scroll { + overflow: scroll !important + } + .overflow-md-x-scroll { + overflow-x: scroll !important + } + .overflow-md-y-scroll { + overflow-y: scroll !important + } +} + +@media (min-width: 1012px) { + .overflow-lg-visible { + overflow: visible !important + } + .overflow-lg-x-visible { + overflow-x: visible !important + } + .overflow-lg-y-visible { + overflow-y: visible !important + } + .overflow-lg-hidden { + overflow: hidden !important + } + .overflow-lg-x-hidden { + overflow-x: hidden !important + } + .overflow-lg-y-hidden { + overflow-y: hidden !important + } + .overflow-lg-auto { + overflow: auto !important + } + .overflow-lg-x-auto { + overflow-x: auto !important + } + .overflow-lg-y-auto { + overflow-y: auto !important + } + .overflow-lg-scroll { + overflow: scroll !important + } + .overflow-lg-x-scroll { + overflow-x: scroll !important + } + .overflow-lg-y-scroll { + overflow-y: scroll !important + } +} + +@media (min-width: 1280px) { + .overflow-xl-visible { + overflow: visible !important + } + .overflow-xl-x-visible { + overflow-x: visible !important + } + .overflow-xl-y-visible { + overflow-y: visible !important + } + .overflow-xl-hidden { + overflow: hidden !important + } + .overflow-xl-x-hidden { + overflow-x: hidden !important + } + .overflow-xl-y-hidden { + overflow-y: hidden !important + } + .overflow-xl-auto { + overflow: auto !important + } + .overflow-xl-x-auto { + overflow-x: auto !important + } + .overflow-xl-y-auto { + overflow-y: auto !important + } + .overflow-xl-scroll { + overflow: scroll !important + } + .overflow-xl-x-scroll { + overflow-x: scroll !important + } + .overflow-xl-y-scroll { + overflow-y: scroll !important + } +} + +.clearfix::before { + display: table; + content: "" +} + +.clearfix::after { + display: table; + clear: both; + content: "" +} + +.float-left { + float: left !important +} + +.float-right { + float: right !important +} + +.float-none { + float: none !important +} + +@media (min-width: 544px) { + .float-sm-left { + float: left !important + } + .float-sm-right { + float: right !important + } + .float-sm-none { + float: none !important + } +} + +@media (min-width: 768px) { + .float-md-left { + float: left !important + } + .float-md-right { + float: right !important + } + .float-md-none { + float: none !important + } +} + +@media (min-width: 1012px) { + .float-lg-left { + float: left !important + } + .float-lg-right { + float: right !important + } + .float-lg-none { + float: none !important + } +} + +@media (min-width: 1280px) { + .float-xl-left { + float: left !important + } + .float-xl-right { + float: right !important + } + .float-xl-none { + float: none !important + } +} + +.width-fit { + max-width: 100% !important +} + +.width-full { + width: 100% !important +} + +.height-fit { + max-height: 100% !important +} + +.height-full { + height: 100% !important +} + +.min-width-0 { + min-width: 0 !important +} + +.width-auto { + width: auto !important +} + +.direction-rtl { + direction: rtl !important +} + +.direction-ltr { + direction: ltr !important +} + +@media (min-width: 544px) { + .width-sm-auto { + width: auto !important + } + .direction-sm-rtl { + direction: rtl !important + } + .direction-sm-ltr { + direction: ltr !important + } +} + +@media (min-width: 768px) { + .width-md-auto { + width: auto !important + } + .direction-md-rtl { + direction: rtl !important + } + .direction-md-ltr { + direction: ltr !important + } +} + +@media (min-width: 1012px) { + .width-lg-auto { + width: auto !important + } + .direction-lg-rtl { + direction: rtl !important + } + .direction-lg-ltr { + direction: ltr !important + } +} + +@media (min-width: 1280px) { + .width-xl-auto { + width: auto !important + } + .direction-xl-rtl { + direction: rtl !important + } + .direction-xl-ltr { + direction: ltr !important + } +} + +.m-0 { + margin: 0 !important +} + +.mt-0 { + margin-top: 0 !important +} + +.mr-0 { + margin-right: 0 !important +} + +.mb-0 { + margin-bottom: 0 !important +} + +.ml-0 { + margin-left: 0 !important +} + +.mx-0 { + margin-right: 0 !important; + margin-left: 0 !important +} + +.my-0 { + margin-top: 0 !important; + margin-bottom: 0 !important +} + +.m-1 { + margin: 4px !important +} + +.mt-1 { + margin-top: 4px !important +} + +.mr-1 { + margin-right: 4px !important +} + +.mb-1 { + margin-bottom: 4px !important +} + +.ml-1 { + margin-left: 4px !important +} + +.mt-n1 { + margin-top: -4px !important +} + +.mr-n1 { + margin-right: -4px !important +} + +.mb-n1 { + margin-bottom: -4px !important +} + +.ml-n1 { + margin-left: -4px !important +} + +.mx-1 { + margin-right: 4px !important; + margin-left: 4px !important +} + +.my-1 { + margin-top: 4px !important; + margin-bottom: 4px !important +} + +.m-2 { + margin: 8px !important +} + +.mt-2 { + margin-top: 8px !important +} + +.mr-2 { + margin-right: 8px !important +} + +.mb-2 { + margin-bottom: 8px !important +} + +.ml-2 { + margin-left: 8px !important +} + +.mt-n2 { + margin-top: -8px !important +} + +.mr-n2 { + margin-right: -8px !important +} + +.mb-n2 { + margin-bottom: -8px !important +} + +.ml-n2 { + margin-left: -8px !important +} + +.mx-2 { + margin-right: 8px !important; + margin-left: 8px !important +} + +.my-2 { + margin-top: 8px !important; + margin-bottom: 8px !important +} + +.m-3 { + margin: 16px !important +} + +.mt-3 { + margin-top: 16px !important +} + +.mr-3 { + margin-right: 16px !important +} + +.mb-3 { + margin-bottom: 16px !important +} + +.ml-3 { + margin-left: 16px !important +} + +.mt-n3 { + margin-top: -16px !important +} + +.mr-n3 { + margin-right: -16px !important +} + +.mb-n3 { + margin-bottom: -16px !important +} + +.ml-n3 { + margin-left: -16px !important +} + +.mx-3 { + margin-right: 16px !important; + margin-left: 16px !important +} + +.my-3 { + margin-top: 16px !important; + margin-bottom: 16px !important +} + +.m-4 { + margin: 24px !important +} + +.mt-4 { + margin-top: 24px !important +} + +.mr-4 { + margin-right: 24px !important +} + +.mb-4 { + margin-bottom: 24px !important +} + +.ml-4 { + margin-left: 24px !important +} + +.mt-n4 { + margin-top: -24px !important +} + +.mr-n4 { + margin-right: -24px !important +} + +.mb-n4 { + margin-bottom: -24px !important +} + +.ml-n4 { + margin-left: -24px !important +} + +.mx-4 { + margin-right: 24px !important; + margin-left: 24px !important +} + +.my-4 { + margin-top: 24px !important; + margin-bottom: 24px !important +} + +.m-5 { + margin: 32px !important +} + +.mt-5 { + margin-top: 32px !important +} + +.mr-5 { + margin-right: 32px !important +} + +.mb-5 { + margin-bottom: 32px !important +} + +.ml-5 { + margin-left: 32px !important +} + +.mt-n5 { + margin-top: -32px !important +} + +.mr-n5 { + margin-right: -32px !important +} + +.mb-n5 { + margin-bottom: -32px !important +} + +.ml-n5 { + margin-left: -32px !important +} + +.mx-5 { + margin-right: 32px !important; + margin-left: 32px !important +} + +.my-5 { + margin-top: 32px !important; + margin-bottom: 32px !important +} + +.m-6 { + margin: 40px !important +} + +.mt-6 { + margin-top: 40px !important +} + +.mr-6 { + margin-right: 40px !important +} + +.mb-6 { + margin-bottom: 40px !important +} + +.ml-6 { + margin-left: 40px !important +} + +.mt-n6 { + margin-top: -40px !important +} + +.mr-n6 { + margin-right: -40px !important +} + +.mb-n6 { + margin-bottom: -40px !important +} + +.ml-n6 { + margin-left: -40px !important +} + +.mx-6 { + margin-right: 40px !important; + margin-left: 40px !important +} + +.my-6 { + margin-top: 40px !important; + margin-bottom: 40px !important +} + +.mx-auto { + margin-right: auto !important; + margin-left: auto !important +} + +@media (min-width: 544px) { + .m-sm-0 { + margin: 0 !important + } + .mt-sm-0 { + margin-top: 0 !important + } + .mr-sm-0 { + margin-right: 0 !important + } + .mb-sm-0 { + margin-bottom: 0 !important + } + .ml-sm-0 { + margin-left: 0 !important + } + .mx-sm-0 { + margin-right: 0 !important; + margin-left: 0 !important + } + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important + } + .m-sm-1 { + margin: 4px !important + } + .mt-sm-1 { + margin-top: 4px !important + } + .mr-sm-1 { + margin-right: 4px !important + } + .mb-sm-1 { + margin-bottom: 4px !important + } + .ml-sm-1 { + margin-left: 4px !important + } + .mt-sm-n1 { + margin-top: -4px !important + } + .mr-sm-n1 { + margin-right: -4px !important + } + .mb-sm-n1 { + margin-bottom: -4px !important + } + .ml-sm-n1 { + margin-left: -4px !important + } + .mx-sm-1 { + margin-right: 4px !important; + margin-left: 4px !important + } + .my-sm-1 { + margin-top: 4px !important; + margin-bottom: 4px !important + } + .m-sm-2 { + margin: 8px !important + } + .mt-sm-2 { + margin-top: 8px !important + } + .mr-sm-2 { + margin-right: 8px !important + } + .mb-sm-2 { + margin-bottom: 8px !important + } + .ml-sm-2 { + margin-left: 8px !important + } + .mt-sm-n2 { + margin-top: -8px !important + } + .mr-sm-n2 { + margin-right: -8px !important + } + .mb-sm-n2 { + margin-bottom: -8px !important + } + .ml-sm-n2 { + margin-left: -8px !important + } + .mx-sm-2 { + margin-right: 8px !important; + margin-left: 8px !important + } + .my-sm-2 { + margin-top: 8px !important; + margin-bottom: 8px !important + } + .m-sm-3 { + margin: 16px !important + } + .mt-sm-3 { + margin-top: 16px !important + } + .mr-sm-3 { + margin-right: 16px !important + } + .mb-sm-3 { + margin-bottom: 16px !important + } + .ml-sm-3 { + margin-left: 16px !important + } + .mt-sm-n3 { + margin-top: -16px !important + } + .mr-sm-n3 { + margin-right: -16px !important + } + .mb-sm-n3 { + margin-bottom: -16px !important + } + .ml-sm-n3 { + margin-left: -16px !important + } + .mx-sm-3 { + margin-right: 16px !important; + margin-left: 16px !important + } + .my-sm-3 { + margin-top: 16px !important; + margin-bottom: 16px !important + } + .m-sm-4 { + margin: 24px !important + } + .mt-sm-4 { + margin-top: 24px !important + } + .mr-sm-4 { + margin-right: 24px !important + } + .mb-sm-4 { + margin-bottom: 24px !important + } + .ml-sm-4 { + margin-left: 24px !important + } + .mt-sm-n4 { + margin-top: -24px !important + } + .mr-sm-n4 { + margin-right: -24px !important + } + .mb-sm-n4 { + margin-bottom: -24px !important + } + .ml-sm-n4 { + margin-left: -24px !important + } + .mx-sm-4 { + margin-right: 24px !important; + margin-left: 24px !important + } + .my-sm-4 { + margin-top: 24px !important; + margin-bottom: 24px !important + } + .m-sm-5 { + margin: 32px !important + } + .mt-sm-5 { + margin-top: 32px !important + } + .mr-sm-5 { + margin-right: 32px !important + } + .mb-sm-5 { + margin-bottom: 32px !important + } + .ml-sm-5 { + margin-left: 32px !important + } + .mt-sm-n5 { + margin-top: -32px !important + } + .mr-sm-n5 { + margin-right: -32px !important + } + .mb-sm-n5 { + margin-bottom: -32px !important + } + .ml-sm-n5 { + margin-left: -32px !important + } + .mx-sm-5 { + margin-right: 32px !important; + margin-left: 32px !important + } + .my-sm-5 { + margin-top: 32px !important; + margin-bottom: 32px !important + } + .m-sm-6 { + margin: 40px !important + } + .mt-sm-6 { + margin-top: 40px !important + } + .mr-sm-6 { + margin-right: 40px !important + } + .mb-sm-6 { + margin-bottom: 40px !important + } + .ml-sm-6 { + margin-left: 40px !important + } + .mt-sm-n6 { + margin-top: -40px !important + } + .mr-sm-n6 { + margin-right: -40px !important + } + .mb-sm-n6 { + margin-bottom: -40px !important + } + .ml-sm-n6 { + margin-left: -40px !important + } + .mx-sm-6 { + margin-right: 40px !important; + margin-left: 40px !important + } + .my-sm-6 { + margin-top: 40px !important; + margin-bottom: 40px !important + } + .mx-sm-auto { + margin-right: auto !important; + margin-left: auto !important + } +} + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important + } + .mt-md-0 { + margin-top: 0 !important + } + .mr-md-0 { + margin-right: 0 !important + } + .mb-md-0 { + margin-bottom: 0 !important + } + .ml-md-0 { + margin-left: 0 !important + } + .mx-md-0 { + margin-right: 0 !important; + margin-left: 0 !important + } + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important + } + .m-md-1 { + margin: 4px !important + } + .mt-md-1 { + margin-top: 4px !important + } + .mr-md-1 { + margin-right: 4px !important + } + .mb-md-1 { + margin-bottom: 4px !important + } + .ml-md-1 { + margin-left: 4px !important + } + .mt-md-n1 { + margin-top: -4px !important + } + .mr-md-n1 { + margin-right: -4px !important + } + .mb-md-n1 { + margin-bottom: -4px !important + } + .ml-md-n1 { + margin-left: -4px !important + } + .mx-md-1 { + margin-right: 4px !important; + margin-left: 4px !important + } + .my-md-1 { + margin-top: 4px !important; + margin-bottom: 4px !important + } + .m-md-2 { + margin: 8px !important + } + .mt-md-2 { + margin-top: 8px !important + } + .mr-md-2 { + margin-right: 8px !important + } + .mb-md-2 { + margin-bottom: 8px !important + } + .ml-md-2 { + margin-left: 8px !important + } + .mt-md-n2 { + margin-top: -8px !important + } + .mr-md-n2 { + margin-right: -8px !important + } + .mb-md-n2 { + margin-bottom: -8px !important + } + .ml-md-n2 { + margin-left: -8px !important + } + .mx-md-2 { + margin-right: 8px !important; + margin-left: 8px !important + } + .my-md-2 { + margin-top: 8px !important; + margin-bottom: 8px !important + } + .m-md-3 { + margin: 16px !important + } + .mt-md-3 { + margin-top: 16px !important + } + .mr-md-3 { + margin-right: 16px !important + } + .mb-md-3 { + margin-bottom: 16px !important + } + .ml-md-3 { + margin-left: 16px !important + } + .mt-md-n3 { + margin-top: -16px !important + } + .mr-md-n3 { + margin-right: -16px !important + } + .mb-md-n3 { + margin-bottom: -16px !important + } + .ml-md-n3 { + margin-left: -16px !important + } + .mx-md-3 { + margin-right: 16px !important; + margin-left: 16px !important + } + .my-md-3 { + margin-top: 16px !important; + margin-bottom: 16px !important + } + .m-md-4 { + margin: 24px !important + } + .mt-md-4 { + margin-top: 24px !important + } + .mr-md-4 { + margin-right: 24px !important + } + .mb-md-4 { + margin-bottom: 24px !important + } + .ml-md-4 { + margin-left: 24px !important + } + .mt-md-n4 { + margin-top: -24px !important + } + .mr-md-n4 { + margin-right: -24px !important + } + .mb-md-n4 { + margin-bottom: -24px !important + } + .ml-md-n4 { + margin-left: -24px !important + } + .mx-md-4 { + margin-right: 24px !important; + margin-left: 24px !important + } + .my-md-4 { + margin-top: 24px !important; + margin-bottom: 24px !important + } + .m-md-5 { + margin: 32px !important + } + .mt-md-5 { + margin-top: 32px !important + } + .mr-md-5 { + margin-right: 32px !important + } + .mb-md-5 { + margin-bottom: 32px !important + } + .ml-md-5 { + margin-left: 32px !important + } + .mt-md-n5 { + margin-top: -32px !important + } + .mr-md-n5 { + margin-right: -32px !important + } + .mb-md-n5 { + margin-bottom: -32px !important + } + .ml-md-n5 { + margin-left: -32px !important + } + .mx-md-5 { + margin-right: 32px !important; + margin-left: 32px !important + } + .my-md-5 { + margin-top: 32px !important; + margin-bottom: 32px !important + } + .m-md-6 { + margin: 40px !important + } + .mt-md-6 { + margin-top: 40px !important + } + .mr-md-6 { + margin-right: 40px !important + } + .mb-md-6 { + margin-bottom: 40px !important + } + .ml-md-6 { + margin-left: 40px !important + } + .mt-md-n6 { + margin-top: -40px !important + } + .mr-md-n6 { + margin-right: -40px !important + } + .mb-md-n6 { + margin-bottom: -40px !important + } + .ml-md-n6 { + margin-left: -40px !important + } + .mx-md-6 { + margin-right: 40px !important; + margin-left: 40px !important + } + .my-md-6 { + margin-top: 40px !important; + margin-bottom: 40px !important + } + .mx-md-auto { + margin-right: auto !important; + margin-left: auto !important + } +} + +@media (min-width: 1012px) { + .m-lg-0 { + margin: 0 !important + } + .mt-lg-0 { + margin-top: 0 !important + } + .mr-lg-0 { + margin-right: 0 !important + } + .mb-lg-0 { + margin-bottom: 0 !important + } + .ml-lg-0 { + margin-left: 0 !important + } + .mx-lg-0 { + margin-right: 0 !important; + margin-left: 0 !important + } + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important + } + .m-lg-1 { + margin: 4px !important + } + .mt-lg-1 { + margin-top: 4px !important + } + .mr-lg-1 { + margin-right: 4px !important + } + .mb-lg-1 { + margin-bottom: 4px !important + } + .ml-lg-1 { + margin-left: 4px !important + } + .mt-lg-n1 { + margin-top: -4px !important + } + .mr-lg-n1 { + margin-right: -4px !important + } + .mb-lg-n1 { + margin-bottom: -4px !important + } + .ml-lg-n1 { + margin-left: -4px !important + } + .mx-lg-1 { + margin-right: 4px !important; + margin-left: 4px !important + } + .my-lg-1 { + margin-top: 4px !important; + margin-bottom: 4px !important + } + .m-lg-2 { + margin: 8px !important + } + .mt-lg-2 { + margin-top: 8px !important + } + .mr-lg-2 { + margin-right: 8px !important + } + .mb-lg-2 { + margin-bottom: 8px !important + } + .ml-lg-2 { + margin-left: 8px !important + } + .mt-lg-n2 { + margin-top: -8px !important + } + .mr-lg-n2 { + margin-right: -8px !important + } + .mb-lg-n2 { + margin-bottom: -8px !important + } + .ml-lg-n2 { + margin-left: -8px !important + } + .mx-lg-2 { + margin-right: 8px !important; + margin-left: 8px !important + } + .my-lg-2 { + margin-top: 8px !important; + margin-bottom: 8px !important + } + .m-lg-3 { + margin: 16px !important + } + .mt-lg-3 { + margin-top: 16px !important + } + .mr-lg-3 { + margin-right: 16px !important + } + .mb-lg-3 { + margin-bottom: 16px !important + } + .ml-lg-3 { + margin-left: 16px !important + } + .mt-lg-n3 { + margin-top: -16px !important + } + .mr-lg-n3 { + margin-right: -16px !important + } + .mb-lg-n3 { + margin-bottom: -16px !important + } + .ml-lg-n3 { + margin-left: -16px !important + } + .mx-lg-3 { + margin-right: 16px !important; + margin-left: 16px !important + } + .my-lg-3 { + margin-top: 16px !important; + margin-bottom: 16px !important + } + .m-lg-4 { + margin: 24px !important + } + .mt-lg-4 { + margin-top: 24px !important + } + .mr-lg-4 { + margin-right: 24px !important + } + .mb-lg-4 { + margin-bottom: 24px !important + } + .ml-lg-4 { + margin-left: 24px !important + } + .mt-lg-n4 { + margin-top: -24px !important + } + .mr-lg-n4 { + margin-right: -24px !important + } + .mb-lg-n4 { + margin-bottom: -24px !important + } + .ml-lg-n4 { + margin-left: -24px !important + } + .mx-lg-4 { + margin-right: 24px !important; + margin-left: 24px !important + } + .my-lg-4 { + margin-top: 24px !important; + margin-bottom: 24px !important + } + .m-lg-5 { + margin: 32px !important + } + .mt-lg-5 { + margin-top: 32px !important + } + .mr-lg-5 { + margin-right: 32px !important + } + .mb-lg-5 { + margin-bottom: 32px !important + } + .ml-lg-5 { + margin-left: 32px !important + } + .mt-lg-n5 { + margin-top: -32px !important + } + .mr-lg-n5 { + margin-right: -32px !important + } + .mb-lg-n5 { + margin-bottom: -32px !important + } + .ml-lg-n5 { + margin-left: -32px !important + } + .mx-lg-5 { + margin-right: 32px !important; + margin-left: 32px !important + } + .my-lg-5 { + margin-top: 32px !important; + margin-bottom: 32px !important + } + .m-lg-6 { + margin: 40px !important + } + .mt-lg-6 { + margin-top: 40px !important + } + .mr-lg-6 { + margin-right: 40px !important + } + .mb-lg-6 { + margin-bottom: 40px !important + } + .ml-lg-6 { + margin-left: 40px !important + } + .mt-lg-n6 { + margin-top: -40px !important + } + .mr-lg-n6 { + margin-right: -40px !important + } + .mb-lg-n6 { + margin-bottom: -40px !important + } + .ml-lg-n6 { + margin-left: -40px !important + } + .mx-lg-6 { + margin-right: 40px !important; + margin-left: 40px !important + } + .my-lg-6 { + margin-top: 40px !important; + margin-bottom: 40px !important + } + .mx-lg-auto { + margin-right: auto !important; + margin-left: auto !important + } +} + +@media (min-width: 1280px) { + .m-xl-0 { + margin: 0 !important + } + .mt-xl-0 { + margin-top: 0 !important + } + .mr-xl-0 { + margin-right: 0 !important + } + .mb-xl-0 { + margin-bottom: 0 !important + } + .ml-xl-0 { + margin-left: 0 !important + } + .mx-xl-0 { + margin-right: 0 !important; + margin-left: 0 !important + } + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important + } + .m-xl-1 { + margin: 4px !important + } + .mt-xl-1 { + margin-top: 4px !important + } + .mr-xl-1 { + margin-right: 4px !important + } + .mb-xl-1 { + margin-bottom: 4px !important + } + .ml-xl-1 { + margin-left: 4px !important + } + .mt-xl-n1 { + margin-top: -4px !important + } + .mr-xl-n1 { + margin-right: -4px !important + } + .mb-xl-n1 { + margin-bottom: -4px !important + } + .ml-xl-n1 { + margin-left: -4px !important + } + .mx-xl-1 { + margin-right: 4px !important; + margin-left: 4px !important + } + .my-xl-1 { + margin-top: 4px !important; + margin-bottom: 4px !important + } + .m-xl-2 { + margin: 8px !important + } + .mt-xl-2 { + margin-top: 8px !important + } + .mr-xl-2 { + margin-right: 8px !important + } + .mb-xl-2 { + margin-bottom: 8px !important + } + .ml-xl-2 { + margin-left: 8px !important + } + .mt-xl-n2 { + margin-top: -8px !important + } + .mr-xl-n2 { + margin-right: -8px !important + } + .mb-xl-n2 { + margin-bottom: -8px !important + } + .ml-xl-n2 { + margin-left: -8px !important + } + .mx-xl-2 { + margin-right: 8px !important; + margin-left: 8px !important + } + .my-xl-2 { + margin-top: 8px !important; + margin-bottom: 8px !important + } + .m-xl-3 { + margin: 16px !important + } + .mt-xl-3 { + margin-top: 16px !important + } + .mr-xl-3 { + margin-right: 16px !important + } + .mb-xl-3 { + margin-bottom: 16px !important + } + .ml-xl-3 { + margin-left: 16px !important + } + .mt-xl-n3 { + margin-top: -16px !important + } + .mr-xl-n3 { + margin-right: -16px !important + } + .mb-xl-n3 { + margin-bottom: -16px !important + } + .ml-xl-n3 { + margin-left: -16px !important + } + .mx-xl-3 { + margin-right: 16px !important; + margin-left: 16px !important + } + .my-xl-3 { + margin-top: 16px !important; + margin-bottom: 16px !important + } + .m-xl-4 { + margin: 24px !important + } + .mt-xl-4 { + margin-top: 24px !important + } + .mr-xl-4 { + margin-right: 24px !important + } + .mb-xl-4 { + margin-bottom: 24px !important + } + .ml-xl-4 { + margin-left: 24px !important + } + .mt-xl-n4 { + margin-top: -24px !important + } + .mr-xl-n4 { + margin-right: -24px !important + } + .mb-xl-n4 { + margin-bottom: -24px !important + } + .ml-xl-n4 { + margin-left: -24px !important + } + .mx-xl-4 { + margin-right: 24px !important; + margin-left: 24px !important + } + .my-xl-4 { + margin-top: 24px !important; + margin-bottom: 24px !important + } + .m-xl-5 { + margin: 32px !important + } + .mt-xl-5 { + margin-top: 32px !important + } + .mr-xl-5 { + margin-right: 32px !important + } + .mb-xl-5 { + margin-bottom: 32px !important + } + .ml-xl-5 { + margin-left: 32px !important + } + .mt-xl-n5 { + margin-top: -32px !important + } + .mr-xl-n5 { + margin-right: -32px !important + } + .mb-xl-n5 { + margin-bottom: -32px !important + } + .ml-xl-n5 { + margin-left: -32px !important + } + .mx-xl-5 { + margin-right: 32px !important; + margin-left: 32px !important + } + .my-xl-5 { + margin-top: 32px !important; + margin-bottom: 32px !important + } + .m-xl-6 { + margin: 40px !important + } + .mt-xl-6 { + margin-top: 40px !important + } + .mr-xl-6 { + margin-right: 40px !important + } + .mb-xl-6 { + margin-bottom: 40px !important + } + .ml-xl-6 { + margin-left: 40px !important + } + .mt-xl-n6 { + margin-top: -40px !important + } + .mr-xl-n6 { + margin-right: -40px !important + } + .mb-xl-n6 { + margin-bottom: -40px !important + } + .ml-xl-n6 { + margin-left: -40px !important + } + .mx-xl-6 { + margin-right: 40px !important; + margin-left: 40px !important + } + .my-xl-6 { + margin-top: 40px !important; + margin-bottom: 40px !important + } + .mx-xl-auto { + margin-right: auto !important; + margin-left: auto !important + } +} + +.p-0 { + padding: 0 !important +} + +.pt-0 { + padding-top: 0 !important +} + +.pr-0 { + padding-right: 0 !important +} + +.pb-0 { + padding-bottom: 0 !important +} + +.pl-0 { + padding-left: 0 !important +} + +.px-0 { + padding-right: 0 !important; + padding-left: 0 !important +} + +.py-0 { + padding-top: 0 !important; + padding-bottom: 0 !important +} + +.p-1 { + padding: 4px !important +} + +.pt-1 { + padding-top: 4px !important +} + +.pr-1 { + padding-right: 4px !important +} + +.pb-1 { + padding-bottom: 4px !important +} + +.pl-1 { + padding-left: 4px !important +} + +.px-1 { + padding-right: 4px !important; + padding-left: 4px !important +} + +.py-1 { + padding-top: 4px !important; + padding-bottom: 4px !important +} + +.p-2 { + padding: 8px !important +} + +.pt-2 { + padding-top: 8px !important +} + +.pr-2 { + padding-right: 8px !important +} + +.pb-2 { + padding-bottom: 8px !important +} + +.pl-2 { + padding-left: 8px !important +} + +.px-2 { + padding-right: 8px !important; + padding-left: 8px !important +} + +.py-2 { + padding-top: 8px !important; + padding-bottom: 8px !important +} + +.p-3 { + padding: 16px !important +} + +.pt-3 { + padding-top: 16px !important +} + +.pr-3 { + padding-right: 16px !important +} + +.pb-3 { + padding-bottom: 16px !important +} + +.pl-3 { + padding-left: 16px !important +} + +.px-3 { + padding-right: 16px !important; + padding-left: 16px !important +} + +.py-3 { + padding-top: 16px !important; + padding-bottom: 16px !important +} + +.p-4 { + padding: 24px !important +} + +.pt-4 { + padding-top: 24px !important +} + +.pr-4 { + padding-right: 24px !important +} + +.pb-4 { + padding-bottom: 24px !important +} + +.pl-4 { + padding-left: 24px !important +} + +.px-4 { + padding-right: 24px !important; + padding-left: 24px !important +} + +.py-4 { + padding-top: 24px !important; + padding-bottom: 24px !important +} + +.p-5 { + padding: 32px !important +} + +.pt-5 { + padding-top: 32px !important +} + +.pr-5 { + padding-right: 32px !important +} + +.pb-5 { + padding-bottom: 32px !important +} + +.pl-5 { + padding-left: 32px !important +} + +.px-5 { + padding-right: 32px !important; + padding-left: 32px !important +} + +.py-5 { + padding-top: 32px !important; + padding-bottom: 32px !important +} + +.p-6 { + padding: 40px !important +} + +.pt-6 { + padding-top: 40px !important +} + +.pr-6 { + padding-right: 40px !important +} + +.pb-6 { + padding-bottom: 40px !important +} + +.pl-6 { + padding-left: 40px !important +} + +.px-6 { + padding-right: 40px !important; + padding-left: 40px !important +} + +.py-6 { + padding-top: 40px !important; + padding-bottom: 40px !important +} + +@media (min-width: 544px) { + .p-sm-0 { + padding: 0 !important + } + .pt-sm-0 { + padding-top: 0 !important + } + .pr-sm-0 { + padding-right: 0 !important + } + .pb-sm-0 { + padding-bottom: 0 !important + } + .pl-sm-0 { + padding-left: 0 !important + } + .px-sm-0 { + padding-right: 0 !important; + padding-left: 0 !important + } + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important + } + .p-sm-1 { + padding: 4px !important + } + .pt-sm-1 { + padding-top: 4px !important + } + .pr-sm-1 { + padding-right: 4px !important + } + .pb-sm-1 { + padding-bottom: 4px !important + } + .pl-sm-1 { + padding-left: 4px !important + } + .px-sm-1 { + padding-right: 4px !important; + padding-left: 4px !important + } + .py-sm-1 { + padding-top: 4px !important; + padding-bottom: 4px !important + } + .p-sm-2 { + padding: 8px !important + } + .pt-sm-2 { + padding-top: 8px !important + } + .pr-sm-2 { + padding-right: 8px !important + } + .pb-sm-2 { + padding-bottom: 8px !important + } + .pl-sm-2 { + padding-left: 8px !important + } + .px-sm-2 { + padding-right: 8px !important; + padding-left: 8px !important + } + .py-sm-2 { + padding-top: 8px !important; + padding-bottom: 8px !important + } + .p-sm-3 { + padding: 16px !important + } + .pt-sm-3 { + padding-top: 16px !important + } + .pr-sm-3 { + padding-right: 16px !important + } + .pb-sm-3 { + padding-bottom: 16px !important + } + .pl-sm-3 { + padding-left: 16px !important + } + .px-sm-3 { + padding-right: 16px !important; + padding-left: 16px !important + } + .py-sm-3 { + padding-top: 16px !important; + padding-bottom: 16px !important + } + .p-sm-4 { + padding: 24px !important + } + .pt-sm-4 { + padding-top: 24px !important + } + .pr-sm-4 { + padding-right: 24px !important + } + .pb-sm-4 { + padding-bottom: 24px !important + } + .pl-sm-4 { + padding-left: 24px !important + } + .px-sm-4 { + padding-right: 24px !important; + padding-left: 24px !important + } + .py-sm-4 { + padding-top: 24px !important; + padding-bottom: 24px !important + } + .p-sm-5 { + padding: 32px !important + } + .pt-sm-5 { + padding-top: 32px !important + } + .pr-sm-5 { + padding-right: 32px !important + } + .pb-sm-5 { + padding-bottom: 32px !important + } + .pl-sm-5 { + padding-left: 32px !important + } + .px-sm-5 { + padding-right: 32px !important; + padding-left: 32px !important + } + .py-sm-5 { + padding-top: 32px !important; + padding-bottom: 32px !important + } + .p-sm-6 { + padding: 40px !important + } + .pt-sm-6 { + padding-top: 40px !important + } + .pr-sm-6 { + padding-right: 40px !important + } + .pb-sm-6 { + padding-bottom: 40px !important + } + .pl-sm-6 { + padding-left: 40px !important + } + .px-sm-6 { + padding-right: 40px !important; + padding-left: 40px !important + } + .py-sm-6 { + padding-top: 40px !important; + padding-bottom: 40px !important + } +} + +@media (min-width: 768px) { + .p-md-0 { + padding: 0 !important + } + .pt-md-0 { + padding-top: 0 !important + } + .pr-md-0 { + padding-right: 0 !important + } + .pb-md-0 { + padding-bottom: 0 !important + } + .pl-md-0 { + padding-left: 0 !important + } + .px-md-0 { + padding-right: 0 !important; + padding-left: 0 !important + } + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important + } + .p-md-1 { + padding: 4px !important + } + .pt-md-1 { + padding-top: 4px !important + } + .pr-md-1 { + padding-right: 4px !important + } + .pb-md-1 { + padding-bottom: 4px !important + } + .pl-md-1 { + padding-left: 4px !important + } + .px-md-1 { + padding-right: 4px !important; + padding-left: 4px !important + } + .py-md-1 { + padding-top: 4px !important; + padding-bottom: 4px !important + } + .p-md-2 { + padding: 8px !important + } + .pt-md-2 { + padding-top: 8px !important + } + .pr-md-2 { + padding-right: 8px !important + } + .pb-md-2 { + padding-bottom: 8px !important + } + .pl-md-2 { + padding-left: 8px !important + } + .px-md-2 { + padding-right: 8px !important; + padding-left: 8px !important + } + .py-md-2 { + padding-top: 8px !important; + padding-bottom: 8px !important + } + .p-md-3 { + padding: 16px !important + } + .pt-md-3 { + padding-top: 16px !important + } + .pr-md-3 { + padding-right: 16px !important + } + .pb-md-3 { + padding-bottom: 16px !important + } + .pl-md-3 { + padding-left: 16px !important + } + .px-md-3 { + padding-right: 16px !important; + padding-left: 16px !important + } + .py-md-3 { + padding-top: 16px !important; + padding-bottom: 16px !important + } + .p-md-4 { + padding: 24px !important + } + .pt-md-4 { + padding-top: 24px !important + } + .pr-md-4 { + padding-right: 24px !important + } + .pb-md-4 { + padding-bottom: 24px !important + } + .pl-md-4 { + padding-left: 24px !important + } + .px-md-4 { + padding-right: 24px !important; + padding-left: 24px !important + } + .py-md-4 { + padding-top: 24px !important; + padding-bottom: 24px !important + } + .p-md-5 { + padding: 32px !important + } + .pt-md-5 { + padding-top: 32px !important + } + .pr-md-5 { + padding-right: 32px !important + } + .pb-md-5 { + padding-bottom: 32px !important + } + .pl-md-5 { + padding-left: 32px !important + } + .px-md-5 { + padding-right: 32px !important; + padding-left: 32px !important + } + .py-md-5 { + padding-top: 32px !important; + padding-bottom: 32px !important + } + .p-md-6 { + padding: 40px !important + } + .pt-md-6 { + padding-top: 40px !important + } + .pr-md-6 { + padding-right: 40px !important + } + .pb-md-6 { + padding-bottom: 40px !important + } + .pl-md-6 { + padding-left: 40px !important + } + .px-md-6 { + padding-right: 40px !important; + padding-left: 40px !important + } + .py-md-6 { + padding-top: 40px !important; + padding-bottom: 40px !important + } +} + +@media (min-width: 1012px) { + .p-lg-0 { + padding: 0 !important + } + .pt-lg-0 { + padding-top: 0 !important + } + .pr-lg-0 { + padding-right: 0 !important + } + .pb-lg-0 { + padding-bottom: 0 !important + } + .pl-lg-0 { + padding-left: 0 !important + } + .px-lg-0 { + padding-right: 0 !important; + padding-left: 0 !important + } + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important + } + .p-lg-1 { + padding: 4px !important + } + .pt-lg-1 { + padding-top: 4px !important + } + .pr-lg-1 { + padding-right: 4px !important + } + .pb-lg-1 { + padding-bottom: 4px !important + } + .pl-lg-1 { + padding-left: 4px !important + } + .px-lg-1 { + padding-right: 4px !important; + padding-left: 4px !important + } + .py-lg-1 { + padding-top: 4px !important; + padding-bottom: 4px !important + } + .p-lg-2 { + padding: 8px !important + } + .pt-lg-2 { + padding-top: 8px !important + } + .pr-lg-2 { + padding-right: 8px !important + } + .pb-lg-2 { + padding-bottom: 8px !important + } + .pl-lg-2 { + padding-left: 8px !important + } + .px-lg-2 { + padding-right: 8px !important; + padding-left: 8px !important + } + .py-lg-2 { + padding-top: 8px !important; + padding-bottom: 8px !important + } + .p-lg-3 { + padding: 16px !important + } + .pt-lg-3 { + padding-top: 16px !important + } + .pr-lg-3 { + padding-right: 16px !important + } + .pb-lg-3 { + padding-bottom: 16px !important + } + .pl-lg-3 { + padding-left: 16px !important + } + .px-lg-3 { + padding-right: 16px !important; + padding-left: 16px !important + } + .py-lg-3 { + padding-top: 16px !important; + padding-bottom: 16px !important + } + .p-lg-4 { + padding: 24px !important + } + .pt-lg-4 { + padding-top: 24px !important + } + .pr-lg-4 { + padding-right: 24px !important + } + .pb-lg-4 { + padding-bottom: 24px !important + } + .pl-lg-4 { + padding-left: 24px !important + } + .px-lg-4 { + padding-right: 24px !important; + padding-left: 24px !important + } + .py-lg-4 { + padding-top: 24px !important; + padding-bottom: 24px !important + } + .p-lg-5 { + padding: 32px !important + } + .pt-lg-5 { + padding-top: 32px !important + } + .pr-lg-5 { + padding-right: 32px !important + } + .pb-lg-5 { + padding-bottom: 32px !important + } + .pl-lg-5 { + padding-left: 32px !important + } + .px-lg-5 { + padding-right: 32px !important; + padding-left: 32px !important + } + .py-lg-5 { + padding-top: 32px !important; + padding-bottom: 32px !important + } + .p-lg-6 { + padding: 40px !important + } + .pt-lg-6 { + padding-top: 40px !important + } + .pr-lg-6 { + padding-right: 40px !important + } + .pb-lg-6 { + padding-bottom: 40px !important + } + .pl-lg-6 { + padding-left: 40px !important + } + .px-lg-6 { + padding-right: 40px !important; + padding-left: 40px !important + } + .py-lg-6 { + padding-top: 40px !important; + padding-bottom: 40px !important + } +} + +@media (min-width: 1280px) { + .p-xl-0 { + padding: 0 !important + } + .pt-xl-0 { + padding-top: 0 !important + } + .pr-xl-0 { + padding-right: 0 !important + } + .pb-xl-0 { + padding-bottom: 0 !important + } + .pl-xl-0 { + padding-left: 0 !important + } + .px-xl-0 { + padding-right: 0 !important; + padding-left: 0 !important + } + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important + } + .p-xl-1 { + padding: 4px !important + } + .pt-xl-1 { + padding-top: 4px !important + } + .pr-xl-1 { + padding-right: 4px !important + } + .pb-xl-1 { + padding-bottom: 4px !important + } + .pl-xl-1 { + padding-left: 4px !important + } + .px-xl-1 { + padding-right: 4px !important; + padding-left: 4px !important + } + .py-xl-1 { + padding-top: 4px !important; + padding-bottom: 4px !important + } + .p-xl-2 { + padding: 8px !important + } + .pt-xl-2 { + padding-top: 8px !important + } + .pr-xl-2 { + padding-right: 8px !important + } + .pb-xl-2 { + padding-bottom: 8px !important + } + .pl-xl-2 { + padding-left: 8px !important + } + .px-xl-2 { + padding-right: 8px !important; + padding-left: 8px !important + } + .py-xl-2 { + padding-top: 8px !important; + padding-bottom: 8px !important + } + .p-xl-3 { + padding: 16px !important + } + .pt-xl-3 { + padding-top: 16px !important + } + .pr-xl-3 { + padding-right: 16px !important + } + .pb-xl-3 { + padding-bottom: 16px !important + } + .pl-xl-3 { + padding-left: 16px !important + } + .px-xl-3 { + padding-right: 16px !important; + padding-left: 16px !important + } + .py-xl-3 { + padding-top: 16px !important; + padding-bottom: 16px !important + } + .p-xl-4 { + padding: 24px !important + } + .pt-xl-4 { + padding-top: 24px !important + } + .pr-xl-4 { + padding-right: 24px !important + } + .pb-xl-4 { + padding-bottom: 24px !important + } + .pl-xl-4 { + padding-left: 24px !important + } + .px-xl-4 { + padding-right: 24px !important; + padding-left: 24px !important + } + .py-xl-4 { + padding-top: 24px !important; + padding-bottom: 24px !important + } + .p-xl-5 { + padding: 32px !important + } + .pt-xl-5 { + padding-top: 32px !important + } + .pr-xl-5 { + padding-right: 32px !important + } + .pb-xl-5 { + padding-bottom: 32px !important + } + .pl-xl-5 { + padding-left: 32px !important + } + .px-xl-5 { + padding-right: 32px !important; + padding-left: 32px !important + } + .py-xl-5 { + padding-top: 32px !important; + padding-bottom: 32px !important + } + .p-xl-6 { + padding: 40px !important + } + .pt-xl-6 { + padding-top: 40px !important + } + .pr-xl-6 { + padding-right: 40px !important + } + .pb-xl-6 { + padding-bottom: 40px !important + } + .pl-xl-6 { + padding-left: 40px !important + } + .px-xl-6 { + padding-right: 40px !important; + padding-left: 40px !important + } + .py-xl-6 { + padding-top: 40px !important; + padding-bottom: 40px !important + } +} + +.p-responsive { + padding-right: 16px !important; + padding-left: 16px !important +} + +@media (min-width: 544px) { + .p-responsive { + padding-right: 40px !important; + padding-left: 40px !important + } +} + +@media (min-width: 1012px) { + .p-responsive { + padding-right: 16px !important; + padding-left: 16px !important + } +} + +.h1 { + font-size: 26px !important +} + +@media (min-width: 768px) { + .h1 { + font-size: 32px !important + } +} + +.h2 { + font-size: 22px !important +} + +@media (min-width: 768px) { + .h2 { + font-size: 24px !important + } +} + +.h3 { + font-size: 18px !important +} + +@media (min-width: 768px) { + .h3 { + font-size: 20px !important + } +} + +.h4 { + font-size: 16px !important +} + +.h5 { + font-size: 14px !important +} + +.h6 { + font-size: 12px !important +} + +.h1, .h2, .h3, .h4, .h5, .h6 { + font-weight: 600 !important +} + +.f1 { + font-size: 26px !important +} + +@media (min-width: 768px) { + .f1 { + font-size: 32px !important + } +} + +.f2 { + font-size: 22px !important +} + +@media (min-width: 768px) { + .f2 { + font-size: 24px !important + } +} + +.f3 { + font-size: 18px !important +} + +@media (min-width: 768px) { + .f3 { + font-size: 20px !important + } +} + +.f4 { + font-size: 16px !important +} + +@media (min-width: 768px) { + .f4 { + font-size: 16px !important + } +} + +.f5 { + font-size: 14px !important +} + +.f6 { + font-size: 12px !important +} + +.f00-light { + font-size: 40px !important; + font-weight: 300 !important +} + +@media (min-width: 768px) { + .f00-light { + font-size: 48px !important + } +} + +.f0-light { + font-size: 32px !important; + font-weight: 300 !important +} + +@media (min-width: 768px) { + .f0-light { + font-size: 40px !important + } +} + +.f1-light { + font-size: 26px !important; + font-weight: 300 !important +} + +@media (min-width: 768px) { + .f1-light { + font-size: 32px !important + } +} + +.f2-light { + font-size: 22px !important; + font-weight: 300 !important +} + +@media (min-width: 768px) { + .f2-light { + font-size: 24px !important + } +} + +.f3-light { + font-size: 18px !important; + font-weight: 300 !important +} + +@media (min-width: 768px) { + .f3-light { + font-size: 20px !important + } +} + +.text-small { + font-size: 12px !important +} + +.lead { + margin-bottom: 30px; + font-size: 20px; + font-weight: 300; + color: #586069 +} + +.lh-condensed-ultra { + line-height: 1 !important +} + +.lh-condensed { + line-height: 1.25 !important +} + +.lh-default { + line-height: 1.5 !important +} + +.lh-0 { + line-height: 0 !important +} + +@media (min-width: 544px) { + .lh-sm-condensed-ultra { + line-height: 1 !important + } + .lh-sm-condensed { + line-height: 1.25 !important + } + .lh-sm-default { + line-height: 1.5 !important + } + .lh-sm-0 { + line-height: 0 !important + } +} + +@media (min-width: 768px) { + .lh-md-condensed-ultra { + line-height: 1 !important + } + .lh-md-condensed { + line-height: 1.25 !important + } + .lh-md-default { + line-height: 1.5 !important + } + .lh-md-0 { + line-height: 0 !important + } +} + +@media (min-width: 1012px) { + .lh-lg-condensed-ultra { + line-height: 1 !important + } + .lh-lg-condensed { + line-height: 1.25 !important + } + .lh-lg-default { + line-height: 1.5 !important + } + .lh-lg-0 { + line-height: 0 !important + } +} + +@media (min-width: 1280px) { + .lh-xl-condensed-ultra { + line-height: 1 !important + } + .lh-xl-condensed { + line-height: 1.25 !important + } + .lh-xl-default { + line-height: 1.5 !important + } + .lh-xl-0 { + line-height: 0 !important + } +} + +.text-right { + text-align: right !important +} + +.text-left { + text-align: left !important +} + +.text-center { + text-align: center !important +} + +@media (min-width: 544px) { + .text-sm-right { + text-align: right !important + } + .text-sm-left { + text-align: left !important + } + .text-sm-center { + text-align: center !important + } +} + +@media (min-width: 768px) { + .text-md-right { + text-align: right !important + } + .text-md-left { + text-align: left !important + } + .text-md-center { + text-align: center !important + } +} + +@media (min-width: 1012px) { + .text-lg-right { + text-align: right !important + } + .text-lg-left { + text-align: left !important + } + .text-lg-center { + text-align: center !important + } +} + +@media (min-width: 1280px) { + .text-xl-right { + text-align: right !important + } + .text-xl-left { + text-align: left !important + } + .text-xl-center { + text-align: center !important + } +} + +.text-normal { + font-weight: 400 !important +} + +.text-bold { + font-weight: 600 !important +} + +.text-italic { + font-style: italic !important +} + +.text-uppercase { + text-transform: uppercase !important +} + +.text-underline { + text-decoration: underline !important +} + +.no-underline { + text-decoration: none !important +} + +.no-wrap { + white-space: nowrap !important +} + +.ws-normal { + white-space: normal !important +} + +.break-word { + word-break: break-word !important; + word-wrap: break-word !important; + overflow-wrap: break-word !important +} + +.wb-break-all { + word-break: break-all !important +} + +.text-emphasized { + font-weight: 600; + color: #24292e +} + +.list-style-none { + list-style: none !important +} + +.text-shadow-dark { + text-shadow: 0 1px 1px rgba(27, 31, 35, 0.25), 0 1px 25px rgba(27, 31, 35, 0.75) +} + +.text-shadow-light { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) +} + +.text-mono { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace !important +} + +.user-select-none { + -webkit-user-select: none !important; + -moz-user-select: none !important; + -ms-user-select: none !important; + user-select: none !important +} + +.d-block { + display: block !important +} + +.d-flex { + display: flex !important +} + +.d-inline { + display: inline !important +} + +.d-inline-block { + display: inline-block !important +} + +.d-inline-flex { + display: inline-flex !important +} + +.d-none { + display: none !important +} + +.d-table { + display: table !important +} + +.d-table-cell { + display: table-cell !important +} + +@media (min-width: 544px) { + .d-sm-block { + display: block !important + } + .d-sm-flex { + display: flex !important + } + .d-sm-inline { + display: inline !important + } + .d-sm-inline-block { + display: inline-block !important + } + .d-sm-inline-flex { + display: inline-flex !important + } + .d-sm-none { + display: none !important + } + .d-sm-table { + display: table !important + } + .d-sm-table-cell { + display: table-cell !important + } +} + +@media (min-width: 768px) { + .d-md-block { + display: block !important + } + .d-md-flex { + display: flex !important + } + .d-md-inline { + display: inline !important + } + .d-md-inline-block { + display: inline-block !important + } + .d-md-inline-flex { + display: inline-flex !important + } + .d-md-none { + display: none !important + } + .d-md-table { + display: table !important + } + .d-md-table-cell { + display: table-cell !important + } +} + +@media (min-width: 1012px) { + .d-lg-block { + display: block !important + } + .d-lg-flex { + display: flex !important + } + .d-lg-inline { + display: inline !important + } + .d-lg-inline-block { + display: inline-block !important + } + .d-lg-inline-flex { + display: inline-flex !important + } + .d-lg-none { + display: none !important + } + .d-lg-table { + display: table !important + } + .d-lg-table-cell { + display: table-cell !important + } +} + +@media (min-width: 1280px) { + .d-xl-block { + display: block !important + } + .d-xl-flex { + display: flex !important + } + .d-xl-inline { + display: inline !important + } + .d-xl-inline-block { + display: inline-block !important + } + .d-xl-inline-flex { + display: inline-flex !important + } + .d-xl-none { + display: none !important + } + .d-xl-table { + display: table !important + } + .d-xl-table-cell { + display: table-cell !important + } +} + +.v-hidden { + visibility: hidden !important +} + +.v-visible { + visibility: visible !important +} + +@media (max-width: 543px) { + .hide-sm { + display: none !important + } +} + +@media (min-width: 544px) and (max-width: 767px) { + .hide-md { + display: none !important + } +} + +@media (min-width: 768px) and (max-width: 1011px) { + .hide-lg { + display: none !important + } +} + +@media (min-width: 1012px) { + .hide-xl { + display: none !important + } +} + +.table-fixed { + table-layout: fixed !important +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + word-wrap: normal; + border: 0 +} + +.show-on-focus { + position: absolute; + width: 1px; + height: 1px; + margin: 0; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px) +} + +.show-on-focus:focus { + z-index: 20; + width: auto; + height: auto; + clip: auto +} + +/*! + * @primer/css/product + * http://primer.style/css + * + * Released under MIT license. Copyright (c) 2019 GitHub Inc. + */ + +.flash { + position: relative; + padding: 20px 16px; + color: #24292e; + border-style: solid; + border-width: 1px; + border-radius: 6px +} + +.flash p:last-child { + margin-bottom: 0 +} + +.flash .octicon { + margin-right: 12px +} + +.flash-messages { + margin-bottom: 24px +} + +.flash-close { + float: right; + padding: 16px; + margin: -16px; + text-align: center; + cursor: pointer; + background: none; + border: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.flash-close:hover { + opacity: 0.7 +} + +.flash-close:active { + opacity: 0.5 +} + +.flash-close .octicon { + margin-right: 0 +} + +.flash-action { + float: right; + margin-top: -3px; + margin-left: 24px; + background-clip: padding-box +} + +.flash { + background-color: #dbedff; + border-color: rgba(4, 66, 137, 0.2) +} + +.flash .octicon { + color: rgba(4, 66, 137, 0.6) +} + +.flash-warn { + background-color: #fffbdd; + border-color: rgba(176, 136, 0, 0.2) +} + +.flash-warn .octicon { + color: #b08800 +} + +.flash-error { + background-color: #ffe3e6; + border-color: rgba(158, 28, 35, 0.2) +} + +.flash-error .octicon { + color: rgba(158, 28, 35, 0.6) +} + +.flash-success { + background-color: #dcffe4; + border-color: rgba(23, 111, 44, 0.2) +} + +.flash-success .octicon { + color: rgba(23, 111, 44, 0.8) +} + +.flash-full { + margin-top: -1px; + border-width: 1px 0; + border-radius: 0 +} + +.flash-banner { + position: fixed; + top: 0; + z-index: 90; + width: 100%; + border-top: 0; + border-right: 0; + border-left: 0; + border-radius: 0 +} + +.warning { + padding: .5em; + margin-bottom: 0.8em; + font-weight: 600; + background-color: #fffbdd +} + +.autocomplete-results { + position: absolute; + z-index: 99; + width: 100%; + max-height: 20em; + overflow-y: auto; + font-size: 13px; + list-style: none; + background: #fff; + border: 1px #e1e4e8 solid; + border-radius: 6px; + box-shadow: 0 3px 6px rgba(149, 157, 165, 0.15) +} + +.autocomplete-item { + display: block; + width: 100%; + padding: 4px 8px; + overflow: hidden; + font-weight: 600; + color: #24292e; + text-align: left; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + cursor: pointer; + background-color: #fff; + border: 0 +} + +.autocomplete-item:hover, .autocomplete-item.selected, .autocomplete-item[aria-selected=true], .autocomplete-item.navigation-focus { + color: #fff; + text-decoration: none; + background-color: #0366d6 +} + +.autocomplete-item:hover *, .autocomplete-item.selected *, .autocomplete-item[aria-selected=true] *, .autocomplete-item.navigation-focus * { + color: inherit !important +} + +.suggester { + position: relative; + top: 0; + left: 0; + min-width: 180px; + padding: 0; + margin: 0; + margin-top: 24px; + list-style: none; + cursor: pointer; + background: #fff; + border: 1px #e1e4e8 solid; + border-radius: 6px; + box-shadow: 0 3px 6px rgba(149, 157, 165, 0.15) +} + +.suggester li { + display: block; + padding: 4px 8px; + font-weight: 500; + border-bottom: 1px solid #eaecef +} + +.suggester li small { + font-weight: 400; + color: #586069 +} + +.suggester li:last-child { + border-bottom: 0; + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px +} + +.suggester li:first-child { + border-top-left-radius: 6px; + border-top-right-radius: 6px +} + +.suggester li:hover, .suggester li[aria-selected="true"], .suggester li.navigation-focus { + color: #fff; + text-decoration: none; + background: #0366d6 +} + +.suggester li:hover small, .suggester li[aria-selected="true"] small, .suggester li.navigation-focus small { + color: #fff +} + +.suggester-container { + position: absolute; + top: 0; + left: 0; + z-index: 30 +} + +@media (max-width: 544px) { + .page-responsive .suggester-container { + right: 8px !important; + left: 8px !important + } + .page-responsive .suggester li { + padding: 8px 16px + } +} + +.avatar { + display: inline-block; + overflow: hidden; + line-height: 1; + vertical-align: middle; + border-radius: 6px +} + +.avatar-link { + float: left; + line-height: 1 +} + +.avatar-group-item { + display: inline-block; + margin-bottom: 3px +} + +.avatar-1, .avatar-2, .avatar-small { + border-radius: 4px +} + +.avatar-1 { + width: 16px; + height: 16px +} + +.avatar-2 { + width: 20px; + height: 20px +} + +.avatar-3 { + width: 24px; + height: 24px +} + +.avatar-4 { + width: 28px; + height: 28px +} + +.avatar-5 { + width: 32px; + height: 32px +} + +.avatar-6 { + width: 40px; + height: 40px +} + +.avatar-7 { + width: 48px; + height: 48px +} + +.avatar-8 { + width: 64px; + height: 64px +} + +.avatar-parent-child { + position: relative +} + +.avatar-child { + position: absolute; + right: -15%; + bottom: -9%; + background-color: #fff; + border-radius: 4px; + box-shadow: -2px -2px 0 rgba(255, 255, 255, 0.8) +} + +.AvatarStack { + position: relative; + min-width: 26px; + height: 20px +} + +.AvatarStack .AvatarStack-body { + position: absolute +} + +.AvatarStack.AvatarStack--two { + min-width: 36px +} + +.AvatarStack.AvatarStack--three-plus { + min-width: 46px +} + +.AvatarStack-body { + display: flex; + background: #fff +} + +.AvatarStack-body .avatar { + position: relative; + z-index: 2; + display: flex; + width: 20px; + height: 20px; + box-sizing: content-box; + margin-right: -11px; + background-color: #fff; + border-right: 1px solid #fff; + border-radius: 4px; + transition: margin 0.1s ease-in-out +} + +.AvatarStack-body .avatar:first-child { + z-index: 3 +} + +.AvatarStack-body .avatar:last-child { + z-index: 1; + border-right: 0 +} + +.AvatarStack-body .avatar img { + border-radius: 4px +} + +.AvatarStack-body .avatar:nth-child(n+4) { + display: none; + opacity: 0 +} + +.AvatarStack-body:hover .avatar { + margin-right: 3px +} + +.AvatarStack-body:hover .avatar:nth-child(n+4) { + display: flex; + opacity: 1 +} + +.AvatarStack-body:hover .avatar-more { + display: none !important +} + +.avatar.avatar-more { + z-index: 1; + margin-right: 0; + background: #f6f8fa +} + +.avatar.avatar-more::before, .avatar.avatar-more::after { + position: absolute; + display: block; + height: 20px; + content: ""; + border-radius: 2px; + outline: 1px solid #fff +} + +.avatar.avatar-more::before { + width: 17px; + background: #e1e4e8 +} + +.avatar.avatar-more::after { + width: 14px; + background: #d1d5da +} + +.AvatarStack--right .AvatarStack-body { + right: 0; + flex-direction: row-reverse +} + +.AvatarStack--right .AvatarStack-body:hover .avatar { + margin-right: 0; + margin-left: 3px +} + +.AvatarStack--right .avatar.avatar-more { + background: #d1d5da +} + +.AvatarStack--right .avatar.avatar-more::before { + width: 5px +} + +.AvatarStack--right .avatar.avatar-more::after { + width: 2px; + background: #f6f8fa +} + +.AvatarStack--right .avatar { + margin-right: 0; + margin-left: -11px; + border-right: 0; + border-left: 1px solid #fff +} + +.CircleBadge { + display: flex; + align-items: center; + justify-content: center; + background-color: #fff; + border-radius: 50%; + box-shadow: 0 3px 6px rgba(149, 157, 165, 0.15) +} + +.CircleBadge-icon { + max-width: 60% !important; + height: auto !important; + max-height: 55% !important +} + +.CircleBadge--small { + width: 56px; + height: 56px +} + +.CircleBadge--medium { + width: 96px; + height: 96px +} + +.CircleBadge--large { + width: 128px; + height: 128px +} + +.DashedConnection { + position: relative +} + +.DashedConnection::before { + position: absolute; + top: 50%; + left: 0; + width: 100%; + content: ""; + border-bottom: 2px dashed #e1e4e8 +} + +.DashedConnection .CircleBadge { + position: relative +} + +.blankslate { + position: relative; + padding: 32px; + text-align: center +} + +.blankslate code { + padding: 2px 5px 3px; + font-size: 14px; + background: #fff; + border: 1px solid #eaecef; + border-radius: 6px +} + +.blankslate img { + width: 56px; + height: 56px +} + +.blankslate-icon { + margin-right: 4px; + margin-bottom: 8px; + margin-left: 4px; + color: #a3aab1 +} + +.blankslate-capped { + border-radius: 0 0 6px 6px +} + +.blankslate-spacious { + padding: 80px 40px +} + +.blankslate-narrow { + max-width: 485px; + margin: 0 auto +} + +.blankslate-large img { + width: 80px; + height: 80px +} + +.blankslate-large h3 { + margin: 16px 0; + font-size: 24px +} + +.blankslate-large p { + font-size: 16px +} + +.blankslate-clean-background { + border: 0 +} + +.branch-name { + display: inline-block; + padding: 2px 6px; + font: 12px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + color: rgba(27, 31, 35, 0.6); + background-color: #eaf5ff; + border-radius: 6px +} + +.branch-name .octicon { + margin: 1px -2px 0 0; + color: #a8bbd0 +} + +a.branch-name { + color: #0366d6 +} + +.dropdown { + position: relative +} + +.dropdown-caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: middle; + content: ""; + border-style: solid; + border-width: 4px 4px 0; + border-right-color: transparent; + border-bottom-color: transparent; + border-left-color: transparent +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 100; + width: 160px; + padding-top: 4px; + padding-bottom: 4px; + margin-top: 2px; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px #e1e4e8 solid; + border-radius: 6px; + box-shadow: 0 8px 24px rgba(149, 157, 165, 0.2) +} + +.dropdown-menu::before, .dropdown-menu::after { + position: absolute; + display: inline-block; + content: "" +} + +.dropdown-menu::before { + border: 8px solid transparent; + border-bottom-color: rgba(27, 31, 35, 0.15) +} + +.dropdown-menu::after { + border: 7px solid transparent; + border-bottom-color: #fff +} + +.dropdown-menu>ul { + list-style: none +} + +.dropdown-menu-no-overflow { + width: auto +} + +.dropdown-menu-no-overflow .dropdown-item { + padding: 4px 16px; + overflow: visible; + text-overflow: inherit +} + +.dropdown-item { + display: block; + padding: 4px 8px 4px 16px; + overflow: hidden; + color: #24292e; + text-overflow: ellipsis; + white-space: nowrap +} + +.dropdown-item:focus, .dropdown-item:hover { + color: #fff; + text-decoration: none; + background-color: #0366d6; + outline: none +} + +.dropdown-item:focus>.octicon, .dropdown-item:hover>.octicon { + color: inherit; + opacity: 1 +} + +.dropdown-item.btn-link { + width: 100%; + text-align: left +} + +.dropdown-signout { + width: 100%; + text-align: left; + background: none; + border: 0 +} + +.dropdown-divider { + display: block; + height: 0; + margin: 8px 0; + border-top: 1px #e1e4e8 solid +} + +.dropdown-header { + padding: 4px 16px; + font-size: 12px; + color: #586069 +} + +.dropdown-item[aria-checked="false"] .octicon-check { + display: none +} + +.dropdown-menu-w { + top: 0; + right: 100%; + left: auto; + width: auto; + margin-top: 0; + margin-right: 8px +} + +.dropdown-menu-w::before { + top: 10px; + right: -16px; + left: auto; + border-color: transparent; + border-left-color: rgba(27, 31, 35, 0.15) +} + +.dropdown-menu-w::after { + top: 11px; + right: -14px; + left: auto; + border-color: transparent; + border-left-color: #fff +} + +.dropdown-menu-e { + top: 0; + left: 100%; + width: auto; + margin-top: 0; + margin-left: 8px +} + +.dropdown-menu-e::before { + top: 8px; + left: -16px; + border-color: transparent; + border-right-color: rgba(27, 31, 35, 0.15) +} + +.dropdown-menu-e::after { + top: 11px; + left: -14px; + border-color: transparent; + border-right-color: #fff +} + +.dropdown-menu-ne { + top: auto; + bottom: 100%; + left: 0; + margin-bottom: 3px +} + +.dropdown-menu-ne::before, .dropdown-menu-ne::after { + top: auto; + right: auto +} + +.dropdown-menu-ne::before { + bottom: -8px; + left: 9px; + border-top: 8px solid rgba(27, 31, 35, 0.15); + border-right: 8px solid transparent; + border-bottom: 0; + border-left: 8px solid transparent +} + +.dropdown-menu-ne::after { + bottom: -7px; + left: 10px; + border-top: 7px solid #fff; + border-right: 7px solid transparent; + border-bottom: 0; + border-left: 7px solid transparent +} + +.dropdown-menu-s { + right: 50%; + left: auto; + transform: translateX(50%) +} + +.dropdown-menu-s::before { + top: -16px; + right: 50%; + transform: translateX(50%) +} + +.dropdown-menu-s::after { + top: -14px; + right: 50%; + transform: translateX(50%) +} + +.dropdown-menu-sw { + right: 0; + left: auto +} + +.dropdown-menu-sw::before { + top: -16px; + right: 9px; + left: auto +} + +.dropdown-menu-sw::after { + top: -14px; + right: 10px; + left: auto +} + +.dropdown-menu-se::before { + top: -16px; + left: 9px +} + +.dropdown-menu-se::after { + top: -14px; + left: 10px +} + +.dropdown-menu-dark { + color: #fff; + background: #2f363d; + border-color: #444d56; + box-shadow: 0 8px 24px rgba(149, 157, 165, 0.2) +} + +.dropdown-menu-dark::before { + border-bottom-color: #444d56 +} + +.dropdown-menu-dark::after { + border-bottom-color: #2f363d +} + +.dropdown-menu-dark .dropdown-header { + color: #d1d5da +} + +.dropdown-menu-dark .dropdown-divider { + border-top-color: #444d56 +} + +.dropdown-menu-dark .dropdown-item { + color: inherit +} + +.dropdown-menu-dark.dropdown-menu-w::before { + border-color: transparent transparent transparent #444d56 +} + +.dropdown-menu-dark.dropdown-menu-w::after { + border-color: transparent transparent transparent #2f363d +} + +.dropdown-menu-dark.dropdown-menu-e::before { + border-color: transparent #444d56 transparent transparent +} + +.dropdown-menu-dark.dropdown-menu-e::after { + border-color: transparent #2f363d transparent transparent +} + +.dropdown-menu-dark.dropdown-menu-ne::before { + border-color: #444d56 transparent transparent transparent +} + +.dropdown-menu-dark.dropdown-menu-ne::after { + border-color: #2f363d transparent transparent transparent +} + +.Header { + z-index: 32; + display: flex; + padding: 16px; + font-size: 14px; + line-height: 1.5; + color: rgba(255, 255, 255, 0.7); + background-color: #24292e; + align-items: center; + flex-wrap: nowrap +} + +.Header-item { + display: flex; + margin-right: 16px; + align-self: stretch; + align-items: center; + flex-wrap: nowrap +} + +.Header-item--full { + flex: auto +} + +.Header-link { + font-weight: 600; + color: #fff; + white-space: nowrap +} + +.Header-link:hover, .Header-link:focus { + color: rgba(255, 255, 255, 0.7); + text-decoration: none +} + +.IssueLabel { + display: inline-block; + padding: 0 7px; + font-size: 12px; + font-weight: 500; + line-height: 18px; + border: 1px solid transparent; + border-radius: 2em +} + +.IssueLabel .g-emoji { + position: relative; + top: -0.05em; + display: inline-block; + font-size: 1em; + line-height: 1 +} + +.IssueLabel:hover { + text-decoration: none +} + +.IssueLabel--big { + padding-right: 10px; + padding-left: 10px; + line-height: 22px +} + +.labels { + position: relative +} + +.label, .Label { + display: inline-block; + padding: 0 7px; + font-size: 12px; + font-weight: 500; + line-height: 18px; + border: 1px solid transparent; + border-radius: 2em; + background-color: transparent !important; + border-color: #e1e4e8 +} + +.label:hover, .Label:hover { + text-decoration: none +} + +.Label--large { + padding-right: 10px; + padding-left: 10px; + line-height: 22px +} + +.Label--inline { + display: inline; + padding: 0.1667em 0.5em; + font-size: 0.9em +} + +.Label--outline, .Label--gray { + color: #586069; + border-color: #e1e4e8 +} + +.Label--gray-darker { + color: #24292e; + border-color: #6a737d +} + +.Label--yellow { + color: #735c0f; + border-color: #b08800 +} + +.Label--orange { + color: #c24e00; + border-color: #f66a0a +} + +.Label--red { + color: #cb2431; + border-color: #cb2431 +} + +.Label--outline-green, .Label--green { + color: #22863a; + border-color: #28a745 +} + +.Label--blue { + color: #0366d6; + border-color: #0366d6 +} + +.Label--purple { + color: #6f42c1; + border-color: #8a63d2 +} + +.Label--pink { + color: #d03592; + border-color: #ec6cb9 +} + +.state, .State { + display: inline-block; + padding: 5px 12px; + font-size: 14px; + font-weight: 500; + line-height: 20px; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #6a737d; + border: 1px solid transparent; + border-radius: 2em +} + +.State--green { + background-color: #28a745 +} + +.State--red { + background-color: #d73a49 +} + +.State--purple { + background-color: #6f42c1 +} + +.State--small { + padding: 0 10px; + font-size: 12px; + line-height: 24px +} + +.State--small .octicon { + width: 1em +} + +.Counter { + display: inline-block; + min-width: 20px; + padding: 0 6px; + font-size: 12px; + font-weight: 500; + line-height: 18px; + color: #24292e; + text-align: center; + background-color: rgba(209, 213, 218, 0.5); + border: 1px solid transparent; + border-radius: 2em +} + +.Counter:empty { + display: none +} + +.Counter .octicon { + vertical-align: text-top; + opacity: 0.8 +} + +.Counter--gray-light { + color: #6a737d +} + +.Counter--gray { + color: #fff; + background-color: #6a737d +} + +.diffstat { + font-size: 12px; + font-weight: 600; + color: #586069; + white-space: nowrap; + cursor: default +} + +.diffstat-block-deleted, .diffstat-block-added, .diffstat-block-neutral { + display: inline-block; + width: 8px; + height: 8px; + margin-left: 1px; + outline-offset: -1px +} + +.diffstat-block-deleted { + background-color: #cb2431; + outline: 1px dashed transparent +} + +.diffstat-block-added { + background-color: #2cbe4e; + outline: 1px solid transparent +} + +.diffstat-block-neutral { + background-color: #d1d5da; + outline: 1px dotted transparent +} + +.AnimatedEllipsis { + display: inline-block; + overflow: hidden; + vertical-align: bottom +} + +.AnimatedEllipsis::after { + display: inline-block; + content: "..."; + animation: AnimatedEllipsis-keyframes 1.2s steps(4, jump-none) infinite +} + +@keyframes AnimatedEllipsis-keyframes { + 0% { + transform: translateX(-100%) + } +} + +.markdown-body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word +} + +.markdown-body kbd { + display: inline-block; + padding: 3px 5px; + font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + line-height: 10px; + color: #444d56; + vertical-align: middle; + background-color: #fafbfc; + border: solid 1px #d1d5da; + border-bottom-color: #d1d5da; + border-radius: 6px; + box-shadow: inset 0 -1px 0 #d1d5da +} + +.markdown-body::before { + display: table; + content: "" +} + +.markdown-body::after { + display: table; + clear: both; + content: "" +} + +.markdown-body>*:first-child { + margin-top: 0 !important +} + +.markdown-body>*:last-child { + margin-bottom: 0 !important +} + +.markdown-body a:not([href]) { + color: inherit; + text-decoration: none +} + +.markdown-body .absent { + color: #cb2431 +} + +.markdown-body .anchor { + float: left; + padding-right: 4px; + margin-left: -20px; + line-height: 1 +} + +.markdown-body .anchor:focus { + outline: none +} + +.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre, .markdown-body details { + margin-top: 0; + margin-bottom: 16px +} + +.markdown-body hr { + height: .25em; + padding: 0; + margin: 24px 0; + background-color: #e1e4e8; + border: 0 +} + +.markdown-body blockquote { + padding: 0 1em; + color: #6a737d; + border-left: 0.25em solid #dfe2e5 +} + +.markdown-body blockquote>:first-child { + margin-top: 0 +} + +.markdown-body blockquote>:last-child { + margin-bottom: 0 +} + +.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { + margin-top: 24px; + margin-bottom: 16px; + font-weight: 600; + line-height: 1.25 +} + +.markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link { + color: #1b1f23; + vertical-align: middle; + visibility: hidden +} + +.markdown-body h1:hover .anchor, .markdown-body h2:hover .anchor, .markdown-body h3:hover .anchor, .markdown-body h4:hover .anchor, .markdown-body h5:hover .anchor, .markdown-body h6:hover .anchor { + text-decoration: none +} + +.markdown-body h1:hover .anchor .octicon-link, .markdown-body h2:hover .anchor .octicon-link, .markdown-body h3:hover .anchor .octicon-link, .markdown-body h4:hover .anchor .octicon-link, .markdown-body h5:hover .anchor .octicon-link, .markdown-body h6:hover .anchor .octicon-link { + visibility: visible +} + +.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code { + font-size: inherit +} + +.markdown-body h1 { + padding-bottom: 0.3em; + font-size: 2em; + border-bottom: 1px solid #eaecef +} + +.markdown-body h2 { + padding-bottom: 0.3em; + font-size: 1.5em; + border-bottom: 1px solid #eaecef +} + +.markdown-body h3 { + font-size: 1.25em +} + +.markdown-body h4 { + font-size: 1em +} + +.markdown-body h5 { + font-size: 0.875em +} + +.markdown-body h6 { + font-size: 0.85em; + color: #6a737d +} + +.markdown-body ul, .markdown-body ol { + padding-left: 2em +} + +.markdown-body ul.no-list, .markdown-body ol.no-list { + padding: 0; + list-style-type: none +} + +.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul { + margin-top: 0; + margin-bottom: 0 +} + +.markdown-body li { + word-wrap: break-all +} + +.markdown-body li>p { + margin-top: 16px +} + +.markdown-body li+li { + margin-top: .25em +} + +.markdown-body dl { + padding: 0 +} + +.markdown-body dl dt { + padding: 0; + margin-top: 16px; + font-size: 1em; + font-style: italic; + font-weight: 600 +} + +.markdown-body dl dd { + padding: 0 16px; + margin-bottom: 16px +} + +.markdown-body table { + display: block; + width: 100%; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + max-width: 100%; + overflow: auto +} + +.markdown-body table th { + font-weight: 600 +} + +.markdown-body table th, .markdown-body table td { + padding: 6px 13px; + border: 1px solid #dfe2e5 +} + +.markdown-body table tr { + background-color: #fff; + border-top: 1px solid #c6cbd1 +} + +.markdown-body table tr:nth-child(2n) { + background-color: #f6f8fa +} + +.markdown-body table img { + background-color: transparent +} + +.markdown-body img { + max-width: 100%; + box-sizing: content-box; + background-color: #fff +} + +.markdown-body img[align=right] { + padding-left: 20px +} + +.markdown-body img[align=left] { + padding-right: 20px +} + +.markdown-body .emoji { + max-width: none; + vertical-align: text-top; + background-color: transparent +} + +.markdown-body span.frame { + display: block; + overflow: hidden +} + +.markdown-body span.frame>span { + display: block; + float: left; + width: auto; + padding: 7px; + margin: 13px 0 0; + overflow: hidden; + border: 1px solid #dfe2e5 +} + +.markdown-body span.frame span img { + display: block; + float: left +} + +.markdown-body span.frame span span { + display: block; + padding: 5px 0 0; + clear: both; + color: #24292e +} + +.markdown-body span.align-center { + display: block; + overflow: hidden; + clear: both +} + +.markdown-body span.align-center>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: center +} + +.markdown-body span.align-center span img { + margin: 0 auto; + text-align: center +} + +.markdown-body span.align-right { + display: block; + overflow: hidden; + clear: both +} + +.markdown-body span.align-right>span { + display: block; + margin: 13px 0 0; + overflow: hidden; + text-align: right +} + +.markdown-body span.align-right span img { + margin: 0; + text-align: right +} + +.markdown-body span.float-left { + display: block; + float: left; + margin-right: 13px; + overflow: hidden +} + +.markdown-body span.float-left span { + margin: 13px 0 0 +} + +.markdown-body span.float-right { + display: block; + float: right; + margin-left: 13px; + overflow: hidden +} + +.markdown-body span.float-right>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: right +} + +.markdown-body code, .markdown-body tt { + padding: 0.2em 0.4em; + margin: 0; + font-size: 85%; + background-color: rgba(27, 31, 35, 0.05); + border-radius: 6px +} + +.markdown-body code br, .markdown-body tt br { + display: none +} + +.markdown-body del code { + text-decoration: inherit +} + +.markdown-body pre { + word-wrap: normal +} + +.markdown-body pre>code { + padding: 0; + margin: 0; + font-size: 100%; + word-break: normal; + white-space: pre; + background: transparent; + border: 0 +} + +.markdown-body .highlight { + margin-bottom: 16px +} + +.markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal +} + +.markdown-body .highlight pre, .markdown-body pre { + padding: 16px; + overflow: auto; + font-size: 85%; + line-height: 1.45; + background-color: #f6f8fa; + border-radius: 6px +} + +.markdown-body pre code, .markdown-body pre tt { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0 +} + +.markdown-body .csv-data td, .markdown-body .csv-data th { + padding: 5px; + overflow: hidden; + font-size: 12px; + line-height: 1; + text-align: left; + white-space: nowrap +} + +.markdown-body .csv-data .blob-num { + padding: 10px 8px 9px; + text-align: right; + background: #fff; + border: 0 +} + +.markdown-body .csv-data tr { + border-top: 0 +} + +.markdown-body .csv-data th { + font-weight: 600; + background: #f6f8fa; + border-top: 0 +} + +.Popover { + position: absolute; + z-index: 100 +} + +.Popover-message { + position: relative; + width: 232px; + margin-right: auto; + margin-left: auto +} + +.Popover-message::before, .Popover-message::after { + position: absolute; + left: 50%; + display: inline-block; + content: "" +} + +.Popover-message::before { + top: -16px; + margin-left: -9px; + border: 8px solid transparent; + border-bottom-color: rgba(27, 31, 35, 0.15) +} + +.Popover-message::after { + top: -14px; + margin-left: -8px; + border: 7px solid transparent; + border-bottom-color: #fff +} + +.Popover-message--bottom::before, .Popover-message--bottom::after, .Popover-message--bottom-right::before, .Popover-message--bottom-right::after, .Popover-message--bottom-left::before, .Popover-message--bottom-left::after { + top: auto; + border-bottom-color: transparent +} + +.Popover-message--bottom::before, .Popover-message--bottom-right::before, .Popover-message--bottom-left::before { + bottom: -16px; + border-top-color: rgba(27, 31, 35, 0.15) +} + +.Popover-message--bottom::after, .Popover-message--bottom-right::after, .Popover-message--bottom-left::after { + bottom: -14px; + border-top-color: #fff +} + +.Popover-message--top-right, .Popover-message--bottom-right { + right: -9px; + margin-right: 0 +} + +.Popover-message--top-right::before, .Popover-message--top-right::after, .Popover-message--bottom-right::before, .Popover-message--bottom-right::after { + left: auto; + margin-left: 0 +} + +.Popover-message--top-right::before, .Popover-message--bottom-right::before { + right: 20px +} + +.Popover-message--top-right::after, .Popover-message--bottom-right::after { + right: 21px +} + +.Popover-message--top-left, .Popover-message--bottom-left { + left: -9px; + margin-left: 0 +} + +.Popover-message--top-left::before, .Popover-message--top-left::after, .Popover-message--bottom-left::before, .Popover-message--bottom-left::after { + left: 24px; + margin-left: 0 +} + +.Popover-message--top-left::after, .Popover-message--bottom-left::after { + left: 25px +} + +.Popover-message--right::before, .Popover-message--right::after, .Popover-message--right-top::before, .Popover-message--right-top::after, .Popover-message--right-bottom::before, .Popover-message--right-bottom::after, .Popover-message--left::before, .Popover-message--left::after, .Popover-message--left-top::before, .Popover-message--left-top::after, .Popover-message--left-bottom::before, .Popover-message--left-bottom::after { + top: 50%; + left: auto; + margin-left: 0; + border-bottom-color: transparent +} + +.Popover-message--right::before, .Popover-message--right-top::before, .Popover-message--right-bottom::before, .Popover-message--left::before, .Popover-message--left-top::before, .Popover-message--left-bottom::before { + margin-top: -9px +} + +.Popover-message--right::after, .Popover-message--right-top::after, .Popover-message--right-bottom::after, .Popover-message--left::after, .Popover-message--left-top::after, .Popover-message--left-bottom::after { + margin-top: -8px +} + +.Popover-message--right::before, .Popover-message--right-top::before, .Popover-message--right-bottom::before { + right: -16px; + border-left-color: rgba(27, 31, 35, 0.15) +} + +.Popover-message--right::after, .Popover-message--right-top::after, .Popover-message--right-bottom::after { + right: -14px; + border-left-color: #fff +} + +.Popover-message--left::before, .Popover-message--left-top::before, .Popover-message--left-bottom::before { + left: -16px; + border-right-color: rgba(27, 31, 35, 0.15) +} + +.Popover-message--left::after, .Popover-message--left-top::after, .Popover-message--left-bottom::after { + left: -14px; + border-right-color: #fff +} + +.Popover-message--right-top::before, .Popover-message--right-top::after, .Popover-message--left-top::before, .Popover-message--left-top::after { + top: 24px +} + +.Popover-message--right-bottom::before, .Popover-message--right-bottom::after, .Popover-message--left-bottom::before, .Popover-message--left-bottom::after { + top: auto +} + +.Popover-message--right-bottom::before, .Popover-message--left-bottom::before { + bottom: 16px +} + +.Popover-message--right-bottom::after, .Popover-message--left-bottom::after { + bottom: 17px +} + +@media (min-width: 544px) { + .Popover-message--large { + min-width: 320px + } +} + +.Progress { + display: flex; + height: 8px; + overflow: hidden; + background-color: #e1e4e8; + border-radius: 6px; + outline: 1px solid transparent +} + +.Progress--large { + height: 10px +} + +.Progress--small { + height: 5px +} + +.Progress-item { + outline: 2px solid transparent +} + +.Progress-item+.Progress-item { + margin-left: 2px +} + +.SelectMenu { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 99; + display: flex; + padding: 16px; + pointer-events: none; + flex-direction: column +} + +@media (min-width: 544px) { + .SelectMenu { + position: absolute; + top: auto; + right: auto; + bottom: auto; + left: auto; + padding: 0 + } +} + +.SelectMenu::before { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + pointer-events: none; + content: ""; + background-color: rgba(27, 31, 35, 0.5) +} + +@media (min-width: 544px) { + .SelectMenu::before { + display: none + } +} + +.SelectMenu-modal { + position: relative; + z-index: 99; + display: flex; + max-height: 66%; + margin: auto 0; + overflow: hidden; + pointer-events: auto; + flex-direction: column; + background-color: #fff; + border-radius: 12px; + box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); + animation: SelectMenu-modal-animation 0.12s cubic-bezier(0, 0.1, 0.1, 1) backwards +} + +@keyframes SelectMenu-modal-animation { + 0% { + opacity: 0; + transform: scale(0.9) + } +} + +@keyframes SelectMenu-modal-animation--sm { + 0% { + opacity: 0; + transform: translateY(-16px) + } +} + +@media (min-width: 544px) { + .SelectMenu-modal { + width: 300px; + height: auto; + max-height: 480px; + margin: 8px 0 16px 0; + font-size: 12px; + border: 1px #e1e4e8 solid; + border-radius: 6px; + box-shadow: 0 8px 24px rgba(149, 157, 165, 0.2); + animation-name: SelectMenu-modal-animation--sm + } +} + +.SelectMenu-header { + display: flex; + padding: 16px; + flex: none; + align-items: center; + border-bottom: 1px solid #eaecef +} + +@media (min-width: 544px) { + .SelectMenu-header { + padding: 7px 7px 7px 16px + } +} + +.SelectMenu-title { + flex: 1; + font-size: 14px; + font-weight: 600 +} + +@media (min-width: 544px) { + .SelectMenu-title { + font-size: inherit + } +} + +.SelectMenu-closeButton { + padding: 16px; + margin: -16px; + line-height: 1; + color: #959da5; + background-color: transparent; + border: 0 +} + +@media (min-width: 544px) { + .SelectMenu-closeButton { + padding: 8px; + margin: -8px -7px + } +} + +.SelectMenu-filter { + padding: 16px; + margin: 0; + border-bottom: 1px solid #eaecef +} + +@media (min-width: 544px) { + .SelectMenu-filter { + padding: 8px + } +} + +.SelectMenu-input { + display: block; + width: 100% +} + +@media (min-width: 544px) { + .SelectMenu-input { + font-size: 14px + } +} + +.SelectMenu-list { + position: relative; + padding: 0; + margin: 0; + margin-bottom: -1px; + flex: auto; + overflow-x: hidden; + overflow-y: auto; + background-color: #fff; + -webkit-overflow-scrolling: touch +} + +.SelectMenu-item { + display: flex; + align-items: center; + width: 100%; + padding: 16px; + overflow: hidden; + color: #24292e; + text-align: left; + cursor: pointer; + background-color: #fff; + border: 0; + border-bottom: 1px solid #eaecef +} + +@media (min-width: 544px) { + .SelectMenu-item { + padding-top: 7px; + padding-bottom: 7px + } +} + +.SelectMenu-list--borderless .SelectMenu-item { + border-bottom: 0 +} + +.SelectMenu-icon { + width: 16px; + margin-right: 8px; + flex-shrink: 0 +} + +.SelectMenu-icon--check { + visibility: hidden; + transition: transform 0.12s cubic-bezier(0.5, 0.1, 1, 0.5), visibility 0s 0.12s linear; + transform: scale(0) +} + +.SelectMenu-tabs { + display: flex; + flex-shrink: 0; + overflow-x: auto; + overflow-y: hidden; + box-shadow: inset 0 -1px 0 #eaecef; + -webkit-overflow-scrolling: touch +} + +.SelectMenu-tabs::-webkit-scrollbar { + display: none +} + +@media (min-width: 544px) { + .SelectMenu-tabs { + padding: 8px 8px 0 8px + } +} + +.SelectMenu-tab { + flex: 1; + padding: 8px 16px; + font-size: 12px; + font-weight: 500; + color: #6a737d; + text-align: center; + background-color: transparent; + border: 0; + box-shadow: inset 0 -1px 0 #eaecef +} + +@media (min-width: 544px) { + .SelectMenu-tab { + flex: none; + padding: 4px 16px; + border: 1px solid transparent; + border-bottom-width: 0; + border-top-left-radius: 6px; + border-top-right-radius: 6px + } +} + +.SelectMenu-tab[aria-selected="true"] { + z-index: 1; + color: #24292e; + cursor: default; + background-color: #fff; + box-shadow: 0 0 0 1px #eaecef +} + +@media (min-width: 544px) { + .SelectMenu-tab[aria-selected="true"] { + border-color: #eaecef; + box-shadow: none + } +} + +.SelectMenu-message { + padding: 7px 16px; + text-align: center; + background-color: #fff; + border-bottom: 1px solid #eaecef +} + +.SelectMenu-blankslate, .SelectMenu-loading { + padding: 24px 16px; + text-align: center; + background-color: #fff +} + +.SelectMenu-divider { + padding: 4px 16px; + margin: 0; + font-size: 12px; + font-weight: 500; + color: #6a737d; + background-color: #f6f8fa; + border-bottom: 1px solid #eaecef +} + +.SelectMenu-list--borderless .SelectMenu-divider { + border-top: 1px solid #eaecef +} + +.SelectMenu-list--borderless .SelectMenu-divider:empty { + padding: 0; + border-top: 0 +} + +.SelectMenu-footer { + z-index: 0; + padding: 8px 16px; + font-size: 12px; + color: #6a737d; + text-align: center; + border-top: 1px solid #eaecef +} + +@media (min-width: 544px) { + .SelectMenu-footer { + padding: 7px 16px + } +} + +.SelectMenu--hasFilter .SelectMenu-modal { + height: 80%; + max-height: none; + margin-top: 0 +} + +@media (min-width: 544px) { + .SelectMenu--hasFilter .SelectMenu-modal { + height: auto; + max-height: 480px; + margin-top: 8px + } +} + +.SelectMenu-closeButton:focus, .SelectMenu-tab:focus, .SelectMenu-item:focus { + outline: 0 +} + +.SelectMenu-item:hover { + text-decoration: none +} + +.SelectMenu-item[aria-checked=true] { + font-weight: 500; + color: #24292e +} + +.SelectMenu-item[aria-checked=true] .SelectMenu-icon--check { + visibility: visible; + transition: transform 0.12s cubic-bezier(0, 0, 0.2, 1), visibility 0s linear; + transform: scale(1) +} + +.SelectMenu-item:disabled, .SelectMenu-item[aria-disabled=true] { + color: #6a737d; + pointer-events: none +} + +@media (hover: hover) { + body:not(.intent-mouse) .SelectMenu-closeButton:focus, .SelectMenu-closeButton:hover { + color: #24292e + } + .SelectMenu-closeButton:active { + color: #586069 + } + body:not(.intent-mouse) .SelectMenu-item:focus, .SelectMenu-item:hover { + background-color: #f6f8fa + } + .SelectMenu-item:active { + background-color: #fafbfc + } + body:not(.intent-mouse) .SelectMenu-tab:focus { + background-color: #dbedff + } + .SelectMenu-tab:hover { + color: #24292e + } + .SelectMenu-tab:not([aria-selected="true"]):active { + color: #24292e; + background-color: #f6f8fa + } +} + +@media (hover: none) { + .SelectMenu-item:focus, .SelectMenu-item:active { + background-color: #fafbfc + } + .SelectMenu-item { + -webkit-tap-highlight-color: rgba(209, 213, 218, 0.5) + } +} + +.Subhead { + display: flex; + padding-bottom: 8px; + margin-bottom: 16px; + border-bottom: 1px #e1e4e8 solid; + flex-flow: row wrap +} + +.Subhead--spacious { + margin-top: 40px +} + +.Subhead-heading { + font-size: 24px; + font-weight: 400; + flex: 1 1 auto +} + +.Subhead-heading--danger { + font-weight: 600; + color: #cb2431 +} + +.Subhead-description { + font-size: 14px; + color: #586069; + flex: 1 100% +} + +.Subhead-actions { + align-self: center; + justify-content: flex-end +} + +.TimelineItem { + position: relative; + display: flex; + padding: 16px 0; + margin-left: 16px +} + +.TimelineItem::before { + position: absolute; + top: 0; + bottom: 0; + left: 0; + display: block; + width: 2px; + content: ""; + background-color: #e1e4e8 +} + +.TimelineItem:target .TimelineItem-badge { + border-color: #2188ff; + box-shadow: 0 0 0.2em #c8e1ff +} + +.TimelineItem-badge { + position: relative; + z-index: 1; + display: flex; + width: 32px; + height: 32px; + margin-right: 8px; + margin-left: -15px; + color: #444d56; + align-items: center; + background-color: #e1e4e8; + border: 2px solid #fff; + border-radius: 50%; + justify-content: center; + flex-shrink: 0 +} + +.TimelineItem-body { + min-width: 0; + max-width: 100%; + margin-top: 4px; + color: #444d56; + flex: auto +} + +.TimelineItem-avatar { + position: absolute; + left: -72px; + z-index: 1 +} + +.TimelineItem-break { + position: relative; + z-index: 1; + height: 24px; + margin: 0; + margin-bottom: -16px; + margin-left: -56px; + background-color: #fff; + border: 0; + border-top: 4px solid #e1e4e8 +} + +.TimelineItem--condensed { + padding-top: 4px; + padding-bottom: 0 +} + +.TimelineItem--condensed:last-child { + padding-bottom: 16px +} + +.TimelineItem--condensed .TimelineItem-badge { + height: 16px; + margin-top: 8px; + margin-bottom: 8px; + color: #959da5; + background-color: #fff; + border: 0 +} + +.Toast { + display: flex; + margin: 8px; + color: #1b1f23; + background-color: #fff; + border-radius: 6px; + box-shadow: inset 0 0 0 1px #e1e4e8, 0 8px 24px rgba(149, 157, 165, 0.2) +} + +@media (min-width: 544px) { + .Toast { + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + max-width: 450px; + margin: 16px + } +} + +.Toast-icon { + display: flex; + align-items: center; + justify-content: center; + width: 48px; + flex-shrink: 0; + color: #fff; + background-color: #0366d6; + border-top-left-radius: inherit; + border-bottom-left-radius: inherit +} + +.Toast-content { + padding: 16px +} + +.Toast-dismissButton { + max-height: 54px; + padding: 16px; + background-color: transparent; + border: 0 +} + +.Toast-dismissButton:focus, .Toast-dismissButton:hover { + color: #586069; + outline: none +} + +.Toast-dismissButton:active { + color: #959da5 +} + +.Toast--error .Toast-icon { + background-color: #d73a49 +} + +.Toast--warning .Toast-icon { + color: #24292e; + background-color: #f9c513 +} + +.Toast--success .Toast-icon { + background-color: #28a745 +} + +.Toast--loading .Toast-icon { + background-color: #586069 +} + +.Toast--animateIn { + animation: Toast--animateIn 0.18s cubic-bezier(0.22, 0.61, 0.36, 1) backwards +} + +@keyframes Toast--animateIn { + 0% { + opacity: 0; + transform: translateY(100%) + } +} + +.Toast--animateOut { + animation: Toast--animateOut 0.18s cubic-bezier(0.55, 0.06, 0.68, 0.19) forwards +} + +@keyframes Toast--animateOut { + 100% { + pointer-events: none; + opacity: 0; + transform: translateY(100%) + } +} + +.Toast--spinner { + animation: Toast--spinner 1000ms linear infinite +} + +@keyframes Toast--spinner { + from { + transform: rotate(0deg) + } + to { + transform: rotate(360deg) + } +} + +/*! + * @primer/css/marketing + * http://primer.style/css + * + * Released under MIT license. Copyright (c) 2019 GitHub Inc. + */ + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400; + src: local("Inter"), local("Inter-Regular"), url("/fonts/Inter-Regular.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 500; + src: local("Inter Medium"), local("Inter-Medium"), url("/fonts/Inter-Medium.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 600; + src: local("Inter Bold"), local("Inter-Bold"), url("/fonts/Inter-Bold.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400; + src: local("Inter"), local("Inter-Regular"), url("/fonts/Inter-Regular.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 500; + src: local("Inter Medium"), local("Inter-Medium"), url("/fonts/Inter-Medium.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 600; + src: local("Inter Bold"), local("Inter-Bold"), url("/fonts/Inter-Bold.woff") format("woff"); + font-display: swap +} + +.h000-mktg, .h00-mktg, .h0-mktg, .h1-mktg, .h2-mktg, .h3-mktg, .h4-mktg, .h5-mktg, .h6-mktg, .lead-mktg { + font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-weight: 500 +} + +.h000-mktg { + font-size: 48px !important +} + +@media (min-width: 768px) { + .h000-mktg { + font-size: 64px !important + } +} + +.h00-mktg { + font-size: 40px !important +} + +@media (min-width: 768px) { + .h00-mktg { + font-size: 48px !important + } +} + +.h0-mktg { + font-size: 32px !important +} + +@media (min-width: 768px) { + .h0-mktg { + font-size: 40px !important + } +} + +.h1-mktg { + font-size: 26px !important +} + +@media (min-width: 768px) { + .h1-mktg { + font-size: 32px !important + } +} + +.h2-mktg { + font-size: 22px !important +} + +@media (min-width: 768px) { + .h2-mktg { + font-size: 24px !important + } +} + +.h3-mktg { + font-size: 18px !important +} + +@media (min-width: 768px) { + .h3-mktg { + font-size: 20px !important + } +} + +.h4-mktg { + font-size: 16px !important +} + +.h5-mktg { + font-size: 14px !important +} + +.h6-mktg { + font-size: 12px !important +} + +.lead-mktg { + font-size: 20px; + font-weight: 400 +} + +.pullquote { + padding-top: 0; + padding-bottom: 0; + padding-left: 8px; + margin-bottom: 24px; + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + font-size: 16px; + line-height: 1.4; + color: #586069; + border-left: 3px solid #e1e4e8 +} + +@media (min-width: 768px) { + .pullquote { + padding-left: 12px; + margin-bottom: 32px; + margin-left: -15px; + font-size: 18px; + line-height: 1.5 + } +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400; + src: local("Inter"), local("Inter-Regular"), url("/fonts/Inter-Regular.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 500; + src: local("Inter Medium"), local("Inter-Medium"), url("/fonts/Inter-Medium.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 600; + src: local("Inter Bold"), local("Inter-Bold"), url("/fonts/Inter-Bold.woff") format("woff"); + font-display: swap +} + +.btn-mktg { + display: inline-block; + padding: 16px 24px; + font-size: 14px; + font-weight: 500; + color: #fff; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: #1074e7; + border: 1px solid #1074e7; + border-radius: 6px; + transition: .2s; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.btn-mktg:hover { + text-decoration: none; + background-color: #0366d6; + border-color: #0366d6 +} + +.btn-mktg:focus { + outline: 0; + box-shadow: 0 0 0 0.2em rgba(3, 102, 214, 0.3) +} + +.btn-mktg:disabled, .btn-mktg.disabled, .btn-mktg[aria-disabled=true] { + pointer-events: none; + cursor: default; + opacity: 0.65 +} + +.btn-primary-mktg { + background-color: #2ebc4f; + border-color: #2ebc4f +} + +.btn-primary-mktg:hover { + background-color: #28a745; + border-color: #28a745 +} + +.btn-primary-mktg:focus { + box-shadow: 0 0 0 0.2em rgba(40, 167, 69, 0.3) +} + +.btn-large-mktg { + padding: 20px 32px; + font-size: 16px +} + +.btn-outline-mktg { + color: #1074e7; + background-color: rgba(255, 255, 255, 0); + border-color: rgba(16, 116, 231, 0.5) +} + +.btn-outline-mktg:hover { + color: #0366d6; + text-decoration: none; + background-color: rgba(255, 255, 255, 0); + border-color: #1074e7 +} + +.btn-transparent { + color: #fff; + background-color: transparent; + background-image: none; + border: 1px solid rgba(255, 255, 255, 0.5) +} + +.btn-transparent:hover, .btn-transparent:active { + color: #2f363d; + background-color: #fff; + background-image: none; + border-color: #fff +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 400; + src: local("Inter"), local("Inter-Regular"), url("/fonts/Inter-Regular.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 500; + src: local("Inter Medium"), local("Inter-Medium"), url("/fonts/Inter-Medium.woff") format("woff"); + font-display: swap +} + +@font-face { + font-family: Inter; + font-style: normal; + font-weight: 600; + src: local("Inter Bold"), local("Inter-Bold"), url("/fonts/Inter-Bold.woff") format("woff"); + font-display: swap +} + +.grayscale { + filter: grayscale(100%) +} + +.top-0 { + top: 0 !important +} + +.right-0 { + right: 0 !important +} + +.bottom-0 { + bottom: 0 !important +} + +.left-0 { + left: 0 !important +} + +.top-n0 { + top: 0 !important +} + +.right-n0 { + right: 0 !important +} + +.bottom-n0 { + bottom: 0 !important +} + +.left-n0 { + left: 0 !important +} + +.top-1 { + top: 4px !important +} + +.right-1 { + right: 4px !important +} + +.bottom-1 { + bottom: 4px !important +} + +.left-1 { + left: 4px !important +} + +.top-n1 { + top: -4px !important +} + +.right-n1 { + right: -4px !important +} + +.bottom-n1 { + bottom: -4px !important +} + +.left-n1 { + left: -4px !important +} + +.top-2 { + top: 8px !important +} + +.right-2 { + right: 8px !important +} + +.bottom-2 { + bottom: 8px !important +} + +.left-2 { + left: 8px !important +} + +.top-n2 { + top: -8px !important +} + +.right-n2 { + right: -8px !important +} + +.bottom-n2 { + bottom: -8px !important +} + +.left-n2 { + left: -8px !important +} + +.top-3 { + top: 16px !important +} + +.right-3 { + right: 16px !important +} + +.bottom-3 { + bottom: 16px !important +} + +.left-3 { + left: 16px !important +} + +.top-n3 { + top: -16px !important +} + +.right-n3 { + right: -16px !important +} + +.bottom-n3 { + bottom: -16px !important +} + +.left-n3 { + left: -16px !important +} + +.top-4 { + top: 24px !important +} + +.right-4 { + right: 24px !important +} + +.bottom-4 { + bottom: 24px !important +} + +.left-4 { + left: 24px !important +} + +.top-n4 { + top: -24px !important +} + +.right-n4 { + right: -24px !important +} + +.bottom-n4 { + bottom: -24px !important +} + +.left-n4 { + left: -24px !important +} + +.top-5 { + top: 32px !important +} + +.right-5 { + right: 32px !important +} + +.bottom-5 { + bottom: 32px !important +} + +.left-5 { + left: 32px !important +} + +.top-n5 { + top: -32px !important +} + +.right-n5 { + right: -32px !important +} + +.bottom-n5 { + bottom: -32px !important +} + +.left-n5 { + left: -32px !important +} + +.top-6 { + top: 40px !important +} + +.right-6 { + right: 40px !important +} + +.bottom-6 { + bottom: 40px !important +} + +.left-6 { + left: 40px !important +} + +.top-n6 { + top: -40px !important +} + +.right-n6 { + right: -40px !important +} + +.bottom-n6 { + bottom: -40px !important +} + +.left-n6 { + left: -40px !important +} + +.top-7 { + top: 48px !important +} + +.right-7 { + right: 48px !important +} + +.bottom-7 { + bottom: 48px !important +} + +.left-7 { + left: 48px !important +} + +.top-n7 { + top: -48px !important +} + +.right-n7 { + right: -48px !important +} + +.bottom-n7 { + bottom: -48px !important +} + +.left-n7 { + left: -48px !important +} + +.top-8 { + top: 64px !important +} + +.right-8 { + right: 64px !important +} + +.bottom-8 { + bottom: 64px !important +} + +.left-8 { + left: 64px !important +} + +.top-n8 { + top: -64px !important +} + +.right-n8 { + right: -64px !important +} + +.bottom-n8 { + bottom: -64px !important +} + +.left-n8 { + left: -64px !important +} + +.top-9 { + top: 80px !important +} + +.right-9 { + right: 80px !important +} + +.bottom-9 { + bottom: 80px !important +} + +.left-9 { + left: 80px !important +} + +.top-n9 { + top: -80px !important +} + +.right-n9 { + right: -80px !important +} + +.bottom-n9 { + bottom: -80px !important +} + +.left-n9 { + left: -80px !important +} + +.top-10 { + top: 96px !important +} + +.right-10 { + right: 96px !important +} + +.bottom-10 { + bottom: 96px !important +} + +.left-10 { + left: 96px !important +} + +.top-n10 { + top: -96px !important +} + +.right-n10 { + right: -96px !important +} + +.bottom-n10 { + bottom: -96px !important +} + +.left-n10 { + left: -96px !important +} + +.top-11 { + top: 112px !important +} + +.right-11 { + right: 112px !important +} + +.bottom-11 { + bottom: 112px !important +} + +.left-11 { + left: 112px !important +} + +.top-n11 { + top: -112px !important +} + +.right-n11 { + right: -112px !important +} + +.bottom-n11 { + bottom: -112px !important +} + +.left-n11 { + left: -112px !important +} + +.top-12 { + top: 128px !important +} + +.right-12 { + right: 128px !important +} + +.bottom-12 { + bottom: 128px !important +} + +.left-12 { + left: 128px !important +} + +.top-n12 { + top: -128px !important +} + +.right-n12 { + right: -128px !important +} + +.bottom-n12 { + bottom: -128px !important +} + +.left-n12 { + left: -128px !important +} + +@media (min-width: 768px) { + .top-md-0 { + top: 0 !important + } + .right-md-0 { + right: 0 !important + } + .bottom-md-0 { + bottom: 0 !important + } + .left-md-0 { + left: 0 !important + } + .top-md-n0 { + top: 0 !important + } + .right-md-n0 { + right: 0 !important + } + .bottom-md-n0 { + bottom: 0 !important + } + .left-md-n0 { + left: 0 !important + } + .top-md-1 { + top: 4px !important + } + .right-md-1 { + right: 4px !important + } + .bottom-md-1 { + bottom: 4px !important + } + .left-md-1 { + left: 4px !important + } + .top-md-n1 { + top: -4px !important + } + .right-md-n1 { + right: -4px !important + } + .bottom-md-n1 { + bottom: -4px !important + } + .left-md-n1 { + left: -4px !important + } + .top-md-2 { + top: 8px !important + } + .right-md-2 { + right: 8px !important + } + .bottom-md-2 { + bottom: 8px !important + } + .left-md-2 { + left: 8px !important + } + .top-md-n2 { + top: -8px !important + } + .right-md-n2 { + right: -8px !important + } + .bottom-md-n2 { + bottom: -8px !important + } + .left-md-n2 { + left: -8px !important + } + .top-md-3 { + top: 16px !important + } + .right-md-3 { + right: 16px !important + } + .bottom-md-3 { + bottom: 16px !important + } + .left-md-3 { + left: 16px !important + } + .top-md-n3 { + top: -16px !important + } + .right-md-n3 { + right: -16px !important + } + .bottom-md-n3 { + bottom: -16px !important + } + .left-md-n3 { + left: -16px !important + } + .top-md-4 { + top: 24px !important + } + .right-md-4 { + right: 24px !important + } + .bottom-md-4 { + bottom: 24px !important + } + .left-md-4 { + left: 24px !important + } + .top-md-n4 { + top: -24px !important + } + .right-md-n4 { + right: -24px !important + } + .bottom-md-n4 { + bottom: -24px !important + } + .left-md-n4 { + left: -24px !important + } + .top-md-5 { + top: 32px !important + } + .right-md-5 { + right: 32px !important + } + .bottom-md-5 { + bottom: 32px !important + } + .left-md-5 { + left: 32px !important + } + .top-md-n5 { + top: -32px !important + } + .right-md-n5 { + right: -32px !important + } + .bottom-md-n5 { + bottom: -32px !important + } + .left-md-n5 { + left: -32px !important + } + .top-md-6 { + top: 40px !important + } + .right-md-6 { + right: 40px !important + } + .bottom-md-6 { + bottom: 40px !important + } + .left-md-6 { + left: 40px !important + } + .top-md-n6 { + top: -40px !important + } + .right-md-n6 { + right: -40px !important + } + .bottom-md-n6 { + bottom: -40px !important + } + .left-md-n6 { + left: -40px !important + } + .top-md-7 { + top: 48px !important + } + .right-md-7 { + right: 48px !important + } + .bottom-md-7 { + bottom: 48px !important + } + .left-md-7 { + left: 48px !important + } + .top-md-n7 { + top: -48px !important + } + .right-md-n7 { + right: -48px !important + } + .bottom-md-n7 { + bottom: -48px !important + } + .left-md-n7 { + left: -48px !important + } + .top-md-8 { + top: 64px !important + } + .right-md-8 { + right: 64px !important + } + .bottom-md-8 { + bottom: 64px !important + } + .left-md-8 { + left: 64px !important + } + .top-md-n8 { + top: -64px !important + } + .right-md-n8 { + right: -64px !important + } + .bottom-md-n8 { + bottom: -64px !important + } + .left-md-n8 { + left: -64px !important + } + .top-md-9 { + top: 80px !important + } + .right-md-9 { + right: 80px !important + } + .bottom-md-9 { + bottom: 80px !important + } + .left-md-9 { + left: 80px !important + } + .top-md-n9 { + top: -80px !important + } + .right-md-n9 { + right: -80px !important + } + .bottom-md-n9 { + bottom: -80px !important + } + .left-md-n9 { + left: -80px !important + } + .top-md-10 { + top: 96px !important + } + .right-md-10 { + right: 96px !important + } + .bottom-md-10 { + bottom: 96px !important + } + .left-md-10 { + left: 96px !important + } + .top-md-n10 { + top: -96px !important + } + .right-md-n10 { + right: -96px !important + } + .bottom-md-n10 { + bottom: -96px !important + } + .left-md-n10 { + left: -96px !important + } + .top-md-11 { + top: 112px !important + } + .right-md-11 { + right: 112px !important + } + .bottom-md-11 { + bottom: 112px !important + } + .left-md-11 { + left: 112px !important + } + .top-md-n11 { + top: -112px !important + } + .right-md-n11 { + right: -112px !important + } + .bottom-md-n11 { + bottom: -112px !important + } + .left-md-n11 { + left: -112px !important + } + .top-md-12 { + top: 128px !important + } + .right-md-12 { + right: 128px !important + } + .bottom-md-12 { + bottom: 128px !important + } + .left-md-12 { + left: 128px !important + } + .top-md-n12 { + top: -128px !important + } + .right-md-n12 { + right: -128px !important + } + .bottom-md-n12 { + bottom: -128px !important + } + .left-md-n12 { + left: -128px !important + } +} + +@media (min-width: 1012px) { + .top-lg-0 { + top: 0 !important + } + .right-lg-0 { + right: 0 !important + } + .bottom-lg-0 { + bottom: 0 !important + } + .left-lg-0 { + left: 0 !important + } + .top-lg-n0 { + top: 0 !important + } + .right-lg-n0 { + right: 0 !important + } + .bottom-lg-n0 { + bottom: 0 !important + } + .left-lg-n0 { + left: 0 !important + } + .top-lg-1 { + top: 4px !important + } + .right-lg-1 { + right: 4px !important + } + .bottom-lg-1 { + bottom: 4px !important + } + .left-lg-1 { + left: 4px !important + } + .top-lg-n1 { + top: -4px !important + } + .right-lg-n1 { + right: -4px !important + } + .bottom-lg-n1 { + bottom: -4px !important + } + .left-lg-n1 { + left: -4px !important + } + .top-lg-2 { + top: 8px !important + } + .right-lg-2 { + right: 8px !important + } + .bottom-lg-2 { + bottom: 8px !important + } + .left-lg-2 { + left: 8px !important + } + .top-lg-n2 { + top: -8px !important + } + .right-lg-n2 { + right: -8px !important + } + .bottom-lg-n2 { + bottom: -8px !important + } + .left-lg-n2 { + left: -8px !important + } + .top-lg-3 { + top: 16px !important + } + .right-lg-3 { + right: 16px !important + } + .bottom-lg-3 { + bottom: 16px !important + } + .left-lg-3 { + left: 16px !important + } + .top-lg-n3 { + top: -16px !important + } + .right-lg-n3 { + right: -16px !important + } + .bottom-lg-n3 { + bottom: -16px !important + } + .left-lg-n3 { + left: -16px !important + } + .top-lg-4 { + top: 24px !important + } + .right-lg-4 { + right: 24px !important + } + .bottom-lg-4 { + bottom: 24px !important + } + .left-lg-4 { + left: 24px !important + } + .top-lg-n4 { + top: -24px !important + } + .right-lg-n4 { + right: -24px !important + } + .bottom-lg-n4 { + bottom: -24px !important + } + .left-lg-n4 { + left: -24px !important + } + .top-lg-5 { + top: 32px !important + } + .right-lg-5 { + right: 32px !important + } + .bottom-lg-5 { + bottom: 32px !important + } + .left-lg-5 { + left: 32px !important + } + .top-lg-n5 { + top: -32px !important + } + .right-lg-n5 { + right: -32px !important + } + .bottom-lg-n5 { + bottom: -32px !important + } + .left-lg-n5 { + left: -32px !important + } + .top-lg-6 { + top: 40px !important + } + .right-lg-6 { + right: 40px !important + } + .bottom-lg-6 { + bottom: 40px !important + } + .left-lg-6 { + left: 40px !important + } + .top-lg-n6 { + top: -40px !important + } + .right-lg-n6 { + right: -40px !important + } + .bottom-lg-n6 { + bottom: -40px !important + } + .left-lg-n6 { + left: -40px !important + } + .top-lg-7 { + top: 48px !important + } + .right-lg-7 { + right: 48px !important + } + .bottom-lg-7 { + bottom: 48px !important + } + .left-lg-7 { + left: 48px !important + } + .top-lg-n7 { + top: -48px !important + } + .right-lg-n7 { + right: -48px !important + } + .bottom-lg-n7 { + bottom: -48px !important + } + .left-lg-n7 { + left: -48px !important + } + .top-lg-8 { + top: 64px !important + } + .right-lg-8 { + right: 64px !important + } + .bottom-lg-8 { + bottom: 64px !important + } + .left-lg-8 { + left: 64px !important + } + .top-lg-n8 { + top: -64px !important + } + .right-lg-n8 { + right: -64px !important + } + .bottom-lg-n8 { + bottom: -64px !important + } + .left-lg-n8 { + left: -64px !important + } + .top-lg-9 { + top: 80px !important + } + .right-lg-9 { + right: 80px !important + } + .bottom-lg-9 { + bottom: 80px !important + } + .left-lg-9 { + left: 80px !important + } + .top-lg-n9 { + top: -80px !important + } + .right-lg-n9 { + right: -80px !important + } + .bottom-lg-n9 { + bottom: -80px !important + } + .left-lg-n9 { + left: -80px !important + } + .top-lg-10 { + top: 96px !important + } + .right-lg-10 { + right: 96px !important + } + .bottom-lg-10 { + bottom: 96px !important + } + .left-lg-10 { + left: 96px !important + } + .top-lg-n10 { + top: -96px !important + } + .right-lg-n10 { + right: -96px !important + } + .bottom-lg-n10 { + bottom: -96px !important + } + .left-lg-n10 { + left: -96px !important + } + .top-lg-11 { + top: 112px !important + } + .right-lg-11 { + right: 112px !important + } + .bottom-lg-11 { + bottom: 112px !important + } + .left-lg-11 { + left: 112px !important + } + .top-lg-n11 { + top: -112px !important + } + .right-lg-n11 { + right: -112px !important + } + .bottom-lg-n11 { + bottom: -112px !important + } + .left-lg-n11 { + left: -112px !important + } + .top-lg-12 { + top: 128px !important + } + .right-lg-12 { + right: 128px !important + } + .bottom-lg-12 { + bottom: 128px !important + } + .left-lg-12 { + left: 128px !important + } + .top-lg-n12 { + top: -128px !important + } + .right-lg-n12 { + right: -128px !important + } + .bottom-lg-n12 { + bottom: -128px !important + } + .left-lg-n12 { + left: -128px !important + } +} + +.offset-n1 { + margin-left: -8.33333% +} + +.offset-n2 { + margin-left: -16.66667% +} + +.offset-n3 { + margin-left: -25% +} + +.offset-n4 { + margin-left: -33.33333% +} + +.offset-n5 { + margin-left: -41.66667% +} + +.offset-n6 { + margin-left: -50% +} + +.offset-n7 { + margin-left: -58.33333% +} + +@media (min-width: 544px) { + .offset-sm-n1 { + margin-left: -8.33333% + } + .offset-sm-n2 { + margin-left: -16.66667% + } + .offset-sm-n3 { + margin-left: -25% + } + .offset-sm-n4 { + margin-left: -33.33333% + } + .offset-sm-n5 { + margin-left: -41.66667% + } + .offset-sm-n6 { + margin-left: -50% + } + .offset-sm-n7 { + margin-left: -58.33333% + } +} + +@media (min-width: 768px) { + .offset-md-n1 { + margin-left: -8.33333% + } + .offset-md-n2 { + margin-left: -16.66667% + } + .offset-md-n3 { + margin-left: -25% + } + .offset-md-n4 { + margin-left: -33.33333% + } + .offset-md-n5 { + margin-left: -41.66667% + } + .offset-md-n6 { + margin-left: -50% + } + .offset-md-n7 { + margin-left: -58.33333% + } +} + +@media (min-width: 1012px) { + .offset-lg-n1 { + margin-left: -8.33333% + } + .offset-lg-n2 { + margin-left: -16.66667% + } + .offset-lg-n3 { + margin-left: -25% + } + .offset-lg-n4 { + margin-left: -33.33333% + } + .offset-lg-n5 { + margin-left: -41.66667% + } + .offset-lg-n6 { + margin-left: -50% + } + .offset-lg-n7 { + margin-left: -58.33333% + } +} + +@media (min-width: 1280px) { + .offset-xl-n1 { + margin-left: -8.33333% + } + .offset-xl-n2 { + margin-left: -16.66667% + } + .offset-xl-n3 { + margin-left: -25% + } + .offset-xl-n4 { + margin-left: -33.33333% + } + .offset-xl-n5 { + margin-left: -41.66667% + } + .offset-xl-n6 { + margin-left: -50% + } + .offset-xl-n7 { + margin-left: -58.33333% + } +} + +.mt-0 { + margin-top: 0 !important +} + +.mb-0 { + margin-bottom: 0 !important +} + +.my-0 { + margin-top: 0 !important; + margin-bottom: 0 !important +} + +.mt-1 { + margin-top: 4px !important +} + +.mb-1 { + margin-bottom: 4px !important +} + +.my-1 { + margin-top: 4px !important; + margin-bottom: 4px !important +} + +.mt-2 { + margin-top: 8px !important +} + +.mb-2 { + margin-bottom: 8px !important +} + +.my-2 { + margin-top: 8px !important; + margin-bottom: 8px !important +} + +.mt-3 { + margin-top: 16px !important +} + +.mb-3 { + margin-bottom: 16px !important +} + +.my-3 { + margin-top: 16px !important; + margin-bottom: 16px !important +} + +.mt-4 { + margin-top: 24px !important +} + +.mb-4 { + margin-bottom: 24px !important +} + +.my-4 { + margin-top: 24px !important; + margin-bottom: 24px !important +} + +.mt-5 { + margin-top: 32px !important +} + +.mb-5 { + margin-bottom: 32px !important +} + +.my-5 { + margin-top: 32px !important; + margin-bottom: 32px !important +} + +.mt-6 { + margin-top: 40px !important +} + +.mb-6 { + margin-bottom: 40px !important +} + +.my-6 { + margin-top: 40px !important; + margin-bottom: 40px !important +} + +.mt-7 { + margin-top: 48px !important +} + +.mb-7 { + margin-bottom: 48px !important +} + +.my-7 { + margin-top: 48px !important; + margin-bottom: 48px !important +} + +.mt-8 { + margin-top: 64px !important +} + +.mb-8 { + margin-bottom: 64px !important +} + +.my-8 { + margin-top: 64px !important; + margin-bottom: 64px !important +} + +.mt-9 { + margin-top: 80px !important +} + +.mb-9 { + margin-bottom: 80px !important +} + +.my-9 { + margin-top: 80px !important; + margin-bottom: 80px !important +} + +.mt-10 { + margin-top: 96px !important +} + +.mb-10 { + margin-bottom: 96px !important +} + +.my-10 { + margin-top: 96px !important; + margin-bottom: 96px !important +} + +.mt-11 { + margin-top: 112px !important +} + +.mb-11 { + margin-bottom: 112px !important +} + +.my-11 { + margin-top: 112px !important; + margin-bottom: 112px !important +} + +.mt-12 { + margin-top: 128px !important +} + +.mb-12 { + margin-bottom: 128px !important +} + +.my-12 { + margin-top: 128px !important; + margin-bottom: 128px !important +} + +@media (min-width: 544px) { + .mt-sm-0 { + margin-top: 0 !important + } + .mb-sm-0 { + margin-bottom: 0 !important + } + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important + } + .mt-sm-1 { + margin-top: 4px !important + } + .mb-sm-1 { + margin-bottom: 4px !important + } + .my-sm-1 { + margin-top: 4px !important; + margin-bottom: 4px !important + } + .mt-sm-2 { + margin-top: 8px !important + } + .mb-sm-2 { + margin-bottom: 8px !important + } + .my-sm-2 { + margin-top: 8px !important; + margin-bottom: 8px !important + } + .mt-sm-3 { + margin-top: 16px !important + } + .mb-sm-3 { + margin-bottom: 16px !important + } + .my-sm-3 { + margin-top: 16px !important; + margin-bottom: 16px !important + } + .mt-sm-4 { + margin-top: 24px !important + } + .mb-sm-4 { + margin-bottom: 24px !important + } + .my-sm-4 { + margin-top: 24px !important; + margin-bottom: 24px !important + } + .mt-sm-5 { + margin-top: 32px !important + } + .mb-sm-5 { + margin-bottom: 32px !important + } + .my-sm-5 { + margin-top: 32px !important; + margin-bottom: 32px !important + } + .mt-sm-6 { + margin-top: 40px !important + } + .mb-sm-6 { + margin-bottom: 40px !important + } + .my-sm-6 { + margin-top: 40px !important; + margin-bottom: 40px !important + } + .mt-sm-7 { + margin-top: 48px !important + } + .mb-sm-7 { + margin-bottom: 48px !important + } + .my-sm-7 { + margin-top: 48px !important; + margin-bottom: 48px !important + } + .mt-sm-8 { + margin-top: 64px !important + } + .mb-sm-8 { + margin-bottom: 64px !important + } + .my-sm-8 { + margin-top: 64px !important; + margin-bottom: 64px !important + } + .mt-sm-9 { + margin-top: 80px !important + } + .mb-sm-9 { + margin-bottom: 80px !important + } + .my-sm-9 { + margin-top: 80px !important; + margin-bottom: 80px !important + } + .mt-sm-10 { + margin-top: 96px !important + } + .mb-sm-10 { + margin-bottom: 96px !important + } + .my-sm-10 { + margin-top: 96px !important; + margin-bottom: 96px !important + } + .mt-sm-11 { + margin-top: 112px !important + } + .mb-sm-11 { + margin-bottom: 112px !important + } + .my-sm-11 { + margin-top: 112px !important; + margin-bottom: 112px !important + } + .mt-sm-12 { + margin-top: 128px !important + } + .mb-sm-12 { + margin-bottom: 128px !important + } + .my-sm-12 { + margin-top: 128px !important; + margin-bottom: 128px !important + } +} + +@media (min-width: 768px) { + .mt-md-0 { + margin-top: 0 !important + } + .mb-md-0 { + margin-bottom: 0 !important + } + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important + } + .mt-md-1 { + margin-top: 4px !important + } + .mb-md-1 { + margin-bottom: 4px !important + } + .my-md-1 { + margin-top: 4px !important; + margin-bottom: 4px !important + } + .mt-md-2 { + margin-top: 8px !important + } + .mb-md-2 { + margin-bottom: 8px !important + } + .my-md-2 { + margin-top: 8px !important; + margin-bottom: 8px !important + } + .mt-md-3 { + margin-top: 16px !important + } + .mb-md-3 { + margin-bottom: 16px !important + } + .my-md-3 { + margin-top: 16px !important; + margin-bottom: 16px !important + } + .mt-md-4 { + margin-top: 24px !important + } + .mb-md-4 { + margin-bottom: 24px !important + } + .my-md-4 { + margin-top: 24px !important; + margin-bottom: 24px !important + } + .mt-md-5 { + margin-top: 32px !important + } + .mb-md-5 { + margin-bottom: 32px !important + } + .my-md-5 { + margin-top: 32px !important; + margin-bottom: 32px !important + } + .mt-md-6 { + margin-top: 40px !important + } + .mb-md-6 { + margin-bottom: 40px !important + } + .my-md-6 { + margin-top: 40px !important; + margin-bottom: 40px !important + } + .mt-md-7 { + margin-top: 48px !important + } + .mb-md-7 { + margin-bottom: 48px !important + } + .my-md-7 { + margin-top: 48px !important; + margin-bottom: 48px !important + } + .mt-md-8 { + margin-top: 64px !important + } + .mb-md-8 { + margin-bottom: 64px !important + } + .my-md-8 { + margin-top: 64px !important; + margin-bottom: 64px !important + } + .mt-md-9 { + margin-top: 80px !important + } + .mb-md-9 { + margin-bottom: 80px !important + } + .my-md-9 { + margin-top: 80px !important; + margin-bottom: 80px !important + } + .mt-md-10 { + margin-top: 96px !important + } + .mb-md-10 { + margin-bottom: 96px !important + } + .my-md-10 { + margin-top: 96px !important; + margin-bottom: 96px !important + } + .mt-md-11 { + margin-top: 112px !important + } + .mb-md-11 { + margin-bottom: 112px !important + } + .my-md-11 { + margin-top: 112px !important; + margin-bottom: 112px !important + } + .mt-md-12 { + margin-top: 128px !important + } + .mb-md-12 { + margin-bottom: 128px !important + } + .my-md-12 { + margin-top: 128px !important; + margin-bottom: 128px !important + } +} + +@media (min-width: 1012px) { + .mt-lg-0 { + margin-top: 0 !important + } + .mb-lg-0 { + margin-bottom: 0 !important + } + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important + } + .mt-lg-1 { + margin-top: 4px !important + } + .mb-lg-1 { + margin-bottom: 4px !important + } + .my-lg-1 { + margin-top: 4px !important; + margin-bottom: 4px !important + } + .mt-lg-2 { + margin-top: 8px !important + } + .mb-lg-2 { + margin-bottom: 8px !important + } + .my-lg-2 { + margin-top: 8px !important; + margin-bottom: 8px !important + } + .mt-lg-3 { + margin-top: 16px !important + } + .mb-lg-3 { + margin-bottom: 16px !important + } + .my-lg-3 { + margin-top: 16px !important; + margin-bottom: 16px !important + } + .mt-lg-4 { + margin-top: 24px !important + } + .mb-lg-4 { + margin-bottom: 24px !important + } + .my-lg-4 { + margin-top: 24px !important; + margin-bottom: 24px !important + } + .mt-lg-5 { + margin-top: 32px !important + } + .mb-lg-5 { + margin-bottom: 32px !important + } + .my-lg-5 { + margin-top: 32px !important; + margin-bottom: 32px !important + } + .mt-lg-6 { + margin-top: 40px !important + } + .mb-lg-6 { + margin-bottom: 40px !important + } + .my-lg-6 { + margin-top: 40px !important; + margin-bottom: 40px !important + } + .mt-lg-7 { + margin-top: 48px !important + } + .mb-lg-7 { + margin-bottom: 48px !important + } + .my-lg-7 { + margin-top: 48px !important; + margin-bottom: 48px !important + } + .mt-lg-8 { + margin-top: 64px !important + } + .mb-lg-8 { + margin-bottom: 64px !important + } + .my-lg-8 { + margin-top: 64px !important; + margin-bottom: 64px !important + } + .mt-lg-9 { + margin-top: 80px !important + } + .mb-lg-9 { + margin-bottom: 80px !important + } + .my-lg-9 { + margin-top: 80px !important; + margin-bottom: 80px !important + } + .mt-lg-10 { + margin-top: 96px !important + } + .mb-lg-10 { + margin-bottom: 96px !important + } + .my-lg-10 { + margin-top: 96px !important; + margin-bottom: 96px !important + } + .mt-lg-11 { + margin-top: 112px !important + } + .mb-lg-11 { + margin-bottom: 112px !important + } + .my-lg-11 { + margin-top: 112px !important; + margin-bottom: 112px !important + } + .mt-lg-12 { + margin-top: 128px !important + } + .mb-lg-12 { + margin-bottom: 128px !important + } + .my-lg-12 { + margin-top: 128px !important; + margin-bottom: 128px !important + } +} + +@media (min-width: 1280px) { + .mt-xl-0 { + margin-top: 0 !important + } + .mb-xl-0 { + margin-bottom: 0 !important + } + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important + } + .mt-xl-1 { + margin-top: 4px !important + } + .mb-xl-1 { + margin-bottom: 4px !important + } + .my-xl-1 { + margin-top: 4px !important; + margin-bottom: 4px !important + } + .mt-xl-2 { + margin-top: 8px !important + } + .mb-xl-2 { + margin-bottom: 8px !important + } + .my-xl-2 { + margin-top: 8px !important; + margin-bottom: 8px !important + } + .mt-xl-3 { + margin-top: 16px !important + } + .mb-xl-3 { + margin-bottom: 16px !important + } + .my-xl-3 { + margin-top: 16px !important; + margin-bottom: 16px !important + } + .mt-xl-4 { + margin-top: 24px !important + } + .mb-xl-4 { + margin-bottom: 24px !important + } + .my-xl-4 { + margin-top: 24px !important; + margin-bottom: 24px !important + } + .mt-xl-5 { + margin-top: 32px !important + } + .mb-xl-5 { + margin-bottom: 32px !important + } + .my-xl-5 { + margin-top: 32px !important; + margin-bottom: 32px !important + } + .mt-xl-6 { + margin-top: 40px !important + } + .mb-xl-6 { + margin-bottom: 40px !important + } + .my-xl-6 { + margin-top: 40px !important; + margin-bottom: 40px !important + } + .mt-xl-7 { + margin-top: 48px !important + } + .mb-xl-7 { + margin-bottom: 48px !important + } + .my-xl-7 { + margin-top: 48px !important; + margin-bottom: 48px !important + } + .mt-xl-8 { + margin-top: 64px !important + } + .mb-xl-8 { + margin-bottom: 64px !important + } + .my-xl-8 { + margin-top: 64px !important; + margin-bottom: 64px !important + } + .mt-xl-9 { + margin-top: 80px !important + } + .mb-xl-9 { + margin-bottom: 80px !important + } + .my-xl-9 { + margin-top: 80px !important; + margin-bottom: 80px !important + } + .mt-xl-10 { + margin-top: 96px !important + } + .mb-xl-10 { + margin-bottom: 96px !important + } + .my-xl-10 { + margin-top: 96px !important; + margin-bottom: 96px !important + } + .mt-xl-11 { + margin-top: 112px !important + } + .mb-xl-11 { + margin-bottom: 112px !important + } + .my-xl-11 { + margin-top: 112px !important; + margin-bottom: 112px !important + } + .mt-xl-12 { + margin-top: 128px !important + } + .mb-xl-12 { + margin-bottom: 128px !important + } + .my-xl-12 { + margin-top: 128px !important; + margin-bottom: 128px !important + } +} + +.p-0 { + padding: 0 !important +} + +.pt-0 { + padding-top: 0 !important +} + +.pr-0 { + padding-right: 0 !important +} + +.pb-0 { + padding-bottom: 0 !important +} + +.pl-0 { + padding-left: 0 !important +} + +.py-0 { + padding-top: 0 !important; + padding-bottom: 0 !important +} + +.p-1 { + padding: 4px !important +} + +.pt-1 { + padding-top: 4px !important +} + +.pr-1 { + padding-right: 4px !important +} + +.pb-1 { + padding-bottom: 4px !important +} + +.pl-1 { + padding-left: 4px !important +} + +.py-1 { + padding-top: 4px !important; + padding-bottom: 4px !important +} + +.p-2 { + padding: 8px !important +} + +.pt-2 { + padding-top: 8px !important +} + +.pr-2 { + padding-right: 8px !important +} + +.pb-2 { + padding-bottom: 8px !important +} + +.pl-2 { + padding-left: 8px !important +} + +.py-2 { + padding-top: 8px !important; + padding-bottom: 8px !important +} + +.p-3 { + padding: 16px !important +} + +.pt-3 { + padding-top: 16px !important +} + +.pr-3 { + padding-right: 16px !important +} + +.pb-3 { + padding-bottom: 16px !important +} + +.pl-3 { + padding-left: 16px !important +} + +.py-3 { + padding-top: 16px !important; + padding-bottom: 16px !important +} + +.p-4 { + padding: 24px !important +} + +.pt-4 { + padding-top: 24px !important +} + +.pr-4 { + padding-right: 24px !important +} + +.pb-4 { + padding-bottom: 24px !important +} + +.pl-4 { + padding-left: 24px !important +} + +.py-4 { + padding-top: 24px !important; + padding-bottom: 24px !important +} + +.p-5 { + padding: 32px !important +} + +.pt-5 { + padding-top: 32px !important +} + +.pr-5 { + padding-right: 32px !important +} + +.pb-5 { + padding-bottom: 32px !important +} + +.pl-5 { + padding-left: 32px !important +} + +.py-5 { + padding-top: 32px !important; + padding-bottom: 32px !important +} + +.p-6 { + padding: 40px !important +} + +.pt-6 { + padding-top: 40px !important +} + +.pr-6 { + padding-right: 40px !important +} + +.pb-6 { + padding-bottom: 40px !important +} + +.pl-6 { + padding-left: 40px !important +} + +.py-6 { + padding-top: 40px !important; + padding-bottom: 40px !important +} + +.p-7 { + padding: 48px !important +} + +.pt-7 { + padding-top: 48px !important +} + +.pr-7 { + padding-right: 48px !important +} + +.pb-7 { + padding-bottom: 48px !important +} + +.pl-7 { + padding-left: 48px !important +} + +.py-7 { + padding-top: 48px !important; + padding-bottom: 48px !important +} + +.p-8 { + padding: 64px !important +} + +.pt-8 { + padding-top: 64px !important +} + +.pr-8 { + padding-right: 64px !important +} + +.pb-8 { + padding-bottom: 64px !important +} + +.pl-8 { + padding-left: 64px !important +} + +.py-8 { + padding-top: 64px !important; + padding-bottom: 64px !important +} + +.p-9 { + padding: 80px !important +} + +.pt-9 { + padding-top: 80px !important +} + +.pr-9 { + padding-right: 80px !important +} + +.pb-9 { + padding-bottom: 80px !important +} + +.pl-9 { + padding-left: 80px !important +} + +.py-9 { + padding-top: 80px !important; + padding-bottom: 80px !important +} + +.p-10 { + padding: 96px !important +} + +.pt-10 { + padding-top: 96px !important +} + +.pr-10 { + padding-right: 96px !important +} + +.pb-10 { + padding-bottom: 96px !important +} + +.pl-10 { + padding-left: 96px !important +} + +.py-10 { + padding-top: 96px !important; + padding-bottom: 96px !important +} + +.p-11 { + padding: 112px !important +} + +.pt-11 { + padding-top: 112px !important +} + +.pr-11 { + padding-right: 112px !important +} + +.pb-11 { + padding-bottom: 112px !important +} + +.pl-11 { + padding-left: 112px !important +} + +.py-11 { + padding-top: 112px !important; + padding-bottom: 112px !important +} + +.p-12 { + padding: 128px !important +} + +.pt-12 { + padding-top: 128px !important +} + +.pr-12 { + padding-right: 128px !important +} + +.pb-12 { + padding-bottom: 128px !important +} + +.pl-12 { + padding-left: 128px !important +} + +.py-12 { + padding-top: 128px !important; + padding-bottom: 128px !important +} + +@media (min-width: 544px) { + .p-sm-0 { + padding: 0 !important + } + .pt-sm-0 { + padding-top: 0 !important + } + .pr-sm-0 { + padding-right: 0 !important + } + .pb-sm-0 { + padding-bottom: 0 !important + } + .pl-sm-0 { + padding-left: 0 !important + } + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important + } + .p-sm-1 { + padding: 4px !important + } + .pt-sm-1 { + padding-top: 4px !important + } + .pr-sm-1 { + padding-right: 4px !important + } + .pb-sm-1 { + padding-bottom: 4px !important + } + .pl-sm-1 { + padding-left: 4px !important + } + .py-sm-1 { + padding-top: 4px !important; + padding-bottom: 4px !important + } + .p-sm-2 { + padding: 8px !important + } + .pt-sm-2 { + padding-top: 8px !important + } + .pr-sm-2 { + padding-right: 8px !important + } + .pb-sm-2 { + padding-bottom: 8px !important + } + .pl-sm-2 { + padding-left: 8px !important + } + .py-sm-2 { + padding-top: 8px !important; + padding-bottom: 8px !important + } + .p-sm-3 { + padding: 16px !important + } + .pt-sm-3 { + padding-top: 16px !important + } + .pr-sm-3 { + padding-right: 16px !important + } + .pb-sm-3 { + padding-bottom: 16px !important + } + .pl-sm-3 { + padding-left: 16px !important + } + .py-sm-3 { + padding-top: 16px !important; + padding-bottom: 16px !important + } + .p-sm-4 { + padding: 24px !important + } + .pt-sm-4 { + padding-top: 24px !important + } + .pr-sm-4 { + padding-right: 24px !important + } + .pb-sm-4 { + padding-bottom: 24px !important + } + .pl-sm-4 { + padding-left: 24px !important + } + .py-sm-4 { + padding-top: 24px !important; + padding-bottom: 24px !important + } + .p-sm-5 { + padding: 32px !important + } + .pt-sm-5 { + padding-top: 32px !important + } + .pr-sm-5 { + padding-right: 32px !important + } + .pb-sm-5 { + padding-bottom: 32px !important + } + .pl-sm-5 { + padding-left: 32px !important + } + .py-sm-5 { + padding-top: 32px !important; + padding-bottom: 32px !important + } + .p-sm-6 { + padding: 40px !important + } + .pt-sm-6 { + padding-top: 40px !important + } + .pr-sm-6 { + padding-right: 40px !important + } + .pb-sm-6 { + padding-bottom: 40px !important + } + .pl-sm-6 { + padding-left: 40px !important + } + .py-sm-6 { + padding-top: 40px !important; + padding-bottom: 40px !important + } + .p-sm-7 { + padding: 48px !important + } + .pt-sm-7 { + padding-top: 48px !important + } + .pr-sm-7 { + padding-right: 48px !important + } + .pb-sm-7 { + padding-bottom: 48px !important + } + .pl-sm-7 { + padding-left: 48px !important + } + .py-sm-7 { + padding-top: 48px !important; + padding-bottom: 48px !important + } + .p-sm-8 { + padding: 64px !important + } + .pt-sm-8 { + padding-top: 64px !important + } + .pr-sm-8 { + padding-right: 64px !important + } + .pb-sm-8 { + padding-bottom: 64px !important + } + .pl-sm-8 { + padding-left: 64px !important + } + .py-sm-8 { + padding-top: 64px !important; + padding-bottom: 64px !important + } + .p-sm-9 { + padding: 80px !important + } + .pt-sm-9 { + padding-top: 80px !important + } + .pr-sm-9 { + padding-right: 80px !important + } + .pb-sm-9 { + padding-bottom: 80px !important + } + .pl-sm-9 { + padding-left: 80px !important + } + .py-sm-9 { + padding-top: 80px !important; + padding-bottom: 80px !important + } + .p-sm-10 { + padding: 96px !important + } + .pt-sm-10 { + padding-top: 96px !important + } + .pr-sm-10 { + padding-right: 96px !important + } + .pb-sm-10 { + padding-bottom: 96px !important + } + .pl-sm-10 { + padding-left: 96px !important + } + .py-sm-10 { + padding-top: 96px !important; + padding-bottom: 96px !important + } + .p-sm-11 { + padding: 112px !important + } + .pt-sm-11 { + padding-top: 112px !important + } + .pr-sm-11 { + padding-right: 112px !important + } + .pb-sm-11 { + padding-bottom: 112px !important + } + .pl-sm-11 { + padding-left: 112px !important + } + .py-sm-11 { + padding-top: 112px !important; + padding-bottom: 112px !important + } + .p-sm-12 { + padding: 128px !important + } + .pt-sm-12 { + padding-top: 128px !important + } + .pr-sm-12 { + padding-right: 128px !important + } + .pb-sm-12 { + padding-bottom: 128px !important + } + .pl-sm-12 { + padding-left: 128px !important + } + .py-sm-12 { + padding-top: 128px !important; + padding-bottom: 128px !important + } +} + +@media (min-width: 768px) { + .p-md-0 { + padding: 0 !important + } + .pt-md-0 { + padding-top: 0 !important + } + .pr-md-0 { + padding-right: 0 !important + } + .pb-md-0 { + padding-bottom: 0 !important + } + .pl-md-0 { + padding-left: 0 !important + } + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important + } + .p-md-1 { + padding: 4px !important + } + .pt-md-1 { + padding-top: 4px !important + } + .pr-md-1 { + padding-right: 4px !important + } + .pb-md-1 { + padding-bottom: 4px !important + } + .pl-md-1 { + padding-left: 4px !important + } + .py-md-1 { + padding-top: 4px !important; + padding-bottom: 4px !important + } + .p-md-2 { + padding: 8px !important + } + .pt-md-2 { + padding-top: 8px !important + } + .pr-md-2 { + padding-right: 8px !important + } + .pb-md-2 { + padding-bottom: 8px !important + } + .pl-md-2 { + padding-left: 8px !important + } + .py-md-2 { + padding-top: 8px !important; + padding-bottom: 8px !important + } + .p-md-3 { + padding: 16px !important + } + .pt-md-3 { + padding-top: 16px !important + } + .pr-md-3 { + padding-right: 16px !important + } + .pb-md-3 { + padding-bottom: 16px !important + } + .pl-md-3 { + padding-left: 16px !important + } + .py-md-3 { + padding-top: 16px !important; + padding-bottom: 16px !important + } + .p-md-4 { + padding: 24px !important + } + .pt-md-4 { + padding-top: 24px !important + } + .pr-md-4 { + padding-right: 24px !important + } + .pb-md-4 { + padding-bottom: 24px !important + } + .pl-md-4 { + padding-left: 24px !important + } + .py-md-4 { + padding-top: 24px !important; + padding-bottom: 24px !important + } + .p-md-5 { + padding: 32px !important + } + .pt-md-5 { + padding-top: 32px !important + } + .pr-md-5 { + padding-right: 32px !important + } + .pb-md-5 { + padding-bottom: 32px !important + } + .pl-md-5 { + padding-left: 32px !important + } + .py-md-5 { + padding-top: 32px !important; + padding-bottom: 32px !important + } + .p-md-6 { + padding: 40px !important + } + .pt-md-6 { + padding-top: 40px !important + } + .pr-md-6 { + padding-right: 40px !important + } + .pb-md-6 { + padding-bottom: 40px !important + } + .pl-md-6 { + padding-left: 40px !important + } + .py-md-6 { + padding-top: 40px !important; + padding-bottom: 40px !important + } + .p-md-7 { + padding: 48px !important + } + .pt-md-7 { + padding-top: 48px !important + } + .pr-md-7 { + padding-right: 48px !important + } + .pb-md-7 { + padding-bottom: 48px !important + } + .pl-md-7 { + padding-left: 48px !important + } + .py-md-7 { + padding-top: 48px !important; + padding-bottom: 48px !important + } + .p-md-8 { + padding: 64px !important + } + .pt-md-8 { + padding-top: 64px !important + } + .pr-md-8 { + padding-right: 64px !important + } + .pb-md-8 { + padding-bottom: 64px !important + } + .pl-md-8 { + padding-left: 64px !important + } + .py-md-8 { + padding-top: 64px !important; + padding-bottom: 64px !important + } + .p-md-9 { + padding: 80px !important + } + .pt-md-9 { + padding-top: 80px !important + } + .pr-md-9 { + padding-right: 80px !important + } + .pb-md-9 { + padding-bottom: 80px !important + } + .pl-md-9 { + padding-left: 80px !important + } + .py-md-9 { + padding-top: 80px !important; + padding-bottom: 80px !important + } + .p-md-10 { + padding: 96px !important + } + .pt-md-10 { + padding-top: 96px !important + } + .pr-md-10 { + padding-right: 96px !important + } + .pb-md-10 { + padding-bottom: 96px !important + } + .pl-md-10 { + padding-left: 96px !important + } + .py-md-10 { + padding-top: 96px !important; + padding-bottom: 96px !important + } + .p-md-11 { + padding: 112px !important + } + .pt-md-11 { + padding-top: 112px !important + } + .pr-md-11 { + padding-right: 112px !important + } + .pb-md-11 { + padding-bottom: 112px !important + } + .pl-md-11 { + padding-left: 112px !important + } + .py-md-11 { + padding-top: 112px !important; + padding-bottom: 112px !important + } + .p-md-12 { + padding: 128px !important + } + .pt-md-12 { + padding-top: 128px !important + } + .pr-md-12 { + padding-right: 128px !important + } + .pb-md-12 { + padding-bottom: 128px !important + } + .pl-md-12 { + padding-left: 128px !important + } + .py-md-12 { + padding-top: 128px !important; + padding-bottom: 128px !important + } +} + +@media (min-width: 1012px) { + .p-lg-0 { + padding: 0 !important + } + .pt-lg-0 { + padding-top: 0 !important + } + .pr-lg-0 { + padding-right: 0 !important + } + .pb-lg-0 { + padding-bottom: 0 !important + } + .pl-lg-0 { + padding-left: 0 !important + } + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important + } + .p-lg-1 { + padding: 4px !important + } + .pt-lg-1 { + padding-top: 4px !important + } + .pr-lg-1 { + padding-right: 4px !important + } + .pb-lg-1 { + padding-bottom: 4px !important + } + .pl-lg-1 { + padding-left: 4px !important + } + .py-lg-1 { + padding-top: 4px !important; + padding-bottom: 4px !important + } + .p-lg-2 { + padding: 8px !important + } + .pt-lg-2 { + padding-top: 8px !important + } + .pr-lg-2 { + padding-right: 8px !important + } + .pb-lg-2 { + padding-bottom: 8px !important + } + .pl-lg-2 { + padding-left: 8px !important + } + .py-lg-2 { + padding-top: 8px !important; + padding-bottom: 8px !important + } + .p-lg-3 { + padding: 16px !important + } + .pt-lg-3 { + padding-top: 16px !important + } + .pr-lg-3 { + padding-right: 16px !important + } + .pb-lg-3 { + padding-bottom: 16px !important + } + .pl-lg-3 { + padding-left: 16px !important + } + .py-lg-3 { + padding-top: 16px !important; + padding-bottom: 16px !important + } + .p-lg-4 { + padding: 24px !important + } + .pt-lg-4 { + padding-top: 24px !important + } + .pr-lg-4 { + padding-right: 24px !important + } + .pb-lg-4 { + padding-bottom: 24px !important + } + .pl-lg-4 { + padding-left: 24px !important + } + .py-lg-4 { + padding-top: 24px !important; + padding-bottom: 24px !important + } + .p-lg-5 { + padding: 32px !important + } + .pt-lg-5 { + padding-top: 32px !important + } + .pr-lg-5 { + padding-right: 32px !important + } + .pb-lg-5 { + padding-bottom: 32px !important + } + .pl-lg-5 { + padding-left: 32px !important + } + .py-lg-5 { + padding-top: 32px !important; + padding-bottom: 32px !important + } + .p-lg-6 { + padding: 40px !important + } + .pt-lg-6 { + padding-top: 40px !important + } + .pr-lg-6 { + padding-right: 40px !important + } + .pb-lg-6 { + padding-bottom: 40px !important + } + .pl-lg-6 { + padding-left: 40px !important + } + .py-lg-6 { + padding-top: 40px !important; + padding-bottom: 40px !important + } + .p-lg-7 { + padding: 48px !important + } + .pt-lg-7 { + padding-top: 48px !important + } + .pr-lg-7 { + padding-right: 48px !important + } + .pb-lg-7 { + padding-bottom: 48px !important + } + .pl-lg-7 { + padding-left: 48px !important + } + .py-lg-7 { + padding-top: 48px !important; + padding-bottom: 48px !important + } + .p-lg-8 { + padding: 64px !important + } + .pt-lg-8 { + padding-top: 64px !important + } + .pr-lg-8 { + padding-right: 64px !important + } + .pb-lg-8 { + padding-bottom: 64px !important + } + .pl-lg-8 { + padding-left: 64px !important + } + .py-lg-8 { + padding-top: 64px !important; + padding-bottom: 64px !important + } + .p-lg-9 { + padding: 80px !important + } + .pt-lg-9 { + padding-top: 80px !important + } + .pr-lg-9 { + padding-right: 80px !important + } + .pb-lg-9 { + padding-bottom: 80px !important + } + .pl-lg-9 { + padding-left: 80px !important + } + .py-lg-9 { + padding-top: 80px !important; + padding-bottom: 80px !important + } + .p-lg-10 { + padding: 96px !important + } + .pt-lg-10 { + padding-top: 96px !important + } + .pr-lg-10 { + padding-right: 96px !important + } + .pb-lg-10 { + padding-bottom: 96px !important + } + .pl-lg-10 { + padding-left: 96px !important + } + .py-lg-10 { + padding-top: 96px !important; + padding-bottom: 96px !important + } + .p-lg-11 { + padding: 112px !important + } + .pt-lg-11 { + padding-top: 112px !important + } + .pr-lg-11 { + padding-right: 112px !important + } + .pb-lg-11 { + padding-bottom: 112px !important + } + .pl-lg-11 { + padding-left: 112px !important + } + .py-lg-11 { + padding-top: 112px !important; + padding-bottom: 112px !important + } + .p-lg-12 { + padding: 128px !important + } + .pt-lg-12 { + padding-top: 128px !important + } + .pr-lg-12 { + padding-right: 128px !important + } + .pb-lg-12 { + padding-bottom: 128px !important + } + .pl-lg-12 { + padding-left: 128px !important + } + .py-lg-12 { + padding-top: 128px !important; + padding-bottom: 128px !important + } +} + +@media (min-width: 1280px) { + .p-xl-0 { + padding: 0 !important + } + .pt-xl-0 { + padding-top: 0 !important + } + .pr-xl-0 { + padding-right: 0 !important + } + .pb-xl-0 { + padding-bottom: 0 !important + } + .pl-xl-0 { + padding-left: 0 !important + } + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important + } + .p-xl-1 { + padding: 4px !important + } + .pt-xl-1 { + padding-top: 4px !important + } + .pr-xl-1 { + padding-right: 4px !important + } + .pb-xl-1 { + padding-bottom: 4px !important + } + .pl-xl-1 { + padding-left: 4px !important + } + .py-xl-1 { + padding-top: 4px !important; + padding-bottom: 4px !important + } + .p-xl-2 { + padding: 8px !important + } + .pt-xl-2 { + padding-top: 8px !important + } + .pr-xl-2 { + padding-right: 8px !important + } + .pb-xl-2 { + padding-bottom: 8px !important + } + .pl-xl-2 { + padding-left: 8px !important + } + .py-xl-2 { + padding-top: 8px !important; + padding-bottom: 8px !important + } + .p-xl-3 { + padding: 16px !important + } + .pt-xl-3 { + padding-top: 16px !important + } + .pr-xl-3 { + padding-right: 16px !important + } + .pb-xl-3 { + padding-bottom: 16px !important + } + .pl-xl-3 { + padding-left: 16px !important + } + .py-xl-3 { + padding-top: 16px !important; + padding-bottom: 16px !important + } + .p-xl-4 { + padding: 24px !important + } + .pt-xl-4 { + padding-top: 24px !important + } + .pr-xl-4 { + padding-right: 24px !important + } + .pb-xl-4 { + padding-bottom: 24px !important + } + .pl-xl-4 { + padding-left: 24px !important + } + .py-xl-4 { + padding-top: 24px !important; + padding-bottom: 24px !important + } + .p-xl-5 { + padding: 32px !important + } + .pt-xl-5 { + padding-top: 32px !important + } + .pr-xl-5 { + padding-right: 32px !important + } + .pb-xl-5 { + padding-bottom: 32px !important + } + .pl-xl-5 { + padding-left: 32px !important + } + .py-xl-5 { + padding-top: 32px !important; + padding-bottom: 32px !important + } + .p-xl-6 { + padding: 40px !important + } + .pt-xl-6 { + padding-top: 40px !important + } + .pr-xl-6 { + padding-right: 40px !important + } + .pb-xl-6 { + padding-bottom: 40px !important + } + .pl-xl-6 { + padding-left: 40px !important + } + .py-xl-6 { + padding-top: 40px !important; + padding-bottom: 40px !important + } + .p-xl-7 { + padding: 48px !important + } + .pt-xl-7 { + padding-top: 48px !important + } + .pr-xl-7 { + padding-right: 48px !important + } + .pb-xl-7 { + padding-bottom: 48px !important + } + .pl-xl-7 { + padding-left: 48px !important + } + .py-xl-7 { + padding-top: 48px !important; + padding-bottom: 48px !important + } + .p-xl-8 { + padding: 64px !important + } + .pt-xl-8 { + padding-top: 64px !important + } + .pr-xl-8 { + padding-right: 64px !important + } + .pb-xl-8 { + padding-bottom: 64px !important + } + .pl-xl-8 { + padding-left: 64px !important + } + .py-xl-8 { + padding-top: 64px !important; + padding-bottom: 64px !important + } + .p-xl-9 { + padding: 80px !important + } + .pt-xl-9 { + padding-top: 80px !important + } + .pr-xl-9 { + padding-right: 80px !important + } + .pb-xl-9 { + padding-bottom: 80px !important + } + .pl-xl-9 { + padding-left: 80px !important + } + .py-xl-9 { + padding-top: 80px !important; + padding-bottom: 80px !important + } + .p-xl-10 { + padding: 96px !important + } + .pt-xl-10 { + padding-top: 96px !important + } + .pr-xl-10 { + padding-right: 96px !important + } + .pb-xl-10 { + padding-bottom: 96px !important + } + .pl-xl-10 { + padding-left: 96px !important + } + .py-xl-10 { + padding-top: 96px !important; + padding-bottom: 96px !important + } + .p-xl-11 { + padding: 112px !important + } + .pt-xl-11 { + padding-top: 112px !important + } + .pr-xl-11 { + padding-right: 112px !important + } + .pb-xl-11 { + padding-bottom: 112px !important + } + .pl-xl-11 { + padding-left: 112px !important + } + .py-xl-11 { + padding-top: 112px !important; + padding-bottom: 112px !important + } + .p-xl-12 { + padding: 128px !important + } + .pt-xl-12 { + padding-top: 128px !important + } + .pr-xl-12 { + padding-right: 128px !important + } + .pb-xl-12 { + padding-bottom: 128px !important + } + .pl-xl-12 { + padding-left: 128px !important + } + .py-xl-12 { + padding-top: 128px !important; + padding-bottom: 128px !important + } +} + +/*# sourceMappingURL=primer.css.map */ \ No newline at end of file diff --git a/docs/language/global-sphinx-files/_templates/layout.html b/docs/language/global-sphinx-files/_templates/layout.html index 2c9efa4fe095..d064b4fe6b8d 100644 --- a/docs/language/global-sphinx-files/_templates/layout.html +++ b/docs/language/global-sphinx-files/_templates/layout.html @@ -1,10 +1,9 @@ {# - Override alabaster/layout.html template to add a header - with the Semmle logo. - This header (including the SVG logo) is copied from the Semmle - documentation home page at help.semmle.com. - - It also adds some JavaScript (in the footer) to allow collapsible sections. + Override alabaster/layout.html template to customize the template + used to generate the CodeQL documentation. + + The classes used in this template are provided by the GitHub Primer https://primer.style/css/. + The CSS for the primer can be found at https://unpkg.com/@primer/css/dist/primer.css The source for the default Alabaster stylesheet can be found at: https://github.com/bitprophet/alabaster/blob/master/alabaster/layout.html @@ -12,116 +11,179 @@ {%- extends "alabaster/layout.html" %} +{%- macro customrelbar() %} + +{%- endmacro %} + {%- block extrahead %} - - - - {{ super() }} - - {% if theme_touch_icon %} - - {% endif %} - {% if theme_canonical_url %} - - {% endif %} - + +CodeQL docs + + + + {% endblock %} {%- block content %} - - - -
    -
    - -